diff --git a/runtime/integration-tests/src/evm/mod.rs b/runtime/integration-tests/src/evm/mod.rs index 99172b9fdb..5ae9067344 100644 --- a/runtime/integration-tests/src/evm/mod.rs +++ b/runtime/integration-tests/src/evm/mod.rs @@ -11,4 +11,3 @@ // GNU General Public License for more details. mod ethereum_transaction; -mod precompile; diff --git a/runtime/integration-tests/src/evm/precompile.rs b/runtime/integration-tests/src/evm/precompile.rs deleted file mode 100644 index 6390d474fd..0000000000 --- a/runtime/integration-tests/src/evm/precompile.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2023 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use std::collections::BTreeMap; - -use axelar_gateway_precompile::SourceConverter; -use cfg_primitives::{AccountId, Balance, CouncilCollective, PoolId, TrancheId, CFG}; -use cfg_traits::{ethereum::EthereumTransactor, liquidity_pools::Codec}; -use cfg_types::{ - domain_address::{Domain, DomainAddress}, - fixed_point::Rate, - tokens::{CurrencyId, CustomMetadata, GeneralCurrencyIndex}, -}; -use ethabi::{Contract, Function, Param, ParamType, Token}; -use ethereum::{LegacyTransaction, TransactionAction, TransactionSignature, TransactionV2}; -use frame_support::{assert_err, assert_ok, dispatch::RawOrigin, BoundedVec}; -use fudge::primitives::Chain; -use hex::ToHex; -use orml_traits::{asset_registry::AssetMetadata, MultiCurrency}; -use pallet_evm::{AddressMapping, FeeCalculator}; -use pallet_liquidity_pools::Message; -use parity_scale_codec::Encode; -use runtime_common::{account_conversion::AccountConverter, evm::precompile::LP_AXELAR_GATEWAY}; -use sp_core::{Get, H160, H256, U256}; -use sp_runtime::traits::{BlakeTwo256, Hash}; -use staging_xcm::{v3::MultiLocation, VersionedMultiLocation}; -use tokio::runtime::Handle; - -use crate::{ - chain::centrifuge::{ - FastTrackVotingPeriod, MinimumDeposit, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - PARA_ID, - }, - evm::ethereum_transaction::TEST_CONTRACT_CODE, - utils::{ - env, - env::{ChainState, EventRange, TestEnv}, - evm::{deploy_contract, mint_balance_into_derived_account}, - }, -}; - -const ED: Balance = 1_234; - -#[tokio::test] -async fn axelar_precompile_execute() { - let mut env = env::test_env_default(Handle::current()); - - env.evolve().unwrap(); - - let currency_id = CurrencyId::ForeignAsset(123456); - - let lp_axelar_gateway = H160::from_low_u64_be(LP_AXELAR_GATEWAY); - let sender_address = H160::from_low_u64_be(1_000_002); - - mint_balance_into_derived_account(&mut env, sender_address, 1_000_000 * CFG); - - let derived_sender_account = env - .with_state(Chain::Para(PARA_ID), || { - ::AddressMapping::into_account_id(sender_address) - }) - .unwrap(); - - let receiver_address = H160::from_low_u64_be(1_000_003); - - let derived_receiver_account = env - .with_state(Chain::Para(PARA_ID), || { - ::AddressMapping::into_account_id(receiver_address) - }) - .unwrap(); - - env.with_state(Chain::Para(PARA_ID), || { - let derived_receiver_balance = - orml_tokens::Pallet::::free_balance(currency_id, &derived_receiver_account); - - assert_eq!(derived_receiver_balance, 0) - }) - .unwrap(); - - let source_address = H160::from_low_u64_be(1111); - let evm_chain_name = String::from("Ethereum"); - let evm_chain_id = 0; - - let currency_metadata = AssetMetadata { - decimals: 18, - name: BoundedVec::::StringLimit>::try_from( - "Test".as_bytes().to_vec(), - ) - .expect("Can create BoundedVec for token name"), - symbol: BoundedVec::::StringLimit>::try_from( - "TST".as_bytes().to_vec(), - ) - .expect("Can create BoundedVec for token symbol"), - existential_deposit: ED, - location: Some(VersionedMultiLocation::V3(MultiLocation::here())), - additional: CustomMetadata { - transferability: Default::default(), - mintable: true, - permissioned: false, - pool_currency: false, - local_representation: None, - }, - }; - - env.with_mut_state(Chain::Para(PARA_ID), || { - orml_asset_registry::Pallet::::register_asset( - RuntimeOrigin::root(), - currency_metadata, - Some(currency_id), - ) - .unwrap(); - - orml_tokens::Pallet::::deposit( - currency_id, - &derived_sender_account, - 1_000_000_000_000 * 10u128.saturating_pow(18), - ) - .unwrap(); - - orml_tokens::Pallet::::deposit(currency_id, &derived_receiver_account, ED) - .unwrap(); - }) - .unwrap(); - - let general_currency_id = env - .with_state(Chain::Para(PARA_ID), || { - pallet_liquidity_pools::Pallet::::try_get_general_index(currency_id).unwrap() - }) - .unwrap(); - - let transfer_amount = 100; - let msg = Message::::Transfer { - currency: general_currency_id, - sender: derived_sender_account.clone().into(), - receiver: derived_receiver_account.clone().into(), - amount: transfer_amount, - }; - - env.with_mut_state(Chain::Para(PARA_ID), || { - axelar_gateway_precompile::Pallet::::set_gateway( - RuntimeOrigin::root(), - sender_address, - ) - .unwrap(); - - axelar_gateway_precompile::Pallet::::set_converter( - RuntimeOrigin::root(), - BlakeTwo256::hash(evm_chain_name.as_bytes()), - SourceConverter { - domain: Domain::EVM(evm_chain_id), - }, - ) - .unwrap(); - - pallet_liquidity_pools_gateway::Pallet::::add_instance( - RuntimeOrigin::root(), - DomainAddress::EVM(evm_chain_id, source_address.0), - ) - .unwrap(); - }); - - let command_id = H256::from_low_u64_be(5678); - - #[allow(deprecated)] - let test_input = Contract { - constructor: None, - functions: BTreeMap::>::from([( - "execute".into(), - vec![Function { - name: "execute".into(), - inputs: vec![ - Param { - name: "commandId".into(), - kind: ParamType::FixedBytes(32), - internal_type: None, - }, - Param { - name: "sourceChain".into(), - kind: ParamType::String, - internal_type: None, - }, - Param { - name: "sourceAddress".into(), - kind: ParamType::String, - internal_type: None, - }, - Param { - name: "payload".into(), - kind: ParamType::Bytes, - internal_type: None, - }, - ], - outputs: vec![], - constant: false, - state_mutability: Default::default(), - }], - )]), - events: Default::default(), - errors: Default::default(), - receive: false, - fallback: false, - } - .function("execute".into()) - .map_err(|_| "cannot retrieve test contract function") - .unwrap() - .encode_input(&[ - Token::FixedBytes(command_id.0.to_vec()), - Token::String(evm_chain_name), - Token::String(String::from_utf8(source_address.as_fixed_bytes().to_vec()).unwrap()), - Token::Bytes(msg.serialize()), - ]) - .map_err(|_| "cannot encode input for test contract function") - .unwrap(); - - env.with_mut_state(Chain::Para(PARA_ID), || { - assert_ok!(pallet_evm::Pallet::::call( - RawOrigin::Signed(derived_sender_account.clone()).into(), - sender_address, - lp_axelar_gateway, - test_input.to_vec(), - U256::from(0), - 0x100000, - U256::from(1_000_000_000), - None, - Some(U256::from(0)), - Vec::new(), - )); - }) - .unwrap(); - - env.with_state(Chain::Para(PARA_ID), || { - let derived_receiver_balance = - orml_tokens::Pallet::::free_balance(currency_id, &derived_receiver_account); - - assert_eq!(derived_receiver_balance, transfer_amount + ED) - }) - .unwrap(); -} diff --git a/runtime/integration-tests/src/generic/cases/liquidity_pools.rs b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs index 5174d130fb..d5d8aa8b74 100644 --- a/runtime/integration-tests/src/generic/cases/liquidity_pools.rs +++ b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs @@ -23,7 +23,6 @@ use frame_support::{ assert_noop, assert_ok, dispatch::RawOrigin, traits::{ - fungible::Mutate as FungibleMutate, fungibles::{Inspect, Mutate as FungiblesMutate}, OriginTrait, PalletInfo, }, @@ -74,7 +73,10 @@ use crate::{ config::Runtime, env::{Blocks, Env}, envs::fudge_env::{handle::FudgeHandle, FudgeEnv, FudgeSupport}, - utils::{democracy::execute_via_democracy, genesis, genesis::Genesis}, + utils::{ + democracy::execute_via_democracy, evm::mint_balance_into_derived_account, genesis, + genesis::Genesis, + }, }, utils::{accounts::Keyring, AUSD_CURRENCY_ID, AUSD_ED, USDT_CURRENCY_ID, USDT_ED}, }; @@ -4763,28 +4765,6 @@ mod development { use super::*; - mod utils { - use super::*; - - pub fn mint_balance_into_derived_account( - env: &mut impl Env, - address: H160, - balance: u128, - ) { - let chain_id = env.parachain_state(|| pallet_evm_chain_id::Pallet::::get()); - - let derived_account = - AccountConverter::convert_evm_address(chain_id, address.to_fixed_bytes()); - - env.parachain_state_mut(|| { - pallet_balances::Pallet::::mint_into(&derived_account.into(), balance) - .unwrap() - }); - } - } - - use utils::*; - fn test_via_outbound_queue() { let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() @@ -4894,22 +4874,15 @@ mod development { nonce.add_assign(T::OutboundMessageNonce::one()); - // Success - - // Note how both the target address and the gateway sender need to have some - // balance. - mint_balance_into_derived_account::( - &mut env, - axelar_contract_address, - cfg(1_000_000_000), - ); - mint_balance_into_derived_account::( - &mut env, - gateway_sender_h160, - cfg(1_000_000), - ); - assert_ok!(env.parachain_state_mut(|| { + // Note how both the target address and the gateway sender need to have some + // balance. + mint_balance_into_derived_account::( + axelar_contract_address, + cfg(1_000_000_000), + ); + mint_balance_into_derived_account::(gateway_sender_h160, cfg(1_000_000)); + as OutboundQueue>::submit( sender.clone(), test_domain.clone(), diff --git a/runtime/integration-tests/src/generic/cases/precompile.rs b/runtime/integration-tests/src/generic/cases/precompile.rs new file mode 100644 index 0000000000..6b8663956a --- /dev/null +++ b/runtime/integration-tests/src/generic/cases/precompile.rs @@ -0,0 +1,137 @@ +use axelar_gateway_precompile::SourceConverter; +use cfg_primitives::{Balance, PoolId, TrancheId, CFG}; +use cfg_traits::liquidity_pools::Codec; +use cfg_types::{ + domain_address::{Domain, DomainAddress}, + fixed_point::Rate, +}; +use ethabi::{Function, Param, ParamType, Token}; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use orml_traits::MultiCurrency; +use pallet_evm::AddressMapping; +use pallet_liquidity_pools::Message; +use runtime_common::evm::precompile::LP_AXELAR_GATEWAY; +use sp_core::{H160, H256, U256}; +use sp_runtime::traits::{BlakeTwo256, Hash}; + +use crate::generic::{ + config::Runtime, + env::Env, + envs::runtime_env::RuntimeEnv, + utils::{ + self, + currency::{usd18, CurrencyInfo, Usd18}, + genesis::{self, Genesis}, + }, +}; + +fn axelar_precompile_execute() { + RuntimeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::assets::(vec![Box::new(Usd18)])) + .storage(), + ) + .parachain_state_mut(|| { + let lp_axelar_gateway = H160::from_low_u64_be(LP_AXELAR_GATEWAY); + + let sender_address = H160::from_low_u64_be(1_000_002); + let receiver_address = H160::from_low_u64_be(1_000_003); + + let source_address = H160::from_low_u64_be(1111); + let evm_chain_name = String::from("Ethereum"); + let evm_chain_id = 0; + let command_id = H256::from_low_u64_be(5678); + let transfer_amount = usd18(100); + + let derived_sender_account = T::AddressMapping::into_account_id(sender_address); + let derived_receiver_account = T::AddressMapping::into_account_id(receiver_address); + + utils::evm::mint_balance_into_derived_account::(sender_address, 1 * CFG); + + let general_currency_id = + pallet_liquidity_pools::Pallet::::try_get_general_index(Usd18.id()).unwrap(); + + axelar_gateway_precompile::Pallet::::set_gateway(RawOrigin::Root.into(), sender_address) + .unwrap(); + + axelar_gateway_precompile::Pallet::::set_converter( + RawOrigin::Root.into(), + BlakeTwo256::hash(evm_chain_name.as_bytes()), + SourceConverter { + domain: Domain::EVM(evm_chain_id), + }, + ) + .unwrap(); + + pallet_liquidity_pools_gateway::Pallet::::add_instance( + RawOrigin::Root.into(), + DomainAddress::EVM(evm_chain_id, source_address.0), + ) + .unwrap(); + + let msg = Message::::Transfer { + currency: general_currency_id, + sender: derived_sender_account.clone().into(), + receiver: derived_receiver_account.clone().into(), + amount: transfer_amount, + }; + + #[allow(deprecated)] // Due `constant` field. Can be remove in future ethabi + let eth_function_encoded = Function { + name: "execute".into(), + inputs: vec![ + Param { + name: "commandId".into(), + kind: ParamType::FixedBytes(32), + internal_type: None, + }, + Param { + name: "sourceChain".into(), + kind: ParamType::String, + internal_type: None, + }, + Param { + name: "sourceAddress".into(), + kind: ParamType::String, + internal_type: None, + }, + Param { + name: "payload".into(), + kind: ParamType::Bytes, + internal_type: None, + }, + ], + outputs: vec![], + constant: false, + state_mutability: Default::default(), + } + .encode_input(&[ + Token::FixedBytes(command_id.0.to_vec()), + Token::String(evm_chain_name), + Token::String(String::from_utf8(source_address.as_fixed_bytes().to_vec()).unwrap()), + Token::Bytes(msg.serialize()), + ]) + .expect("cannot encode input for test contract function"); + + assert_ok!(pallet_evm::Pallet::::call( + RawOrigin::Signed(derived_sender_account.clone()).into(), + sender_address, + lp_axelar_gateway, + eth_function_encoded.to_vec(), + U256::from(0), + 0x100000, + U256::from(1_000_000_000), + None, + Some(U256::from(0)), + Vec::new(), + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance(Usd18.id(), &derived_receiver_account), + transfer_amount + ); + }); +} + +crate::test_for_runtimes!([development], axelar_precompile_execute); diff --git a/runtime/integration-tests/src/generic/config.rs b/runtime/integration-tests/src/generic/config.rs index 98a1c87cae..5f643b07f9 100644 --- a/runtime/integration-tests/src/generic/config.rs +++ b/runtime/integration-tests/src/generic/config.rs @@ -150,6 +150,7 @@ pub trait Runtime: + pallet_collective::Config + pallet_democracy::Config> + pallet_evm_chain_id::Config + + pallet_evm::Config + pallet_remarks::Config + pallet_utility::Config + pallet_rewards::Config< @@ -162,7 +163,7 @@ pub trait Runtime: FixedI128, SingleCurrencyMovement, >, - > + > + axelar_gateway_precompile::Config { /// Just the RuntimeCall type, but redefined with extra bounds. /// You can add `From` bounds in order to convert pallet calls to diff --git a/runtime/integration-tests/src/generic/mod.rs b/runtime/integration-tests/src/generic/mod.rs index 5e0b2a3cdd..f7251348ef 100644 --- a/runtime/integration-tests/src/generic/mod.rs +++ b/runtime/integration-tests/src/generic/mod.rs @@ -20,6 +20,7 @@ mod cases { mod liquidity_pools; mod loans; mod oracles; + mod precompile; mod proxy; mod restricted_transfers; mod rewards; diff --git a/runtime/integration-tests/src/generic/utils/mod.rs b/runtime/integration-tests/src/generic/utils/mod.rs index 2d5a4d2e00..0993480df0 100644 --- a/runtime/integration-tests/src/generic/utils/mod.rs +++ b/runtime/integration-tests/src/generic/utils/mod.rs @@ -21,13 +21,14 @@ use cfg_types::{ pools::TrancheMetadata, tokens::{CurrencyId, TrancheCurrency}, }; -use frame_support::BoundedVec; +use frame_support::{traits::fungible::Mutate, BoundedVec}; use frame_system::RawOrigin; use pallet_oracle_collection::types::CollectionInfo; use pallet_pool_system::tranches::{TrancheInput, TrancheType}; -use runtime_common::oracle::Feeder; +use runtime_common::{account_conversion::convert_evm_address, oracle::Feeder}; +use sp_core::H160; use sp_runtime::{ - traits::{One, StaticLookup}, + traits::{Get, One, StaticLookup}, Perquintill, }; @@ -252,3 +253,17 @@ pub mod oracle { .unwrap(); } } + +pub mod evm { + use super::*; + + pub fn mint_balance_into_derived_account( + address: H160, + balance: Balance, + ) -> Balance { + let chain_id = pallet_evm_chain_id::Pallet::::get(); + let derived_account = convert_evm_address(chain_id, address.to_fixed_bytes()); + + pallet_balances::Pallet::::mint_into(&derived_account.into(), balance).unwrap() + } +}