From 72b93bbe54f675e912dc57baba37a9378011fc16 Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Tue, 7 Jan 2025 14:22:43 +0200 Subject: [PATCH] feat: move erc20 transfer to its own shared crate --- Cargo.lock | 14 +++ Cargo.toml | 2 + precompiles/erc20-utils/Cargo.toml | 25 ++++ precompiles/erc20-utils/src/lib.rs | 110 ++++++++++++++++++ precompiles/multi-asset-delegation/Cargo.toml | 6 +- precompiles/multi-asset-delegation/src/lib.rs | 73 +----------- 6 files changed, 155 insertions(+), 75 deletions(-) create mode 100644 precompiles/erc20-utils/Cargo.toml create mode 100644 precompiles/erc20-utils/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a74935ba1..8752cd2ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3938,6 +3938,19 @@ dependencies = [ "serde", ] +[[package]] +name = "evm-erc20-utils" +version = "0.1.0" +dependencies = [ + "ethabi", + "fp-evm", + "hex", + "log", + "precompile-utils", + "sp-core", + "sp-std", +] + [[package]] name = "evm-gasometer" version = "0.41.0" @@ -8912,6 +8925,7 @@ dependencies = [ "ethabi", "ethereum", "ethers", + "evm-erc20-utils", "fp-account", "fp-consensus", "fp-dynamic-fee", diff --git a/Cargo.toml b/Cargo.toml index af6817ed3..79ca35772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "precompiles/preimage", "precompiles/balances-erc20", "precompiles/assets-erc20", + "precompiles/erc20-utils", "precompiles/verify-ecdsa-secp256k1-signature", "precompiles/verify-ecdsa-secp256r1-signature", "precompiles/verify-ecdsa-stark-signature", @@ -306,6 +307,7 @@ pallet-hotfix-sufficients = { git = "https://github.com/paritytech/frontier.git" precompile-utils = { git = "https://github.com/paritytech/frontier.git", branch = "stable2407", default-features = false } # Local precompiles +evm-erc20-utils = { path = "precompiles/erc20-utils", default-features = false } pallet-evm-precompile-democracy = { path = "precompiles/pallet-democracy", default-features = false } pallet-evm-precompile-batch = { path = "precompiles/batch", default-features = false } pallet-evm-precompile-balances-erc20 = { path = "precompiles/balances-erc20", default-features = false } diff --git a/precompiles/erc20-utils/Cargo.toml b/precompiles/erc20-utils/Cargo.toml new file mode 100644 index 000000000..1aaaf232d --- /dev/null +++ b/precompiles/erc20-utils/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "evm-erc20-utils" +authors = { workspace = true } +description = "A Package containing ERC20 utility functions for EVM precompiles." +edition = { workspace = true } +version = "0.1.0" + +[dependencies] +ethabi = { workspace = true } +hex = { workspace = true, default-features = false } +precompile-utils = { workspace = true } +log = { workspace = true, default-features = false } + +# Substrate +sp-core = { workspace = true } +sp-std = { workspace = true } + +# Frontier +fp-evm = { workspace = true } + +[dev-dependencies] + +[features] +default = ["std"] +std = ["ethabi/std", "hex/std", "fp-evm/std", "precompile-utils/std", "sp-core/std", "sp-std/std"] diff --git a/precompiles/erc20-utils/src/lib.rs b/precompiles/erc20-utils/src/lib.rs new file mode 100644 index 000000000..4f033f637 --- /dev/null +++ b/precompiles/erc20-utils/src/lib.rs @@ -0,0 +1,110 @@ +// This file is part of Tangle. +// Copyright (C) 2022-2024 Tangle Foundation. +// +// This file is part of pallet-evm-precompile-multi-asset-delegation package. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + +use ethabi::Function; +use fp_evm::PrecompileFailure; +use precompile_utils::prelude::*; +use sp_core::U256; +use sp_std::prelude::*; + +#[cfg(not(feature = "std"))] +use alloc::format; + +/// Executes an ERC20 token transfer by calling the `transfer` function on the specified ERC20 contract +/// +/// # Arguments +/// +/// * `handle` - Handle for the precompile execution context +/// * `erc20` - Address of the ERC20 token contract +/// * `to` - Destination address to transfer tokens to +/// * `amount` - Amount of tokens to transfer +/// +/// # Returns +/// +/// * `Ok(bool)` - Returns true if transfer succeeded, false otherwise +/// * `Err(PrecompileFailure)` - If the transfer call fails or returns invalid data +pub fn erc20_transfer( + handle: &mut impl PrecompileHandle, + erc20: Address, + to: Address, + amount: U256, +) -> EvmResult { + #[allow(deprecated)] + let transfer_fn = Function { + name: String::from("transfer"), + inputs: Vec::from([ + ethabi::Param { + name: String::from("to"), + kind: ethabi::ParamType::Address, + internal_type: None, + }, + ethabi::Param { + name: String::from("value"), + kind: ethabi::ParamType::Uint(256), + internal_type: None, + }, + ]), + outputs: Vec::from([ethabi::Param { + name: String::from("success"), + kind: ethabi::ParamType::Bool, + internal_type: None, + }]), + constant: None, + state_mutability: ethabi::StateMutability::NonPayable, + }; + + let args = [ethabi::Token::Address(to.0), ethabi::Token::Uint(ethabi::Uint::from(amount))]; + + let data = transfer_fn + .encode_input(&args) + .map_err(|e| revert(format!("failed to encode IERC20.transfer call: {e:?}")))?; + // let gas_limit = Some(handle.remaining_gas()); + let gas_limit = None; + let is_static = false; + let caller = handle.context().caller; + let context = fp_evm::Context { address: erc20.0, caller, apparent_value: U256::zero() }; + let (exit_reason, output) = handle.call(erc20.0, None, data, gas_limit, is_static, &context); + + log::debug!( + target: "evm", + "erc20_transfer: context: {:?}, exit_reason: {:?}, input: ({:?}, {}), output: 0x{}", + context, + exit_reason, + to.0, + amount, + hex::encode(&output), + ); + + match exit_reason { + fp_evm::ExitReason::Succeed(_) => { + // decode the result and return it + let result = transfer_fn + .decode_output(&output) + .map_err(|e| revert(format!("failed to decode IERC20.transfer result: {e:?}")))?; + let first_token = result.first().ok_or(RevertReason::custom("no return value"))?; + let s = if let ethabi::Token::Bool(val) = first_token { *val } else { false }; + Ok(s) + }, + fp_evm::ExitReason::Error(e) => Err(PrecompileFailure::Error { exit_status: e }), + fp_evm::ExitReason::Revert(e) => Err(PrecompileFailure::Revert { exit_status: e, output }), + fp_evm::ExitReason::Fatal(e) => Err(PrecompileFailure::Fatal { exit_status: e }), + } +} diff --git a/precompiles/multi-asset-delegation/Cargo.toml b/precompiles/multi-asset-delegation/Cargo.toml index 3be2e45a0..e701346ff 100644 --- a/precompiles/multi-asset-delegation/Cargo.toml +++ b/precompiles/multi-asset-delegation/Cargo.toml @@ -7,8 +7,6 @@ description = "A Precompile to make pallet-multi-asset-delegation calls encoding [dependencies] precompile-utils = { workspace = true } -ethabi = { workspace = true } -hex = { workspace = true, default-features = false } # Substrate frame-support = { workspace = true } @@ -23,6 +21,7 @@ sp-std = { workspace = true } # Frontier fp-evm = { workspace = true } pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] } +evm-erc20-utils = { workspace = true } tangle-primitives = { workspace = true } @@ -174,7 +173,6 @@ std = [ "sp-std/std", "tangle-primitives/std", "pallet-assets/std", - "hex/std", "scale-info/std", "sp-runtime/std", "frame-support/std", @@ -207,9 +205,9 @@ std = [ "pallet-evm-precompile-ed25519/std", "precompile-utils/std", "serde/std", - "hex/std", "pallet-session/std", "pallet-staking/std", "sp-staking/std", "frame-election-provider-support/std", + "evm-erc20-utils/std", ] diff --git a/precompiles/multi-asset-delegation/src/lib.rs b/precompiles/multi-asset-delegation/src/lib.rs index abeed5147..7a8fbfb0a 100644 --- a/precompiles/multi-asset-delegation/src/lib.rs +++ b/precompiles/multi-asset-delegation/src/lib.rs @@ -40,8 +40,8 @@ pub mod mock_evm; #[cfg(test)] mod tests; -use ethabi::Function; -use fp_evm::{PrecompileFailure, PrecompileHandle}; +use evm_erc20_utils::*; +use fp_evm::PrecompileHandle; use frame_support::{ __private::log, dispatch::{GetDispatchInfo, PostDispatchInfo}, @@ -51,7 +51,6 @@ use pallet_evm::AddressMapping; use pallet_multi_asset_delegation::types::DelegatorBlueprintSelection; use precompile_utils::prelude::*; use sp_core::{H160, H256, U256}; -use sp_runtime::format; use sp_runtime::traits::Dispatchable; use sp_std::{marker::PhantomData, vec::Vec}; use tangle_primitives::{services::Asset, types::WrappedAccountId32}; @@ -441,71 +440,3 @@ where Ok(()) } } - -fn erc20_transfer( - handle: &mut impl PrecompileHandle, - erc20: Address, - to: Address, - amount: U256, -) -> EvmResult { - #[allow(deprecated)] - let transfer_fn = Function { - name: String::from("transfer"), - inputs: Vec::from([ - ethabi::Param { - name: String::from("to"), - kind: ethabi::ParamType::Address, - internal_type: None, - }, - ethabi::Param { - name: String::from("value"), - kind: ethabi::ParamType::Uint(256), - internal_type: None, - }, - ]), - outputs: Vec::from([ethabi::Param { - name: String::from("success"), - kind: ethabi::ParamType::Bool, - internal_type: None, - }]), - constant: None, - state_mutability: ethabi::StateMutability::NonPayable, - }; - - let args = [ethabi::Token::Address(to.0), ethabi::Token::Uint(ethabi::Uint::from(amount))]; - - let data = transfer_fn - .encode_input(&args) - .map_err(|e| revert(format!("failed to encode IERC20.transfer call: {e:?}")))?; - // let gas_limit = Some(handle.remaining_gas()); - let gas_limit = None; - let is_static = false; - let caller = handle.context().caller; - let context = fp_evm::Context { address: erc20.0, caller, apparent_value: U256::zero() }; - let (exit_reason, output) = handle.call(erc20.0, None, data, gas_limit, is_static, &context); - - log::debug!( - target: "evm", - "erc20_transfer: context: {:?}, exit_reason: {:?}, input: ({:?}, {}), output: 0x{}", - context, - exit_reason, - to.0, - amount, - hex::encode(&output), - ); - - match exit_reason { - fp_evm::ExitReason::Succeed(_) => { - // decode the result and return it - let result = transfer_fn - .decode_output(&output) - .map_err(|e| revert(format!("failed to decode IERC20.transfer result: {e:?}")))?; - let first_token = result.first().ok_or(RevertReason::custom("no return value"))?; - let s = if let ethabi::Token::Bool(val) = first_token { *val } else { false }; - Ok(s) - }, - fp_evm::ExitReason::Error(e) => Err(PrecompileFailure::Error { exit_status: e }), - fp_evm::ExitReason::Revert(e) => Err(PrecompileFailure::Revert { exit_status: e, output }), - fp_evm::ExitReason::Fatal(e) => Err(PrecompileFailure::Fatal { exit_status: e }), - } -}