diff --git a/Cargo.lock b/Cargo.lock
index 585296c771..e2cf44a10a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -413,7 +413,7 @@ dependencies = [
[[package]]
name = "astar-collator"
-version = "5.23.0"
+version = "5.24.0"
dependencies = [
"astar-primitives",
"astar-runtime",
@@ -543,7 +543,7 @@ dependencies = [
[[package]]
name = "astar-runtime"
-version = "5.23.0"
+version = "5.24.0"
dependencies = [
"array-bytes 6.1.0",
"astar-primitives",
@@ -695,7 +695,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -706,7 +706,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -819,6 +819,15 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+[[package]]
+name = "basic-toml"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bfc506e7a2370ec239e1d072507b2a80c833083699d3c6fa176fbb4de8448c6"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "bech32"
version = "0.9.1"
@@ -864,13 +873,13 @@ dependencies = [
"lazy_static",
"lazycell",
"peeking_take_while",
- "prettyplease 0.2.10",
+ "prettyplease 0.2.15",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -1160,6 +1169,12 @@ dependencies = [
"thiserror",
]
+[[package]]
+name = "case"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c"
+
[[package]]
name = "cc"
version = "1.0.79"
@@ -1343,7 +1358,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2100,7 +2115,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2398,7 +2413,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2415,7 +2430,7 @@ checksum = "81b2dab6991c7ab1572fea8cb049db819b1aeea1e2dac74c0869f244d9f21a7c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2605,6 +2620,12 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "diff"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
[[package]]
name = "difflib"
version = "0.4.0"
@@ -2690,7 +2711,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2917,7 +2938,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -2928,7 +2949,7 @@ checksum = "c9838a970f5de399d3070ae1739e131986b2f5dcc223c7423ca0927e3a878522"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -3135,13 +3156,13 @@ dependencies = [
"dunce",
"ethers-core",
"eyre",
- "prettyplease 0.2.10",
+ "prettyplease 0.2.15",
"proc-macro2",
"quote",
"regex",
"serde",
"serde_json",
- "syn 2.0.25",
+ "syn 2.0.32",
"toml 0.7.6",
"walkdir",
]
@@ -3159,7 +3180,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_json",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -3185,7 +3206,7 @@ dependencies = [
"serde",
"serde_json",
"strum 0.25.0",
- "syn 2.0.25",
+ "syn 2.0.32",
"tempfile",
"thiserror",
"tiny-keccak",
@@ -3419,7 +3440,7 @@ dependencies = [
"fs-err",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -3946,7 +3967,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -4063,7 +4084,7 @@ dependencies = [
"proc-macro-warning",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -4075,7 +4096,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -4085,7 +4106,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -4263,7 +4284,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -6143,6 +6164,21 @@ dependencies = [
"libc",
]
+[[package]]
+name = "macrotest"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7489ae0986ce45414b7b3122c2e316661343ecf396b206e3e15f07c846616f10"
+dependencies = [
+ "diff",
+ "glob",
+ "prettyplease 0.1.25",
+ "serde",
+ "serde_json",
+ "syn 1.0.109",
+ "toml 0.5.11",
+]
+
[[package]]
name = "maplit"
version = "1.0.2"
@@ -6972,7 +7008,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -6984,7 +7020,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -7641,7 +7677,7 @@ source = "git+https://github.com/AstarNetwork/substrate?branch=astar-polkadot-v0
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -8548,7 +8584,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -9082,7 +9118,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -9133,7 +9169,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -10467,6 +10503,52 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "precompile-utils-macro-v2"
+version = "0.1.0"
+dependencies = [
+ "case",
+ "fp-evm",
+ "frame-support",
+ "macrotest",
+ "num_enum 0.5.11",
+ "precompile-utils-v2",
+ "prettyplease 0.2.15",
+ "proc-macro2",
+ "quote",
+ "sp-core-hashing",
+ "sp-std",
+ "syn 1.0.109",
+ "trybuild",
+]
+
+[[package]]
+name = "precompile-utils-v2"
+version = "0.1.0"
+dependencies = [
+ "derive_more",
+ "environmental",
+ "evm",
+ "fp-evm",
+ "frame-support",
+ "frame-system",
+ "hex",
+ "hex-literal",
+ "impl-trait-for-tuples",
+ "log",
+ "num_enum 0.5.11",
+ "pallet-evm",
+ "parity-scale-codec",
+ "precompile-utils-macro-v2",
+ "scale-info",
+ "serde",
+ "similar-asserts",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "predicates"
version = "2.1.5"
@@ -10509,12 +10591,12 @@ dependencies = [
[[package]]
name = "prettyplease"
-version = "0.2.10"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387"
+checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
dependencies = [
"proc-macro2",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -10589,7 +10671,7 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -10965,7 +11047,7 @@ checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -11603,7 +11685,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -12571,7 +12653,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -12901,7 +12983,7 @@ checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -13017,7 +13099,7 @@ dependencies = [
[[package]]
name = "shibuya-runtime"
-version = "5.23.0"
+version = "5.24.0"
dependencies = [
"array-bytes 6.1.0",
"astar-primitives",
@@ -13124,7 +13206,7 @@ dependencies = [
[[package]]
name = "shiden-runtime"
-version = "5.23.0"
+version = "5.24.0"
dependencies = [
"array-bytes 6.1.0",
"astar-primitives",
@@ -13456,7 +13538,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -13698,7 +13780,7 @@ dependencies = [
"proc-macro2",
"quote",
"sp-core-hashing",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -13717,7 +13799,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.43#5e
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -13928,7 +14010,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14114,7 +14196,7 @@ dependencies = [
"parity-scale-codec",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14316,7 +14398,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14480,9 +14562,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.25"
+version = "2.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
+checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2"
dependencies = [
"proc-macro2",
"quote",
@@ -14579,7 +14661,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14759,7 +14841,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14916,7 +14998,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -14959,7 +15041,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -15127,6 +15209,21 @@ dependencies = [
"zstd 0.12.3+zstd.1.5.2",
]
+[[package]]
+name = "trybuild"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "196a58260a906cedb9bf6d8034b6379d0c11f552416960452f267402ceeddff1"
+dependencies = [
+ "basic-toml",
+ "glob",
+ "once_cell",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "termcolor",
+]
+
[[package]]
name = "tt-call"
version = "1.0.9"
@@ -15160,7 +15257,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"digest 0.10.7",
- "rand 0.7.3",
+ "rand 0.8.5",
"static_assertions",
]
@@ -15409,7 +15506,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
"wasm-bindgen-shared",
]
@@ -15443,7 +15540,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -16573,7 +16670,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
@@ -16706,7 +16803,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.25",
+ "syn 2.0.32",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 4593bcfba4..70c72ef58f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -96,6 +96,7 @@ ethers = { version = "2.0.9", default_features = false }
# Substrate
# (wasm)
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
+sp-core-hashing = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false }
@@ -301,6 +302,7 @@ assets-chain-extension-types = { path = "./chain-extensions/types/assets", defau
unified-accounts-chain-extension-types = { path = "./chain-extensions/types/unified-accounts", default-features = false }
precompile-utils = { path = "./precompiles/utils", default-features = false }
+precompile-utils-v2 = { path = "./precompiles/utils_v2", default-features = false }
local-runtime = { path = "./runtime/local", default-features = false }
shibuya-runtime = { path = "./runtime/shibuya", default-features = false }
diff --git a/bin/collator/Cargo.toml b/bin/collator/Cargo.toml
index a6edaf5836..8f93b8e1ec 100644
--- a/bin/collator/Cargo.toml
+++ b/bin/collator/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "astar-collator"
-version = "5.23.0"
+version = "5.24.0"
description = "Astar collator implementation in Rust."
build = "build.rs"
default-run = "astar-collator"
diff --git a/precompiles/assets-erc20/src/lib.rs b/precompiles/assets-erc20/src/lib.rs
index 3c7eb308ad..cc6a70fce2 100644
--- a/precompiles/assets-erc20/src/lib.rs
+++ b/precompiles/assets-erc20/src/lib.rs
@@ -36,7 +36,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use fp_evm::{IsPrecompileResult, PrecompileHandle, PrecompileOutput};
+use fp_evm::{IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileOutput};
use frame_support::traits::fungibles::approvals::Inspect as ApprovalInspect;
use frame_support::traits::fungibles::metadata::Inspect as MetadataInspect;
use frame_support::traits::fungibles::Inspect;
@@ -47,8 +47,8 @@ use frame_support::{
};
use pallet_evm::{AddressMapping, PrecompileSet};
use precompile_utils::{
- keccak256, succeed, Address, Bytes, EvmData, EvmDataWriter, EvmResult, FunctionModifier,
- LogExt, LogsBuilder, PrecompileHandleExt, RuntimeHelper,
+ keccak256, revert, succeed, Address, Bytes, EvmData, EvmDataWriter, EvmResult,
+ FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt, RuntimeHelper,
};
use sp_runtime::traits::{Bounded, Zero};
@@ -358,12 +358,13 @@ where
input.expect_arguments(2)?;
let to: H160 = input.read::
()?.into();
- let amount = input.read::>()?;
+ let amount: U256 = input.read()?;
// Build call with origin.
{
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let to = Runtime::AddressMapping::into_account_id(to);
+ let amount = Self::u256_to_amount(amount)?;
// Dispatch call (if enough gas).
RuntimeHelper::::try_dispatch(
@@ -400,13 +401,14 @@ where
let from: H160 = input.read::()?.into();
let to: H160 = input.read::()?.into();
- let amount = input.read::>()?;
+ let amount: U256 = input.read()?;
{
let caller: Runtime::AccountId =
Runtime::AddressMapping::into_account_id(handle.context().caller);
let from: Runtime::AccountId = Runtime::AddressMapping::into_account_id(from);
let to: Runtime::AccountId = Runtime::AddressMapping::into_account_id(to);
+ let amount = Self::u256_to_amount(amount)?;
// If caller is "from", it can spend as much as it wants from its own balance.
if caller != from {
@@ -539,7 +541,7 @@ where
input.expect_arguments(2)?;
let beneficiary: H160 = input.read::()?.into();
- let amount = input.read::>()?;
+ let amount = Self::u256_to_amount(input.read::()?)?;
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let beneficiary = Runtime::AddressMapping::into_account_id(beneficiary);
@@ -566,7 +568,7 @@ where
input.expect_arguments(2)?;
let who: H160 = input.read::()?.into();
- let amount = input.read::>()?;
+ let amount = Self::u256_to_amount(input.read::()?)?;
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let who = Runtime::AddressMapping::into_account_id(who);
@@ -584,4 +586,10 @@ where
Ok(succeed(EvmDataWriter::new().write(true).build()))
}
+
+ fn u256_to_amount(value: U256) -> Result, PrecompileFailure> {
+ value
+ .try_into()
+ .map_err(|_| revert("Error processing amount"))
+ }
}
diff --git a/precompiles/assets-erc20/src/tests.rs b/precompiles/assets-erc20/src/tests.rs
index 6751a0288c..a7af894de7 100644
--- a/precompiles/assets-erc20/src/tests.rs
+++ b/precompiles/assets-erc20/src/tests.rs
@@ -204,6 +204,172 @@ fn get_balances_unknown_user() {
});
}
+#[test]
+fn mint_is_ok() {
+ ExtBuilder::default().build().execute_with(|| {
+ let asset_id = 0;
+ assert_ok!(Assets::force_create(
+ RuntimeOrigin::root(),
+ asset_id,
+ Account::Alice.into(),
+ true,
+ 1,
+ ));
+
+ // Sanity check, Bob should be without assets
+ assert!(Assets::balance(asset_id, &Account::Bob.into()).is_zero());
+
+ // Mint some assets for Bob
+ let mint_amount = 7 * 11 * 19;
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(asset_id),
+ EvmDataWriter::new_with_selector(Action::Mint)
+ .write(Address(Account::Bob.into()))
+ .write(U256::from(mint_amount))
+ .build(),
+ )
+ .expect_no_logs()
+ .execute_returns(EvmDataWriter::new().write(true).build());
+
+ // Ensure Bob's asset balance was increased
+ assert_eq!(Assets::balance(asset_id, &Account::Bob.into()), mint_amount);
+ });
+}
+
+#[test]
+fn mint_non_admin_is_not_ok() {
+ ExtBuilder::default().build().execute_with(|| {
+ let asset_id = 0;
+ assert_ok!(Assets::force_create(
+ RuntimeOrigin::root(),
+ asset_id,
+ Account::Alice.into(),
+ true,
+ 1,
+ ));
+
+ precompiles()
+ .prepare_test(
+ Account::Bob,
+ Account::AssetId(asset_id),
+ EvmDataWriter::new_with_selector(Action::Mint)
+ .write(Address(Account::Bob.into()))
+ .write(U256::from(42))
+ .build(),
+ )
+ .expect_no_logs()
+ .execute_reverts(|output| from_utf8(&output).unwrap().contains("NoPermission"));
+
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(0u128),
+ EvmDataWriter::new_with_selector(Action::Mint)
+ .write(Address(Account::Alice.into()))
+ .write(U256::from(1) << 128)
+ .build(),
+ )
+ .execute_reverts(|output| {
+ from_utf8(&output)
+ .unwrap()
+ .contains("Error processing amount")
+ });
+ });
+}
+
+#[test]
+fn burn_is_ok() {
+ ExtBuilder::default().build().execute_with(|| {
+ let asset_id = 0;
+ assert_ok!(Assets::force_create(
+ RuntimeOrigin::root(),
+ asset_id,
+ Account::Alice.into(),
+ true,
+ 1,
+ ));
+
+ // Issue some initial assets for Bob
+ let init_amount = 123;
+ assert_ok!(Assets::mint(
+ RuntimeOrigin::signed(Account::Alice),
+ asset_id,
+ Account::Bob.into(),
+ init_amount,
+ ));
+ assert_eq!(Assets::balance(asset_id, &Account::Bob.into()), init_amount);
+
+ // Burn some assets from Bob
+ let burn_amount = 19;
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(asset_id),
+ EvmDataWriter::new_with_selector(Action::Burn)
+ .write(Address(Account::Bob.into()))
+ .write(U256::from(burn_amount))
+ .build(),
+ )
+ .expect_no_logs()
+ .execute_returns(EvmDataWriter::new().write(true).build());
+
+ // Ensure Bob's asset balance was decreased
+ assert_eq!(
+ Assets::balance(asset_id, &Account::Bob.into()),
+ init_amount - burn_amount
+ );
+ });
+}
+
+#[test]
+fn burn_non_admin_is_not_ok() {
+ ExtBuilder::default().build().execute_with(|| {
+ let asset_id = 0;
+ assert_ok!(Assets::force_create(
+ RuntimeOrigin::root(),
+ asset_id,
+ Account::Alice.into(),
+ true,
+ 1,
+ ));
+ assert_ok!(Assets::mint(
+ RuntimeOrigin::signed(Account::Alice),
+ asset_id,
+ Account::Bob.into(),
+ 1000000,
+ ));
+
+ precompiles()
+ .prepare_test(
+ Account::Bob,
+ Account::AssetId(asset_id),
+ EvmDataWriter::new_with_selector(Action::Burn)
+ .write(Address(Account::Bob.into()))
+ .write(U256::from(42))
+ .build(),
+ )
+ .expect_no_logs()
+ .execute_reverts(|output| from_utf8(&output).unwrap().contains("NoPermission"));
+
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(0u128),
+ EvmDataWriter::new_with_selector(Action::Burn)
+ .write(Address(Account::Alice.into()))
+ .write(U256::from(1) << 128)
+ .build(),
+ )
+ .execute_reverts(|output| {
+ from_utf8(&output)
+ .unwrap()
+ .contains("Error processing amount")
+ });
+ });
+}
+
#[test]
fn approve() {
ExtBuilder::default()
@@ -468,6 +634,21 @@ fn transfer_not_enough_founds() {
.contains("Dispatched call failed with error: DispatchErrorWithPostInfo")
&& from_utf8(&output).unwrap().contains("BalanceLow")
});
+
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(0u128),
+ EvmDataWriter::new_with_selector(Action::Transfer)
+ .write(Address(Account::Charlie.into()))
+ .write(U256::from(1) << 128)
+ .build(),
+ )
+ .execute_reverts(|output| {
+ from_utf8(&output)
+ .unwrap()
+ .contains("Error processing amount")
+ });
});
}
@@ -697,6 +878,22 @@ fn transfer_from_above_allowance() {
error: Module(ModuleError { index: 2, error: [10, 0, 0, 0], \
message: Some(\"Unapproved\") }) }"
});
+
+ precompiles()
+ .prepare_test(
+ Account::Alice,
+ Account::AssetId(0u128),
+ EvmDataWriter::new_with_selector(Action::TransferFrom)
+ .write(Address(Account::Alice.into()))
+ .write(Address(Account::Bob.into()))
+ .write(U256::from(1) << 128)
+ .build(),
+ )
+ .execute_reverts(|output| {
+ from_utf8(&output)
+ .unwrap()
+ .contains("Error processing amount")
+ });
});
}
@@ -845,139 +1042,3 @@ fn minimum_balance_is_right() {
.execute_returns(EvmDataWriter::new().write(expected_min_balance).build());
});
}
-
-#[test]
-fn mint_is_ok() {
- ExtBuilder::default().build().execute_with(|| {
- let asset_id = 0;
- assert_ok!(Assets::force_create(
- RuntimeOrigin::root(),
- asset_id,
- Account::Alice.into(),
- true,
- 1,
- ));
-
- // Sanity check, Bob should be without assets
- assert!(Assets::balance(asset_id, &Account::Bob.into()).is_zero());
-
- // Mint some assets for Bob
- let mint_amount = 7 * 11 * 19;
- precompiles()
- .prepare_test(
- Account::Alice,
- Account::AssetId(asset_id),
- EvmDataWriter::new_with_selector(Action::Mint)
- .write(Address(Account::Bob.into()))
- .write(U256::from(mint_amount))
- .build(),
- )
- .expect_no_logs()
- .execute_returns(EvmDataWriter::new().write(true).build());
-
- // Ensure Bob's asset balance was increased
- assert_eq!(Assets::balance(asset_id, &Account::Bob.into()), mint_amount);
- });
-}
-
-#[test]
-fn mint_non_admin_is_not_ok() {
- ExtBuilder::default().build().execute_with(|| {
- let asset_id = 0;
- assert_ok!(Assets::force_create(
- RuntimeOrigin::root(),
- asset_id,
- Account::Alice.into(),
- true,
- 1,
- ));
-
- precompiles()
- .prepare_test(
- Account::Bob,
- Account::AssetId(asset_id),
- EvmDataWriter::new_with_selector(Action::Mint)
- .write(Address(Account::Bob.into()))
- .write(U256::from(42))
- .build(),
- )
- .expect_no_logs()
- .execute_reverts(|output| from_utf8(&output).unwrap().contains("NoPermission"));
- });
-}
-
-#[test]
-fn burn_is_ok() {
- ExtBuilder::default().build().execute_with(|| {
- let asset_id = 0;
- assert_ok!(Assets::force_create(
- RuntimeOrigin::root(),
- asset_id,
- Account::Alice.into(),
- true,
- 1,
- ));
-
- // Issue some initial assets for Bob
- let init_amount = 123;
- assert_ok!(Assets::mint(
- RuntimeOrigin::signed(Account::Alice),
- asset_id,
- Account::Bob.into(),
- init_amount,
- ));
- assert_eq!(Assets::balance(asset_id, &Account::Bob.into()), init_amount);
-
- // Burn some assets from Bob
- let burn_amount = 19;
- precompiles()
- .prepare_test(
- Account::Alice,
- Account::AssetId(asset_id),
- EvmDataWriter::new_with_selector(Action::Burn)
- .write(Address(Account::Bob.into()))
- .write(U256::from(burn_amount))
- .build(),
- )
- .expect_no_logs()
- .execute_returns(EvmDataWriter::new().write(true).build());
-
- // Ensure Bob's asset balance was decreased
- assert_eq!(
- Assets::balance(asset_id, &Account::Bob.into()),
- init_amount - burn_amount
- );
- });
-}
-
-#[test]
-fn burn_non_admin_is_not_ok() {
- ExtBuilder::default().build().execute_with(|| {
- let asset_id = 0;
- assert_ok!(Assets::force_create(
- RuntimeOrigin::root(),
- asset_id,
- Account::Alice.into(),
- true,
- 1,
- ));
- assert_ok!(Assets::mint(
- RuntimeOrigin::signed(Account::Alice),
- asset_id,
- Account::Bob.into(),
- 1000000,
- ));
-
- precompiles()
- .prepare_test(
- Account::Bob,
- Account::AssetId(asset_id),
- EvmDataWriter::new_with_selector(Action::Burn)
- .write(Address(Account::Bob.into()))
- .write(U256::from(42))
- .build(),
- )
- .expect_no_logs()
- .execute_reverts(|output| from_utf8(&output).unwrap().contains("NoPermission"));
- });
-}
diff --git a/precompiles/utils_v2/Cargo.toml b/precompiles/utils_v2/Cargo.toml
new file mode 100644
index 0000000000..936fd6194e
--- /dev/null
+++ b/precompiles/utils_v2/Cargo.toml
@@ -0,0 +1,53 @@
+[package]
+name = "precompile-utils-v2"
+authors = { workspace = true }
+description = "Utils to write EVM precompiles."
+edition = "2021"
+version = "0.1.0"
+
+[dependencies]
+derive_more = { workspace = true, optional = true }
+environmental = { workspace = true }
+hex = { workspace = true }
+hex-literal = { workspace = true, optional = true }
+impl-trait-for-tuples = { workspace = true }
+log = { workspace = true }
+num_enum = { workspace = true }
+scale-info = { workspace = true, optional = true, features = ["derive"] }
+serde = { workspace = true, optional = true }
+similar-asserts = { workspace = true, optional = true }
+
+# Moonbeam
+precompile-utils-macro-v2 = { path = "macro" }
+
+# Substrate
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+parity-scale-codec = { workspace = true }
+sp-core = { workspace = true }
+sp-io = { workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
+# Frontier
+evm = { workspace = true, features = ["with-codec"] }
+fp-evm = { workspace = true }
+pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }
+
+[dev-dependencies]
+hex-literal = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "environmental/std",
+ "fp-evm/std",
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-evm/std",
+ "parity-scale-codec/std",
+ "sp-core/std",
+ "sp-io/std",
+ "sp-std/std",
+]
+testing = ["derive_more", "hex-literal", "scale-info", "serde", "similar-asserts", "std"]
diff --git a/precompiles/utils_v2/macro/Cargo.toml b/precompiles/utils_v2/macro/Cargo.toml
new file mode 100644
index 0000000000..cc6e820931
--- /dev/null
+++ b/precompiles/utils_v2/macro/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+name = "precompile-utils-macro-v2"
+authors = { workspace = true }
+description = ""
+edition = "2021"
+version = "0.1.0"
+
+[lib]
+proc-macro = true
+
+[[test]]
+name = "tests"
+path = "tests/tests.rs"
+
+[dependencies]
+case = "1.0"
+num_enum = { workspace = true }
+prettyplease = "0.2.12"
+proc-macro2 = "1.0"
+quote = "1.0"
+sp-core-hashing = { workspace = true }
+syn = { version = "1.0", features = ["extra-traits", "fold", "full", "visit"] }
+
+[dev-dependencies]
+macrotest = "1.0.9"
+trybuild = "1.0"
+
+precompile-utils-v2 = { path = "../", features = ["testing"] }
+
+fp-evm = { workspace = true }
+frame-support = { workspace = true }
+sp-core-hashing = { workspace = true }
+sp-std = { workspace = true }
diff --git a/precompiles/utils_v2/macro/docs/precompile_macro.md b/precompiles/utils_v2/macro/docs/precompile_macro.md
new file mode 100644
index 0000000000..e72a4e002a
--- /dev/null
+++ b/precompiles/utils_v2/macro/docs/precompile_macro.md
@@ -0,0 +1,199 @@
+# `#[precompile]` procedural macro.
+
+This procedural macro allows to simplify the implementation of an EVM precompile or precompile set
+using an `impl` block with annotations to automatically generate:
+
+- the implementation of the trait `Precompile` or `PrecompileSet` (exposed by the `fp_evm` crate)
+- parsing of the method parameters from Solidity encoding into Rust type, based on the `solidity::Codec`
+ trait (exposed by the `precompile-utils` crate)
+- a test to ensure the types expressed in the Solidity signature match the Rust types in the
+ implementation.
+
+## How to use
+
+Define your precompile type and write an `impl` block that will contain the precompile methods
+implementation. This `impl` block can have type parameters and a `where` clause, which will be
+reused to generate the `Precompile`/`PrecompileSet` trait implementation and the enum representing
+each public function of precompile with its parsed arguments.
+
+```rust,ignore
+pub struct ExemplePrecompile(PhantomData<(R,I)>);
+
+#[precomile_utils::precompile]
+impl ExemplePrecompile
+where
+ R: pallet_evm::Config
+{
+ #[precompile::public("example(uint32)")]
+ fn example(handle: &mut impl PrecompileHandle, arg: u32) -> EvmResult {
+ Ok(arg * 2)
+ }
+}
+```
+
+The example code above will automatically generate an enum like
+
+```rust,ignore
+#[allow(non_camel_case_types)]
+pub enum ExemplePrecompileCall
+where
+ R: pallet_evm::Config
+{
+ example {
+ arg: u32
+ },
+ // + an non constrible variant with a PhantomData<(R,I)>
+}
+```
+
+This enum have the function `parse_call_data` that can parse the calldata, recognize the Solidity
+4-bytes selector and parse the appropriate enum variant.
+
+It will also generate automatically an implementation of `Precompile`/`PrecompileSet` that calls
+this function and the content of the variant to its associated function of the `impl` block.
+
+## Function attributes
+
+`#[precompile::public("signature")]` allows to declare a function as a public method of the
+precompile with the provided Solidity signature. A function can have multiple `public` attributes to
+support renamed functions with backward compatibility, however the arguments must have the same
+type. It is not allowed to use the exact same signature multiple times.
+
+The function must take a `&mut impl PrecompileHandle` as parameter, followed by all the parameters
+of the Solidity function in the same order. Those parameters types must implement `solidity::Codec`, and
+their name should match the one used in the Solidity interface (.sol) while being in `snake_case`,
+which will automatically be converted to `camelCase` in revert messages. The function must return an
+`EvmResult`, which is an alias of `Result`. This `T` must implement the
+`solidity::Codec` trait and must match the return type in the Solidity interface. The macro will
+automatically encode it to Solidity format.
+
+By default those functions are considered non-payable and non-view (can cause state changes). This
+can be changed using either `#[precompile::payable]` or `#[precompile::view]`. Only one can be used.
+
+It is also possible to declare a fallback function using `#[precompile::fallback]`. This function
+will be called if the selector is unknown or if the input is less than 4-bytes long (no selector).
+This function cannot have any parameter outside of the `PrecompileHandle`. A function can be both
+`public` and `fallback`.
+
+In case some check must be performed before parsing the input, such as forbidding being called from
+some address, a function can be annotated with `#[precompile::pre_check]`:
+
+```rust,ignore
+#[precompile::pre_check]
+fn pre_check(handle: &mut impl PrecompileHandle) -> EvmResult {
+ todo!("Perform your check here")
+}
+```
+
+This function cannot have other attributes.
+
+## PrecompileSet
+
+By default the macro considers the `impl` block to represent a precompile and this will implement
+the `Precompile` trait. If you want to instead implement a precompile set, you must add the
+`#[precompile::precompile_set]` to the `impl` block.
+
+Then, it is necessary to have a function annotated with the `#[precompile::discriminant]` attribute.
+This function is called with the **code address**, the address of the precompile. It must return
+`None` if this address is not part of the precompile set, or `Some` if it is. The `Some` variants
+contains a value of a type of your choice that represents which member of the set this address
+corresponds to. For example for our XC20 precompile sets this function returns the asset id
+corresponding to this address if it exists.
+
+Finally, every other function annotated with a `precompile::_` attribute must now take this
+discriminant as first parameter, before the `PrecompileHandle`.
+
+```rust,ignore
+pub struct ExemplePrecompileSet(PhantomData);
+
+#[precompile_utils::precompile]
+#[precompile::precompile_set]
+impl ExamplePrecompileSet
+where
+ R: pallet_evm::Config
+{
+ #[precompile::discriminant]
+ fn discriminant(address: H160) -> Option {
+ // Replace with your discriminant logic.
+ Some(match address {
+ a if a == H160::from(42) => 1
+ a if a == H160::from(43) => 2,
+ _ => return None,
+ })
+ }
+
+ #[precompile::public("example(uint32)")]
+ fn example(discriminant: u8, handle: &mut impl PrecompileHandle, arg: u32) -> EvmResult {
+ // Discriminant can be used here.
+ Ok(arg * discriminant)
+ }
+}
+```
+
+## Solidity signatures test
+
+The macro will automatically generate a unit test to ensure that the types expressed in a `public`
+attribute matches the Rust parameters of the function, thanks to the `solidity::Codec` trait having the
+`solidity_type() -> String` function.
+
+If any **parsed** argument (discriminant is not concerned) depends on the type parameters of the
+`impl` block, the macro will not be able to produce valid code and output an error like:
+
+```text
+error[E0412]: cannot find type `R` in this scope
+ --> tests/precompile/compile-fail/test/generic-arg.rs:25:63
+ |
+23 | impl> Precompile {
+ | - help: you might be missing a type parameter: ``
+24 | #[precompile::public("foo(bytes)")]
+25 | fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult {
+ | ^ not found in this scope
+```
+
+In this case you need to annotate the `impl` block with the `#[precompile::test_concrete_types(...)]`
+attributes. The `...` should be replaced with concrete types for each type parameter, like a mock
+runtime. Those types are only used to generate the test and only one set of types can be used.
+
+```rust,ignore
+pub struct ExamplePrecompile(PhantomData<(R, I)>);
+
+pub struct GetMaxSize(PhantomData<(R, I)>);
+
+impl Get for GetMaxSize {
+ fn get() -> u32 {
+ >::SomeConstant::get()
+ }
+}
+
+#[precompile_utils::precompile]
+#[precompile::test_concrete_types(mock::Runtime, Instance1)]
+impl ExamplePrecompile
+where
+ R: pallet_evm::Config + SomeConfig
+{
+ #[precompile::public("example(bytes)")]
+ fn example(
+ handle: &mut impl PrecompileHandle,
+ data: BoundedBytes>,
+ ) -> EvmResult {
+ todo!("Method implementation")
+ }
+}
+```
+
+## Enum functions
+
+The generated enums exposes the following public functions:
+
+- `parse_call_data`: take a `PrecompileHandle` and tries to parse the call data. Returns an
+ `EvmResult`. It **DOES NOT** execute the code of the annotated `impl` block.
+- `supports_selector`: take a selector as a `u32` is returns if this selector is supported by the
+ precompile(set) as a `bool`. Note that the presence of a fallback function is not taken into
+ account.
+- `selectors`: returns a static array (`&'static [u32]`) of all the supported selectors.
+- For each variant/public function `foo`, there is a function `foo_selectors` which returns a static
+ array of all the supported selectors **for that function**. That can be used to ensure in tests
+ that some function have a selector that was computed by hand.
+- `encode`: take `self` and encodes it in Solidity format. Additionally, `Vec` implements
+ `From` which simply call encodes. This is useful to write tests as you can construct the
+ variant you want and it will be encoded to Solidity format for you.
diff --git a/precompiles/utils_v2/macro/src/derive_codec.rs b/precompiles/utils_v2/macro/src/derive_codec.rs
new file mode 100644
index 0000000000..f4ef4770e6
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/derive_codec.rs
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::{quote, quote_spanned};
+use syn::{
+ parse_macro_input, punctuated::Punctuated, spanned::Spanned, DeriveInput, Ident, LitStr, Path,
+ PathSegment, PredicateType, TraitBound, TraitBoundModifier,
+};
+
+pub fn main(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ mut generics,
+ data,
+ ..
+ } = parse_macro_input!(input as DeriveInput);
+
+ let syn::Data::Struct(syn::DataStruct {
+ fields: syn::Fields::Named(fields),
+ ..
+ }) = data
+ else {
+ return quote_spanned! { ident.span() =>
+ compile_error!("Codec can only be derived for structs with named fields");
+ }
+ .into();
+ };
+ let fields = fields.named;
+
+ if fields.is_empty() {
+ return quote_spanned! { ident.span() =>
+ compile_error!("Codec can only be derived for structs with at least one field");
+ }
+ .into();
+ }
+
+ if let Some(unamed_field) = fields.iter().find(|f| f.ident.is_none()) {
+ return quote_spanned! { unamed_field.ty.span() =>
+ compile_error!("Codec can only be derived for structs with named fields");
+ }
+ .into();
+ }
+
+ let fields_ty: Vec<_> = fields.iter().map(|f| &f.ty).collect();
+ let fields_ident: Vec<_> = fields
+ .iter()
+ .map(|f| f.ident.as_ref().expect("None case checked above"))
+ .collect();
+ let fields_name_lit: Vec<_> = fields_ident
+ .iter()
+ .map(|i| LitStr::new(&i.to_string(), i.span()))
+ .collect();
+
+ let evm_data_trait_path = {
+ let mut segments = Punctuated::::new();
+ segments.push(Ident::new("precompile_utils", Span::call_site()).into());
+ segments.push(Ident::new("solidity", Span::call_site()).into());
+ segments.push(Ident::new("Codec", Span::call_site()).into());
+ Path {
+ leading_colon: Some(Default::default()),
+ segments,
+ }
+ };
+ let where_clause = generics.make_where_clause();
+
+ for ty in &fields_ty {
+ let mut bounds = Punctuated::new();
+ bounds.push(
+ TraitBound {
+ paren_token: None,
+ modifier: TraitBoundModifier::None,
+ lifetimes: None,
+ path: evm_data_trait_path.clone(),
+ }
+ .into(),
+ );
+
+ where_clause.predicates.push(
+ PredicateType {
+ lifetimes: None,
+ bounded_ty: (*ty).clone(),
+ colon_token: Default::default(),
+ bounds,
+ }
+ .into(),
+ );
+ }
+
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+ quote! {
+ impl #impl_generics ::precompile_utils::solidity::codec::Codec for #ident #ty_generics
+ #where_clause {
+ fn read(
+ reader: &mut ::precompile_utils::solidity::codec::Reader
+ ) -> ::precompile_utils::solidity::revert::MayRevert {
+ use ::precompile_utils::solidity::revert::BacktraceExt as _;
+ let (#(#fields_ident,)*): (#(#fields_ty,)*) = reader
+ .read()
+ .map_in_tuple_to_field(&[#(#fields_name_lit),*])?;
+ Ok(Self {
+ #(#fields_ident,)*
+ })
+ }
+
+ fn write(writer: &mut ::precompile_utils::solidity::codec::Writer, value: Self) {
+ ::precompile_utils::solidity::codec::Codec::write(writer, (#(value.#fields_ident,)*));
+ }
+
+ fn has_static_size() -> bool {
+ <(#(#fields_ty,)*)>::has_static_size()
+ }
+
+ fn signature() -> String {
+ <(#(#fields_ty,)*)>::signature()
+ }
+ }
+ }
+ .into()
+}
diff --git a/precompiles/utils_v2/macro/src/generate_function_selector.rs b/precompiles/utils_v2/macro/src/generate_function_selector.rs
new file mode 100644
index 0000000000..7b0a62e1ce
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/generate_function_selector.rs
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use super::*;
+
+pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
+ let item = parse_macro_input!(input as ItemEnum);
+
+ let ItemEnum {
+ attrs,
+ vis,
+ enum_token,
+ ident,
+ variants,
+ ..
+ } = item;
+
+ let mut ident_expressions: Vec = vec![];
+ let mut variant_expressions: Vec = vec![];
+ let mut variant_attrs: Vec> = vec![];
+ for variant in variants {
+ match variant.discriminant {
+ Some((_, Expr::Lit(ExprLit { lit, .. }))) => {
+ if let Lit::Str(lit_str) = lit {
+ let digest = Keccak256::digest(lit_str.value().as_bytes());
+ let selector = u32::from_be_bytes([digest[0], digest[1], digest[2], digest[3]]);
+ ident_expressions.push(variant.ident);
+ variant_expressions.push(Expr::Lit(ExprLit {
+ lit: Lit::Verbatim(Literal::u32_suffixed(selector)),
+ attrs: Default::default(),
+ }));
+ variant_attrs.push(variant.attrs);
+ } else {
+ return quote_spanned! {
+ lit.span() => compile_error!("Expected literal string");
+ }
+ .into();
+ }
+ }
+ Some((_eg, expr)) => {
+ return quote_spanned! {
+ expr.span() => compile_error!("Expected literal");
+ }
+ .into()
+ }
+ None => {
+ return quote_spanned! {
+ variant.span() => compile_error!("Each variant must have a discriminant");
+ }
+ .into()
+ }
+ }
+ }
+
+ (quote! {
+ #(#attrs)*
+ #[derive(num_enum::TryFromPrimitive, num_enum::IntoPrimitive)]
+ #[repr(u32)]
+ #vis #enum_token #ident {
+ #(
+ #(#variant_attrs)*
+ #ident_expressions = #variant_expressions,
+ )*
+ }
+ })
+ .into()
+}
diff --git a/precompiles/utils_v2/macro/src/lib.rs b/precompiles/utils_v2/macro/src/lib.rs
new file mode 100644
index 0000000000..0b595ca042
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/lib.rs
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+#![crate_type = "proc-macro"]
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use sp_core_hashing::keccak_256;
+use syn::{parse_macro_input, spanned::Spanned, Expr, Ident, ItemType, Lit, LitStr};
+
+mod derive_codec;
+mod precompile;
+mod precompile_name_from_address;
+
+struct Bytes(Vec);
+
+impl ::std::fmt::Debug for Bytes {
+ #[inline]
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
+ let data = &self.0;
+ write!(f, "[")?;
+ if !data.is_empty() {
+ write!(f, "{:#04x}u8", data[0])?;
+ for unit in data.iter().skip(1) {
+ write!(f, ", {:#04x}", unit)?;
+ }
+ }
+ write!(f, "]")
+ }
+}
+
+#[proc_macro]
+pub fn keccak256(input: TokenStream) -> TokenStream {
+ let lit_str = parse_macro_input!(input as LitStr);
+
+ let hash = keccak_256(lit_str.value().as_bytes());
+
+ let bytes = Bytes(hash.to_vec());
+ let eval_str = format!("{:?}", bytes);
+ let eval_ts: proc_macro2::TokenStream = eval_str.parse().unwrap_or_else(|_| {
+ panic!(
+ "Failed to parse the string \"{}\" to TokenStream.",
+ eval_str
+ );
+ });
+ quote!(#eval_ts).into()
+}
+
+#[proc_macro_attribute]
+pub fn precompile(attr: TokenStream, input: TokenStream) -> TokenStream {
+ precompile::main(attr, input)
+}
+
+#[proc_macro_attribute]
+pub fn precompile_name_from_address(attr: TokenStream, input: TokenStream) -> TokenStream {
+ precompile_name_from_address::main(attr, input)
+}
+
+#[proc_macro_derive(Codec)]
+pub fn derive_codec(input: TokenStream) -> TokenStream {
+ derive_codec::main(input)
+}
diff --git a/precompiles/utils_v2/macro/src/precompile/attr.rs b/precompiles/utils_v2/macro/src/precompile/attr.rs
new file mode 100644
index 0000000000..d7c7a459f5
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/precompile/attr.rs
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use proc_macro2::Span;
+use quote::ToTokens;
+use syn::spanned::Spanned;
+
+pub fn take_attributes(attributes: &mut Vec) -> syn::Result>
+where
+ A: syn::parse::Parse,
+{
+ let mut output = vec![];
+ let pred = |attr: &syn::Attribute| {
+ attr.path
+ .segments
+ .first()
+ .map_or(false, |segment| segment.ident == "precompile")
+ };
+
+ while let Some(index) = attributes.iter().position(pred) {
+ let attr = attributes.remove(index);
+ let attr = syn::parse2(attr.into_token_stream())?;
+ output.push(attr)
+ }
+ Ok(output)
+}
+
+/// List of additional token to be used for parsing.
+pub mod keyword {
+ syn::custom_keyword!(precompile);
+ syn::custom_keyword!(public);
+ syn::custom_keyword!(fallback);
+ syn::custom_keyword!(payable);
+ syn::custom_keyword!(view);
+ syn::custom_keyword!(discriminant);
+ syn::custom_keyword!(precompile_set);
+ syn::custom_keyword!(test_concrete_types);
+ syn::custom_keyword!(pre_check);
+}
+
+/// Attributes for methods.
+pub enum MethodAttr {
+ Public(Span, syn::LitStr),
+ Fallback(Span),
+ Payable(Span),
+ View(Span),
+ Discriminant(Span),
+ PreCheck(Span),
+}
+
+impl syn::parse::Parse for MethodAttr {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ input.parse::()?;
+ let content;
+ syn::bracketed!(content in input);
+ content.parse::()?;
+ content.parse::()?;
+
+ let lookahead = content.lookahead1();
+
+ if lookahead.peek(keyword::public) {
+ let span = content.parse::()?.span();
+
+ let inner;
+ syn::parenthesized!(inner in content);
+ let signature = inner.parse::()?;
+
+ Ok(MethodAttr::Public(span, signature))
+ } else if lookahead.peek(keyword::fallback) {
+ Ok(MethodAttr::Fallback(
+ content.parse::()?.span(),
+ ))
+ } else if lookahead.peek(keyword::payable) {
+ Ok(MethodAttr::Payable(
+ content.parse::()?.span(),
+ ))
+ } else if lookahead.peek(keyword::view) {
+ Ok(MethodAttr::View(content.parse::()?.span()))
+ } else if lookahead.peek(keyword::discriminant) {
+ Ok(MethodAttr::Discriminant(
+ content.parse::()?.span(),
+ ))
+ } else if lookahead.peek(keyword::pre_check) {
+ Ok(MethodAttr::PreCheck(
+ content.parse::()?.span(),
+ ))
+ } else {
+ Err(lookahead.error())
+ }
+ }
+}
+
+/// Attributes for the main impl Block.
+pub enum ImplAttr {
+ PrecompileSet(Span),
+ TestConcreteTypes(Span, Vec),
+}
+
+impl syn::parse::Parse for ImplAttr {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ input.parse::()?;
+ let content;
+ syn::bracketed!(content in input);
+ content.parse::()?;
+ content.parse::()?;
+
+ let lookahead = content.lookahead1();
+
+ if lookahead.peek(keyword::precompile_set) {
+ Ok(ImplAttr::PrecompileSet(
+ content.parse::()?.span(),
+ ))
+ } else if lookahead.peek(keyword::test_concrete_types) {
+ let span = content.parse::()?.span();
+
+ let inner;
+ syn::parenthesized!(inner in content);
+ let types = inner.parse_terminated::<_, syn::Token![,]>(syn::Type::parse)?;
+
+ Ok(ImplAttr::TestConcreteTypes(
+ span,
+ types.into_iter().collect(),
+ ))
+ } else {
+ Err(lookahead.error())
+ }
+ }
+}
diff --git a/precompiles/utils_v2/macro/src/precompile/expand.rs b/precompiles/utils_v2/macro/src/precompile/expand.rs
new file mode 100644
index 0000000000..aed4fce127
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/precompile/expand.rs
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use super::*;
+
+impl Precompile {
+ /// Main expand function, which expands everything else.
+ pub fn expand(&self) -> impl ToTokens {
+ let enum_ = self.expand_enum_decl();
+ let enum_impl = self.expand_enum_impl();
+ let precomp_impl = self.expand_precompile_impl();
+ let test_signature = self.expand_test_solidity_signature();
+
+ quote! {
+ #enum_
+ #enum_impl
+ #precomp_impl
+ #test_signature
+ }
+ }
+
+ /// Expands the call enum declaration.
+ pub fn expand_enum_decl(&self) -> impl ToTokens {
+ let enum_ident = &self.enum_ident;
+ let (_impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
+
+ let type_parameters = self.generics.type_params().map(|p| &p.ident);
+
+ let variants: Vec<_> = self.variants_content.keys().collect();
+ let idents: Vec> = self
+ .variants_content
+ .values()
+ .map(|v| v.arguments.iter().map(|a| &a.ident).collect())
+ .collect();
+ let types: Vec> = self
+ .variants_content
+ .values()
+ .map(|v| v.arguments.iter().map(|a| &a.ty).collect())
+ .collect();
+
+ quote!(
+ #[allow(non_camel_case_types)]
+ pub enum #enum_ident #ty_generics #where_clause {
+ #(
+ #variants {
+ #(
+ #idents: #types
+ ),*
+ },
+ )*
+
+ #[doc(hidden)]
+ __phantom(
+ ::core::marker::PhantomData<( #( #type_parameters ),* )>,
+ ::core::convert::Infallible
+ ),
+ }
+ )
+ }
+
+ /// Expands the parse function for each variants.
+ pub fn expand_variants_parse_fn(&self) -> impl ToTokens {
+ let span = Span::call_site();
+
+ let fn_parse = self
+ .variants_content
+ .keys()
+ .map(Self::variant_ident_to_parse_fn);
+
+ let modifier_check = self.variants_content.values().map(|variant| {
+ let modifier = match variant.modifier {
+ Modifier::NonPayable => "NonPayable",
+ Modifier::Payable => "Payable",
+ Modifier::View => "View",
+ };
+
+ let modifier = syn::Ident::new(modifier, span);
+
+ quote!(
+ use ::precompile_utils::solidity::modifier::FunctionModifier;
+ use ::precompile_utils::evm::handle::PrecompileHandleExt;
+ handle.check_function_modifier(FunctionModifier::#modifier)?;
+ )
+ });
+
+ let variant_parsing = self
+ .variants_content
+ .iter()
+ .map(|(variant_ident, variant)| {
+ Self::expand_variant_parsing_from_handle(variant_ident, variant)
+ });
+
+ quote!(
+ #(
+ fn #fn_parse(
+ handle: &mut impl PrecompileHandle
+ ) -> ::precompile_utils::EvmResult {
+ use ::precompile_utils::solidity::revert::InjectBacktrace;
+
+ #modifier_check
+ #variant_parsing
+ }
+ )*
+ )
+ }
+
+ /// Generates the parsing code for a variant, reading the input from the handle and
+ /// parsing it using Reader.
+ fn expand_variant_parsing_from_handle(
+ variant_ident: &syn::Ident,
+ variant: &Variant,
+ ) -> impl ToTokens {
+ if variant.arguments.is_empty() {
+ quote!( Ok(Self::#variant_ident {})).to_token_stream()
+ } else {
+ use case::CaseExt;
+
+ let args_parse = variant.arguments.iter().map(|arg| {
+ let ident = &arg.ident;
+ let span = ident.span();
+ let name = ident.to_string().to_camel_lowercase();
+
+ quote_spanned!(span=> #ident: input.read().in_field(#name)?,)
+ });
+ let args_count = variant.arguments.len();
+
+ quote!(
+ let mut input = handle.read_after_selector()?;
+ input.expect_arguments(#args_count)?;
+
+ Ok(Self::#variant_ident {
+ #(#args_parse)*
+ })
+ )
+ .to_token_stream()
+ }
+ }
+
+ /// Expands the call enum impl block.
+ pub fn expand_enum_impl(&self) -> impl ToTokens {
+ let enum_ident = &self.enum_ident;
+ let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
+
+ let match_selectors = self.selector_to_variant.keys();
+ let match_selectors2 = self.selector_to_variant.keys();
+
+ let variants_parsing = self.expand_variants_parse_fn();
+
+ let variants_ident2: Vec<_> = self.variants_content.keys().collect();
+ let variants_selectors_fn: Vec<_> = self
+ .variants_content
+ .keys()
+ .map(|name| format_ident!("{}_selectors", name))
+ .collect();
+ let variants_selectors: Vec<_> = self
+ .variants_content
+ .values()
+ .map(|variant| &variant.selectors)
+ .collect();
+
+ let variants_list: Vec> = self
+ .variants_content
+ .values()
+ .map(|variant| variant.arguments.iter().map(|arg| &arg.ident).collect())
+ .collect();
+
+ let variants_encode: Vec<_> = self
+ .variants_content
+ .values()
+ .map(Self::expand_variant_encoding)
+ .collect();
+
+ let parse_call_data_fn = self.expand_enum_parse_call_data();
+ let execute_fn = self.expand_enum_execute_fn();
+
+ quote!(
+ impl #impl_generics #enum_ident #ty_generics #where_clause {
+ #parse_call_data_fn
+
+ #variants_parsing
+
+ #execute_fn
+
+ pub fn supports_selector(selector: u32) -> bool {
+ match selector {
+ #(
+ #match_selectors => true,
+ )*
+ _ => false,
+ }
+ }
+
+ pub fn selectors() -> &'static [u32] {
+ &[#(
+ #match_selectors2
+ ),*]
+ }
+
+ #(
+ pub fn #variants_selectors_fn() -> &'static [u32] {
+ &[#(
+ #variants_selectors
+ ),*]
+ }
+ )*
+
+ pub fn encode(self) -> ::sp_std::vec::Vec {
+ use ::precompile_utils::solidity::codec::Writer;
+ match self {
+ #(
+ Self::#variants_ident2 { #(#variants_list),* } => {
+ #variants_encode
+ },
+ )*
+ Self::__phantom(_, _) => panic!("__phantom variant should not be used"),
+ }
+ }
+ }
+
+ impl #impl_generics From<#enum_ident #ty_generics> for ::sp_std::vec::Vec
+ #where_clause
+ {
+ fn from(a: #enum_ident #ty_generics) -> ::sp_std::vec::Vec {
+ a.encode()
+ }
+ }
+ )
+ }
+
+ /// Expand the execute fn of the enum.
+ fn expand_enum_execute_fn(&self) -> impl ToTokens {
+ let impl_type = &self.impl_type;
+
+ let variants_ident: Vec<_> = self.variants_content.keys().collect();
+
+ let variants_arguments: Vec> = self
+ .variants_content
+ .values()
+ .map(|variant| variant.arguments.iter().map(|arg| &arg.ident).collect())
+ .collect();
+
+ // If there is no precompile set there is no discriminant.
+ let opt_discriminant_arg = self
+ .precompile_set_discriminant_type
+ .as_ref()
+ .map(|ty| quote!( discriminant: #ty,));
+
+ let variants_call = self
+ .variants_content
+ .iter()
+ .map(|(variant_ident, variant)| {
+ let arguments = variant.arguments.iter().map(|arg| &arg.ident);
+
+ let output_span = variant.fn_output.span();
+ let opt_discriminant_arg = self
+ .precompile_set_discriminant_fn
+ .as_ref()
+ .map(|_| quote!(discriminant,));
+
+ let write_output = quote_spanned!(output_span=>
+ ::precompile_utils::solidity::encode_return_value(output?)
+ );
+
+ quote!(
+ let output = <#impl_type>::#variant_ident(
+ #opt_discriminant_arg
+ handle,
+ #(#arguments),*
+ );
+ #write_output
+ )
+ });
+
+ quote!(
+ pub fn execute(
+ self,
+ #opt_discriminant_arg
+ handle: &mut impl PrecompileHandle
+ ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> {
+ use ::precompile_utils::solidity::codec::Writer;
+ use ::fp_evm::{PrecompileOutput, ExitSucceed};
+
+ let output = match self {
+ #(
+ Self::#variants_ident { #(#variants_arguments),* } => {
+ #variants_call
+ },
+ )*
+ Self::__phantom(_, _) => panic!("__phantom variant should not be used"),
+ };
+
+ Ok(PrecompileOutput {
+ exit_status: ExitSucceed::Returned,
+ output
+ })
+ }
+ )
+ }
+
+ /// Expand how a variant can be Solidity encoded.
+ fn expand_variant_encoding(variant: &Variant) -> impl ToTokens {
+ match variant.selectors.first() {
+ Some(selector) => {
+ let write_arguments = variant.arguments.iter().map(|arg| {
+ let ident = &arg.ident;
+ let span = ident.span();
+ quote_spanned!(span=> .write(#ident))
+ });
+
+ quote!(
+ Writer::new_with_selector(#selector)
+ #(#write_arguments)*
+ .build()
+ )
+ .to_token_stream()
+ }
+ None => quote!(Default::default()).to_token_stream(),
+ }
+ }
+
+ /// Expand the main parsing function that, based on the selector in the
+ /// input, dispatch the decoding to one of the variants parsing function.
+ fn expand_enum_parse_call_data(&self) -> impl ToTokens {
+ let selectors = self.selector_to_variant.keys();
+ let parse_fn = self
+ .selector_to_variant
+ .values()
+ .map(Self::variant_ident_to_parse_fn);
+
+ let match_fallback = match &self.fallback_to_variant {
+ Some(variant) => {
+ let parse_fn = Self::variant_ident_to_parse_fn(variant);
+ quote!(_ => Self::#parse_fn(handle),).to_token_stream()
+ }
+ None => quote!(
+ Some(_) => Err(RevertReason::UnknownSelector.into()),
+ None => Err(RevertReason::read_out_of_bounds("selector").into()),
+ )
+ .to_token_stream(),
+ };
+
+ quote!(
+ pub fn parse_call_data(
+ handle: &mut impl PrecompileHandle
+ ) -> ::precompile_utils::EvmResult {
+ use ::precompile_utils::solidity::revert::RevertReason;
+
+ let input = handle.input();
+
+ let selector = input.get(0..4).map(|s| {
+ let mut buffer = [0u8; 4];
+ buffer.copy_from_slice(s);
+ u32::from_be_bytes(buffer)
+ });
+
+ match selector {
+ #(
+ Some(#selectors) => Self::#parse_fn(handle),
+ )*
+ #match_fallback
+ }
+ }
+ )
+ }
+
+ fn variant_ident_to_parse_fn(ident: &syn::Ident) -> syn::Ident {
+ format_ident!("_parse_{}", ident)
+ }
+
+ /// Expands the impl of the Precomile(Set) trait.
+ pub fn expand_precompile_impl(&self) -> impl ToTokens {
+ let impl_type = &self.impl_type;
+ let enum_ident = &self.enum_ident;
+ let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
+
+ if let Some(discriminant_fn) = &self.precompile_set_discriminant_fn {
+ let opt_pre_check = self.pre_check.as_ref().map(|ident| {
+ let span = ident.span();
+ quote_spanned!(span=>
+ let _: () = <#impl_type>::#ident(discriminant, handle)
+ .map_err(|err| Some(err))?;
+ )
+ });
+
+ quote!(
+ impl #impl_generics ::fp_evm::PrecompileSet for #impl_type #where_clause {
+ fn execute(
+ &self,
+ handle: &mut impl PrecompileHandle
+ ) -> Option<::precompile_utils::EvmResult<::fp_evm::PrecompileOutput>> {
+ use ::precompile_utils::precompile_set::DiscriminantResult;
+
+ let discriminant = <#impl_type>::#discriminant_fn(
+ handle.code_address(),
+ handle.remaining_gas()
+ );
+
+ if let DiscriminantResult::Some(_, cost) | DiscriminantResult::None(cost) = discriminant {
+ let result = handle.record_cost(cost);
+ if let Err(e) = result {
+ return Some(Err(e.into()));
+ }
+ }
+
+ let discriminant = match discriminant {
+ DiscriminantResult::Some(d, _) => d,
+ DiscriminantResult::None(cost) => return None,
+ DiscriminantResult::OutOfGas => return Some(Err(ExitError::OutOfGas.into()))
+ };
+
+ #opt_pre_check
+
+ Some(
+ <#enum_ident #ty_generics>::parse_call_data(handle)
+ .and_then(|call| call.execute(discriminant, handle))
+ )
+ }
+
+ fn is_precompile(&self, address: H160, gas: u64) -> ::fp_evm::IsPrecompileResult {
+ <#impl_type>::#discriminant_fn(address, gas).into()
+ }
+ }
+ )
+ .to_token_stream()
+ } else {
+ let opt_pre_check = self.pre_check.as_ref().map(|ident| {
+ let span = ident.span();
+ quote_spanned!(span=>let _: () = <#impl_type>::#ident(handle)?;)
+ });
+
+ quote!(
+ impl #impl_generics ::fp_evm::Precompile for #impl_type #where_clause {
+ fn execute(
+ handle: &mut impl PrecompileHandle
+ ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> {
+ #opt_pre_check
+
+ <#enum_ident #ty_generics>::parse_call_data(handle)?.execute(handle)
+ }
+ }
+ )
+ .to_token_stream()
+ }
+ }
+
+ /// Expands the Solidity signature test.
+ /// The macro expands an "inner" function in all build profiles, which is
+ /// then called by a test in test profile. This allows to display errors that occurs in
+ /// the expansion of the test without having to build in test profile, which is usually
+ /// related to the use of a type parameter in one of the parsed parameters of a method.
+ pub fn expand_test_solidity_signature(&self) -> impl ToTokens {
+ let variant_test: Vec<_> = self
+ .variants_content
+ .iter()
+ .map(|(ident, variant)| {
+ let span = ident.span();
+
+ let solidity = &variant.solidity_arguments_type;
+ let name = ident.to_string();
+ let types: Vec<_> = variant.arguments.iter().map(|arg| &arg.ty).collect();
+
+ quote_spanned!(span=>
+ assert_eq!(
+ #solidity,
+ <(#(#types,)*) as Codec>::signature(),
+ "{} function signature doesn't match (left: attribute, right: computed \
+ from Rust types)",
+ #name
+ );
+ )
+ })
+ .collect();
+
+ let test_name = format_ident!("__{}_test_solidity_signatures", self.impl_ident);
+ let inner_name = format_ident!("__{}_test_solidity_signatures_inner", self.impl_ident);
+
+ if let Some(test_types) = &self.test_concrete_types {
+ let (impl_generics, _ty_generics, where_clause) = self.generics.split_for_impl();
+
+ quote!(
+ #[allow(non_snake_case)]
+ pub(crate) fn #inner_name #impl_generics () #where_clause {
+ use ::precompile_utils::solidity::Codec;
+ #(#variant_test)*
+ }
+
+ #[test]
+ #[allow(non_snake_case)]
+ fn #test_name() {
+ #inner_name::< #(#test_types),* >();
+ }
+ )
+ .to_token_stream()
+ } else {
+ quote!(
+ #[allow(non_snake_case)]
+ pub(crate) fn #inner_name() {
+ use ::precompile_utils::solidity::Codec;
+ #(#variant_test)*
+ }
+
+ #[test]
+ #[allow(non_snake_case)]
+ fn #test_name() {
+ #inner_name();
+ }
+ )
+ .to_token_stream()
+ }
+ }
+}
diff --git a/precompiles/utils_v2/macro/src/precompile/mod.rs b/precompiles/utils_v2/macro/src/precompile/mod.rs
new file mode 100644
index 0000000000..2c8ac1df8e
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/precompile/mod.rs
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+#![doc = include_str!("../../docs/precompile_macro.md")]
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::{format_ident, quote, quote_spanned, ToTokens};
+use sp_core_hashing::keccak_256;
+use std::collections::BTreeMap;
+use syn::{parse_macro_input, spanned::Spanned};
+
+pub mod attr;
+pub mod expand;
+pub mod parse;
+
+pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ // Macro must be used on `impl` block.
+ let mut impl_item = parse_macro_input!(item as syn::ItemImpl);
+
+ // We inspect the block to collect all the data we need for the
+ // expansion, and make various checks.
+ let precompile = match Precompile::try_from(&mut impl_item) {
+ Ok(p) => p,
+ Err(e) => return e.into_compile_error().into(),
+ };
+
+ // We generate additional code based on the collected data.
+ let new_items = precompile.expand();
+ let output = quote!(
+ #impl_item
+ #new_items
+ );
+
+ output.into()
+}
+
+struct Precompile {
+ /// Impl struct type.
+ impl_type: syn::Type,
+
+ /// Impl struct ident.
+ impl_ident: syn::Ident,
+
+ /// New parsing enum ident.
+ enum_ident: syn::Ident,
+
+ /// Generic part that needs to also be used by the input enum.
+ generics: syn::Generics,
+
+ /// Which selector corresponds to which variant of the input enum.
+ selector_to_variant: BTreeMap,
+
+ /// Optional fallback function if no selector matches.
+ fallback_to_variant: Option,
+
+ /// Describes the content of each variant based on the precompile methods.
+ variants_content: BTreeMap,
+
+ /// Since being a precompile set implies lots of changes, we must know it early
+ /// in the form of an attribute on the impl block itself.
+ tagged_as_precompile_set: bool,
+
+ /// Ident of the function returning the PrecompileSet discriminant.
+ precompile_set_discriminant_fn: Option,
+
+ /// Type of the PrecompileSet discriminant.
+ precompile_set_discriminant_type: Option,
+
+ /// When generating the selector test the data types might depend on type parameters.
+ /// The test thus need to be written using concrete types.
+ test_concrete_types: Option>,
+
+ /// Ident of a function that performs a check before the call is dispatched to the proper
+ /// function.
+ pre_check: Option,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+enum Modifier {
+ NonPayable,
+ Payable,
+ View,
+}
+
+#[derive(Debug)]
+struct Variant {
+ /// Description of the arguments of this method, which will also
+ /// be members of a struct variant.
+ arguments: Vec,
+
+ /// String extracted from the selector attribute.
+ /// A unit test will be generated to check that this selector matches
+ /// the Rust arguments.
+ ///
+ /// > solidity::Codec trait allows to generate this string at runtime only. Thus
+ /// > it is required to write it manually in the selector attribute, and
+ /// > a unit test is generated to check it matches.
+ solidity_arguments_type: String,
+
+ /// Modifier of the function. They are all exclusive and defaults to
+ /// `NonPayable`.
+ modifier: Modifier,
+
+ /// Selectors of this function to be able to encode back the data.
+ /// Empty if it only the fallback function.
+ selectors: Vec,
+
+ /// Output of the variant fn (for better error messages).
+ fn_output: syn::Type,
+}
+
+#[derive(Debug)]
+struct Argument {
+ /// Identifier of the argument, which will be used in the struct variant.
+ ident: syn::Ident,
+
+ /// Type of the argument, which will be used in the struct variant and
+ /// to parse the input.
+ ty: syn::Type,
+}
diff --git a/precompiles/utils_v2/macro/src/precompile/parse.rs b/precompiles/utils_v2/macro/src/precompile/parse.rs
new file mode 100644
index 0000000000..a41a1ba650
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/precompile/parse.rs
@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use super::*;
+
+impl Precompile {
+ /// Try to extract information out of an annotated `impl` block.
+ pub fn try_from(impl_: &mut syn::ItemImpl) -> syn::Result {
+ // Extract the name of the type used in the `impl` block.
+ let impl_ident = Self::extract_impl_ident(impl_)?;
+ let enum_ident = format_ident!("{}Call", impl_ident);
+
+ // We setup the data collection struct.
+ let mut precompile = Precompile {
+ impl_type: impl_.self_ty.as_ref().clone(),
+ impl_ident,
+ enum_ident,
+ generics: impl_.generics.clone(),
+ selector_to_variant: BTreeMap::new(),
+ variants_content: BTreeMap::new(),
+ fallback_to_variant: None,
+ tagged_as_precompile_set: false,
+ precompile_set_discriminant_fn: None,
+ precompile_set_discriminant_type: None,
+ test_concrete_types: None,
+ pre_check: None,
+ };
+
+ precompile.process_impl_attr(impl_)?;
+ for mut item in &mut impl_.items {
+ // We only interact with methods and leave the rest as-is.
+ if let syn::ImplItem::Method(ref mut method) = &mut item {
+ precompile.process_method(method)?;
+ }
+ }
+
+ // Check constraint of PrecompileSet.
+ if precompile.tagged_as_precompile_set
+ && precompile.precompile_set_discriminant_fn.is_none()
+ {
+ let msg = "A PrecompileSet must have exactly one function tagged with \
+ `#[precompile::discriminant]`";
+ return Err(syn::Error::new(Span::call_site(), msg));
+ }
+
+ Ok(precompile)
+ }
+
+ /// Process the attributes used on the `impl` block, which allows to declare
+ /// if it is a PrecompileSet or not, and to provide concrete types for tests if necessary.
+ fn process_impl_attr(&mut self, impl_: &mut syn::ItemImpl) -> syn::Result<()> {
+ let attrs = attr::take_attributes::(&mut impl_.attrs)?;
+
+ for attr in attrs {
+ match attr {
+ attr::ImplAttr::PrecompileSet(_) => {
+ self.tagged_as_precompile_set = true;
+ }
+ attr::ImplAttr::TestConcreteTypes(span, types) => {
+ if types.len() != self.generics.params.len() {
+ let msg = "The amount of types should match the amount of type parameters \
+ of the impl block";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ if self.test_concrete_types.is_some() {
+ let msg = "Only one set of types can be provided to generate tests";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ self.test_concrete_types = Some(types);
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Extract the ident of the type of the `impl` block.
+ /// This ident is used to generate new idents such as the name of the Call enum and
+ /// the Solidity selector test.
+ fn extract_impl_ident(impl_: &syn::ItemImpl) -> syn::Result {
+ let type_path = match impl_.self_ty.as_ref() {
+ syn::Type::Path(p) => p,
+ _ => {
+ let msg = "The type in the impl block must be a path, like `Precompile` or
+ `example::Precompile`";
+ return Err(syn::Error::new(impl_.self_ty.span(), msg));
+ }
+ };
+
+ let final_path = type_path.path.segments.last().ok_or_else(|| {
+ let msg = "The type path must be non empty.";
+ syn::Error::new(impl_.self_ty.span(), msg)
+ })?;
+
+ Ok(final_path.ident.clone())
+ }
+
+ /// Process a single method, looking for attributes and checking mandatory parameters.
+ fn process_method(&mut self, method: &mut syn::ImplItemMethod) -> syn::Result<()> {
+ // Take (remove) all attributes related to this macro.
+ let attrs = attr::take_attributes::(&mut method.attrs)?;
+
+ // If there are no attributes it is a private function and we ignore it.
+ if attrs.is_empty() {
+ return Ok(());
+ }
+
+ // A method cannot have modifiers if it isn't a fallback and/or doesn't have a selector.
+ let mut used = false;
+
+ let method_name = method.sig.ident.clone();
+ let mut modifier = Modifier::NonPayable;
+ let mut solidity_arguments_type: Option = None;
+ let mut arguments = vec![];
+ let mut is_fallback = false;
+ let mut selectors = vec![];
+ let initial_arguments = if self.tagged_as_precompile_set { 2 } else { 1 };
+
+ // We first look for unique attributes.
+ if let Some(attr::MethodAttr::Discriminant(span)) = attrs.first() {
+ let span = *span;
+
+ if attrs.len() != 1 {
+ let msg = "The discriminant attribute must be the only precompile attribute of \
+ a function";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ return self.parse_discriminant_fn(span, method);
+ }
+
+ if let Some(attr::MethodAttr::PreCheck(span)) = attrs.first() {
+ let span = *span;
+
+ if attrs.len() != 1 {
+ let msg = "The pre_check attribute must be the only precompile attribute of \
+ a function";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ return self.parse_pre_check_fn(span, method);
+ }
+
+ // We iterate over all attributes of the method.
+ for attr in attrs {
+ match attr {
+ attr::MethodAttr::Discriminant(span) => {
+ let msg = "The discriminant attribute must be the only precompile \
+ attribute of the function";
+ return Err(syn::Error::new(span, msg));
+ }
+ attr::MethodAttr::PreCheck(span) => {
+ let msg = "The pre_check attribute must be the only precompile \
+ attribute of the function";
+ return Err(syn::Error::new(span, msg));
+ }
+ attr::MethodAttr::Fallback(span) => {
+ if self.fallback_to_variant.is_some() {
+ let msg = "A precompile can only have 1 fallback function";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ self.fallback_to_variant = Some(method_name.clone());
+ used = true;
+ is_fallback = true;
+ }
+ attr::MethodAttr::Payable(span) => {
+ if modifier != Modifier::NonPayable {
+ let msg =
+ "A precompile method can have at most one modifier (payable, view)";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ modifier = Modifier::Payable;
+ }
+ attr::MethodAttr::View(span) => {
+ if modifier != Modifier::NonPayable {
+ let msg =
+ "A precompile method can have at most one modifier (payable, view)";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ modifier = Modifier::View;
+ }
+ attr::MethodAttr::Public(_, signature_lit) => {
+ used = true;
+
+ let selector = self.parse_public_attr(
+ signature_lit,
+ &method_name,
+ &mut solidity_arguments_type,
+ )?;
+ selectors.push(selector);
+ }
+ }
+ }
+
+ // A method cannot have attributes without being public or fallback.
+ if !used {
+ let msg =
+ "A precompile method cannot have modifiers without being a fallback or having\
+ a `public` attribute";
+ return Err(syn::Error::new(method.span(), msg));
+ }
+
+ // We forbid type parameters.
+ if let Some(param) = method.sig.generics.params.first() {
+ let msg = "Exposed precompile methods cannot have type parameters";
+ return Err(syn::Error::new(param.span(), msg));
+ }
+
+ // Fallback method cannot have custom parameters.
+ if is_fallback {
+ if let Some(input) = method.sig.inputs.iter().nth(initial_arguments) {
+ let msg = if self.tagged_as_precompile_set {
+ "Fallback methods cannot take any parameter outside of the discriminant and \
+ PrecompileHandle"
+ } else {
+ "Fallback methods cannot take any parameter outside of the PrecompileHandle"
+ };
+
+ return Err(syn::Error::new(input.span(), msg));
+ }
+ }
+
+ let mut method_inputs = method.sig.inputs.iter();
+
+ // We check the first parameters of the method.
+ // If this is a PrecompileSet it will look for a discriminant.
+ // Then for all precompile(set)s it will look for the PrecompileHandle.
+ // We take them from the iterator such that we are only left with the
+ // custom arguments.
+ self.check_initial_parameters(&mut method_inputs, method.sig.span())?;
+
+ // We go through each parameter to collect each name and type that will be used to
+ // generate the input enum and parse the call data.
+ for input in method_inputs {
+ let input = match input {
+ syn::FnArg::Typed(t) => t,
+ _ => {
+ // I don't think it is possible to encounter this error since a self receiver
+ // seems to only be possible in the first position which is checked in
+ // `check_initial_parameters`.
+ let msg = "Exposed precompile methods cannot have a `self` parameter";
+ return Err(syn::Error::new(input.span(), msg));
+ }
+ };
+
+ let msg = "Parameter must be of the form `name: Type`, optionally prefixed by `mut`";
+ let ident = match input.pat.as_ref() {
+ syn::Pat::Ident(pat) => {
+ if pat.by_ref.is_some() || pat.subpat.is_some() {
+ return Err(syn::Error::new(pat.span(), msg));
+ }
+
+ pat.ident.clone()
+ }
+ _ => {
+ return Err(syn::Error::new(input.pat.span(), msg));
+ }
+ };
+ let ty = input.ty.as_ref().clone();
+ self.check_type_parameter_usage(&ty)?;
+
+ arguments.push(Argument { ident, ty })
+ }
+
+ // Function output.
+ let output_type = match &method.sig.output {
+ syn::ReturnType::Type(_, t) => t,
+ _ => {
+ let msg = "A precompile method must have a return type of `EvmResult<_>` (exposed \
+ by `precompile_utils`)";
+ return Err(syn::Error::new(method.sig.span(), msg));
+ }
+ };
+
+ // We insert the collected data in self.
+ if self
+ .variants_content
+ .insert(
+ method_name.clone(),
+ Variant {
+ arguments,
+ solidity_arguments_type: solidity_arguments_type.unwrap_or(String::from("()")),
+ modifier,
+ selectors,
+ fn_output: output_type.as_ref().clone(),
+ },
+ )
+ .is_some()
+ {
+ let msg = "Duplicate method name";
+ return Err(syn::Error::new(method_name.span(), msg));
+ }
+
+ Ok(())
+ }
+
+ /// Check the initial parameters of most methods of a Precompile(Set).
+ fn check_initial_parameters<'a>(
+ &mut self,
+ method_inputs: &mut impl Iterator- ,
+ method_span: Span,
+ ) -> syn::Result<()> {
+ // Discriminant input
+ if self.tagged_as_precompile_set {
+ let input = match method_inputs.next() {
+ Some(a) => a,
+ None => {
+ let msg = "PrecompileSet methods must have at least 2 parameters (the \
+ precompile instance discriminant and the PrecompileHandle)";
+ return Err(syn::Error::new(method_span, msg));
+ }
+ };
+
+ let input = match input {
+ syn::FnArg::Typed(a) => a,
+ _ => {
+ let msg = "self is not allowed in precompile methods";
+ return Err(syn::Error::new(input.span(), msg));
+ }
+ };
+
+ let input_type = input.ty.as_ref();
+
+ self.try_register_discriminant_type(input_type)?;
+ }
+
+ // Precompile handle input
+ {
+ let input = match method_inputs.next() {
+ Some(a) => a,
+ None => {
+ let msg = if self.tagged_as_precompile_set {
+ "PrecompileSet methods must have at least 2 parameters (the precompile \
+ instance discriminant and the PrecompileHandle)"
+ } else {
+ "Precompile methods must have at least 1 parameter (the PrecompileHandle)"
+ };
+
+ return Err(syn::Error::new(method_span, msg));
+ }
+ };
+
+ let input = match input {
+ syn::FnArg::Typed(a) => a,
+ _ => {
+ let msg = "self is not allowed in precompile methods";
+ return Err(syn::Error::new(input.span(), msg));
+ }
+ };
+
+ let input_type = input.ty.as_ref();
+
+ if !is_same_type(input_type, &syn::parse_quote! {&mut impl PrecompileHandle}) {
+ let msg = "This parameter must have type `&mut impl PrecompileHandle`";
+ return Err(syn::Error::new(input_type.span(), msg));
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Records the type of the discriminant and ensure they all have the same type.
+ fn try_register_discriminant_type(&mut self, ty: &syn::Type) -> syn::Result<()> {
+ if let Some(known_type) = &self.precompile_set_discriminant_type {
+ if !is_same_type(known_type, ty) {
+ let msg = format!(
+ "All discriminants must have the same type (found {} before)",
+ known_type.to_token_stream()
+ );
+ return Err(syn::Error::new(ty.span(), msg));
+ }
+ } else {
+ self.precompile_set_discriminant_type = Some(ty.clone());
+ }
+
+ Ok(())
+ }
+
+ /// Process the discriminant function.
+ fn parse_discriminant_fn(
+ &mut self,
+ span: Span,
+ method: &syn::ImplItemMethod,
+ ) -> syn::Result<()> {
+ if !self.tagged_as_precompile_set {
+ let msg = "The impl block must be tagged with `#[precompile::precompile_set]` for
+ the discriminant attribute to be used";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ if self.precompile_set_discriminant_fn.is_some() {
+ let msg = "A PrecompileSet can only have 1 discriminant function";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ let span = method.sig.span();
+
+ if method.sig.inputs.len() != 2 {
+ let msg = "The discriminant function must only take code address (H160) and \
+ remaining gas (u64) as parameters.";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ let msg = "The discriminant function must return an DiscriminantResult<_> (no type alias)";
+
+ let return_type = match &method.sig.output {
+ syn::ReturnType::Type(_, t) => t.as_ref(),
+ _ => return Err(syn::Error::new(span, msg)),
+ };
+
+ let return_path = match return_type {
+ syn::Type::Path(p) => p,
+ _ => return Err(syn::Error::new(span, msg)),
+ };
+
+ if return_path.qself.is_some() {
+ return Err(syn::Error::new(span, msg));
+ }
+
+ let return_path = &return_path.path;
+
+ if return_path.leading_colon.is_some() || return_path.segments.len() != 1 {
+ return Err(syn::Error::new(span, msg));
+ }
+
+ let return_segment = &return_path.segments[0];
+
+ if return_segment.ident != "DiscriminantResult" {
+ return Err(syn::Error::new(return_segment.ident.span(), msg));
+ }
+
+ let result_arguments = match &return_segment.arguments {
+ syn::PathArguments::AngleBracketed(args) => args,
+ _ => return Err(syn::Error::new(return_segment.ident.span(), msg)),
+ };
+
+ if result_arguments.args.len() != 1 {
+ let msg = "DiscriminantResult type should only have 1 type argument";
+ return Err(syn::Error::new(result_arguments.args.span(), msg));
+ }
+
+ let discriminant_type: &syn::Type = match &result_arguments.args[0] {
+ syn::GenericArgument::Type(t) => t,
+ _ => return Err(syn::Error::new(result_arguments.args.span(), msg)),
+ };
+
+ self.try_register_discriminant_type(discriminant_type)?;
+
+ self.precompile_set_discriminant_fn = Some(method.sig.ident.clone());
+
+ Ok(())
+ }
+
+ /// Process the pre_check function.
+ fn parse_pre_check_fn(&mut self, span: Span, method: &syn::ImplItemMethod) -> syn::Result<()> {
+ if self.pre_check.is_some() {
+ let msg = "A Precompile can only have 1 pre_check function";
+ return Err(syn::Error::new(span, msg));
+ }
+
+ let span = method.sig.span();
+
+ let mut method_inputs = method.sig.inputs.iter();
+
+ self.check_initial_parameters(&mut method_inputs, span)?;
+
+ if method_inputs.next().is_some() {
+ let msg = if self.tagged_as_precompile_set {
+ "PrecompileSet pre_check method must have exactly 2 parameters (the precompile \
+ instance discriminant and the PrecompileHandle)"
+ } else {
+ "Precompile pre_check method must have exactly 1 parameter (the \
+ PrecompileHandle)"
+ };
+
+ return Err(syn::Error::new(span, msg));
+ }
+
+ self.pre_check = Some(method.sig.ident.clone());
+
+ Ok(())
+ }
+
+ /// Process a `public` attribute on a method.
+ fn parse_public_attr(
+ &mut self,
+ signature_lit: syn::LitStr,
+ method_name: &syn::Ident,
+ solidity_arguments_type: &mut Option,
+ ) -> syn::Result {
+ let signature = signature_lit.value();
+ // Split signature to get arguments type.
+ let split: Vec<_> = signature.splitn(2, '(').collect();
+ if split.len() != 2 {
+ let msg = "Selector must have form \"foo(arg1,arg2,...)\"";
+ return Err(syn::Error::new(signature_lit.span(), msg));
+ }
+
+ let local_args_type = format!("({}", split[1]); // add back initial parenthesis
+
+ // If there are multiple public attributes we check that they all have
+ // the same type.
+ if let Some(ref args_type) = solidity_arguments_type {
+ if args_type != &local_args_type {
+ let msg = "Method cannot have selectors with different types.";
+ return Err(syn::Error::new(signature_lit.span(), msg));
+ }
+ } else {
+ *solidity_arguments_type = Some(local_args_type);
+ }
+
+ // Compute the 4-bytes selector.
+ let digest = keccak_256(signature.as_bytes());
+ let selector = u32::from_be_bytes([digest[0], digest[1], digest[2], digest[3]]);
+
+ if let Some(previous) = self
+ .selector_to_variant
+ .insert(selector, method_name.clone())
+ {
+ let msg = format!("Selector collision with method {previous}");
+ return Err(syn::Error::new(signature_lit.span(), msg));
+ }
+
+ Ok(selector)
+ }
+
+ /// Check that the provided type doesn't depend on one of the type parameters of the
+ /// precompile. Check is skipped if `test_concrete_types` attribute is used.
+ fn check_type_parameter_usage(&self, ty: &syn::Type) -> syn::Result<()> {
+ if self.test_concrete_types.is_some() {
+ return Ok(());
+ }
+
+ const ERR_MESSAGE: &str =
+ "impl type parameter is used in functions arguments. Arguments should not have a type
+depending on a type parameter, unless it is a length bound for BoundedBytes,
+BoundedString or alike, which doesn't affect the Solidity type.
+
+In that case, you must add a #[precompile::test_concrete_types(...)] attribute on the impl
+block to provide concrete types that will be used to run the automatically generated tests
+ensuring the Solidity function signatures are correct.";
+
+ match ty {
+ syn::Type::Array(syn::TypeArray { elem, .. })
+ | syn::Type::Group(syn::TypeGroup { elem, .. })
+ | syn::Type::Paren(syn::TypeParen { elem, .. })
+ | syn::Type::Reference(syn::TypeReference { elem, .. })
+ | syn::Type::Ptr(syn::TypePtr { elem, .. })
+ | syn::Type::Slice(syn::TypeSlice { elem, .. }) => {
+ self.check_type_parameter_usage(elem)?
+ }
+
+ syn::Type::Path(syn::TypePath {
+ path: syn::Path { segments, .. },
+ ..
+ }) => {
+ let impl_params: Vec<_> = self
+ .generics
+ .params
+ .iter()
+ .filter_map(|param| match param {
+ syn::GenericParam::Type(syn::TypeParam { ident, .. }) => Some(ident),
+ _ => None,
+ })
+ .collect();
+
+ for segment in segments {
+ if impl_params.contains(&&segment.ident) {
+ return Err(syn::Error::new(segment.ident.span(), ERR_MESSAGE));
+ }
+
+ if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
+ let types = args.args.iter().filter_map(|arg| match arg {
+ syn::GenericArgument::Type(ty)
+ | syn::GenericArgument::Binding(syn::Binding { ty, .. }) => Some(ty),
+ _ => None,
+ });
+
+ for ty in types {
+ self.check_type_parameter_usage(ty)?;
+ }
+ }
+ }
+ }
+ syn::Type::Tuple(tuple) => {
+ for ty in tuple.elems.iter() {
+ self.check_type_parameter_usage(ty)?;
+ }
+ }
+ // BareFn => very unlikely this appear as parameter
+ // ImplTrait => will cause other errors, it must be a concrete type
+ // TypeInfer => it must be explicit concrete types since it ends up in enum fields
+ // Macro => Cannot check easily
+ // Never => Function will not be callable.
+ ty => println!("Skipping type parameter check for non supported kind of type: {ty:?}"),
+ }
+
+ Ok(())
+ }
+}
+
+/// Helper to check 2 types are equal.
+/// Having a function with explicit type annotation helps type inference at callsite,
+/// which have trouble if `==` is used inline.
+fn is_same_type(a: &syn::Type, b: &syn::Type) -> bool {
+ a == b
+}
diff --git a/precompiles/utils_v2/macro/src/precompile_name_from_address.rs b/precompiles/utils_v2/macro/src/precompile_name_from_address.rs
new file mode 100644
index 0000000000..50da96e44a
--- /dev/null
+++ b/precompiles/utils_v2/macro/src/precompile_name_from_address.rs
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use super::*;
+use syn::{GenericArgument, Type};
+
+pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
+ let item = parse_macro_input!(input as ItemType);
+
+ let ItemType {
+ attrs,
+ vis,
+ type_token,
+ ident,
+ generics,
+ eq_token,
+ ty,
+ semi_token,
+ } = item;
+
+ if let Type::Tuple(ref type_tuple) = *ty {
+ let variants: Vec<(Ident, u64)> = type_tuple
+ .elems
+ .iter()
+ .filter_map(extract_precompile_name_and_prefix)
+ .collect();
+
+ let ident_expressions: Vec<&Ident> = variants.iter().map(|(ident, _)| ident).collect();
+ let variant_expressions: Vec<&u64> = variants.iter().map(|(_, id)| id).collect();
+
+ (quote! {
+ #(#attrs)*
+ #vis #type_token #ident #generics #eq_token #ty #semi_token
+
+ #[derive(num_enum::TryFromPrimitive, num_enum::IntoPrimitive, Debug)]
+ #[repr(u64)]
+ pub enum PrecompileName {
+ #(
+ #ident_expressions = #variant_expressions,
+ )*
+ }
+
+ impl PrecompileName {
+ pub fn from_address(address: sp_core::H160) -> Option {
+ let _u64 = address.to_low_u64_be();
+ if address == sp_core::H160::from_low_u64_be(_u64) {
+ use num_enum::TryFromPrimitive;
+ Self::try_from_primitive(_u64).ok()
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .into()
+ } else {
+ quote_spanned! {
+ ty.span() => compile_error!("Expected tuple");
+ }
+ .into()
+ }
+}
+
+fn extract_precompile_name_and_prefix(type_: &Type) -> Option<(Ident, u64)> {
+ match type_ {
+ Type::Path(type_path) => {
+ if let Some(path_segment) = type_path.path.segments.last() {
+ match path_segment.ident.to_string().as_ref() {
+ "PrecompileAt" => {
+ extract_precompile_name_and_prefix_for_precompile_at(path_segment)
+ }
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+ _ => None,
+ }
+}
+
+fn extract_precompile_name_and_prefix_for_precompile_at(
+ path_segment: &syn::PathSegment,
+) -> Option<(Ident, u64)> {
+ if let syn::PathArguments::AngleBracketed(generics) = &path_segment.arguments {
+ let mut iter = generics.args.iter();
+ if let (
+ Some(GenericArgument::Type(Type::Path(type_path_1))),
+ Some(GenericArgument::Type(Type::Path(type_path_2))),
+ ) = (iter.next(), iter.next())
+ {
+ if let (Some(path_segment_1), Some(path_segment_2)) = (
+ type_path_1.path.segments.last(),
+ type_path_2.path.segments.last(),
+ ) {
+ if let syn::PathArguments::AngleBracketed(generics_) = &path_segment_1.arguments {
+ if let Some(GenericArgument::Const(Expr::Lit(lit))) = generics_.args.first() {
+ if let Lit::Int(int) = &lit.lit {
+ if let Ok(precompile_id) = int.base10_parse() {
+ if &path_segment_2.ident.to_string() == "CollectivePrecompile" {
+ if let Some(instance_ident) =
+ precompile_instance_ident(path_segment_2)
+ {
+ return Some((instance_ident, precompile_id));
+ }
+ } else {
+ return Some((path_segment_2.ident.clone(), precompile_id));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ None
+}
+
+fn precompile_instance_ident(path_segment: &syn::PathSegment) -> Option {
+ if let syn::PathArguments::AngleBracketed(generics_) = &path_segment.arguments {
+ if let Some(GenericArgument::Type(Type::Path(instance_type_path))) = generics_.args.last() {
+ if let Some(instance_type) = instance_type_path.path.segments.last() {
+ return Some(instance_type.ident.clone());
+ }
+ }
+ }
+
+ None
+}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.rs b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.rs
new file mode 100644
index 0000000000..86bfbcaa32
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.rs
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use precompile_utils::prelude::*;
+
+#[derive(solidity::Codec)]
+struct Empty1;
+
+#[derive(solidity::Codec)]
+struct Empty2 {}
+
+#[derive(solidity::Codec)]
+struct Empty3();
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.stderr b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.stderr
new file mode 100644
index 0000000000..8c0a9d8bae
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/empty_struct.stderr
@@ -0,0 +1,17 @@
+error: Codec can only be derived for structs with named fields
+ --> tests/compile-fail/derive_codec/empty_struct.rs:20:8
+ |
+20 | struct Empty1;
+ | ^^^^^^
+
+error: Codec can only be derived for structs with at least one field
+ --> tests/compile-fail/derive_codec/empty_struct.rs:23:8
+ |
+23 | struct Empty2 {}
+ | ^^^^^^
+
+error: Codec can only be derived for structs with named fields
+ --> tests/compile-fail/derive_codec/empty_struct.rs:26:8
+ |
+26 | struct Empty3 ();
+ | ^^^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.rs b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.rs
new file mode 100644
index 0000000000..76eb6740ee
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.rs
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use precompile_utils::prelude::*;
+
+#[derive(solidity::Codec)]
+enum Test {
+ One,
+ Two(u8),
+ Three { test: u16 },
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.stderr b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.stderr
new file mode 100644
index 0000000000..42a65d4a17
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/derive_codec/enum.stderr
@@ -0,0 +1,5 @@
+error: Codec can only be derived for structs with named fields
+ --> tests/compile-fail/derive_codec/enum.rs:20:6
+ |
+20 | enum Test {
+ | ^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs
new file mode 100644
index 0000000000..4fea235ac7
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+use fp_evm::PrecompileHandle;
+use precompile_utils::EvmResult;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr
new file mode 100644
index 0000000000..1e87e3c771
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/arg-dont-impl-codec.stderr
@@ -0,0 +1,61 @@
+error[E0277]: the trait bound `String: Codec` is not satisfied
+ --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:43
+ |
+26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult {
+ | ^^^ the trait `Codec` is not implemented for `String`
+ |
+ = help: the following other types implement trait `Codec`:
+ ()
+ (TupleElement0, TupleElement1)
+ (TupleElement0, TupleElement1, TupleElement2)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
+ and $N others
+note: required by a bound in `Reader::<'inner>::read`
+ --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs
+ |
+ | pub fn read(&mut self) -> MayRevert {
+ | ^^^^^ required by this bound in `Reader::<'inner>::read`
+
+error[E0277]: the trait bound `String: Codec` is not satisfied
+ --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:43
+ |
+26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult {
+ | ^^^ the trait `Codec` is not implemented for `String`
+ |
+ = help: the following other types implement trait `Codec`:
+ ()
+ (TupleElement0, TupleElement1)
+ (TupleElement0, TupleElement1, TupleElement2)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
+ and $N others
+note: required by a bound in `precompile_utils::solidity::codec::Writer::write`
+ --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs
+ |
+ | pub fn write(mut self, value: T) -> Self {
+ | ^^^^^ required by this bound in `Writer::write`
+
+error[E0277]: the trait bound `String: Codec` is not satisfied
+ --> tests/compile-fail/precompile/codec/arg-dont-impl-codec.rs:26:5
+ |
+26 | fn foo(test: &mut impl PrecompileHandle, arg: String) -> EvmResult {
+ | ^^^ the trait `Codec` is not implemented for `String`
+ |
+ = help: the following other types implement trait `Codec`:
+ ()
+ (TupleElement0, TupleElement1)
+ (TupleElement0, TupleElement1, TupleElement2)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
+ and $N others
+ = note: required for `(String,)` to implement `Codec`
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.rs
new file mode 100644
index 0000000000..a721ada56f
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(test: &mut impl PrecompileHandle) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.stderr
new file mode 100644
index 0000000000..7a2758d0f5
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/no-output.stderr
@@ -0,0 +1,5 @@
+error: A precompile method must have a return type of `EvmResult<_>` (exposed by `precompile_utils`)
+ --> tests/compile-fail/precompile/codec/no-output.rs:24:2
+ |
+24 | fn foo(test: &mut impl PrecompileHandle) {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs
new file mode 100644
index 0000000000..ada7b05afa
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.rs
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+use fp_evm::PrecompileHandle;
+use precompile_utils::EvmResult;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr
new file mode 100644
index 0000000000..52ba67e654
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-dont-impl-codec.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `String: Codec` is not satisfied
+ --> tests/compile-fail/precompile/codec/output-dont-impl-codec.rs:26:46
+ |
+26 | fn foo(test: &mut impl PrecompileHandle) -> EvmResult {
+ | ^^^^^^^^^ the trait `Codec` is not implemented for `String`
+ |
+ = help: the following other types implement trait `Codec`:
+ ()
+ (TupleElement0, TupleElement1)
+ (TupleElement0, TupleElement1, TupleElement2)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6)
+ (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7)
+ and $N others
+note: required by a bound in `encode_arguments`
+ --> $WORKSPACE/precompiles/utils/src/solidity/codec/mod.rs
+ |
+ | pub fn encode_arguments(value: T) -> Vec {
+ | ^^^^^ required by this bound in `encode_arguments`
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.rs
new file mode 100644
index 0000000000..278cffe68c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+use fp_evm::PrecompileHandle;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(test: &mut impl PrecompileHandle) -> String {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.stderr
new file mode 100644
index 0000000000..c104ae8fb3
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-not-result.stderr
@@ -0,0 +1,7 @@
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+ --> tests/compile-fail/precompile/codec/output-not-result.rs:25:46
+ |
+25 | fn foo(test: &mut impl PrecompileHandle) -> String {
+ | ^^^^^^ the `?` operator cannot be applied to type `String`
+ |
+ = help: the trait `Try` is not implemented for `String`
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs
new file mode 100644
index 0000000000..d5d7c31a2c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+use fp_evm::PrecompileHandle;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(test: &mut impl PrecompileHandle) -> Result<(), String> {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr
new file mode 100644
index 0000000000..fa1fc8f71c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/codec/output-wrong-error-result.stderr
@@ -0,0 +1,13 @@
+error[E0277]: `?` couldn't convert the error to `PrecompileFailure`
+ --> tests/compile-fail/precompile/codec/output-wrong-error-result.rs:25:51
+ |
+25 | fn foo(test: &mut impl PrecompileHandle) -> Result<(), String> {
+ | ^ the trait `From` is not implemented for `PrecompileFailure`
+ |
+ = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+ = help: the following other types implement trait `From`:
+ >
+ >
+ >
+ >
+ = note: required for `Result` to implement `FromResidual>`
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs
new file mode 100644
index 0000000000..5c7d5fe26b
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::discriminant]
+ fn discriminant(address: H160) -> u32 {
+ 42
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr
new file mode 100644
index 0000000000..66d90708f0
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/dont-return-option.stderr
@@ -0,0 +1,5 @@
+error: The discriminant function must return an Option<_> (no type alias)
+ --> tests/compile-fail/precompile/discriminant/dont-return-option.rs:25:36
+ |
+25 | fn discriminant(address: H160) -> u32 {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs
new file mode 100644
index 0000000000..0a27ed2bd4
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, handle: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr
new file mode 100644
index 0000000000..b24b8dddcb
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-fn.stderr
@@ -0,0 +1,7 @@
+error: A PrecompileSet must have exactly one function tagged with `#[precompile::discriminant]`
+ --> tests/compile-fail/precompile/discriminant/missing-fn.rs:21:1
+ |
+21 | #[precompile_utils_macro::precompile]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the attribute macro `precompile_utils_macro::precompile` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.rs
new file mode 100644
index 0000000000..0447b2031a
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::discriminant]
+ fn discriminant() -> Option {
+ Some(42)
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr
new file mode 100644
index 0000000000..3026b5ce42
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/missing-param.stderr
@@ -0,0 +1,5 @@
+error: The discriminant function must only take the code address (H160) as parameter.
+ --> tests/compile-fail/precompile/discriminant/missing-param.rs:25:2
+ |
+25 | fn discriminant() -> Option {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs
new file mode 100644
index 0000000000..41f577bbc2
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::discriminant]
+ fn discriminant(address: H160) -> Option {
+ None
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr
new file mode 100644
index 0000000000..dc6db9f600
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/return-incomplete-option.stderr
@@ -0,0 +1,5 @@
+error: The discriminant function must return an Option<_> (no type alias)
+ --> tests/compile-fail/precompile/discriminant/return-incomplete-option.rs:25:36
+ |
+25 | fn discriminant(address: H160) -> Option {
+ | ^^^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs
new file mode 100644
index 0000000000..bbc2e1fe5f
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::discriminant]
+ fn discriminant(address: H160, other: u32) -> Option {
+ Some(42)
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr
new file mode 100644
index 0000000000..a1b6e87865
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/too-many-arguments.stderr
@@ -0,0 +1,5 @@
+error: The discriminant function must only take the code address (H160) as parameter.
+ --> tests/compile-fail/precompile/discriminant/too-many-arguments.rs:25:2
+ |
+25 | fn discriminant(address: H160, other: u32) -> Option {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs
new file mode 100644
index 0000000000..620a0a98da
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::discriminant]
+ fn discriminant(address: H160) -> Option {
+ Some(42)
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr
new file mode 100644
index 0000000000..8d999769e4
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-1.stderr
@@ -0,0 +1,5 @@
+error: All discriminants must have the same type (found u64 before)
+ --> tests/compile-fail/precompile/discriminant/type-mismatch-1.rs:30:24
+ |
+30 | fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs
new file mode 100644
index 0000000000..e0b2970863
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, test: &mut impl PrecompileHandle) -> EvmResult {
+ todo!()
+ }
+
+ #[precompile::discriminant]
+ fn discriminant(address: H160) -> Option {
+ Some(42)
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr
new file mode 100644
index 0000000000..d5ed6750af
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/discriminant/type-mismatch-2.stderr
@@ -0,0 +1,5 @@
+error: All discriminants must have the same type (found u32 before)
+ --> tests/compile-fail/precompile/discriminant/type-mismatch-2.rs:30:43
+ |
+30 | fn discriminant(address: H160) -> Option {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs
new file mode 100644
index 0000000000..ebe1bafcea
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct PrecompileSet(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl PrecompileSet {
+ #[precompile::discriminant]
+ #[precompile::view]
+ fn foo(address: H160) -> Option {
+ None
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr
new file mode 100644
index 0000000000..93f4fb2617
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.stderr
@@ -0,0 +1,5 @@
+error: The discriminant attribute must be the only precompile attribute of a function
+ --> tests/compile-fail/precompile/fn-modifiers/discriminant-multiple.rs:24:16
+ |
+24 | #[precompile::discriminant]
+ | ^^^^^^^^^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs
new file mode 100644
index 0000000000..25db0a521c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ #[precompile::view]
+ #[precompile::payable]
+ fn foo(_handle: &mut impl PrecompileHandle) -> EvmResult {
+ Ok(())
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr
new file mode 100644
index 0000000000..678199ab8b
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.stderr
@@ -0,0 +1,5 @@
+error: A precompile method can have at most one modifier (payable, view)
+ --> tests/compile-fail/precompile/fn-modifiers/multiple-modifiers.rs:25:16
+ |
+25 | #[precompile::payable]
+ | ^^^^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs
new file mode 100644
index 0000000000..31015637cc
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::pre_check]
+ #[precompile::view]
+ fn foo(handle: &mut impl PrecompileHandle) -> EvmResult {
+ Ok(())
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr
new file mode 100644
index 0000000000..7f96f8b568
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.stderr
@@ -0,0 +1,5 @@
+error: The pre_check attribute must be the only precompile attribute of a function
+ --> tests/compile-fail/precompile/fn-modifiers/pre-check-multiple.rs:23:16
+ |
+23 | #[precompile::pre_check]
+ | ^^^^^^^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.rs
new file mode 100644
index 0000000000..81770e7b5f
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo() {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.stderr
new file mode 100644
index 0000000000..2760a71988
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/missing.stderr
@@ -0,0 +1,5 @@
+error: Precompile methods must have at least 1 parameter (the PrecompileHandle)
+ --> tests/compile-fail/precompile/handle/missing.rs:24:2
+ |
+24 | fn foo() {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.rs
new file mode 100644
index 0000000000..8700c2742d
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(_: u32) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.stderr
new file mode 100644
index 0000000000..8f0a10d8ff
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-missing.stderr
@@ -0,0 +1,5 @@
+error: PrecompileSet methods must have at least 2 parameters (the precompile instance discriminant and the PrecompileHandle)
+ --> tests/compile-fail/precompile/handle/set-missing.rs:25:2
+ |
+25 | fn foo(_: u32) {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs
new file mode 100644
index 0000000000..d571768bfa
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.rs
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+#[precompile::precompile_set]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(_discriminant: u32, _handle: u32) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr
new file mode 100644
index 0000000000..bf62c1381c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/set-wrong-type.stderr
@@ -0,0 +1,5 @@
+error: This parameter must have type `&mut impl PrecompileHandle`
+ --> tests/compile-fail/precompile/handle/set-wrong-type.rs:25:38
+ |
+25 | fn foo(_discriminant: u32, _handle: u32) {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.rs
new file mode 100644
index 0000000000..01e1fb0f97
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::public("foo()")]
+ fn foo(_handle: u32) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.stderr
new file mode 100644
index 0000000000..12e06e4889
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/handle/wrong-type.stderr
@@ -0,0 +1,5 @@
+error: This parameter must have type `&mut impl PrecompileHandle`
+ --> tests/compile-fail/precompile/handle/wrong-type.rs:24:18
+ |
+24 | fn foo(_handle: u32) {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs
new file mode 100644
index 0000000000..7530add4dd
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::pre_check]
+ fn pre_check() {
+ todo!()
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_handle: &mut impl PrecompileHandle) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr
new file mode 100644
index 0000000000..406806c51c
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/no-parameter.stderr
@@ -0,0 +1,5 @@
+error: Precompile methods must have at least 1 parameter (the PrecompileHandle)
+ --> tests/compile-fail/precompile/pre-check/no-parameter.rs:24:2
+ |
+24 | fn pre_check() {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs
new file mode 100644
index 0000000000..742c1d2c6f
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::pre_check]
+ fn pre_check(_: &mut impl PrecompileHandle, _: u32) {
+ todo!()
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_handle: &mut impl PrecompileHandle) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr
new file mode 100644
index 0000000000..474b5c9dcd
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/too-many-parameters.stderr
@@ -0,0 +1,5 @@
+error: Precompile pre_check method must have exactly 1 parameter (the PrecompileHandle)
+ --> tests/compile-fail/precompile/pre-check/too-many-parameters.rs:24:2
+ |
+24 | fn pre_check(_: &mut impl PrecompileHandle, _: u32) {
+ | ^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs
new file mode 100644
index 0000000000..42b63886da
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.rs
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl Precompile {
+ #[precompile::pre_check]
+ fn pre_check(_: u32) {
+ todo!()
+ }
+
+ #[precompile::public("foo()")]
+ fn foo(_handle: &mut impl PrecompileHandle) {
+ todo!()
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr
new file mode 100644
index 0000000000..ea95fb0de8
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/pre-check/wrong-parameter.stderr
@@ -0,0 +1,5 @@
+error: This parameter must have type `&mut impl PrecompileHandle`
+ --> tests/compile-fail/precompile/pre-check/wrong-parameter.rs:24:18
+ |
+24 | fn pre_check(_: u32) {
+ | ^^^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs
new file mode 100644
index 0000000000..a5615e8fda
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.rs
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+use core::marker::PhantomData;
+
+pub struct Precompile(PhantomData);
+
+#[precompile_utils_macro::precompile]
+impl> Precompile {
+ #[precompile::public("foo(bytes)")]
+ fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult {
+ Ok(())
+ }
+}
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr
new file mode 100644
index 0000000000..8b4daeb35f
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile/test-gen/generic-arg.stderr
@@ -0,0 +1,11 @@
+error: impl type parameter is used in functions arguments. Arguments should not have a type
+ depending on a type parameter, unless it is a length bound for BoundedBytes,
+ BoundedString or alike, which doesn't affect the Solidity type.
+
+ In that case, you must add a #[precompile::test_concrete_types(...)] attribute on the impl
+ block to provide concrete types that will be used to run the automatically generated tests
+ ensuring the Solidity function signatures are correct.
+ --> tests/compile-fail/precompile/test-gen/generic-arg.rs:24:63
+ |
+24 | fn foo(handle: &mut impl PrecompileHandle, arg: BoundedBytes) -> EvmResult {
+ | ^
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.rs b/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.rs
new file mode 100644
index 0000000000..e328bfc092
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.rs
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
+// This file is part of Frontier.
+//
+// Copyright (c) 2019-2022 Moonsong Labs.
+// Copyright (c) 2023 Parity Technologies (UK) Ltd.
+//
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+struct Dummy;
+
+#[precompile_utils_macro::precompile_name_from_address]
+type Precompiles = Dummy;
+
+fn main() {}
diff --git a/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.stderr b/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.stderr
new file mode 100644
index 0000000000..343443a248
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/compile-fail/precompile_name/not_tuple.stderr
@@ -0,0 +1,5 @@
+error: Expected tuple
+ --> tests/compile-fail/precompile_name/not_tuple.rs:20:20
+ |
+20 | type Precompiles = Dummy;
+ | ^^^^^
diff --git a/precompiles/utils_v2/macro/tests/expand/precompile.expanded.rs b/precompiles/utils_v2/macro/tests/expand/precompile.expanded.rs
new file mode 100644
index 0000000000..3153ec9d06
--- /dev/null
+++ b/precompiles/utils_v2/macro/tests/expand/precompile.expanded.rs
@@ -0,0 +1,364 @@
+use core::marker::PhantomData;
+use frame_support::pallet_prelude::{ConstU32, Get};
+use precompile_utils::{prelude::*, EvmResult};
+use sp_core::{H160, U256};
+struct BatchPrecompile(PhantomData);
+type GetCallDataLimit = ConstU32<42>;
+type GetArrayLimit = ConstU32<42>;
+impl BatchPrecompile
+where
+ Runtime: Get,
+{
+ fn pre_check(handle: &mut impl PrecompileHandle) -> EvmResult {
+ ::core::panicking::panic_fmt(format_args!(
+ "not yet implemented: {0}",
+ format_args!("pre_check")
+ ))
+ }
+ fn batch_some(
+ handle: &mut impl PrecompileHandle,
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ ) -> EvmResult {
+ ::core::panicking::panic_fmt(format_args!(
+ "not yet implemented: {0}",
+ format_args!("batch_some")
+ ))
+ }
+ fn batch_some_until_failure(
+ handle: &mut impl PrecompileHandle,
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ ) -> EvmResult {
+ ::core::panicking::panic_fmt(format_args!(
+ "not yet implemented: {0}",
+ format_args!("batch_some_until_failure")
+ ))
+ }
+ fn batch_all(
+ handle: &mut impl PrecompileHandle,
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ ) -> EvmResult {
+ ::core::panicking::panic_fmt(format_args!(
+ "not yet implemented: {0}",
+ format_args!("batch_all")
+ ))
+ }
+ fn fallback(handle: &mut impl PrecompileHandle) -> EvmResult {
+ ::core::panicking::panic_fmt(format_args!(
+ "not yet implemented: {0}",
+ format_args!("fallback")
+ ))
+ }
+}
+#[allow(non_camel_case_types)]
+pub enum BatchPrecompileCall
+where
+ Runtime: Get,
+{
+ batch_all {
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ },
+ batch_some {
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ },
+ batch_some_until_failure {
+ to: BoundedVec,
+ value: BoundedVec,
+ call_data: BoundedVec, GetArrayLimit>,
+ gas_limit: BoundedVec,
+ },
+ fallback {},
+ #[doc(hidden)]
+ __phantom(
+ ::core::marker::PhantomData<(Runtime)>,
+ ::core::convert::Infallible,
+ ),
+}
+impl BatchPrecompileCall
+where
+ Runtime: Get,
+{
+ pub fn parse_call_data(
+ handle: &mut impl PrecompileHandle,
+ ) -> ::precompile_utils::EvmResult {
+ use precompile_utils::solidity::revert::RevertReason;
+ let input = handle.input();
+ let selector = input.get(0..4).map(|s| {
+ let mut buffer = [0u8; 4];
+ buffer.copy_from_slice(s);
+ u32::from_be_bytes(buffer)
+ });
+ match selector {
+ Some(2044677020u32) => Self::_parse_batch_some(handle),
+ Some(2531431096u32) => Self::_parse_batch_all(handle),
+ Some(3473183175u32) => Self::_parse_batch_some_until_failure(handle),
+ _ => Self::_parse_fallback(handle),
+ }
+ }
+ fn _parse_batch_all(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult {
+ use precompile_utils::{
+ evm::handle::PrecompileHandleExt,
+ solidity::{modifier::FunctionModifier, revert::InjectBacktrace},
+ };
+ handle.check_function_modifier(FunctionModifier::NonPayable)?;
+ let mut input = handle.read_after_selector()?;
+ input.expect_arguments(4usize)?;
+ Ok(Self::batch_all {
+ to: input.read().in_field("to")?,
+ value: input.read().in_field("value")?,
+ call_data: input.read().in_field("callData")?,
+ gas_limit: input.read().in_field("gasLimit")?,
+ })
+ }
+ fn _parse_batch_some(
+ handle: &mut impl PrecompileHandle,
+ ) -> ::precompile_utils::EvmResult {
+ use precompile_utils::{
+ evm::handle::PrecompileHandleExt,
+ solidity::{modifier::FunctionModifier, revert::InjectBacktrace},
+ };
+ handle.check_function_modifier(FunctionModifier::NonPayable)?;
+ let mut input = handle.read_after_selector()?;
+ input.expect_arguments(4usize)?;
+ Ok(Self::batch_some {
+ to: input.read().in_field("to")?,
+ value: input.read().in_field("value")?,
+ call_data: input.read().in_field("callData")?,
+ gas_limit: input.read().in_field("gasLimit")?,
+ })
+ }
+ fn _parse_batch_some_until_failure(
+ handle: &mut impl PrecompileHandle,
+ ) -> ::precompile_utils::EvmResult {
+ use precompile_utils::{
+ evm::handle::PrecompileHandleExt,
+ solidity::{modifier::FunctionModifier, revert::InjectBacktrace},
+ };
+ handle.check_function_modifier(FunctionModifier::NonPayable)?;
+ let mut input = handle.read_after_selector()?;
+ input.expect_arguments(4usize)?;
+ Ok(Self::batch_some_until_failure {
+ to: input.read().in_field("to")?,
+ value: input.read().in_field("value")?,
+ call_data: input.read().in_field("callData")?,
+ gas_limit: input.read().in_field("gasLimit")?,
+ })
+ }
+ fn _parse_fallback(handle: &mut impl PrecompileHandle) -> ::precompile_utils::EvmResult {
+ use precompile_utils::{
+ evm::handle::PrecompileHandleExt,
+ solidity::{modifier::FunctionModifier, revert::InjectBacktrace},
+ };
+ handle.check_function_modifier(FunctionModifier::NonPayable)?;
+ Ok(Self::fallback {})
+ }
+ pub fn execute(
+ self,
+ handle: &mut impl PrecompileHandle,
+ ) -> ::precompile_utils::EvmResult<::fp_evm::PrecompileOutput> {
+ use fp_evm::{ExitSucceed, PrecompileOutput};
+ use precompile_utils::solidity::codec::Writer;
+ let output = match self {
+ Self::batch_all {
+ to,
+ value,
+ call_data,
+ gas_limit,
+ } => {
+ let output =
+