--- a/Cargo.lock +++ b/Cargo.lock @@ -1096,26 +1096,6 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] -name = "const_format" -version = "0.2.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2373,7 +2353,6 @@ version = "0.1.3" dependencies = [ "concat-idents", - "const_format", "ethereum", "evm-coder-procedural", "evm-core 0.35.0 (git+https://github.com/uniquenetwork/evm?branch=unique-polkadot-v0.9.30)", --- a/crates/evm-coder/Cargo.toml +++ b/crates/evm-coder/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] -const_format = { version = "0.2.26", default-features = false } sha3-const = { version = "0.1.1", default-features = false } # Ethereum uses keccak (=sha3) for selectors # sha3 = "0.10.1" --- a/crates/evm-coder/procedural/src/solidity_interface.rs +++ b/crates/evm-coder/procedural/src/solidity_interface.rs @@ -20,7 +20,7 @@ // about Procedural Macros in Rust book: // https://doc.rust-lang.org/reference/procedural-macros.html -use proc_macro2::{TokenStream, token_stream}; +use proc_macro2::TokenStream; use quote::{quote, ToTokens, format_ident}; use inflector::cases; use std::fmt::Write; --- a/crates/evm-coder/src/custom_signature.rs +++ b/crates/evm-coder/src/custom_signature.rs @@ -1,23 +1,140 @@ +//! # A module for custom signature support. +//! +//! ## Overview +//! This module allows you to create arbitrary signatures for types and functions in compile time. +//! +//! ### Type signatures +//! To create the desired type signature, you need to create your own trait with the `SIGNATURE` constant. +//! Then in the implementation, for the required type, use the macro [`make_signature`] +//! #### Example +//! ``` +//! use std::str::from_utf8; +//! use evm_coder::make_signature; +//! use evm_coder::custom_signature::{ +//! SignatureUnit, +//! SIGNATURE_SIZE_LIMIT +//! }; +//! +//! // Create trait for our signature +//! trait SoliditySignature { +//! const SIGNATURE: SignatureUnit; +//! +//! fn name() -> &'static str { +//! from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") +//! } +//! } +//! +//! // Make signatures for some types +//! impl SoliditySignature for u8 { +//! make_signature!(new fixed("uint8")); +//! } +//! impl SoliditySignature for u32 { +//! make_signature!(new fixed("uint32")); +//! } +//! impl SoliditySignature for Vec { +//! make_signature!(new nameof(T) fixed("[]")); +//! } +//! impl SoliditySignature for (A, B) { +//! make_signature!(new fixed("(") nameof(A) fixed(",") nameof(B) fixed(")")); +//! } +//! impl SoliditySignature for (A,) { +//! make_signature!(new fixed("(") nameof(A) fixed(",") shift_left(1) fixed(")")); +//! } +//! +//! assert_eq!(u8::name(), "uint8"); +//! assert_eq!(>::name(), "uint8[]"); +//! assert_eq!(<(u32, u8)>::name(), "(uint32,uint8)"); +//! ``` +//! +//! ### Function signatures +//! To create a function signature, the macro [`make_signature`] is also used, which accepts +//! settings for the function format [`SignaturePreferences`] and function parameters [`SignatureUnit`] +//! #### Example +//! ``` +//! use core::str::from_utf8; +//! use evm_coder::{ +//! make_signature, +//! custom_signature::{ +//! SIGNATURE_SIZE_LIMIT, SignatureUnit, SignaturePreferences, FunctionSignature, +//! }, +//! }; +//! // Trait for our signature +//! trait SoliditySignature { +//! const SIGNATURE: SignatureUnit; +//! +//! fn name() -> &'static str { +//! from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8") +//! } +//! } +//! +//! // Make signatures for some types +//! impl SoliditySignature for u8 { +//! make_signature!(new fixed("uint8")); +//! } +//! impl SoliditySignature for Vec { +//! make_signature!(new nameof(T) fixed("[]")); +//! } +//! +//! // Function signature settings +//! const SIGNATURE_PREFERENCES: SignaturePreferences = SignaturePreferences { +//! open_name: Some(SignatureUnit::new("some_funk")), +//! open_delimiter: Some(SignatureUnit::new("(")), +//! param_delimiter: Some(SignatureUnit::new(",")), +//! close_delimiter: Some(SignatureUnit::new(")")), +//! close_name: None, +//! }; +//! +//! // Create functions signatures +//! fn make_func_without_args() { +//! const SIG: FunctionSignature = make_signature!( +//! new fn(SIGNATURE_PREFERENCES), +//! ); +//! let name = SIG.as_str(); +//! similar_asserts::assert_eq!(name, "some_funk()"); +//! } +//! +//! fn make_func_with_3_args() { +//! const SIG: FunctionSignature = make_signature!( +//! new fn(SIGNATURE_PREFERENCES), +//! (::SIGNATURE), +//! (::SIGNATURE), +//! (>::SIGNATURE), +//! ); +//! let name = SIG.as_str(); +//! similar_asserts::assert_eq!(name, "some_funk(uint8,uint8,uint8[])"); +//! } +//! ``` use core::str::from_utf8; +/// The maximum length of the signature. pub const SIGNATURE_SIZE_LIMIT: usize = 256; +/// Function signature formatting preferences. #[derive(Debug)] pub struct SignaturePreferences { + /// The name of the function before the list of parameters: `*some*(param1,param2)func` pub open_name: Option, + /// Opening separator: `some*(*param1,param2)func` pub open_delimiter: Option, + /// Parameters separator: `some(param1*,*param2)func` pub param_delimiter: Option, + /// Closinging separator: `some(param1,param2*)*func` pub close_delimiter: Option, + /// The name of the function after the list of parameters: `some(param1,param2)*func*` pub close_name: Option, } +/// Constructs and stores the signature of the function. #[derive(Debug)] pub struct FunctionSignature { + /// Storage for function signature. pub unit: SignatureUnit, preferences: SignaturePreferences, } impl FunctionSignature { + /// Start constructing the signature. It is written to the storage + /// [`SignaturePreferences::open_name`] and [`SignaturePreferences::open_delimiter`]. pub const fn new(preferences: SignaturePreferences) -> FunctionSignature { let mut dst = [0_u8; SIGNATURE_SIZE_LIMIT]; let mut dst_offset = 0; @@ -36,6 +153,8 @@ } } + /// Add a function parameter to the signature. It is written to the storage + /// `param` [`SignatureUnit`] and [`SignaturePreferences::param_delimiter`]. pub const fn add_param( signature: FunctionSignature, param: SignatureUnit, @@ -55,6 +174,8 @@ } } + /// Complete signature construction. It is written to the storage + /// [`SignaturePreferences::close_delimiter`] and [`SignaturePreferences::close_name`]. pub const fn done(signature: FunctionSignature, owerride: bool) -> FunctionSignature { let mut dst = signature.unit.data; let mut dst_offset = signature.unit.len - if owerride { 1 } else { 0 }; @@ -73,18 +194,23 @@ } } + /// Represent the signature as `&str'. pub fn as_str(&self) -> &str { from_utf8(&self.unit.data[..self.unit.len]).expect("bad utf-8") } } +/// Storage for the signature or its elements. #[derive(Debug)] pub struct SignatureUnit { + /// Signature data. pub data: [u8; SIGNATURE_SIZE_LIMIT], + /// The actual size of the data. pub len: usize, } impl SignatureUnit { + /// Create a signature from `&str'. pub const fn new(name: &'static str) -> SignatureUnit { let mut signature = [0_u8; SIGNATURE_SIZE_LIMIT]; let name = name.as_bytes(); @@ -98,9 +224,23 @@ } } +/// ### Macro to create signatures of types and functions. +/// +/// Format for creating a type of signature: +/// ```ignore +/// make_signature!(new fixed("uint8")); // Simple type +/// make_signature!(new fixed("(") nameof(u8) fixed(",") nameof(u8) fixed(")")); // Composite type +/// ``` +/// Format for creating a function of the function: +/// ```ignore +/// const SIG: FunctionSignature = make_signature!( +/// new fn(SIGNATURE_PREFERENCES), +/// (u8::SIGNATURE), +/// (<(u8,u8)>::SIGNATURE), +/// ); +/// ``` #[macro_export] -#[allow(missing_docs)] -macro_rules! make_signature { // May be "define_signature"? +macro_rules! make_signature { (new fn($func:expr)$(,)+) => { { let fs = FunctionSignature::new($func); @@ -283,10 +423,13 @@ assert_eq!(::name(), "!".repeat(SIGNATURE_SIZE_LIMIT)); } - // This test must NOT compile! + // This test must NOT compile with "index out of bounds"! // #[test] // fn over_max_size() { - // assert_eq!(>::name(), "!".repeat(SIZE_LIMIT) + "[]"); + // assert_eq!( + // >::name(), + // "!".repeat(SIGNATURE_SIZE_LIMIT) + "[]" + // ); // } #[test] --- a/crates/evm-coder/src/lib.rs +++ b/crates/evm-coder/src/lib.rs @@ -15,8 +15,7 @@ // along with Unique Network. If not, see . #![doc = include_str!("../README.md")] -// #![deny(missing_docs)] -#![warn(missing_docs)] +#![deny(missing_docs)] #![macro_use] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] @@ -94,7 +93,6 @@ pub use evm_coder_procedural::solidity; /// See [`solidity_interface`] pub use evm_coder_procedural::weight; -pub use const_format; pub use sha3_const; /// Derives [`ToLog`] for enum @@ -384,18 +382,6 @@ fn function_selector_generation() { assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb); } - - // #[test] - // fn function_selector_generation_1() { - // assert_eq!( - // fn_selector!(transferFromCrossAccountToCrossAccount( - // EthCrossAccount, - // EthCrossAccount, - // uint256 - // )), - // 2543295963 - // ); - // } #[test] fn event_topic_generation() { --- a/crates/evm-coder/tests/conditional_is.rs +++ b/crates/evm-coder/tests/conditional_is.rs @@ -1,4 +1,11 @@ use evm_coder::{types::*, solidity_interface, execution::Result}; +use evm_coder::{ + make_signature, + custom_signature::{ + SIGNATURE_SIZE_LIMIT, SignatureUnit, SignaturePreferences, FunctionSignature, + }, + types::Signature, +}; pub struct Contract(bool); --- a/crates/evm-coder/tests/generics.rs +++ b/crates/evm-coder/tests/generics.rs @@ -16,6 +16,12 @@ use std::marker::PhantomData; use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; +use evm_coder::{ + make_signature, + custom_signature::{ + SIGNATURE_SIZE_LIMIT, SignatureUnit, SignaturePreferences, FunctionSignature, + }, +}; pub struct Generic(PhantomData); --- a/crates/evm-coder/tests/random.rs +++ b/crates/evm-coder/tests/random.rs @@ -17,6 +17,13 @@ #![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::{ + make_signature, + custom_signature::{ + SIGNATURE_SIZE_LIMIT, SignatureUnit, SignaturePreferences, FunctionSignature, + }, + types::Signature, +}; pub struct Impls; --- a/crates/evm-coder/tests/solidity_generation.rs +++ b/crates/evm-coder/tests/solidity_generation.rs @@ -15,6 +15,13 @@ // along with Unique Network. If not, see . use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*}; +use evm_coder::{ + make_signature, + custom_signature::{ + SIGNATURE_SIZE_LIMIT, SignatureUnit, SignaturePreferences, FunctionSignature, + }, + types::Signature, +}; pub struct ERC20; --- a/tests/src/eth/base.test.ts +++ b/tests/src/eth/base.test.ts @@ -116,8 +116,8 @@ await checkInterface(helper, '0x780e9d63', true, true); }); - itEth('ERC721UniqueExtensions - 0x244543ee - support', async ({helper}) => { - await checkInterface(helper, '0x244543ee', true, true); + itEth('ERC721UniqueExtensions support', async ({helper}) => { + expect(await contract(helper).methods.supportsInterface('0xb76006ac').call()).to.be.true; }); itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => { --- a/tests/src/eth/nonFungible.test.ts +++ b/tests/src/eth/nonFungible.test.ts @@ -410,14 +410,13 @@ }); itEth('Can perform transferFromCross()', async ({helper, privateKey}) => { - const alice = privateKey('//Alice'); - const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const collection = await helper.nft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); const owner = privateKey('//Bob'); - const spender = await helper.eth.createAccountWithBalance(alice, 100n); + const spender = await helper.eth.createAccountWithBalance(donor, 100n); const receiver = privateKey('//Charlie'); - const token = await collection.mintToken(alice, {Substrate: owner.address}); + const token = await collection.mintToken(donor, {Substrate: owner.address}); const address = helper.ethAddress.fromCollectionId(collection.collectionId); const contract = helper.ethNativeContract.collection(address, 'nft'); --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -295,14 +295,13 @@ }); itEth('Can perform transferFromCross()', async ({helper, privateKey}) => { - const alice = privateKey('//Alice'); - const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}); + const collection = await helper.rft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'}); const owner = privateKey('//Bob'); - const spender = await helper.eth.createAccountWithBalance(alice, 100n); + const spender = await helper.eth.createAccountWithBalance(donor, 100n); const receiver = privateKey('//Charlie'); - const token = await collection.mintToken(alice, 100n, {Substrate: owner.address}); + const token = await collection.mintToken(donor, 100n, {Substrate: owner.address}); const address = helper.ethAddress.fromCollectionId(collection.collectionId); const contract = helper.ethNativeContract.collection(address, 'rft');