Skip to content

Commit

Permalink
feat: move erc20 transfer to its own shared crate
Browse files Browse the repository at this point in the history
  • Loading branch information
shekohex committed Jan 7, 2025
1 parent de96ce5 commit 72b93bb
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 75 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 }
Expand Down
25 changes: 25 additions & 0 deletions precompiles/erc20-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
110 changes: 110 additions & 0 deletions precompiles/erc20-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<bool> {
#[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 }),
}
}
6 changes: 2 additions & 4 deletions precompiles/multi-asset-delegation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
]
73 changes: 2 additions & 71 deletions precompiles/multi-asset-delegation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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};
Expand Down Expand Up @@ -441,71 +440,3 @@ where
Ok(())
}
}

fn erc20_transfer(
handle: &mut impl PrecompileHandle,
erc20: Address,
to: Address,
amount: U256,
) -> EvmResult<bool> {
#[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 }),
}
}

0 comments on commit 72b93bb

Please sign in to comment.