difftreelog
refactor abi module
in: master
17 files changed
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -328,14 +328,14 @@
}
}
-trait AbiType {
+trait AbiTypeHelper {
fn plain(&self) -> syn::Result<&Ident>;
fn is_value(&self) -> bool;
fn is_caller(&self) -> bool;
fn is_special(&self) -> bool;
}
-impl AbiType for Type {
+impl AbiTypeHelper for Type {
fn plain(&self) -> syn::Result<&Ident> {
let path = parse_path(self)?;
let segment = parse_path_segment(path)?;
crates/evm-coder/src/abi.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi.rs
+++ /dev/null
@@ -1,968 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implementation of EVM RLP reader/writer
-
-#![allow(dead_code)]
-
-#[cfg(not(feature = "std"))]
-use alloc::vec::Vec;
-use evm_core::ExitError;
-use primitive_types::{H160, U256};
-
-use crate::{
- execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},
- types::*,
- make_signature,
- custom_signature::{SignatureUnit},
-};
-use crate::execution::Result;
-
-const ABI_ALIGNMENT: usize = 32;
-
-trait TypeHelper {
- /// Is type dynamic sized.
- fn is_dynamic() -> bool;
-
- /// Size for type aligned to [`ABI_ALIGNMENT`].
- fn size() -> usize;
-}
-
-/// View into RLP data, which provides method to read typed items from it
-#[derive(Clone)]
-pub struct AbiReader<'i> {
- buf: &'i [u8],
- subresult_offset: usize,
- offset: usize,
-}
-impl<'i> AbiReader<'i> {
- /// Start reading RLP buffer, assuming there is no padding bytes
- pub fn new(buf: &'i [u8]) -> Self {
- Self {
- buf,
- subresult_offset: 0,
- offset: 0,
- }
- }
- /// Start reading RLP buffer, parsing first 4 bytes as selector
- pub fn new_call(buf: &'i [u8]) -> Result<(bytes4, Self)> {
- if buf.len() < 4 {
- return Err(Error::Error(ExitError::OutOfOffset));
- }
- let mut method_id = [0; 4];
- method_id.copy_from_slice(&buf[0..4]);
-
- Ok((
- method_id,
- Self {
- buf,
- subresult_offset: 4,
- offset: 4,
- },
- ))
- }
-
- 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];
- let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);
- if !is_pad_zeroed {
- return Err(Error::Error(ExitError::InvalidRange));
- }
- block.copy_from_slice(&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;
- 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,
- )
- }
-
- /// Read [`H160`] at current position, then advance
- pub fn address(&mut self) -> Result<H160> {
- Ok(H160(self.read_padleft()?))
- }
-
- /// Read [`bool`] at current position, then advance
- pub fn bool(&mut self) -> Result<bool> {
- let data: [u8; 1] = self.read_padleft()?;
- match data[0] {
- 0 => Ok(false),
- 1 => Ok(true),
- _ => Err(Error::Error(ExitError::InvalidRange)),
- }
- }
-
- /// Read [`[u8; 4]`] at current position, then advance
- pub fn bytes4(&mut self) -> Result<[u8; 4]> {
- self.read_padright()
- }
-
- /// Read [`Vec<u8>`] at current position, then advance
- pub fn bytes(&mut self) -> Result<Vec<u8>> {
- let mut subresult = self.subresult(None)?;
- let length = subresult.uint32()? as usize;
- if subresult.buf.len() < subresult.offset + length {
- return Err(Error::Error(ExitError::OutOfOffset));
- }
- Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
- }
-
- /// Read [`string`] at current position, then advance
- pub fn string(&mut self) -> Result<string> {
- string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
- }
-
- /// Read [`u8`] at current position, then advance
- pub fn uint8(&mut self) -> Result<u8> {
- Ok(self.read_padleft::<1>()?[0])
- }
-
- /// Read [`u32`] at current position, then advance
- pub fn uint32(&mut self) -> Result<u32> {
- Ok(u32::from_be_bytes(self.read_padleft()?))
- }
-
- /// Read [`u128`] at current position, then advance
- pub fn uint128(&mut self) -> Result<u128> {
- Ok(u128::from_be_bytes(self.read_padleft()?))
- }
-
- /// Read [`U256`] at current position, then advance
- pub fn uint256(&mut self) -> Result<U256> {
- let buf: [u8; 32] = self.read_padleft()?;
- Ok(U256::from_big_endian(&buf))
- }
-
- /// Read [`u64`] at current position, then advance
- pub fn uint64(&mut self) -> Result<u64> {
- Ok(u64::from_be_bytes(self.read_padleft()?))
- }
-
- /// Read [`usize`] at current position, then advance
- #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
- pub fn read_usize(&mut self) -> Result<usize> {
- Ok(usize::from_be_bytes(self.read_padleft()?))
- }
-
- /// Slice recursive buffer, advance one word for buffer offset
- /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`].
- fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {
- let subresult_offset = self.subresult_offset;
- let offset = if let Some(size) = size {
- self.offset += size;
- self.subresult_offset += size;
- 0
- } else {
- self.uint32()? as usize
- };
-
- if offset + self.subresult_offset > self.buf.len() {
- return Err(Error::Error(ExitError::InvalidRange));
- }
-
- let new_offset = offset + subresult_offset;
- Ok(AbiReader {
- buf: self.buf,
- subresult_offset: new_offset,
- offset: new_offset,
- })
- }
-
- /// Is this parser reached end of buffer?
- pub fn is_finished(&self) -> bool {
- self.buf.len() == self.offset
- }
-}
-
-/// Writer for RLP encoded data
-#[derive(Default)]
-pub struct AbiWriter {
- static_part: Vec<u8>,
- dynamic_part: Vec<(usize, AbiWriter)>,
- had_call: bool,
- is_dynamic: bool,
-}
-impl AbiWriter {
- /// Initialize internal buffers for output data, assuming no padding required
- pub fn new() -> Self {
- Self::default()
- }
-
- /// Initialize internal buffers with data size
- pub fn new_dynamic(is_dynamic: bool) -> Self {
- Self {
- is_dynamic,
- ..Default::default()
- }
- }
- /// Initialize internal buffers, inserting method selector at beginning
- pub fn new_call(method_id: u32) -> Self {
- let mut val = Self::new();
- val.static_part.extend(&method_id.to_be_bytes());
- val.had_call = true;
- val
- }
-
- fn write_padleft(&mut self, block: &[u8]) {
- assert!(block.len() <= ABI_ALIGNMENT);
- self.static_part
- .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);
- self.static_part.extend(block);
- }
-
- fn write_padright(&mut self, block: &[u8]) {
- assert!(block.len() <= ABI_ALIGNMENT);
- self.static_part.extend(block);
- self.static_part
- .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);
- }
-
- /// Write [`H160`] to end of buffer
- pub fn address(&mut self, address: &H160) {
- self.write_padleft(&address.0)
- }
-
- /// Write [`bool`] to end of buffer
- pub fn bool(&mut self, value: &bool) {
- self.write_padleft(&[if *value { 1 } else { 0 }])
- }
-
- /// Write [`u8`] to end of buffer
- pub fn uint8(&mut self, value: &u8) {
- self.write_padleft(&[*value])
- }
-
- /// Write [`u32`] to end of buffer
- pub fn uint32(&mut self, value: &u32) {
- self.write_padleft(&u32::to_be_bytes(*value))
- }
-
- /// Write [`u128`] to end of buffer
- pub fn uint128(&mut self, value: &u128) {
- self.write_padleft(&u128::to_be_bytes(*value))
- }
-
- /// Write [`U256`] to end of buffer
- pub fn uint256(&mut self, value: &U256) {
- let mut out = [0; 32];
- value.to_big_endian(&mut out);
- self.write_padleft(&out)
- }
-
- /// Write [`usize`] to end of buffer
- #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
- pub fn write_usize(&mut self, value: &usize) {
- self.write_padleft(&usize::to_be_bytes(*value))
- }
-
- /// Append recursive data, writing pending offset at end of buffer
- pub fn write_subresult(&mut self, result: Self) {
- self.dynamic_part.push((self.static_part.len(), result));
- // Empty block, to be filled later
- self.write_padleft(&[]);
- }
-
- fn memory(&mut self, value: &[u8]) {
- let mut sub = Self::new();
- sub.uint32(&(value.len() as u32));
- for chunk in value.chunks(ABI_ALIGNMENT) {
- sub.write_padright(chunk);
- }
- self.write_subresult(sub);
- }
-
- /// Append recursive [`str`] at end of buffer
- pub fn string(&mut self, value: &str) {
- self.memory(value.as_bytes())
- }
-
- /// Append recursive [`[u8]`] at end of buffer
- pub fn bytes(&mut self, value: &[u8]) {
- self.memory(value)
- }
-
- /// Finish writer, concatenating all internal buffers
- pub fn finish(mut self) -> Vec<u8> {
- for (static_offset, part) in self.dynamic_part {
- let part_offset = self.static_part.len()
- - if self.had_call { 4 } else { 0 }
- - if self.is_dynamic { ABI_ALIGNMENT } else { 0 };
-
- let encoded_dynamic_offset = usize::to_be_bytes(part_offset);
- let start = static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len();
- let stop = static_offset + ABI_ALIGNMENT;
- self.static_part[start..stop].copy_from_slice(&encoded_dynamic_offset);
- self.static_part.extend(part.finish())
- }
- self.static_part
- }
-}
-
-/// [`AbiReader`] implements reading of many types.
-pub trait AbiRead {
- /// Read item from current position, advanding decoder
- fn abi_read(reader: &mut AbiReader) -> Result<Self>
- where
- Self: Sized;
-}
-
-macro_rules! impl_abi_readable {
- ($ty:ty, $method:ident, $dynamic:literal) => {
- impl sealed::CanBePlacedInVec for $ty {}
-
- impl TypeHelper for $ty {
- fn is_dynamic() -> bool {
- $dynamic
- }
-
- fn size() -> usize {
- ABI_ALIGNMENT
- }
- }
-
- impl AbiRead for $ty {
- fn abi_read(reader: &mut AbiReader) -> Result<$ty> {
- reader.$method()
- }
- }
- };
-}
-
-impl_abi_readable!(bool, bool, false);
-impl_abi_readable!(uint32, uint32, false);
-impl_abi_readable!(uint64, uint64, false);
-impl_abi_readable!(uint128, uint128, false);
-impl_abi_readable!(uint256, uint256, false);
-impl_abi_readable!(bytes4, bytes4, false);
-impl_abi_readable!(address, address, false);
-impl_abi_readable!(string, string, true);
-
-impl TypeHelper for uint8 {
- fn is_dynamic() -> bool {
- false
- }
- fn size() -> usize {
- ABI_ALIGNMENT
- }
-}
-impl AbiRead for uint8 {
- fn abi_read(reader: &mut AbiReader) -> Result<uint8> {
- reader.uint8()
- }
-}
-
-impl TypeHelper for bytes {
- fn is_dynamic() -> bool {
- true
- }
- fn size() -> usize {
- ABI_ALIGNMENT
- }
-}
-impl AbiRead for bytes {
- fn abi_read(reader: &mut AbiReader) -> Result<bytes> {
- Ok(bytes(reader.bytes()?))
- }
-}
-
-mod sealed {
- /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
- pub trait CanBePlacedInVec {}
-}
-
-impl<R: AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<R> {
- fn abi_read(reader: &mut AbiReader) -> Result<Vec<R>> {
- let mut sub = reader.subresult(None)?;
- let size = sub.uint32()? as usize;
- sub.subresult_offset = sub.offset;
- let mut out = Vec::with_capacity(size);
- for _ in 0..size {
- out.push(<R>::abi_read(&mut sub)?);
- }
- Ok(out)
- }
-}
-
-impl<R: Signature> Signature for Vec<R> {
- const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]"));
-}
-
-impl sealed::CanBePlacedInVec for EthCrossAccount {}
-
-impl TypeHelper for EthCrossAccount {
- fn is_dynamic() -> bool {
- address::is_dynamic() || uint256::is_dynamic()
- }
-
- fn size() -> usize {
- <address as TypeHelper>::size() + <uint256 as TypeHelper>::size()
- }
-}
-
-impl AbiRead for EthCrossAccount {
- fn abi_read(reader: &mut AbiReader) -> Result<EthCrossAccount> {
- let size = if !EthCrossAccount::is_dynamic() {
- Some(<EthCrossAccount as TypeHelper>::size())
- } else {
- None
- };
- let mut subresult = reader.subresult(size)?;
- let eth = <address>::abi_read(&mut subresult)?;
- let sub = <uint256>::abi_read(&mut subresult)?;
-
- Ok(EthCrossAccount { eth, sub })
- }
-}
-
-impl AbiWrite for EthCrossAccount {
- fn abi_write(&self, writer: &mut AbiWriter) {
- self.eth.abi_write(writer);
- self.sub.abi_write(writer);
- }
-}
-
-macro_rules! impl_tuples {
- ($($ident:ident)+) => {
- impl<$($ident: TypeHelper,)+> TypeHelper for ($($ident,)+)
- where
- $(
- $ident: TypeHelper,
- )+
- {
- fn is_dynamic() -> bool {
- false
- $(
- || <$ident>::is_dynamic()
- )*
- }
-
- fn size() -> usize {
- 0 $(+ <$ident>::size())+
- }
- }
-
- impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
-
- impl<$($ident),+> AbiRead for ($($ident,)+)
- where
- $($ident: AbiRead,)+
- ($($ident,)+): TypeHelper,
- {
- fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> {
- let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None };
- let mut subresult = reader.subresult(size)?;
- Ok((
- $(<$ident>::abi_read(&mut subresult)?,)+
- ))
- }
- }
-
- #[allow(non_snake_case)]
- impl<$($ident),+> AbiWrite for ($($ident,)+)
- where
- $($ident: AbiWrite,)+
- {
- fn abi_write(&self, writer: &mut AbiWriter) {
- let ($($ident,)+) = self;
- if writer.is_dynamic {
- let mut sub = AbiWriter::new();
- $($ident.abi_write(&mut sub);)+
- writer.write_subresult(sub);
- } else {
- $($ident.abi_write(writer);)+
- }
- }
- }
-
- impl<$($ident),+> Signature for ($($ident,)+)
- where
- $($ident: Signature,)+
- {
- const SIGNATURE: SignatureUnit = make_signature!(
- new fixed("(")
- $(nameof(<$ident>::SIGNATURE) fixed(","))+
- shift_left(1)
- fixed(")")
- );
- }
- };
-}
-
-impl_tuples! {A}
-impl_tuples! {A B}
-impl_tuples! {A B C}
-impl_tuples! {A B C D}
-impl_tuples! {A B C D E}
-impl_tuples! {A B C D E F}
-impl_tuples! {A B C D E F G}
-impl_tuples! {A B C D E F G H}
-impl_tuples! {A B C D E F G H I}
-impl_tuples! {A B C D E F G H I J}
-
-/// For questions about inability to provide custom implementations,
-/// see [`AbiRead`]
-pub trait AbiWrite {
- /// Write value to end of specified encoder
- fn abi_write(&self, writer: &mut AbiWriter);
- /// Specialization for [`crate::solidity_interface`] implementation,
- /// see comment in `impl AbiWrite for ResultWithPostInfo`
- fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
- let mut writer = AbiWriter::new();
- self.abi_write(&mut writer);
- Ok(writer.into())
- }
-}
-
-/// This particular AbiWrite implementation should be split to another trait,
-/// which only implements `to_result`, but due to lack of specialization feature
-/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,
-/// so here we abusing default trait methods for it
-impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {
- fn abi_write(&self, _writer: &mut AbiWriter) {
- debug_assert!(false, "shouldn't be called, see comment")
- }
- fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
- match self {
- Ok(v) => Ok(WithPostDispatchInfo {
- post_info: v.post_info.clone(),
- data: {
- let mut out = AbiWriter::new();
- v.data.abi_write(&mut out);
- out
- },
- }),
- Err(e) => Err(e.clone()),
- }
- }
-}
-
-macro_rules! impl_abi_writeable {
- ($ty:ty, $method:ident) => {
- impl AbiWrite for $ty {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.$method(&self)
- }
- }
- };
-}
-
-impl_abi_writeable!(u8, uint8);
-impl_abi_writeable!(u32, uint32);
-impl_abi_writeable!(u128, uint128);
-impl_abi_writeable!(U256, uint256);
-impl_abi_writeable!(H160, address);
-impl_abi_writeable!(bool, bool);
-impl_abi_writeable!(&str, string);
-
-impl AbiWrite for string {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.string(self)
- }
-}
-
-impl AbiWrite for bytes {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.bytes(self.0.as_slice())
- }
-}
-
-impl<T: AbiWrite + TypeHelper> AbiWrite for Vec<T> {
- fn abi_write(&self, writer: &mut AbiWriter) {
- let is_dynamic = T::is_dynamic();
- let mut sub = if is_dynamic {
- AbiWriter::new_dynamic(is_dynamic)
- } else {
- AbiWriter::new()
- };
-
- // Write items count
- (self.len() as u32).abi_write(&mut sub);
-
- for item in self {
- item.abi_write(&mut sub);
- }
- writer.write_subresult(sub);
- }
-}
-
-impl AbiWrite for () {
- fn abi_write(&self, _writer: &mut AbiWriter) {}
-}
-
-/// Helper macros to parse reader into variables
-#[deprecated]
-#[macro_export]
-macro_rules! abi_decode {
- ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {
- $(
- let $name = $reader.$typ()?;
- )+
- }
-}
-
-/// Helper macros to construct RLP-encoded buffer
-#[deprecated]
-#[macro_export]
-macro_rules! abi_encode {
- ($($typ:ident($value:expr)),* $(,)?) => {{
- #[allow(unused_mut)]
- let mut writer = ::evm_coder::abi::AbiWriter::new();
- $(
- writer.$typ($value);
- )*
- writer
- }};
- (call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{
- #[allow(unused_mut)]
- let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);
- $(
- writer.$typ($value);
- )*
- writer
- }}
-}
-
-#[cfg(test)]
-pub mod test {
- use crate::{
- abi::{AbiRead, AbiWrite},
- types::*,
- };
-
- use super::{AbiReader, AbiWriter};
- use hex_literal::hex;
- use primitive_types::{H160, U256};
- use concat_idents::concat_idents;
-
- macro_rules! test_impl {
- ($name:ident, $type:ty, $function_identifier:expr, $decoded_data:expr, $encoded_data:expr) => {
- concat_idents!(test_name = encode_decode_, $name {
- #[test]
- fn test_name() {
- let function_identifier: u32 = $function_identifier;
- let decoded_data = $decoded_data;
- let encoded_data = $encoded_data;
-
- let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
- assert_eq!(call, u32::to_be_bytes(function_identifier));
- let data = <$type>::abi_read(&mut decoder).unwrap();
- assert_eq!(data, decoded_data);
-
- let mut writer = AbiWriter::new_call(function_identifier);
- decoded_data.abi_write(&mut writer);
- let ed = writer.finish();
- similar_asserts::assert_eq!(encoded_data, ed.as_slice());
- }
- });
- };
- }
-
- macro_rules! test_impl_uint {
- ($type:ident) => {
- test_impl!(
- $type,
- $type,
- 0xdeadbeef,
- 255 as $type,
- &hex!(
- "
- deadbeef
- 00000000000000000000000000000000000000000000000000000000000000ff
- "
- )
- );
- };
- }
-
- test_impl_uint!(uint8);
- test_impl_uint!(uint32);
- test_impl_uint!(uint128);
-
- test_impl!(
- uint256,
- uint256,
- 0xdeadbeef,
- U256([255, 0, 0, 0]),
- &hex!(
- "
- deadbeef
- 00000000000000000000000000000000000000000000000000000000000000ff
- "
- )
- );
-
- test_impl!(
- vec_tuple_address_uint256,
- Vec<(address, uint256)>,
- 0x1ACF2D55,
- vec![
- (
- H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),
- U256([10, 0, 0, 0]),
- ),
- (
- H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),
- U256([20, 0, 0, 0]),
- ),
- (
- H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),
- U256([30, 0, 0, 0]),
- ),
- ],
- &hex!(
- "
- 1ACF2D55
- 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]
- 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]
-
- 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address
- 000000000000000000000000000000000000000000000000000000000000000A // uint256
-
- 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address
- 0000000000000000000000000000000000000000000000000000000000000014 // uint256
-
- 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address
- 000000000000000000000000000000000000000000000000000000000000001E // uint256
- "
- )
- );
-
- test_impl!(
- vec_tuple_uint256_string,
- Vec<(uint256, string)>,
- 0xdeadbeef,
- vec![
- (1.into(), "Test URI 0".to_string()),
- (11.into(), "Test URI 1".to_string()),
- (12.into(), "Test URI 2".to_string()),
- ],
- &hex!(
- "
- deadbeef
- 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[]
- 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
-
- 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
- 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
- 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
-
- 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203000000000000000000000000000000000000000000000 // string
-
- 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203100000000000000000000000000000000000000000000 // string
-
- 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203200000000000000000000000000000000000000000000 // string
- "
- )
- );
-
- test_impl!(
- vec_tuple_string_bytes,
- Vec<(string, bytes)>,
- 0xdeadbeef,
- vec![
- (
- "Test URI 0".to_string(),
- bytes(vec![
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
- ])
- ),
- (
- "Test URI 1".to_string(),
- bytes(vec![
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22
- ])
- ),
- ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])),
- ],
- &hex!(
- "
- deadbeef
- 0000000000000000000000000000000000000000000000000000000000000020
- 0000000000000000000000000000000000000000000000000000000000000003
-
- 0000000000000000000000000000000000000000000000000000000000000060
- 0000000000000000000000000000000000000000000000000000000000000140
- 0000000000000000000000000000000000000000000000000000000000000220
-
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
- 000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203000000000000000000000000000000000000000000000
- 0000000000000000000000000000000000000000000000000000000000000030
- 1111111111111111111111111111111111111111111111111111111111111111
- 1111111111111111111111111111111100000000000000000000000000000000
-
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
- 000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203100000000000000000000000000000000000000000000
- 000000000000000000000000000000000000000000000000000000000000002f
- 2222222222222222222222222222222222222222222222222222222222222222
- 2222222222222222222222222222220000000000000000000000000000000000
-
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
- 000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203200000000000000000000000000000000000000000000
- 0000000000000000000000000000000000000000000000000000000000000002
- 3333000000000000000000000000000000000000000000000000000000000000
- "
- )
- );
-
- #[test]
- fn dynamic_after_static() {
- let mut encoder = AbiWriter::new();
- encoder.bool(&true);
- encoder.string("test");
- let encoded = encoder.finish();
-
- let mut encoder = AbiWriter::new();
- encoder.bool(&true);
- // Offset to subresult
- encoder.uint32(&(32 * 2));
- // Len of "test"
- encoder.uint32(&4);
- encoder.write_padright(&[b't', b'e', b's', b't']);
- let alternative_encoded = encoder.finish();
-
- assert_eq!(encoded, alternative_encoded);
-
- let mut decoder = AbiReader::new(&encoded);
- assert!(decoder.bool().unwrap());
- assert_eq!(decoder.string().unwrap(), "test");
- }
-
- #[test]
- fn mint_sample() {
- let (call, mut decoder) = AbiReader::new_call(&hex!(
- "
- 50bb4e7f
- 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374
- 0000000000000000000000000000000000000000000000000000000000000001
- 0000000000000000000000000000000000000000000000000000000000000060
- 0000000000000000000000000000000000000000000000000000000000000008
- 5465737420555249000000000000000000000000000000000000000000000000
- "
- ))
- .unwrap();
- assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));
- assert_eq!(
- format!("{:?}", decoder.address().unwrap()),
- "0xad2c0954693c2b5404b7e50967d3481bea432374"
- );
- assert_eq!(decoder.uint32().unwrap(), 1);
- assert_eq!(decoder.string().unwrap(), "Test URI");
- }
-
- #[test]
- fn parse_vec_with_dynamic_type() {
- let decoded_data = (
- 0x36543006,
- vec![
- (1.into(), "Test URI 0".to_string()),
- (11.into(), "Test URI 1".to_string()),
- (12.into(), "Test URI 2".to_string()),
- ],
- );
-
- let encoded_data = &hex!(
- "
- 36543006
- 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]
- 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
-
- 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
- 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
- 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
-
- 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203000000000000000000000000000000000000000000000 // string
-
- 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203100000000000000000000000000000000000000000000 // string
-
- 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203200000000000000000000000000000000000000000000 // string
- "
- );
-
- let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
- assert_eq!(call, u32::to_be_bytes(decoded_data.0));
- let address = decoder.address().unwrap();
- let data = <Vec<(uint256, string)>>::abi_read(&mut decoder).unwrap();
- assert_eq!(data, decoded_data.1);
-
- let mut writer = AbiWriter::new_call(decoded_data.0);
- address.abi_write(&mut writer);
- decoded_data.1.abi_write(&mut writer);
- let ed = writer.finish();
- similar_asserts::assert_eq!(encoded_data, ed.as_slice());
- }
-}
crates/evm-coder/src/abi/impls.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/abi/impls.rs
@@ -0,0 +1,303 @@
+use crate::{
+ execution::{Result, ResultWithPostInfo, WithPostDispatchInfo},
+ types::*,
+ make_signature,
+ custom_signature::SignatureUnit,
+};
+use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter};
+use primitive_types::{U256, H160};
+
+#[cfg(not(feature = "std"))]
+use alloc::vec::Vec;
+
+macro_rules! impl_abi_readable {
+ ($ty:ty, $method:ident, $dynamic:literal) => {
+ impl sealed::CanBePlacedInVec for $ty {}
+
+ impl AbiType for $ty {
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ty)));
+
+ fn is_dynamic() -> bool {
+ $dynamic
+ }
+
+ fn size() -> usize {
+ ABI_ALIGNMENT
+ }
+ }
+
+ impl AbiRead for $ty {
+ fn abi_read(reader: &mut AbiReader) -> Result<$ty> {
+ reader.$method()
+ }
+ }
+ };
+}
+
+impl_abi_readable!(uint32, uint32, false);
+impl_abi_readable!(uint64, uint64, false);
+impl_abi_readable!(uint128, uint128, false);
+impl_abi_readable!(uint256, uint256, false);
+impl_abi_readable!(bytes4, bytes4, false);
+impl_abi_readable!(address, address, false);
+impl_abi_readable!(string, string, true);
+
+impl sealed::CanBePlacedInVec for bool {}
+
+impl AbiType for bool {
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool"));
+
+ fn is_dynamic() -> bool {
+ false
+ }
+ fn size() -> usize {
+ ABI_ALIGNMENT
+ }
+}
+impl AbiRead for bool {
+ fn abi_read(reader: &mut AbiReader) -> Result<bool> {
+ reader.bool()
+ }
+}
+
+impl AbiType for uint8 {
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));
+
+ fn is_dynamic() -> bool {
+ false
+ }
+ fn size() -> usize {
+ ABI_ALIGNMENT
+ }
+}
+impl AbiRead for uint8 {
+ fn abi_read(reader: &mut AbiReader) -> Result<uint8> {
+ reader.uint8()
+ }
+}
+
+impl AbiType for bytes {
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));
+
+ fn is_dynamic() -> bool {
+ true
+ }
+ fn size() -> usize {
+ ABI_ALIGNMENT
+ }
+}
+impl AbiRead for bytes {
+ fn abi_read(reader: &mut AbiReader) -> Result<bytes> {
+ Ok(bytes(reader.bytes()?))
+ }
+}
+
+impl<R: AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<R> {
+ fn abi_read(reader: &mut AbiReader) -> Result<Vec<R>> {
+ let mut sub = reader.subresult(None)?;
+ let size = sub.uint32()? as usize;
+ sub.subresult_offset = sub.offset;
+ let mut out = Vec::with_capacity(size);
+ for _ in 0..size {
+ out.push(<R>::abi_read(&mut sub)?);
+ }
+ Ok(out)
+ }
+}
+
+impl<R: AbiType> AbiType for Vec<R> {
+ const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]"));
+
+ fn is_dynamic() -> bool {
+ true
+ }
+
+ fn size() -> usize {
+ ABI_ALIGNMENT
+ }
+}
+
+impl sealed::CanBePlacedInVec for EthCrossAccount {}
+
+impl AbiType for EthCrossAccount {
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));
+
+ fn is_dynamic() -> bool {
+ address::is_dynamic() || uint256::is_dynamic()
+ }
+
+ fn size() -> usize {
+ <address as AbiType>::size() + <uint256 as AbiType>::size()
+ }
+}
+
+impl AbiRead for EthCrossAccount {
+ fn abi_read(reader: &mut AbiReader) -> Result<EthCrossAccount> {
+ let size = if !EthCrossAccount::is_dynamic() {
+ Some(<EthCrossAccount as AbiType>::size())
+ } else {
+ None
+ };
+ let mut subresult = reader.subresult(size)?;
+ let eth = <address>::abi_read(&mut subresult)?;
+ let sub = <uint256>::abi_read(&mut subresult)?;
+
+ Ok(EthCrossAccount { eth, sub })
+ }
+}
+
+impl AbiWrite for EthCrossAccount {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ self.eth.abi_write(writer);
+ self.sub.abi_write(writer);
+ }
+}
+
+macro_rules! impl_abi_writeable {
+ ($ty:ty, $method:ident) => {
+ impl AbiWrite for $ty {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ writer.$method(&self)
+ }
+ }
+ };
+}
+
+impl_abi_writeable!(u8, uint8);
+impl_abi_writeable!(u32, uint32);
+impl_abi_writeable!(u128, uint128);
+impl_abi_writeable!(U256, uint256);
+impl_abi_writeable!(H160, address);
+impl_abi_writeable!(bool, bool);
+impl_abi_writeable!(&str, string);
+
+impl AbiWrite for string {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ writer.string(self)
+ }
+}
+
+impl AbiWrite for bytes {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ writer.bytes(self.0.as_slice())
+ }
+}
+
+impl<T: AbiWrite + AbiType> AbiWrite for Vec<T> {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ let is_dynamic = T::is_dynamic();
+ let mut sub = if is_dynamic {
+ AbiWriter::new_dynamic(is_dynamic)
+ } else {
+ AbiWriter::new()
+ };
+
+ // Write items count
+ (self.len() as u32).abi_write(&mut sub);
+
+ for item in self {
+ item.abi_write(&mut sub);
+ }
+ writer.write_subresult(sub);
+ }
+}
+
+impl AbiWrite for () {
+ fn abi_write(&self, _writer: &mut AbiWriter) {}
+}
+
+/// This particular AbiWrite implementation should be split to another trait,
+/// which only implements `to_result`, but due to lack of specialization feature
+/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,
+/// so here we abusing default trait methods for it
+impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {
+ fn abi_write(&self, _writer: &mut AbiWriter) {
+ debug_assert!(false, "shouldn't be called, see comment")
+ }
+ fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
+ match self {
+ Ok(v) => Ok(WithPostDispatchInfo {
+ post_info: v.post_info.clone(),
+ data: {
+ let mut out = AbiWriter::new();
+ v.data.abi_write(&mut out);
+ out
+ },
+ }),
+ Err(e) => Err(e.clone()),
+ }
+ }
+}
+
+macro_rules! impl_tuples {
+ ($($ident:ident)+) => {
+ impl<$($ident: AbiType,)+> AbiType for ($($ident,)+)
+ where
+ $(
+ $ident: AbiType,
+ )+
+ {
+ const SIGNATURE: SignatureUnit = make_signature!(
+ new fixed("(")
+ $(nameof(<$ident>::SIGNATURE) fixed(","))+
+ shift_left(1)
+ fixed(")")
+ );
+
+ fn is_dynamic() -> bool {
+ false
+ $(
+ || <$ident>::is_dynamic()
+ )*
+ }
+
+ fn size() -> usize {
+ 0 $(+ <$ident>::size())+
+ }
+ }
+
+ impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
+
+ impl<$($ident),+> AbiRead for ($($ident,)+)
+ where
+ $($ident: AbiRead,)+
+ ($($ident,)+): AbiType,
+ {
+ fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> {
+ let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None };
+ let mut subresult = reader.subresult(size)?;
+ Ok((
+ $(<$ident>::abi_read(&mut subresult)?,)+
+ ))
+ }
+ }
+
+ #[allow(non_snake_case)]
+ impl<$($ident),+> AbiWrite for ($($ident,)+)
+ where
+ $($ident: AbiWrite,)+
+ {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ let ($($ident,)+) = self;
+ if writer.is_dynamic {
+ let mut sub = AbiWriter::new();
+ $($ident.abi_write(&mut sub);)+
+ writer.write_subresult(sub);
+ } else {
+ $($ident.abi_write(writer);)+
+ }
+ }
+ }
+ };
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
crates/evm-coder/src/abi/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/abi/mod.rs
@@ -0,0 +1,339 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Implementation of EVM RLP reader/writer
+
+#![allow(dead_code)]
+
+mod traits;
+pub use traits::*;
+mod impls;
+
+#[cfg(test)]
+mod test;
+
+#[cfg(not(feature = "std"))]
+use alloc::vec::Vec;
+use evm_core::ExitError;
+use primitive_types::{H160, U256};
+
+use crate::{
+ execution::{Result, Error},
+ types::*,
+};
+
+const ABI_ALIGNMENT: usize = 32;
+
+/// View into RLP data, which provides method to read typed items from it
+#[derive(Clone)]
+pub struct AbiReader<'i> {
+ buf: &'i [u8],
+ subresult_offset: usize,
+ offset: usize,
+}
+impl<'i> AbiReader<'i> {
+ /// Start reading RLP buffer, assuming there is no padding bytes
+ pub fn new(buf: &'i [u8]) -> Self {
+ Self {
+ buf,
+ subresult_offset: 0,
+ offset: 0,
+ }
+ }
+ /// Start reading RLP buffer, parsing first 4 bytes as selector
+ pub fn new_call(buf: &'i [u8]) -> Result<(bytes4, Self)> {
+ if buf.len() < 4 {
+ return Err(Error::Error(ExitError::OutOfOffset));
+ }
+ let mut method_id = [0; 4];
+ method_id.copy_from_slice(&buf[0..4]);
+
+ Ok((
+ method_id,
+ Self {
+ buf,
+ subresult_offset: 4,
+ offset: 4,
+ },
+ ))
+ }
+
+ 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];
+ let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);
+ if !is_pad_zeroed {
+ return Err(Error::Error(ExitError::InvalidRange));
+ }
+ block.copy_from_slice(&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;
+ 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,
+ )
+ }
+
+ /// Read [`H160`] at current position, then advance
+ pub fn address(&mut self) -> Result<H160> {
+ Ok(H160(self.read_padleft()?))
+ }
+
+ /// Read [`bool`] at current position, then advance
+ pub fn bool(&mut self) -> Result<bool> {
+ let data: [u8; 1] = self.read_padleft()?;
+ match data[0] {
+ 0 => Ok(false),
+ 1 => Ok(true),
+ _ => Err(Error::Error(ExitError::InvalidRange)),
+ }
+ }
+
+ /// Read [`[u8; 4]`] at current position, then advance
+ pub fn bytes4(&mut self) -> Result<[u8; 4]> {
+ self.read_padright()
+ }
+
+ /// Read [`Vec<u8>`] at current position, then advance
+ pub fn bytes(&mut self) -> Result<Vec<u8>> {
+ let mut subresult = self.subresult(None)?;
+ let length = subresult.uint32()? as usize;
+ if subresult.buf.len() < subresult.offset + length {
+ return Err(Error::Error(ExitError::OutOfOffset));
+ }
+ Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
+ }
+
+ /// Read [`string`] at current position, then advance
+ pub fn string(&mut self) -> Result<string> {
+ string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
+ }
+
+ /// Read [`u8`] at current position, then advance
+ pub fn uint8(&mut self) -> Result<u8> {
+ Ok(self.read_padleft::<1>()?[0])
+ }
+
+ /// Read [`u32`] at current position, then advance
+ pub fn uint32(&mut self) -> Result<u32> {
+ Ok(u32::from_be_bytes(self.read_padleft()?))
+ }
+
+ /// Read [`u128`] at current position, then advance
+ pub fn uint128(&mut self) -> Result<u128> {
+ Ok(u128::from_be_bytes(self.read_padleft()?))
+ }
+
+ /// Read [`U256`] at current position, then advance
+ pub fn uint256(&mut self) -> Result<U256> {
+ let buf: [u8; 32] = self.read_padleft()?;
+ Ok(U256::from_big_endian(&buf))
+ }
+
+ /// Read [`u64`] at current position, then advance
+ pub fn uint64(&mut self) -> Result<u64> {
+ Ok(u64::from_be_bytes(self.read_padleft()?))
+ }
+
+ /// Read [`usize`] at current position, then advance
+ #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
+ pub fn read_usize(&mut self) -> Result<usize> {
+ Ok(usize::from_be_bytes(self.read_padleft()?))
+ }
+
+ /// Slice recursive buffer, advance one word for buffer offset
+ /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`].
+ fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {
+ let subresult_offset = self.subresult_offset;
+ let offset = if let Some(size) = size {
+ self.offset += size;
+ self.subresult_offset += size;
+ 0
+ } else {
+ self.uint32()? as usize
+ };
+
+ if offset + self.subresult_offset > self.buf.len() {
+ return Err(Error::Error(ExitError::InvalidRange));
+ }
+
+ let new_offset = offset + subresult_offset;
+ Ok(AbiReader {
+ buf: self.buf,
+ subresult_offset: new_offset,
+ offset: new_offset,
+ })
+ }
+
+ /// Is this parser reached end of buffer?
+ pub fn is_finished(&self) -> bool {
+ self.buf.len() == self.offset
+ }
+}
+
+/// Writer for RLP encoded data
+#[derive(Default)]
+pub struct AbiWriter {
+ static_part: Vec<u8>,
+ dynamic_part: Vec<(usize, AbiWriter)>,
+ had_call: bool,
+ is_dynamic: bool,
+}
+impl AbiWriter {
+ /// Initialize internal buffers for output data, assuming no padding required
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Initialize internal buffers with data size
+ pub fn new_dynamic(is_dynamic: bool) -> Self {
+ Self {
+ is_dynamic,
+ ..Default::default()
+ }
+ }
+ /// Initialize internal buffers, inserting method selector at beginning
+ pub fn new_call(method_id: u32) -> Self {
+ let mut val = Self::new();
+ val.static_part.extend(&method_id.to_be_bytes());
+ val.had_call = true;
+ val
+ }
+
+ fn write_padleft(&mut self, block: &[u8]) {
+ assert!(block.len() <= ABI_ALIGNMENT);
+ self.static_part
+ .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);
+ self.static_part.extend(block);
+ }
+
+ fn write_padright(&mut self, block: &[u8]) {
+ assert!(block.len() <= ABI_ALIGNMENT);
+ self.static_part.extend(block);
+ self.static_part
+ .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);
+ }
+
+ /// Write [`H160`] to end of buffer
+ pub fn address(&mut self, address: &H160) {
+ self.write_padleft(&address.0)
+ }
+
+ /// Write [`bool`] to end of buffer
+ pub fn bool(&mut self, value: &bool) {
+ self.write_padleft(&[if *value { 1 } else { 0 }])
+ }
+
+ /// Write [`u8`] to end of buffer
+ pub fn uint8(&mut self, value: &u8) {
+ self.write_padleft(&[*value])
+ }
+
+ /// Write [`u32`] to end of buffer
+ pub fn uint32(&mut self, value: &u32) {
+ self.write_padleft(&u32::to_be_bytes(*value))
+ }
+
+ /// Write [`u128`] to end of buffer
+ pub fn uint128(&mut self, value: &u128) {
+ self.write_padleft(&u128::to_be_bytes(*value))
+ }
+
+ /// Write [`U256`] to end of buffer
+ pub fn uint256(&mut self, value: &U256) {
+ let mut out = [0; 32];
+ value.to_big_endian(&mut out);
+ self.write_padleft(&out)
+ }
+
+ /// Write [`usize`] to end of buffer
+ #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
+ pub fn write_usize(&mut self, value: &usize) {
+ self.write_padleft(&usize::to_be_bytes(*value))
+ }
+
+ /// Append recursive data, writing pending offset at end of buffer
+ pub fn write_subresult(&mut self, result: Self) {
+ self.dynamic_part.push((self.static_part.len(), result));
+ // Empty block, to be filled later
+ self.write_padleft(&[]);
+ }
+
+ fn memory(&mut self, value: &[u8]) {
+ let mut sub = Self::new();
+ sub.uint32(&(value.len() as u32));
+ for chunk in value.chunks(ABI_ALIGNMENT) {
+ sub.write_padright(chunk);
+ }
+ self.write_subresult(sub);
+ }
+
+ /// Append recursive [`str`] at end of buffer
+ pub fn string(&mut self, value: &str) {
+ self.memory(value.as_bytes())
+ }
+
+ /// Append recursive [`[u8]`] at end of buffer
+ pub fn bytes(&mut self, value: &[u8]) {
+ self.memory(value)
+ }
+
+ /// Finish writer, concatenating all internal buffers
+ pub fn finish(mut self) -> Vec<u8> {
+ for (static_offset, part) in self.dynamic_part {
+ let part_offset = self.static_part.len()
+ - if self.had_call { 4 } else { 0 }
+ - if self.is_dynamic { ABI_ALIGNMENT } else { 0 };
+
+ let encoded_dynamic_offset = usize::to_be_bytes(part_offset);
+ let start = static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len();
+ let stop = static_offset + ABI_ALIGNMENT;
+ self.static_part[start..stop].copy_from_slice(&encoded_dynamic_offset);
+ self.static_part.extend(part.finish())
+ }
+ self.static_part
+ }
+}
crates/evm-coder/src/abi/test.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/abi/test.rs
@@ -0,0 +1,297 @@
+use crate::{
+ abi::{AbiRead, AbiWrite},
+ types::*,
+};
+
+use super::{AbiReader, AbiWriter};
+use hex_literal::hex;
+use primitive_types::{H160, U256};
+use concat_idents::concat_idents;
+
+macro_rules! test_impl {
+ ($name:ident, $type:ty, $function_identifier:expr, $decoded_data:expr, $encoded_data:expr) => {
+ concat_idents!(test_name = encode_decode_, $name {
+ #[test]
+ fn test_name() {
+ let function_identifier: u32 = $function_identifier;
+ let decoded_data = $decoded_data;
+ let encoded_data = $encoded_data;
+
+ let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
+ assert_eq!(call, u32::to_be_bytes(function_identifier));
+ let data = <$type>::abi_read(&mut decoder).unwrap();
+ assert_eq!(data, decoded_data);
+
+ let mut writer = AbiWriter::new_call(function_identifier);
+ decoded_data.abi_write(&mut writer);
+ let ed = writer.finish();
+ similar_asserts::assert_eq!(encoded_data, ed.as_slice());
+ }
+ });
+ };
+}
+
+macro_rules! test_impl_uint {
+ ($type:ident) => {
+ test_impl!(
+ $type,
+ $type,
+ 0xdeadbeef,
+ 255 as $type,
+ &hex!(
+ "
+ deadbeef
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ "
+ )
+ );
+ };
+}
+
+test_impl_uint!(uint8);
+test_impl_uint!(uint32);
+test_impl_uint!(uint128);
+
+test_impl!(
+ uint256,
+ uint256,
+ 0xdeadbeef,
+ U256([255, 0, 0, 0]),
+ &hex!(
+ "
+ deadbeef
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ "
+ )
+);
+
+test_impl!(
+ vec_tuple_address_uint256,
+ Vec<(address, uint256)>,
+ 0x1ACF2D55,
+ vec![
+ (
+ H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),
+ U256([10, 0, 0, 0]),
+ ),
+ (
+ H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),
+ U256([20, 0, 0, 0]),
+ ),
+ (
+ H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),
+ U256([30, 0, 0, 0]),
+ ),
+ ],
+ &hex!(
+ "
+ 1ACF2D55
+ 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]
+ 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]
+
+ 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address
+ 000000000000000000000000000000000000000000000000000000000000000A // uint256
+
+ 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address
+ 0000000000000000000000000000000000000000000000000000000000000014 // uint256
+
+ 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address
+ 000000000000000000000000000000000000000000000000000000000000001E // uint256
+ "
+ )
+);
+
+test_impl!(
+ vec_tuple_uint256_string,
+ Vec<(uint256, string)>,
+ 0xdeadbeef,
+ vec![
+ (1.into(), "Test URI 0".to_string()),
+ (11.into(), "Test URI 1".to_string()),
+ (12.into(), "Test URI 2".to_string()),
+ ],
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[]
+ 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
+
+ 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
+ 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
+ 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
+
+ 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203000000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203100000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203200000000000000000000000000000000000000000000 // string
+ "
+ )
+);
+
+#[test]
+fn dynamic_after_static() {
+ let mut encoder = AbiWriter::new();
+ encoder.bool(&true);
+ encoder.string("test");
+ let encoded = encoder.finish();
+
+ let mut encoder = AbiWriter::new();
+ encoder.bool(&true);
+ // Offset to subresult
+ encoder.uint32(&(32 * 2));
+ // Len of "test"
+ encoder.uint32(&4);
+ encoder.write_padright(&[b't', b'e', b's', b't']);
+ let alternative_encoded = encoder.finish();
+
+ assert_eq!(encoded, alternative_encoded);
+
+ let mut decoder = AbiReader::new(&encoded);
+ assert!(decoder.bool().unwrap());
+ assert_eq!(decoder.string().unwrap(), "test");
+}
+
+#[test]
+fn mint_sample() {
+ let (call, mut decoder) = AbiReader::new_call(&hex!(
+ "
+ 50bb4e7f
+ 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374
+ 0000000000000000000000000000000000000000000000000000000000000001
+ 0000000000000000000000000000000000000000000000000000000000000060
+ 0000000000000000000000000000000000000000000000000000000000000008
+ 5465737420555249000000000000000000000000000000000000000000000000
+ "
+ ))
+ .unwrap();
+ assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));
+ assert_eq!(
+ format!("{:?}", decoder.address().unwrap()),
+ "0xad2c0954693c2b5404b7e50967d3481bea432374"
+ );
+ assert_eq!(decoder.uint32().unwrap(), 1);
+ assert_eq!(decoder.string().unwrap(), "Test URI");
+}
+
+#[test]
+fn parse_vec_with_dynamic_type() {
+ let decoded_data = (
+ 0x36543006,
+ vec![
+ (1.into(), "Test URI 0".to_string()),
+ (11.into(), "Test URI 1".to_string()),
+ (12.into(), "Test URI 2".to_string()),
+ ],
+ );
+
+ let encoded_data = &hex!(
+ "
+ 36543006
+ 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]
+ 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
+
+ 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
+ 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
+ 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
+
+ 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203000000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203100000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203200000000000000000000000000000000000000000000 // string
+ "
+ );
+
+ let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
+ assert_eq!(call, u32::to_be_bytes(decoded_data.0));
+ let address = decoder.address().unwrap();
+ let data = <Vec<(uint256, string)>>::abi_read(&mut decoder).unwrap();
+ assert_eq!(data, decoded_data.1);
+
+ let mut writer = AbiWriter::new_call(decoded_data.0);
+ address.abi_write(&mut writer);
+ decoded_data.1.abi_write(&mut writer);
+ let ed = writer.finish();
+ similar_asserts::assert_eq!(encoded_data, ed.as_slice());
+}
+
+test_impl!(
+ vec_tuple_string_bytes,
+ Vec<(string, bytes)>,
+ 0xdeadbeef,
+ vec![
+ (
+ "Test URI 0".to_string(),
+ bytes(vec![
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+ ])
+ ),
+ (
+ "Test URI 1".to_string(),
+ bytes(vec![
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22
+ ])
+ ),
+ ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])),
+ ],
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000003
+
+ 0000000000000000000000000000000000000000000000000000000000000060
+ 0000000000000000000000000000000000000000000000000000000000000140
+ 0000000000000000000000000000000000000000000000000000000000000220
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203000000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000030
+ 1111111111111111111111111111111111111111111111111111111111111111
+ 1111111111111111111111111111111100000000000000000000000000000000
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203100000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000002f
+ 2222222222222222222222222222222222222222222222222222222222222222
+ 2222222222222222222222222222220000000000000000000000000000000000
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203200000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000002
+ 3333000000000000000000000000000000000000000000000000000000000000
+ "
+ )
+);
crates/evm-coder/src/abi/traits.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/abi/traits.rs
@@ -0,0 +1,51 @@
+use super::{AbiReader, AbiWriter};
+use crate::{
+ custom_signature::*,
+ execution::{Result, ResultWithPostInfo},
+};
+use core::str::from_utf8;
+
+/// Helper for type.
+pub trait AbiType {
+ /// Signature for Etherium ABI.
+ const SIGNATURE: SignatureUnit;
+
+ /// Signature as str.
+ fn as_str() -> &'static str {
+ from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8")
+ }
+
+ /// Is type dynamic sized.
+ fn is_dynamic() -> bool;
+
+ /// Size for type aligned to [`ABI_ALIGNMENT`].
+ fn size() -> usize;
+}
+
+/// Sealed traits.
+pub mod sealed {
+ /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
+ pub trait CanBePlacedInVec {}
+}
+
+/// [`AbiReader`] implements reading of many types.
+pub trait AbiRead {
+ /// Read item from current position, advanding decoder
+ fn abi_read(reader: &mut AbiReader) -> Result<Self>
+ where
+ Self: Sized;
+}
+
+/// For questions about inability to provide custom implementations,
+/// see [`AbiRead`]
+pub trait AbiWrite {
+ /// Write value to end of specified encoder
+ fn abi_write(&self, writer: &mut AbiWriter);
+ /// Specialization for [`crate::solidity_interface`] implementation,
+ /// see comment in `impl AbiWrite for ResultWithPostInfo`
+ fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
+ let mut writer = AbiWriter::new();
+ self.abi_write(&mut writer);
+ Ok(writer.into())
+ }
+}
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -121,53 +121,24 @@
use alloc::{vec::Vec};
use pallet_evm::account::CrossAccountId;
use primitive_types::{U256, H160, H256};
- use core::str::from_utf8;
-
- use crate::custom_signature::SignatureUnit;
-
- pub trait Signature {
- const SIGNATURE: SignatureUnit;
-
- fn as_str() -> &'static str {
- from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8")
- }
- }
-
- impl Signature for bool {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool"));
- }
-
- macro_rules! define_simple_type {
- (type $ident:ident = $ty:ty) => {
- pub type $ident = $ty;
- impl Signature for $ty {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ident)));
- }
- };
- }
- define_simple_type!(type address = H160);
-
- define_simple_type!(type uint8 = u8);
- define_simple_type!(type uint16 = u16);
- define_simple_type!(type uint32 = u32);
- define_simple_type!(type uint64 = u64);
- define_simple_type!(type uint128 = u128);
- define_simple_type!(type uint256 = U256);
- define_simple_type!(type bytes4 = [u8; 4]);
-
- define_simple_type!(type topic = H256);
+ pub type address = H160;
+ pub type uint8 = u8;
+ pub type uint16 = u16;
+ pub type uint32 = u32;
+ pub type uint64 = u64;
+ pub type uint128 = u128;
+ pub type uint256 = U256;
+ pub type bytes4 = [u8; 4];
+ pub type topic = H256;
#[cfg(not(feature = "std"))]
- define_simple_type!(type string = ::alloc::string::String);
+ pub type string = ::alloc::string::String;
#[cfg(feature = "std")]
- define_simple_type!(type string = ::std::string::String);
+ pub type string = ::std::string::String;
#[derive(Default, Debug, PartialEq)]
pub struct bytes(pub Vec<u8>);
- impl Signature for bytes {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));
- }
/// Solidity doesn't have `void` type, however we have special implementation
/// for empty tuple return type
@@ -257,10 +228,6 @@
Err("All fields of cross account is non zeroed".into())
}
}
- }
-
- impl Signature for EthCrossAccount {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));
}
/// Convert `CrossAccountId` to `uint256`.
crates/evm-coder/tests/random.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/random.rs
+++ b/crates/evm-coder/tests/random.rs
@@ -16,8 +16,9 @@
#![allow(dead_code)] // This test only checks that macros is not panicking
-use evm_coder::{ToLog, execution::Result, solidity_interface, types::*, solidity, weight};
-use evm_coder::{types::Signature};
+use evm_coder::{
+ abi::AbiType, ToLog, execution::Result, solidity_interface, types::*, solidity, weight,
+};
pub struct Impls;
crates/evm-coder/tests/solidity_generation.rsdiffbeforeafterboth--- a/crates/evm-coder/tests/solidity_generation.rs
+++ b/crates/evm-coder/tests/solidity_generation.rs
@@ -14,8 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*};
-use evm_coder::{types::Signature};
+use evm_coder::{abi::AbiType, execution::Result, generate_stubgen, solidity_interface, types::*};
pub struct ERC20;
pallets/common/src/erc.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//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 solidity_interface, solidity, ToLog,21 types::*,22 execution::{Result, Error},23 weight,24};25pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};26use pallet_evm_coder_substrate::dispatch_to_evm;27use sp_std::vec::Vec;28use up_data_structs::{29 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,30 SponsoringRateLimit, SponsorshipState,31};32use alloc::format;3334use crate::{35 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,36 eth::convert_cross_account_to_uint256, weights::WeightInfo,37};3839/// Events for ethereum collection helper.40#[derive(ToLog)]41pub enum CollectionHelpersEvents {42 /// The collection has been created.43 CollectionCreated {44 /// Collection owner.45 #[indexed]46 owner: address,4748 /// Collection ID.49 #[indexed]50 collection_id: address,51 },52 /// The collection has been destroyed.53 CollectionDestroyed {54 /// Collection ID.55 #[indexed]56 collection_id: address,57 },58}5960/// Does not always represent a full collection, for RFT it is either61/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).62pub trait CommonEvmHandler {63 /// Raw compiled binary code of the contract stub64 const CODE: &'static [u8];6566 /// Call precompiled handle.67 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;68}6970/// @title A contract that allows you to work with collections.71#[solidity_interface(name = Collection)]72impl<T: Config> CollectionHandle<T>73where74 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,75{76 /// Set collection property.77 ///78 /// @param key Property key.79 /// @param value Propery value.80 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]81 fn set_collection_property(82 &mut self,83 caller: caller,84 key: string,85 value: bytes,86 ) -> Result<void> {87 let caller = T::CrossAccountId::from_eth(caller);88 let key = <Vec<u8>>::from(key)89 .try_into()90 .map_err(|_| "key too large")?;91 let value = value.0.try_into().map_err(|_| "value too large")?;9293 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })94 .map_err(dispatch_to_evm::<T>)95 }9697 /// Set collection properties.98 ///99 /// @param properties Vector of properties key/value pair.100 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]101 fn set_collection_properties(102 &mut self,103 caller: caller,104 properties: Vec<(string, bytes)>,105 ) -> Result<void> {106 let caller = T::CrossAccountId::from_eth(caller);107108 let properties = properties109 .into_iter()110 .map(|(key, value)| {111 let key = <Vec<u8>>::from(key)112 .try_into()113 .map_err(|_| "key too large")?;114115 let value = value.0.try_into().map_err(|_| "value too large")?;116117 Ok(Property { key, value })118 })119 .collect::<Result<Vec<_>>>()?;120121 <Pallet<T>>::set_collection_properties(self, &caller, properties)122 .map_err(dispatch_to_evm::<T>)123 }124125 /// Delete collection property.126 ///127 /// @param key Property key.128 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]129 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {130 let caller = T::CrossAccountId::from_eth(caller);131 let key = <Vec<u8>>::from(key)132 .try_into()133 .map_err(|_| "key too large")?;134135 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)136 }137138 /// Delete collection properties.139 ///140 /// @param keys Properties keys.141 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]142 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {143 let caller = T::CrossAccountId::from_eth(caller);144 let keys = keys145 .into_iter()146 .map(|key| {147 <Vec<u8>>::from(key)148 .try_into()149 .map_err(|_| Error::Revert("key too large".into()))150 })151 .collect::<Result<Vec<_>>>()?;152153 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)154 }155156 /// Get collection property.157 ///158 /// @dev Throws error if key not found.159 ///160 /// @param key Property key.161 /// @return bytes The property corresponding to the key.162 fn collection_property(&self, key: string) -> Result<bytes> {163 let key = <Vec<u8>>::from(key)164 .try_into()165 .map_err(|_| "key too large")?;166167 let props = CollectionProperties::<T>::get(self.id);168 let prop = props.get(&key).ok_or("key not found")?;169170 Ok(bytes(prop.to_vec()))171 }172173 /// Get collection properties.174 ///175 /// @param keys Properties keys. Empty keys for all propertyes.176 /// @return Vector of properties key/value pairs.177 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {178 let keys = keys179 .into_iter()180 .map(|key| {181 <Vec<u8>>::from(key)182 .try_into()183 .map_err(|_| Error::Revert("key too large".into()))184 })185 .collect::<Result<Vec<_>>>()?;186187 let properties = Pallet::<T>::filter_collection_properties(188 self.id,189 if keys.is_empty() { None } else { Some(keys) },190 )191 .map_err(dispatch_to_evm::<T>)?;192193 let properties = properties194 .into_iter()195 .map(|p| {196 let key =197 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;198 let value = bytes(p.value.to_vec());199 Ok((key, value))200 })201 .collect::<Result<Vec<_>>>()?;202 Ok(properties)203 }204205 /// Set the sponsor of the collection.206 ///207 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.208 ///209 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.210 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {211 self.consume_store_reads_and_writes(1, 1)?;212213 check_is_owner_or_admin(caller, self)?;214215 let sponsor = T::CrossAccountId::from_eth(sponsor);216 self.set_sponsor(sponsor.as_sub().clone())217 .map_err(dispatch_to_evm::<T>)?;218 save(self)219 }220221 /// Set the sponsor of the collection.222 ///223 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.224 ///225 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.226 fn set_collection_sponsor_cross(227 &mut self,228 caller: caller,229 sponsor: EthCrossAccount,230 ) -> Result<void> {231 self.consume_store_reads_and_writes(1, 1)?;232233 check_is_owner_or_admin(caller, self)?;234235 let sponsor = sponsor.into_sub_cross_account::<T>()?;236 self.set_sponsor(sponsor.as_sub().clone())237 .map_err(dispatch_to_evm::<T>)?;238 save(self)239 }240241 /// Whether there is a pending sponsor.242 fn has_collection_pending_sponsor(&self) -> Result<bool> {243 Ok(matches!(244 self.collection.sponsorship,245 SponsorshipState::Unconfirmed(_)246 ))247 }248249 /// Collection sponsorship confirmation.250 ///251 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.252 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {253 self.consume_store_writes(1)?;254255 let caller = T::CrossAccountId::from_eth(caller);256 if !self257 .confirm_sponsorship(caller.as_sub())258 .map_err(dispatch_to_evm::<T>)?259 {260 return Err("caller is not set as sponsor".into());261 }262 save(self)263 }264265 /// Remove collection sponsor.266 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {267 self.consume_store_reads_and_writes(1, 1)?;268 check_is_owner_or_admin(caller, self)?;269 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;270 save(self)271 }272273 /// Get current sponsor.274 ///275 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.276 fn collection_sponsor(&self) -> Result<(address, uint256)> {277 let sponsor = match self.collection.sponsorship.sponsor() {278 Some(sponsor) => sponsor,279 None => return Ok(Default::default()),280 };281 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());282 let result: (address, uint256) = if sponsor.is_canonical_substrate() {283 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);284 (Default::default(), sponsor)285 } else {286 let sponsor = *sponsor.as_eth();287 (sponsor, Default::default())288 };289 Ok(result)290 }291292 /// Set limits for the collection.293 /// @dev Throws error if limit not found.294 /// @param limit Name of the limit. Valid names:295 /// "accountTokenOwnershipLimit",296 /// "sponsoredDataSize",297 /// "sponsoredDataRateLimit",298 /// "tokenLimit",299 /// "sponsorTransferTimeout",300 /// "sponsorApproveTimeout"301 /// @param value Value of the limit.302 #[solidity(rename_selector = "setCollectionLimit")]303 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {304 self.consume_store_reads_and_writes(1, 1)?;305306 check_is_owner_or_admin(caller, self)?;307 let mut limits = self.limits.clone();308309 match limit.as_str() {310 "accountTokenOwnershipLimit" => {311 limits.account_token_ownership_limit = Some(value);312 }313 "sponsoredDataSize" => {314 limits.sponsored_data_size = Some(value);315 }316 "sponsoredDataRateLimit" => {317 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));318 }319 "tokenLimit" => {320 limits.token_limit = Some(value);321 }322 "sponsorTransferTimeout" => {323 limits.sponsor_transfer_timeout = Some(value);324 }325 "sponsorApproveTimeout" => {326 limits.sponsor_approve_timeout = Some(value);327 }328 _ => {329 return Err(Error::Revert(format!(330 "unknown integer limit \"{}\"",331 limit332 )))333 }334 }335 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)336 .map_err(dispatch_to_evm::<T>)?;337 save(self)338 }339340 /// Set limits for the collection.341 /// @dev Throws error if limit not found.342 /// @param limit Name of the limit. Valid names:343 /// "ownerCanTransfer",344 /// "ownerCanDestroy",345 /// "transfersEnabled"346 /// @param value Value of the limit.347 #[solidity(rename_selector = "setCollectionLimit")]348 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {349 self.consume_store_reads_and_writes(1, 1)?;350351 check_is_owner_or_admin(caller, self)?;352 let mut limits = self.limits.clone();353354 match limit.as_str() {355 "ownerCanTransfer" => {356 limits.owner_can_transfer = Some(value);357 }358 "ownerCanDestroy" => {359 limits.owner_can_destroy = Some(value);360 }361 "transfersEnabled" => {362 limits.transfers_enabled = Some(value);363 }364 _ => {365 return Err(Error::Revert(format!(366 "unknown boolean limit \"{}\"",367 limit368 )))369 }370 }371 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)372 .map_err(dispatch_to_evm::<T>)?;373 save(self)374 }375376 /// Get contract address.377 fn contract_address(&self) -> Result<address> {378 Ok(crate::eth::collection_id_to_address(self.id))379 }380381 /// Add collection admin.382 /// @param newAdmin Cross account administrator address.383 fn add_collection_admin_cross(384 &mut self,385 caller: caller,386 new_admin: EthCrossAccount,387 ) -> Result<void> {388 self.consume_store_writes(2)?;389390 let caller = T::CrossAccountId::from_eth(caller);391 let new_admin = new_admin.into_sub_cross_account::<T>()?;392 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;393 Ok(())394 }395396 /// Remove collection admin.397 /// @param admin Cross account administrator address.398 fn remove_collection_admin_cross(399 &mut self,400 caller: caller,401 admin: EthCrossAccount,402 ) -> Result<void> {403 self.consume_store_writes(2)?;404405 let caller = T::CrossAccountId::from_eth(caller);406 let admin = admin.into_sub_cross_account::<T>()?;407 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;408 Ok(())409 }410411 /// Add collection admin.412 /// @param newAdmin Address of the added administrator.413 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {414 self.consume_store_writes(2)?;415416 let caller = T::CrossAccountId::from_eth(caller);417 let new_admin = T::CrossAccountId::from_eth(new_admin);418 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;419 Ok(())420 }421422 /// Remove collection admin.423 ///424 /// @param admin Address of the removed administrator.425 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {426 self.consume_store_writes(2)?;427428 let caller = T::CrossAccountId::from_eth(caller);429 let admin = T::CrossAccountId::from_eth(admin);430 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;431 Ok(())432 }433434 /// Toggle accessibility of collection nesting.435 ///436 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'437 #[solidity(rename_selector = "setCollectionNesting")]438 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {439 self.consume_store_reads_and_writes(1, 1)?;440441 check_is_owner_or_admin(caller, self)?;442443 let mut permissions = self.collection.permissions.clone();444 let mut nesting = permissions.nesting().clone();445 nesting.token_owner = enable;446 nesting.restricted = None;447 permissions.nesting = Some(nesting);448449 self.collection.permissions = <Pallet<T>>::clamp_permissions(450 self.collection.mode.clone(),451 &self.collection.permissions,452 permissions,453 )454 .map_err(dispatch_to_evm::<T>)?;455456 save(self)457 }458459 /// Toggle accessibility of collection nesting.460 ///461 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'462 /// @param collections Addresses of collections that will be available for nesting.463 #[solidity(rename_selector = "setCollectionNesting")]464 fn set_nesting(465 &mut self,466 caller: caller,467 enable: bool,468 collections: Vec<address>,469 ) -> Result<void> {470 self.consume_store_reads_and_writes(1, 1)?;471472 if collections.is_empty() {473 return Err("no addresses provided".into());474 }475 check_is_owner_or_admin(caller, self)?;476477 let mut permissions = self.collection.permissions.clone();478 match enable {479 false => {480 let mut nesting = permissions.nesting().clone();481 nesting.token_owner = false;482 nesting.restricted = None;483 permissions.nesting = Some(nesting);484 }485 true => {486 let mut bv = OwnerRestrictedSet::new();487 for i in collections {488 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {489 Error::Revert("Can't convert address into collection id".into())490 })?)491 .map_err(|_| "too many collections")?;492 }493 let mut nesting = permissions.nesting().clone();494 nesting.token_owner = true;495 nesting.restricted = Some(bv);496 permissions.nesting = Some(nesting);497 }498 };499500 self.collection.permissions = <Pallet<T>>::clamp_permissions(501 self.collection.mode.clone(),502 &self.collection.permissions,503 permissions,504 )505 .map_err(dispatch_to_evm::<T>)?;506507 save(self)508 }509510 /// Set the collection access method.511 /// @param mode Access mode512 /// 0 for Normal513 /// 1 for AllowList514 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {515 self.consume_store_reads_and_writes(1, 1)?;516517 check_is_owner_or_admin(caller, self)?;518 let permissions = CollectionPermissions {519 access: Some(match mode {520 0 => AccessMode::Normal,521 1 => AccessMode::AllowList,522 _ => return Err("not supported access mode".into()),523 }),524 ..Default::default()525 };526 self.collection.permissions = <Pallet<T>>::clamp_permissions(527 self.collection.mode.clone(),528 &self.collection.permissions,529 permissions,530 )531 .map_err(dispatch_to_evm::<T>)?;532533 save(self)534 }535536 /// Checks that user allowed to operate with collection.537 ///538 /// @param user User address to check.539 fn allowed(&self, user: address) -> Result<bool> {540 Ok(Pallet::<T>::allowed(541 self.id,542 T::CrossAccountId::from_eth(user),543 ))544 }545546 /// Add the user to the allowed list.547 ///548 /// @param user Address of a trusted user.549 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {550 self.consume_store_writes(1)?;551552 let caller = T::CrossAccountId::from_eth(caller);553 let user = T::CrossAccountId::from_eth(user);554 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;555 Ok(())556 }557558 /// Add user to allowed list.559 ///560 /// @param user User cross account address.561 fn add_to_collection_allow_list_cross(562 &mut self,563 caller: caller,564 user: EthCrossAccount,565 ) -> Result<void> {566 self.consume_store_writes(1)?;567568 let caller = T::CrossAccountId::from_eth(caller);569 let user = user.into_sub_cross_account::<T>()?;570 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;571 Ok(())572 }573574 /// Remove the user from the allowed list.575 ///576 /// @param user Address of a removed user.577 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {578 self.consume_store_writes(1)?;579580 let caller = T::CrossAccountId::from_eth(caller);581 let user = T::CrossAccountId::from_eth(user);582 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;583 Ok(())584 }585586 /// Remove user from allowed list.587 ///588 /// @param user User cross account address.589 fn remove_from_collection_allow_list_cross(590 &mut self,591 caller: caller,592 user: EthCrossAccount,593 ) -> Result<void> {594 self.consume_store_writes(1)?;595596 let caller = T::CrossAccountId::from_eth(caller);597 let user = user.into_sub_cross_account::<T>()?;598 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;599 Ok(())600 }601602 /// Switch permission for minting.603 ///604 /// @param mode Enable if "true".605 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {606 self.consume_store_reads_and_writes(1, 1)?;607608 check_is_owner_or_admin(caller, self)?;609 let permissions = CollectionPermissions {610 mint_mode: Some(mode),611 ..Default::default()612 };613 self.collection.permissions = <Pallet<T>>::clamp_permissions(614 self.collection.mode.clone(),615 &self.collection.permissions,616 permissions,617 )618 .map_err(dispatch_to_evm::<T>)?;619620 save(self)621 }622623 /// Check that account is the owner or admin of the collection624 ///625 /// @param user account to verify626 /// @return "true" if account is the owner or admin627 #[solidity(rename_selector = "isOwnerOrAdmin")]628 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {629 let user = T::CrossAccountId::from_eth(user);630 Ok(self.is_owner_or_admin(&user))631 }632633 /// Check that account is the owner or admin of the collection634 ///635 /// @param user User cross account to verify636 /// @return "true" if account is the owner or admin637 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {638 let user = user.into_sub_cross_account::<T>()?;639 Ok(self.is_owner_or_admin(&user))640 }641642 /// Returns collection type643 ///644 /// @return `Fungible` or `NFT` or `ReFungible`645 fn unique_collection_type(&self) -> Result<string> {646 let mode = match self.collection.mode {647 CollectionMode::Fungible(_) => "Fungible",648 CollectionMode::NFT => "NFT",649 CollectionMode::ReFungible => "ReFungible",650 };651 Ok(mode.into())652 }653654 /// Get collection owner.655 ///656 /// @return Tuble with sponsor address and his substrate mirror.657 /// If address is canonical then substrate mirror is zero and vice versa.658 fn collection_owner(&self) -> Result<EthCrossAccount> {659 Ok(EthCrossAccount::from_sub_cross_account::<T>(660 &T::CrossAccountId::from_sub(self.owner.clone()),661 ))662 }663664 /// Changes collection owner to another account665 ///666 /// @dev Owner can be changed only by current owner667 /// @param newOwner new owner account668 #[solidity(rename_selector = "changeCollectionOwner")]669 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {670 self.consume_store_writes(1)?;671672 let caller = T::CrossAccountId::from_eth(caller);673 let new_owner = T::CrossAccountId::from_eth(new_owner);674 self.set_owner_internal(caller, new_owner)675 .map_err(dispatch_to_evm::<T>)676 }677678 /// Get collection administrators679 ///680 /// @return Vector of tuples with admins address and his substrate mirror.681 /// If address is canonical then substrate mirror is zero and vice versa.682 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {683 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))684 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))685 .collect();686 Ok(result)687 }688689 /// Changes collection owner to another account690 ///691 /// @dev Owner can be changed only by current owner692 /// @param newOwner new owner cross account693 fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {694 self.consume_store_writes(1)?;695696 let caller = T::CrossAccountId::from_eth(caller);697 let new_owner = new_owner.into_sub_cross_account::<T>()?;698 self.set_owner_internal(caller, new_owner)699 .map_err(dispatch_to_evm::<T>)700 }701}702703/// ### Note704/// Do not forget to add: `self.consume_store_reads(1)?;`705fn check_is_owner_or_admin<T: Config>(706 caller: caller,707 collection: &CollectionHandle<T>,708) -> Result<T::CrossAccountId> {709 let caller = T::CrossAccountId::from_eth(caller);710 collection711 .check_is_owner_or_admin(&caller)712 .map_err(dispatch_to_evm::<T>)?;713 Ok(caller)714}715716/// ### Note717/// Do not forget to add: `self.consume_store_writes(1)?;`718fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {719 collection720 .check_is_internal()721 .map_err(dispatch_to_evm::<T>)?;722 collection.save().map_err(dispatch_to_evm::<T>)?;723 Ok(())724}725726/// Contains static property keys and values.727pub mod static_property {728 use evm_coder::{729 execution::{Result, Error},730 };731 use alloc::format;732733 const EXPECT_CONVERT_ERROR: &str = "length < limit";734735 /// Keys.736 pub mod key {737 use super::*;738739 /// Key "baseURI".740 pub fn base_uri() -> up_data_structs::PropertyKey {741 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)742 }743744 /// Key "url".745 pub fn url() -> up_data_structs::PropertyKey {746 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)747 }748749 /// Key "suffix".750 pub fn suffix() -> up_data_structs::PropertyKey {751 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)752 }753754 /// Key "parentNft".755 pub fn parent_nft() -> up_data_structs::PropertyKey {756 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)757 }758 }759760 /// Convert `byte` to [`PropertyKey`].761 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {762 bytes.to_vec().try_into().map_err(|_| {763 Error::Revert(format!(764 "Property key is too long. Max length is {}.",765 up_data_structs::PropertyKey::bound()766 ))767 })768 }769770 /// Convert `bytes` to [`PropertyValue`].771 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {772 bytes.to_vec().try_into().map_err(|_| {773 Error::Revert(format!(774 "Property key is too long. Max length is {}.",775 up_data_structs::PropertyKey::bound()776 ))777 })778 }779}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//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 abi::AbiType,21 solidity_interface, solidity, ToLog,22 types::*,23 execution::{Result, Error},24 weight,25};26pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};27use pallet_evm_coder_substrate::dispatch_to_evm;28use sp_std::vec::Vec;29use up_data_structs::{30 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,31 SponsoringRateLimit, SponsorshipState,32};33use alloc::format;3435use crate::{36 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,37 eth::convert_cross_account_to_uint256, weights::WeightInfo,38};3940/// Events for ethereum collection helper.41#[derive(ToLog)]42pub enum CollectionHelpersEvents {43 /// The collection has been created.44 CollectionCreated {45 /// Collection owner.46 #[indexed]47 owner: address,4849 /// Collection ID.50 #[indexed]51 collection_id: address,52 },53 /// The collection has been destroyed.54 CollectionDestroyed {55 /// Collection ID.56 #[indexed]57 collection_id: address,58 },59}6061/// Does not always represent a full collection, for RFT it is either62/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).63pub trait CommonEvmHandler {64 /// Raw compiled binary code of the contract stub65 const CODE: &'static [u8];6667 /// Call precompiled handle.68 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;69}7071/// @title A contract that allows you to work with collections.72#[solidity_interface(name = Collection)]73impl<T: Config> CollectionHandle<T>74where75 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,76{77 /// Set collection property.78 ///79 /// @param key Property key.80 /// @param value Propery value.81 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]82 fn set_collection_property(83 &mut self,84 caller: caller,85 key: string,86 value: bytes,87 ) -> Result<void> {88 let caller = T::CrossAccountId::from_eth(caller);89 let key = <Vec<u8>>::from(key)90 .try_into()91 .map_err(|_| "key too large")?;92 let value = value.0.try_into().map_err(|_| "value too large")?;9394 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })95 .map_err(dispatch_to_evm::<T>)96 }9798 /// Set collection properties.99 ///100 /// @param properties Vector of properties key/value pair.101 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]102 fn set_collection_properties(103 &mut self,104 caller: caller,105 properties: Vec<(string, bytes)>,106 ) -> Result<void> {107 let caller = T::CrossAccountId::from_eth(caller);108109 let properties = properties110 .into_iter()111 .map(|(key, value)| {112 let key = <Vec<u8>>::from(key)113 .try_into()114 .map_err(|_| "key too large")?;115116 let value = value.0.try_into().map_err(|_| "value too large")?;117118 Ok(Property { key, value })119 })120 .collect::<Result<Vec<_>>>()?;121122 <Pallet<T>>::set_collection_properties(self, &caller, properties)123 .map_err(dispatch_to_evm::<T>)124 }125126 /// Delete collection property.127 ///128 /// @param key Property key.129 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]130 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {131 let caller = T::CrossAccountId::from_eth(caller);132 let key = <Vec<u8>>::from(key)133 .try_into()134 .map_err(|_| "key too large")?;135136 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)137 }138139 /// Delete collection properties.140 ///141 /// @param keys Properties keys.142 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]143 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {144 let caller = T::CrossAccountId::from_eth(caller);145 let keys = keys146 .into_iter()147 .map(|key| {148 <Vec<u8>>::from(key)149 .try_into()150 .map_err(|_| Error::Revert("key too large".into()))151 })152 .collect::<Result<Vec<_>>>()?;153154 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)155 }156157 /// Get collection property.158 ///159 /// @dev Throws error if key not found.160 ///161 /// @param key Property key.162 /// @return bytes The property corresponding to the key.163 fn collection_property(&self, key: string) -> Result<bytes> {164 let key = <Vec<u8>>::from(key)165 .try_into()166 .map_err(|_| "key too large")?;167168 let props = CollectionProperties::<T>::get(self.id);169 let prop = props.get(&key).ok_or("key not found")?;170171 Ok(bytes(prop.to_vec()))172 }173174 /// Get collection properties.175 ///176 /// @param keys Properties keys. Empty keys for all propertyes.177 /// @return Vector of properties key/value pairs.178 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {179 let keys = keys180 .into_iter()181 .map(|key| {182 <Vec<u8>>::from(key)183 .try_into()184 .map_err(|_| Error::Revert("key too large".into()))185 })186 .collect::<Result<Vec<_>>>()?;187188 let properties = Pallet::<T>::filter_collection_properties(189 self.id,190 if keys.is_empty() { None } else { Some(keys) },191 )192 .map_err(dispatch_to_evm::<T>)?;193194 let properties = properties195 .into_iter()196 .map(|p| {197 let key =198 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;199 let value = bytes(p.value.to_vec());200 Ok((key, value))201 })202 .collect::<Result<Vec<_>>>()?;203 Ok(properties)204 }205206 /// Set the sponsor of the collection.207 ///208 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.209 ///210 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.211 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {212 self.consume_store_reads_and_writes(1, 1)?;213214 check_is_owner_or_admin(caller, self)?;215216 let sponsor = T::CrossAccountId::from_eth(sponsor);217 self.set_sponsor(sponsor.as_sub().clone())218 .map_err(dispatch_to_evm::<T>)?;219 save(self)220 }221222 /// Set the sponsor of the collection.223 ///224 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.225 ///226 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.227 fn set_collection_sponsor_cross(228 &mut self,229 caller: caller,230 sponsor: EthCrossAccount,231 ) -> Result<void> {232 self.consume_store_reads_and_writes(1, 1)?;233234 check_is_owner_or_admin(caller, self)?;235236 let sponsor = sponsor.into_sub_cross_account::<T>()?;237 self.set_sponsor(sponsor.as_sub().clone())238 .map_err(dispatch_to_evm::<T>)?;239 save(self)240 }241242 /// Whether there is a pending sponsor.243 fn has_collection_pending_sponsor(&self) -> Result<bool> {244 Ok(matches!(245 self.collection.sponsorship,246 SponsorshipState::Unconfirmed(_)247 ))248 }249250 /// Collection sponsorship confirmation.251 ///252 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.253 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {254 self.consume_store_writes(1)?;255256 let caller = T::CrossAccountId::from_eth(caller);257 if !self258 .confirm_sponsorship(caller.as_sub())259 .map_err(dispatch_to_evm::<T>)?260 {261 return Err("caller is not set as sponsor".into());262 }263 save(self)264 }265266 /// Remove collection sponsor.267 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {268 self.consume_store_reads_and_writes(1, 1)?;269 check_is_owner_or_admin(caller, self)?;270 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;271 save(self)272 }273274 /// Get current sponsor.275 ///276 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.277 fn collection_sponsor(&self) -> Result<(address, uint256)> {278 let sponsor = match self.collection.sponsorship.sponsor() {279 Some(sponsor) => sponsor,280 None => return Ok(Default::default()),281 };282 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());283 let result: (address, uint256) = if sponsor.is_canonical_substrate() {284 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);285 (Default::default(), sponsor)286 } else {287 let sponsor = *sponsor.as_eth();288 (sponsor, Default::default())289 };290 Ok(result)291 }292293 /// Set limits for the collection.294 /// @dev Throws error if limit not found.295 /// @param limit Name of the limit. Valid names:296 /// "accountTokenOwnershipLimit",297 /// "sponsoredDataSize",298 /// "sponsoredDataRateLimit",299 /// "tokenLimit",300 /// "sponsorTransferTimeout",301 /// "sponsorApproveTimeout"302 /// @param value Value of the limit.303 #[solidity(rename_selector = "setCollectionLimit")]304 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {305 self.consume_store_reads_and_writes(1, 1)?;306307 check_is_owner_or_admin(caller, self)?;308 let mut limits = self.limits.clone();309310 match limit.as_str() {311 "accountTokenOwnershipLimit" => {312 limits.account_token_ownership_limit = Some(value);313 }314 "sponsoredDataSize" => {315 limits.sponsored_data_size = Some(value);316 }317 "sponsoredDataRateLimit" => {318 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));319 }320 "tokenLimit" => {321 limits.token_limit = Some(value);322 }323 "sponsorTransferTimeout" => {324 limits.sponsor_transfer_timeout = Some(value);325 }326 "sponsorApproveTimeout" => {327 limits.sponsor_approve_timeout = Some(value);328 }329 _ => {330 return Err(Error::Revert(format!(331 "unknown integer limit \"{}\"",332 limit333 )))334 }335 }336 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)337 .map_err(dispatch_to_evm::<T>)?;338 save(self)339 }340341 /// Set limits for the collection.342 /// @dev Throws error if limit not found.343 /// @param limit Name of the limit. Valid names:344 /// "ownerCanTransfer",345 /// "ownerCanDestroy",346 /// "transfersEnabled"347 /// @param value Value of the limit.348 #[solidity(rename_selector = "setCollectionLimit")]349 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {350 self.consume_store_reads_and_writes(1, 1)?;351352 check_is_owner_or_admin(caller, self)?;353 let mut limits = self.limits.clone();354355 match limit.as_str() {356 "ownerCanTransfer" => {357 limits.owner_can_transfer = Some(value);358 }359 "ownerCanDestroy" => {360 limits.owner_can_destroy = Some(value);361 }362 "transfersEnabled" => {363 limits.transfers_enabled = Some(value);364 }365 _ => {366 return Err(Error::Revert(format!(367 "unknown boolean limit \"{}\"",368 limit369 )))370 }371 }372 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)373 .map_err(dispatch_to_evm::<T>)?;374 save(self)375 }376377 /// Get contract address.378 fn contract_address(&self) -> Result<address> {379 Ok(crate::eth::collection_id_to_address(self.id))380 }381382 /// Add collection admin.383 /// @param newAdmin Cross account administrator address.384 fn add_collection_admin_cross(385 &mut self,386 caller: caller,387 new_admin: EthCrossAccount,388 ) -> Result<void> {389 self.consume_store_writes(2)?;390391 let caller = T::CrossAccountId::from_eth(caller);392 let new_admin = new_admin.into_sub_cross_account::<T>()?;393 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;394 Ok(())395 }396397 /// Remove collection admin.398 /// @param admin Cross account administrator address.399 fn remove_collection_admin_cross(400 &mut self,401 caller: caller,402 admin: EthCrossAccount,403 ) -> Result<void> {404 self.consume_store_writes(2)?;405406 let caller = T::CrossAccountId::from_eth(caller);407 let admin = admin.into_sub_cross_account::<T>()?;408 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;409 Ok(())410 }411412 /// Add collection admin.413 /// @param newAdmin Address of the added administrator.414 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {415 self.consume_store_writes(2)?;416417 let caller = T::CrossAccountId::from_eth(caller);418 let new_admin = T::CrossAccountId::from_eth(new_admin);419 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;420 Ok(())421 }422423 /// Remove collection admin.424 ///425 /// @param admin Address of the removed administrator.426 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {427 self.consume_store_writes(2)?;428429 let caller = T::CrossAccountId::from_eth(caller);430 let admin = T::CrossAccountId::from_eth(admin);431 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;432 Ok(())433 }434435 /// Toggle accessibility of collection nesting.436 ///437 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'438 #[solidity(rename_selector = "setCollectionNesting")]439 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {440 self.consume_store_reads_and_writes(1, 1)?;441442 check_is_owner_or_admin(caller, self)?;443444 let mut permissions = self.collection.permissions.clone();445 let mut nesting = permissions.nesting().clone();446 nesting.token_owner = enable;447 nesting.restricted = None;448 permissions.nesting = Some(nesting);449450 self.collection.permissions = <Pallet<T>>::clamp_permissions(451 self.collection.mode.clone(),452 &self.collection.permissions,453 permissions,454 )455 .map_err(dispatch_to_evm::<T>)?;456457 save(self)458 }459460 /// Toggle accessibility of collection nesting.461 ///462 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'463 /// @param collections Addresses of collections that will be available for nesting.464 #[solidity(rename_selector = "setCollectionNesting")]465 fn set_nesting(466 &mut self,467 caller: caller,468 enable: bool,469 collections: Vec<address>,470 ) -> Result<void> {471 self.consume_store_reads_and_writes(1, 1)?;472473 if collections.is_empty() {474 return Err("no addresses provided".into());475 }476 check_is_owner_or_admin(caller, self)?;477478 let mut permissions = self.collection.permissions.clone();479 match enable {480 false => {481 let mut nesting = permissions.nesting().clone();482 nesting.token_owner = false;483 nesting.restricted = None;484 permissions.nesting = Some(nesting);485 }486 true => {487 let mut bv = OwnerRestrictedSet::new();488 for i in collections {489 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {490 Error::Revert("Can't convert address into collection id".into())491 })?)492 .map_err(|_| "too many collections")?;493 }494 let mut nesting = permissions.nesting().clone();495 nesting.token_owner = true;496 nesting.restricted = Some(bv);497 permissions.nesting = Some(nesting);498 }499 };500501 self.collection.permissions = <Pallet<T>>::clamp_permissions(502 self.collection.mode.clone(),503 &self.collection.permissions,504 permissions,505 )506 .map_err(dispatch_to_evm::<T>)?;507508 save(self)509 }510511 /// Set the collection access method.512 /// @param mode Access mode513 /// 0 for Normal514 /// 1 for AllowList515 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {516 self.consume_store_reads_and_writes(1, 1)?;517518 check_is_owner_or_admin(caller, self)?;519 let permissions = CollectionPermissions {520 access: Some(match mode {521 0 => AccessMode::Normal,522 1 => AccessMode::AllowList,523 _ => return Err("not supported access mode".into()),524 }),525 ..Default::default()526 };527 self.collection.permissions = <Pallet<T>>::clamp_permissions(528 self.collection.mode.clone(),529 &self.collection.permissions,530 permissions,531 )532 .map_err(dispatch_to_evm::<T>)?;533534 save(self)535 }536537 /// Checks that user allowed to operate with collection.538 ///539 /// @param user User address to check.540 fn allowed(&self, user: address) -> Result<bool> {541 Ok(Pallet::<T>::allowed(542 self.id,543 T::CrossAccountId::from_eth(user),544 ))545 }546547 /// Add the user to the allowed list.548 ///549 /// @param user Address of a trusted user.550 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {551 self.consume_store_writes(1)?;552553 let caller = T::CrossAccountId::from_eth(caller);554 let user = T::CrossAccountId::from_eth(user);555 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;556 Ok(())557 }558559 /// Add user to allowed list.560 ///561 /// @param user User cross account address.562 fn add_to_collection_allow_list_cross(563 &mut self,564 caller: caller,565 user: EthCrossAccount,566 ) -> Result<void> {567 self.consume_store_writes(1)?;568569 let caller = T::CrossAccountId::from_eth(caller);570 let user = user.into_sub_cross_account::<T>()?;571 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;572 Ok(())573 }574575 /// Remove the user from the allowed list.576 ///577 /// @param user Address of a removed user.578 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {579 self.consume_store_writes(1)?;580581 let caller = T::CrossAccountId::from_eth(caller);582 let user = T::CrossAccountId::from_eth(user);583 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;584 Ok(())585 }586587 /// Remove user from allowed list.588 ///589 /// @param user User cross account address.590 fn remove_from_collection_allow_list_cross(591 &mut self,592 caller: caller,593 user: EthCrossAccount,594 ) -> Result<void> {595 self.consume_store_writes(1)?;596597 let caller = T::CrossAccountId::from_eth(caller);598 let user = user.into_sub_cross_account::<T>()?;599 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;600 Ok(())601 }602603 /// Switch permission for minting.604 ///605 /// @param mode Enable if "true".606 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {607 self.consume_store_reads_and_writes(1, 1)?;608609 check_is_owner_or_admin(caller, self)?;610 let permissions = CollectionPermissions {611 mint_mode: Some(mode),612 ..Default::default()613 };614 self.collection.permissions = <Pallet<T>>::clamp_permissions(615 self.collection.mode.clone(),616 &self.collection.permissions,617 permissions,618 )619 .map_err(dispatch_to_evm::<T>)?;620621 save(self)622 }623624 /// Check that account is the owner or admin of the collection625 ///626 /// @param user account to verify627 /// @return "true" if account is the owner or admin628 #[solidity(rename_selector = "isOwnerOrAdmin")]629 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {630 let user = T::CrossAccountId::from_eth(user);631 Ok(self.is_owner_or_admin(&user))632 }633634 /// Check that account is the owner or admin of the collection635 ///636 /// @param user User cross account to verify637 /// @return "true" if account is the owner or admin638 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {639 let user = user.into_sub_cross_account::<T>()?;640 Ok(self.is_owner_or_admin(&user))641 }642643 /// Returns collection type644 ///645 /// @return `Fungible` or `NFT` or `ReFungible`646 fn unique_collection_type(&self) -> Result<string> {647 let mode = match self.collection.mode {648 CollectionMode::Fungible(_) => "Fungible",649 CollectionMode::NFT => "NFT",650 CollectionMode::ReFungible => "ReFungible",651 };652 Ok(mode.into())653 }654655 /// Get collection owner.656 ///657 /// @return Tuble with sponsor address and his substrate mirror.658 /// If address is canonical then substrate mirror is zero and vice versa.659 fn collection_owner(&self) -> Result<EthCrossAccount> {660 Ok(EthCrossAccount::from_sub_cross_account::<T>(661 &T::CrossAccountId::from_sub(self.owner.clone()),662 ))663 }664665 /// Changes collection owner to another account666 ///667 /// @dev Owner can be changed only by current owner668 /// @param newOwner new owner account669 #[solidity(rename_selector = "changeCollectionOwner")]670 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {671 self.consume_store_writes(1)?;672673 let caller = T::CrossAccountId::from_eth(caller);674 let new_owner = T::CrossAccountId::from_eth(new_owner);675 self.set_owner_internal(caller, new_owner)676 .map_err(dispatch_to_evm::<T>)677 }678679 /// Get collection administrators680 ///681 /// @return Vector of tuples with admins address and his substrate mirror.682 /// If address is canonical then substrate mirror is zero and vice versa.683 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {684 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))685 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))686 .collect();687 Ok(result)688 }689690 /// Changes collection owner to another account691 ///692 /// @dev Owner can be changed only by current owner693 /// @param newOwner new owner cross account694 fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {695 self.consume_store_writes(1)?;696697 let caller = T::CrossAccountId::from_eth(caller);698 let new_owner = new_owner.into_sub_cross_account::<T>()?;699 self.set_owner_internal(caller, new_owner)700 .map_err(dispatch_to_evm::<T>)701 }702}703704/// ### Note705/// Do not forget to add: `self.consume_store_reads(1)?;`706fn check_is_owner_or_admin<T: Config>(707 caller: caller,708 collection: &CollectionHandle<T>,709) -> Result<T::CrossAccountId> {710 let caller = T::CrossAccountId::from_eth(caller);711 collection712 .check_is_owner_or_admin(&caller)713 .map_err(dispatch_to_evm::<T>)?;714 Ok(caller)715}716717/// ### Note718/// Do not forget to add: `self.consume_store_writes(1)?;`719fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {720 collection721 .check_is_internal()722 .map_err(dispatch_to_evm::<T>)?;723 collection.save().map_err(dispatch_to_evm::<T>)?;724 Ok(())725}726727/// Contains static property keys and values.728pub mod static_property {729 use evm_coder::{730 execution::{Result, Error},731 };732 use alloc::format;733734 const EXPECT_CONVERT_ERROR: &str = "length < limit";735736 /// Keys.737 pub mod key {738 use super::*;739740 /// Key "baseURI".741 pub fn base_uri() -> up_data_structs::PropertyKey {742 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)743 }744745 /// Key "url".746 pub fn url() -> up_data_structs::PropertyKey {747 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)748 }749750 /// Key "suffix".751 pub fn suffix() -> up_data_structs::PropertyKey {752 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)753 }754755 /// Key "parentNft".756 pub fn parent_nft() -> up_data_structs::PropertyKey {757 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)758 }759 }760761 /// Convert `byte` to [`PropertyKey`].762 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {763 bytes.to_vec().try_into().map_err(|_| {764 Error::Revert(format!(765 "Property key is too long. Max length is {}.",766 up_data_structs::PropertyKey::bound()767 ))768 })769 }770771 /// Convert `bytes` to [`PropertyValue`].772 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {773 bytes.to_vec().try_into().map_err(|_| {774 Error::Revert(format!(775 "Property key is too long. Max length is {}.",776 up_data_structs::PropertyKey::bound()777 ))778 })779 }780}pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -19,7 +19,11 @@
extern crate alloc;
use core::marker::PhantomData;
use evm_coder::{
- abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,
+ abi::{AbiWriter, AbiType},
+ execution::Result,
+ generate_stubgen, solidity_interface,
+ types::*,
+ ToLog,
};
use pallet_evm::{
ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -19,7 +19,9 @@
extern crate alloc;
use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
use core::convert::TryInto;
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use evm_coder::{
+ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight,
+};
use up_data_structs::CollectionMode;
use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
use sp_std::vec::Vec;
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -24,7 +24,10 @@
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,
+ weight,
+};
use frame_support::BoundedVec;
use up_data_structs::{
TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -25,7 +25,10 @@
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,
+ weight,
+};
use frame_support::{BoundedBTreeMap, BoundedVec};
use pallet_common::{
CollectionHandle, CollectionPropertyPermissions,
pallets/refungible/src/erc_token.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -29,7 +29,9 @@
convert::TryInto,
ops::Deref,
};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use evm_coder::{
+ abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight,
+};
use pallet_common::{
CommonWeightInfo,
erc::{CommonEvmHandler, PrecompileResult},
pallets/unique/Cargo.tomldiffbeforeafterboth--- a/pallets/unique/Cargo.toml
+++ b/pallets/unique/Cargo.toml
@@ -35,6 +35,7 @@
try-runtime = ["frame-support/try-runtime"]
limit-testing = ["up-data-structs/limit-testing"]
stubgen = ["evm-coder/stubgen", "pallet-common/stubgen"]
+
################################################################################
# Standart Dependencies
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -18,14 +18,16 @@
use core::marker::PhantomData;
use ethereum as _;
-use evm_coder::{execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
+use evm_coder::{
+ abi::AbiType, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight,
+};
use frame_support::traits::Get;
use crate::Pallet;
use pallet_common::{
CollectionById,
dispatch::CollectionDispatch,
- erc::{static_property::key, CollectionHelpersEvents},
+ erc::{CollectionHelpersEvents, static_property::key},
Pallet as PalletCommon,
};
use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};