Skip to content

Commit

Permalink
EVM foreign assets (#2869)
Browse files Browse the repository at this point in the history
* Add call ethereumXcm.forceTransactAs (to create contracts from runtime)

* add pallet-moonbeam-foreign-assets skeleton

* inject solidity bytecode

* add freeze/unfreeze calls

* refactor foreign assets pallet: create evm module

* implement all evm calls

* encode create args

* build contract initcode: support 0x prefix

* fix ethereum-xcm rust tests

* pallet moonbeam-foreign-assets: prepare rust tests

* add pallet moonbeam-foreign-assets for moonbase runtime

* disable creation of foreign assets from pallet asset manager

* fix matcher, remove useless genericity

* rustfmt

* improve build script: support direct copy of hex calldata

* improve solidity impl: disable transfer & approve when paused

* refactor evm foreign pallet: create AssetStatus & rename types

* forbid old AssetsIds (with filter) and rework freeze mechanism

* fix rust compilation for pallet-moonbeam-foreign-assets alone

* fix: calldata need hex bytecode

* wip

* Revert "fix: calldata need hex bytecode"

This reverts commit 67fc5be.

* recompile solidity contract with solc 0.8.26+commit8a97fa7a

* rust tests: fix pov ratio and storage ratio

* fix rust tests compilation

* fix constructor args encoding

* test freeze/unfreeze

* rewrite rust moonbase test asset_can_be_registered

* estimate crate gas limit with manual binary search

* determine gas limit for freeze/unfreeze

* use branch moonbeam-polkadot-v1.11.0

* add calls force_burn and force_mint @ define gas limit for mint/burn

* add transfer call

* fix benchmarks compilation

* add feature insecure_zero_ed for new pallet rust tests

* improve benchmarks: create max foreign assets for worst case scenario

* create weight info fns for freeze/unfreeze

* add foreign asset migrator precompile

* fix tracing compilation

* add EvmForeignassets to CurrencyIdToLocation convertion

* remove useless calls and rework rust tests

* rustfmt

* fix copyright: new code should be own by MBF

* AssetsTransactors: for some reason Erc20XcmBridge should be at the end

* fix moonbase rust integration tests

* allow FastGeneralAdmin & 5/9 techCommittee to manage evm foreign assets

* rustfmt

* rename  some fns/events and remove unused code

* ticker -> symbol

* change base implementation of registerForeignAsset()

* re-arrange tests in test-assets (WIP)

* review suggestions

* create v4 locations for foreign assets testing

* basic testing for evm foreign assets pallet

* ForeignAssetTypeChanged -> ForeignAssetXcmLocationChanged

* mockAssetBalance wip

* fix mockAssetBalance

* restore old foreign asset helpers

* allow creation of old foreign assets

* fix imports

* use old foreign assets for xcm fees runtime api tests

* remove Mytoken.sol (fail to compile on CI) and use parseAbi instead

* fix some ts tests

* fix more ts tests

* remove test specific to substrate assets mechanism (is_sufficient)

* fix ts imports

* prettier

* remove unused imports

* prettier again

* prettier

* prettier

---------

Co-authored-by: Gonza Montiel <gon.montiel@gmail.com>
  • Loading branch information
librelois and gonzamontiel authored Jul 25, 2024
1 parent 20ba7fe commit 3b954cf
Show file tree
Hide file tree
Showing 98 changed files with 3,417 additions and 1,127 deletions.
675 changes: 368 additions & 307 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"pallets/asset-manager",
"pallets/erc20-xcm-bridge",
"pallets/ethereum-xcm",
"pallets/moonbeam-foreign-assets",
"pallets/moonbeam-lazy-migrations",
"pallets/moonbeam-orbiters",
"pallets/moonbeam-xcm-benchmarks",
Expand All @@ -24,6 +25,7 @@ members = [
"precompiles/collective",
"precompiles/conviction-voting",
"precompiles/crowdloan-rewards",
"precompiles/foreign-asset-migrator",
"precompiles/gmp",
"precompiles/identity",
"precompiles/parachain-staking",
Expand Down Expand Up @@ -75,9 +77,9 @@ moonbeam-xcm-benchmarks = { path = "pallets/moonbeam-xcm-benchmarks", default-fe
pallet-asset-manager = { path = "pallets/asset-manager", default-features = false }
pallet-erc20-xcm-bridge = { path = "pallets/erc20-xcm-bridge", default-features = false }
pallet-ethereum-xcm = { path = "pallets/ethereum-xcm", default-features = false }


pallet-moonbeam-foreign-assets = { path = "pallets/moonbeam-foreign-assets", default-features = false }
pallet-moonbeam-lazy-migrations = { path = "pallets/moonbeam-lazy-migrations", default-features = false }

pallet-evm-precompile-author-mapping = { path = "precompiles/author-mapping", default-features = false }
pallet-evm-precompile-balances-erc20 = { path = "precompiles/balances-erc20", default-features = false }
pallet-evm-precompile-batch = { path = "precompiles/batch", default-features = false }
Expand Down Expand Up @@ -105,6 +107,7 @@ pallet-parachain-staking = { path = "pallets/parachain-staking", default-feature
pallet-precompile-benchmarks = { path = "pallets/precompile-benchmarks", default-features = false }
pallet-proxy-genesis-companion = { path = "pallets/proxy-genesis-companion", default-features = false }
pallet-xcm-transactor = { path = "pallets/xcm-transactor", default-features = false }
precompile-foreign-asset-migrator = { path = "precompiles/foreign-asset-migrator", default-features = false }
xcm-primitives = { path = "primitives/xcm", default-features = false }

pallet-crowdloan-rewards = { git = "https://github.com/moonbeam-foundation/crowdloan-rewards", branch = "moonbeam-polkadot-v1.11.0", default-features = false }
Expand Down Expand Up @@ -257,9 +260,10 @@ pallet-evm-precompile-simple = { git = "https://github.com/moonbeam-foundation/f
pallet-evm-precompile-storage-cleaner = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0", default-features = false }

precompile-utils = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0", default-features = false }
precompile-utils-macro = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0", default-features = false }

# Frontier (client)

# Frontier (client)
fc-consensus = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0" }
fc-db = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0" }
fc-api = { git = "https://github.com/moonbeam-foundation/frontier", branch = "moonbeam-polkadot-v1.11.0" }
Expand Down
4 changes: 2 additions & 2 deletions pallets/asset-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ pub mod pallet {
units_per_second: u128,
},
/// Changed the xcm type mapping for a given asset id
ForeignAssetTypeChanged {
ForeignAssetXcmLocationChanged {
asset_id: T::AssetId,
new_asset_type: T::ForeignAssetType,
},
Expand Down Expand Up @@ -367,7 +367,7 @@ pub mod pallet {
AssetTypeUnitsPerSecond::<T>::insert(&new_asset_type, units);
}

Self::deposit_event(Event::ForeignAssetTypeChanged {
Self::deposit_event(Event::ForeignAssetXcmLocationChanged {
asset_id,
new_asset_type,
});
Expand Down
2 changes: 1 addition & 1 deletion pallets/asset-manager/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ fn test_root_can_change_asset_id_type() {
asset_type: MockAssetType::MockAsset(1),
units_per_second: 200,
},
crate::Event::ForeignAssetTypeChanged {
crate::Event::ForeignAssetXcmLocationChanged {
asset_id: 1,
new_asset_type: MockAssetType::MockAsset(2),
},
Expand Down
46 changes: 42 additions & 4 deletions pallets/ethereum-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ pub mod pallet {
type EnsureProxy: EnsureProxy<Self::AccountId>;
/// The origin that is allowed to resume or suspend the XCM to Ethereum executions.
type ControllerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// An origin that can submit a create tx type
type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
}

#[pallet::pallet]
Expand Down Expand Up @@ -160,7 +162,7 @@ pub mod pallet {
}
}
);
Self::validate_and_apply(source, xcm_transaction)
Self::validate_and_apply(source, xcm_transaction, false, None)
}

/// Xcm Transact an Ethereum transaction through proxy.
Expand Down Expand Up @@ -202,7 +204,7 @@ pub mod pallet {
error: sp_runtime::DispatchError::Other(e),
})?;

Self::validate_and_apply(transact_as, xcm_transaction)
Self::validate_and_apply(transact_as, xcm_transaction, false, None)
}

/// Suspends all Ethereum executions from XCM.
Expand All @@ -228,6 +230,39 @@ pub mod pallet {

Ok(())
}

/// Xcm Transact an Ethereum transaction, but allow to force the caller and create address.
/// This call should be restricted (callable only by the runtime or governance).
/// Weight: Gas limit plus the db reads involving the suspension and proxy checks
#[pallet::weight({
let without_base_extrinsic_weight = false;
<T as pallet_evm::Config>::GasWeightMapping::gas_to_weight({
match xcm_transaction {
EthereumXcmTransaction::V1(v1_tx) => v1_tx.gas_limit.unique_saturated_into(),
EthereumXcmTransaction::V2(v2_tx) => v2_tx.gas_limit.unique_saturated_into()
}
}, without_base_extrinsic_weight).saturating_add(T::DbWeight::get().reads(1))
})]
pub fn force_transact_as(
origin: OriginFor<T>,
transact_as: H160,
xcm_transaction: EthereumXcmTransaction,
force_create_address: Option<H160>,
) -> DispatchResultWithPostInfo {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(
!EthereumXcmSuspended::<T>::get(),
DispatchErrorWithPostInfo {
error: Error::<T>::EthereumXcmExecutionSuspended.into(),
post_info: PostDispatchInfo {
actual_weight: Some(T::DbWeight::get().reads(1)),
pays_fee: Pays::Yes
}
}
);

Self::validate_and_apply(transact_as, xcm_transaction, true, force_create_address)
}
}
}

Expand All @@ -243,6 +278,8 @@ impl<T: Config> Pallet<T> {
fn validate_and_apply(
source: H160,
xcm_transaction: EthereumXcmTransaction,
allow_create: bool,
maybe_force_create_address: Option<H160>,
) -> DispatchResultWithPostInfo {
// The lack of a real signature where different callers with the
// same nonce are providing identical transaction payloads results in a collision and
Expand All @@ -253,7 +290,7 @@ impl<T: Config> Pallet<T> {
let error_weight = T::DbWeight::get().reads(1);

let transaction: Option<Transaction> =
xcm_transaction.into_transaction_v2(current_nonce, T::ChainId::get());
xcm_transaction.into_transaction_v2(current_nonce, T::ChainId::get(), allow_create);
if let Some(transaction) = transaction {
let transaction_data: TransactionData = (&transaction).into();

Expand Down Expand Up @@ -300,7 +337,8 @@ impl<T: Config> Pallet<T> {
// transaction on chain - we increase the global nonce.
<Nonce<T>>::put(current_nonce.saturating_add(U256::one()));

let (dispatch_info, _) = T::ValidatedTransaction::apply(source, transaction)?;
let (dispatch_info, _) =
T::ValidatedTransaction::apply(source, transaction, maybe_force_create_address)?;
Ok(dispatch_info)
} else {
Err(sp_runtime::DispatchErrorWithPostInfo {
Expand Down
1 change: 1 addition & 0 deletions pallets/ethereum-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl crate::Config for Test {
type ReservedXcmpWeight = ReservedXcmpWeight;
type EnsureProxy = EthereumXcmEnsureProxy;
type ControllerOrigin = EnsureRoot<AccountId32>;
type ForceOrigin = EnsureRoot<AccountId32>;
}

impl fp_self_contained::SelfContainedCall for RuntimeCall {
Expand Down
2 changes: 1 addition & 1 deletion pallets/ethereum-xcm/src/tests/v1/eip1559.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ fn test_transact_xcm_evm_call_works() {
input: hex::decode(CONTRACT).unwrap(),
}
.sign(&alice.private_key, None);
assert_ok!(Ethereum::execute(alice.address, &t, None,));
assert_ok!(Ethereum::execute(alice.address, &t, None, None));

let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap();
let foo = hex::decode("c2985578").unwrap();
Expand Down
2 changes: 1 addition & 1 deletion pallets/ethereum-xcm/src/tests/v1/eip2930.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ fn test_transact_xcm_evm_call_works() {
input: hex::decode(CONTRACT).unwrap(),
}
.sign(&alice.private_key, None);
assert_ok!(Ethereum::execute(alice.address, &t, None,));
assert_ok!(Ethereum::execute(alice.address, &t, None, None));

let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap();
let foo = hex::decode("c2985578").unwrap();
Expand Down
2 changes: 1 addition & 1 deletion pallets/ethereum-xcm/src/tests/v1/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ fn test_transact_xcm_evm_call_works() {
input: hex::decode(CONTRACT).unwrap(),
}
.sign(&alice.private_key);
assert_ok!(Ethereum::execute(alice.address, &t, None,));
assert_ok!(Ethereum::execute(alice.address, &t, None, None));

let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap();
let foo = hex::decode("c2985578").unwrap();
Expand Down
2 changes: 1 addition & 1 deletion pallets/ethereum-xcm/src/tests/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fn test_transact_xcm_evm_call_works() {
input: hex::decode(CONTRACT).unwrap(),
}
.sign(&alice.private_key, None);
assert_ok!(Ethereum::execute(alice.address, &t, None,));
assert_ok!(Ethereum::execute(alice.address, &t, None, None));

let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap();
let foo = hex::decode("c2985578").unwrap();
Expand Down
62 changes: 62 additions & 0 deletions pallets/moonbeam-foreign-assets/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[package]
name = "pallet-moonbeam-foreign-assets"
authors = { workspace = true }
edition = "2021"
version = "0.1.0"

[dependencies]
ethereum-types = { workspace = true }
log = { workspace = true }

# Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
parity-scale-codec = { workspace = true, features = [ "derive" ] }
precompile-utils = { workspace = true }
precompile-utils-macro = { workspace = true }
scale-info = { workspace = true, features = [ "derive" ] }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

# Frontier
fp-evm = { workspace = true }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }

# Polkadot
xcm = { workspace = true }
xcm-executor = { workspace = true }

# Benchmarks
frame-benchmarking = { workspace = true, optional = true }

[build-dependencies]
hex = { workspace = true, features = [ "std" ] }

[dev-dependencies]
hex = { workspace = true }
pallet-balances = { workspace = true, features = [ "insecure_zero_ed", "std" ] }
pallet-timestamp = { workspace = true, features = [ "std" ] }
sp-core = { workspace = true, features = [ "std" ] }

[features]
default = [ "std" ]
std = [
"ethereum-types/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"precompile-utils/std",
"precompile-utils/testing",
"pallet-evm/std",
"parity-scale-codec/std",
"scale-info/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"xcm/std",
"xcm-executor/std"
]

runtime-benchmarks = [ "frame-benchmarking"]
try-runtime = [ "frame-support/try-runtime" ]
45 changes: 45 additions & 0 deletions pallets/moonbeam-foreign-assets/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 Moonbeam Foundation.
// This file is part of Moonbeam.

// Moonbeam 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.

// Moonbeam 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 Moonbeam. If not, see <http://www.gnu.org/licenses/>.

use std::fs::File;
use std::io::prelude::*;

// Length of encoded constructor parameters
const PARAMS_LEN: usize = 256;

fn main() {
let hex_str = include_str!("resources/foreign_erc20_initcode.hex");
let prefix_0x = hex_str.chars().nth(1) == Some('x');
let bytecode = if prefix_0x {
hex::decode(&hex_str[2..])
} else {
hex::decode(hex_str)
}
.expect("fail to decode hexadecimal string in file foreign_erc20_initcode.hex");

// The encoded parameters at the end of the initializer bytecode should be removed,
// (the runtime will append the constructor parameters dynamically).
let bytecode_end = if bytecode.len() > PARAMS_LEN {
bytecode.len() - PARAMS_LEN
} else {
0
};

let mut file = File::create("resources/foreign_erc20_initcode.bin")
.expect("Fail to create file resources/foreign_erc20_initcode.bin");
file.write_all(&bytecode[..bytecode_end])
.expect("fail to write bytecode in /foreign_erc20_initcode.bin");
}
Loading

0 comments on commit 3b954cf

Please sign in to comment.