diff --git a/Cargo.lock b/Cargo.lock index 06d4b2b2e5..5d575ab412 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,7 +123,6 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" name = "altair-runtime" version = "0.14.2" dependencies = [ - "axelar-gateway-precompile", "cfg-primitives", "cfg-traits", "cfg-types", @@ -149,7 +148,6 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", - "liquidity-pools-gateway-routers", "log", "orml-asset-registry", "orml-tokens", @@ -160,6 +158,7 @@ dependencies = [ "pallet-anchors", "pallet-aura", "pallet-authorship", + "pallet-axelar-router", "pallet-balances", "pallet-base-fee", "pallet-block-rewards", @@ -932,29 +931,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "axelar-gateway-precompile" -version = "0.1.0" -dependencies = [ - "cfg-traits", - "cfg-types", - "cfg-utils", - "ethabi", - "fp-evm", - "frame-support", - "frame-system", - "hex", - "pallet-evm", - "pallet-liquidity-pools-gateway", - "parity-scale-codec", - "precompile-utils", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.2)", -] - [[package]] name = "backtrace" version = "0.3.73" @@ -1471,7 +1447,6 @@ dependencies = [ name = "centrifuge-runtime" version = "0.14.2" dependencies = [ - "axelar-gateway-precompile", "cfg-primitives", "cfg-traits", "cfg-types", @@ -1496,7 +1471,6 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", - "liquidity-pools-gateway-routers", "log", "orml-asset-registry", "orml-tokens", @@ -1507,6 +1481,7 @@ dependencies = [ "pallet-anchors", "pallet-aura", "pallet-authorship", + "pallet-axelar-router", "pallet-balances", "pallet-base-fee", "pallet-block-rewards", @@ -3082,7 +3057,6 @@ dependencies = [ name = "development-runtime" version = "0.14.2" dependencies = [ - "axelar-gateway-precompile", "cfg-primitives", "cfg-traits", "cfg-types", @@ -3108,7 +3082,6 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", - "liquidity-pools-gateway-routers", "log", "orml-asset-registry", "orml-tokens", @@ -3120,6 +3093,7 @@ dependencies = [ "pallet-anchors-v2", "pallet-aura", "pallet-authorship", + "pallet-axelar-router", "pallet-balances", "pallet-base-fee", "pallet-block-rewards", @@ -6293,36 +6267,6 @@ dependencies = [ "keystream", ] -[[package]] -name = "liquidity-pools-gateway-routers" -version = "0.0.1" -dependencies = [ - "cfg-mocks", - "cfg-primitives", - "cfg-traits", - "cfg-types", - "cumulus-primitives-core", - "ethabi", - "frame-support", - "frame-system", - "hex", - "lazy_static", - "orml-traits", - "pallet-balances", - "pallet-ethereum", - "pallet-ethereum-transaction", - "pallet-evm", - "pallet-evm-chain-id", - "pallet-evm-precompile-simple", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.2)", -] - [[package]] name = "lock_api" version = "0.4.12" @@ -7497,6 +7441,29 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.2)", ] +[[package]] +name = "pallet-axelar-router" +version = "1.0.0" +dependencies = [ + "cfg-mocks", + "cfg-traits", + "cfg-types", + "cfg-utils", + "ethabi", + "fp-evm", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "parity-scale-codec", + "precompile-utils", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.7.2)", +] + [[package]] name = "pallet-babe" version = "28.0.0" @@ -11627,7 +11594,6 @@ dependencies = [ name = "runtime-common" version = "1.0.0" dependencies = [ - "axelar-gateway-precompile", "cfg-mocks", "cfg-primitives", "cfg-traits", @@ -11656,6 +11622,7 @@ dependencies = [ "pallet-anchors", "pallet-aura", "pallet-authorship", + "pallet-axelar-router", "pallet-balances", "pallet-base-fee", "pallet-block-rewards", @@ -11738,7 +11705,6 @@ name = "runtime-integration-tests" version = "0.1.0" dependencies = [ "altair-runtime", - "axelar-gateway-precompile", "centrifuge-runtime", "cfg-primitives", "cfg-traits", @@ -11765,7 +11731,6 @@ dependencies = [ "hex", "hex-literal", "lazy_static", - "liquidity-pools-gateway-routers", "orml-asset-registry", "orml-tokens", "orml-traits", @@ -11774,6 +11739,7 @@ dependencies = [ "pallet-anchors", "pallet-aura", "pallet-authorship", + "pallet-axelar-router", "pallet-babe", "pallet-balances", "pallet-base-fee", diff --git a/Cargo.toml b/Cargo.toml index ae2be64715..6bd9f8ea5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "libs/utils", "pallets/anchors", "pallets/anchors-v2", + "pallets/axelar-router", "pallets/bridge", "pallets/block-rewards", "pallets/collator-allowlist", @@ -21,8 +22,6 @@ members = [ "pallets/keystore", "pallets/liquidity-pools", "pallets/liquidity-pools-gateway", - "pallets/liquidity-pools-gateway/axelar-gateway-precompile", - "pallets/liquidity-pools-gateway/routers", "pallets/liquidity-pools-gateway/queue", "pallets/liquidity-rewards", "pallets/loans", @@ -221,10 +220,9 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk", b substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.7.2" } # Centrifuge pallets -axelar-gateway-precompile = { path = "pallets/liquidity-pools-gateway/axelar-gateway-precompile", default-features = false } -liquidity-pools-gateway-routers = { path = "pallets/liquidity-pools-gateway/routers", default-features = false } pallet-anchors = { path = "pallets/anchors", default-features = false } pallet-anchors-v2 = { path = "pallets/anchors-v2", default-features = false } +pallet-axelar-router = { path = "pallets/axelar-router", default-features = false } pallet-block-rewards = { path = "pallets/block-rewards", default-features = false } pallet-bridge = { path = "pallets/bridge", default-features = false } pallet-collator-allowlist = { path = "pallets/collator-allowlist", default-features = false } diff --git a/libs/mocks/src/ethereum_transactor.rs b/libs/mocks/src/ethereum_transactor.rs new file mode 100644 index 0000000000..92fa65be86 --- /dev/null +++ b/libs/mocks/src/ethereum_transactor.rs @@ -0,0 +1,37 @@ +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use cfg_traits::ethereum::EthereumTransactor; + use frame_support::pallet_prelude::*; + use mock_builder::{execute_call, register_call}; + use sp_core::{H160, U256}; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + type CallIds = StorageMap<_, _, String, mock_builder::CallId>; + + impl Pallet { + pub fn mock_call( + func: impl Fn(H160, H160, &[u8], U256, U256, U256) -> DispatchResultWithPostInfo + 'static, + ) { + register_call!(move |(a, b, c, d, e, f)| func(a, b, c, d, e, f)); + } + } + + impl EthereumTransactor for Pallet { + fn call( + a: H160, + b: H160, + c: &[u8], + d: U256, + e: U256, + f: U256, + ) -> DispatchResultWithPostInfo { + execute_call!((a, b, c, d, e, f)) + } + } +} diff --git a/libs/mocks/src/lib.rs b/libs/mocks/src/lib.rs index 0e1a86c8f3..ca9fb241bc 100644 --- a/libs/mocks/src/lib.rs +++ b/libs/mocks/src/lib.rs @@ -3,6 +3,7 @@ pub mod change_guard; pub mod converter; pub mod currency_conversion; pub mod data; +pub mod ethereum_transactor; pub mod fees; pub mod foreign_investment; pub mod foreign_investment_hooks; @@ -10,12 +11,12 @@ pub mod investment; pub mod liquidity_pools; pub mod liquidity_pools_gateway; pub mod liquidity_pools_gateway_queue; -pub mod liquidity_pools_gateway_routers; pub mod pay_fee; pub mod permissions; pub mod pools; pub mod pre_conditions; pub mod rewards; +pub mod router_message; pub mod status_notification; pub mod time; pub mod token_swaps; @@ -30,7 +31,6 @@ pub use investment::pallet as pallet_mock_investment; pub use liquidity_pools::pallet as pallet_mock_liquidity_pools; pub use liquidity_pools_gateway::pallet as pallet_mock_liquidity_pools_gateway; pub use liquidity_pools_gateway_queue::pallet as pallet_mock_liquidity_pools_gateway_queue; -pub use liquidity_pools_gateway_routers::{pallet as pallet_mock_routers, RouterMock}; pub use pay_fee::pallet as pallet_mock_pay_fee; pub use permissions::pallet as pallet_mock_permissions; pub use pools::pallet as pallet_mock_pools; diff --git a/libs/mocks/src/liquidity_pools_gateway_routers.rs b/libs/mocks/src/liquidity_pools_gateway_routers.rs deleted file mode 100644 index 22bfdf1bb0..0000000000 --- a/libs/mocks/src/liquidity_pools_gateway_routers.rs +++ /dev/null @@ -1,102 +0,0 @@ -use cfg_traits::liquidity_pools::Router; -use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; -use mock_builder::{execute_call, register_call}; -use sp_std::default::Default; - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::storage] - type CallIds = StorageMap<_, _, String, mock_builder::CallId>; - - impl Pallet { - pub fn mock_init(f: impl Fn() -> DispatchResult + 'static) { - register_call!(move |()| f()); - } - - pub fn mock_send( - f: impl Fn(T::AccountId, Vec) -> DispatchResultWithPostInfo + 'static, - ) { - register_call!(move |(sender, message)| f(sender, message)); - } - } - - impl MockedRouter for Pallet { - type Sender = T::AccountId; - - fn init() -> DispatchResult { - execute_call!(()) - } - - fn send(sender: Self::Sender, message: Vec) -> DispatchResultWithPostInfo { - execute_call!((sender, message)) - } - } -} - -/// This wraps the mocking functionality of the pallet that we build here and is -/// necessary since this will kept in storage, therefore it has to implement the -/// below traits that make that possible. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct RouterMock { - _marker: PhantomData, -} - -impl Default for RouterMock { - fn default() -> Self { - RouterMock:: { - _marker: Default::default(), - } - } -} - -impl RouterMock { - pub fn mock_init(&self, f: impl Fn() -> DispatchResult + 'static) { - pallet::Pallet::::mock_init(f) - } - - pub fn mock_send( - &self, - f: impl Fn(T::AccountId, Vec) -> DispatchResultWithPostInfo + 'static, - ) { - pallet::Pallet::::mock_send(f) - } -} - -/// Here we implement the actual Router trait for the `RouterMock` which in turn -/// calls the `MockedRouter` trait implementation. -impl Router for RouterMock { - type Sender = T::AccountId; - - fn init(&self) -> DispatchResult { - pallet::Pallet::::init() - } - - fn send(&self, sender: Self::Sender, message: Vec) -> DispatchResultWithPostInfo { - pallet::Pallet::::send(sender, message) - } -} - -/// A mocked Router trait that emulates the actual Router trait but without -/// the inclusion of &self in the function parameters. This allows us to have -/// the mocked Routers pallet (defined above) implementing a Router-like trait -/// (and not just like regular pallet functions) when defining the mocked calls, -/// which is implicitly required by mock-builder or else it fails with `Location -/// must have trait info"`. -trait MockedRouter { - /// The sender type of the outbound message. - type Sender; - - /// Initialize the router. - fn init() -> DispatchResult; - - /// Send the message to the router's destination. - fn send(sender: Self::Sender, message: Vec) -> DispatchResultWithPostInfo; -} diff --git a/libs/mocks/src/router_message.rs b/libs/mocks/src/router_message.rs new file mode 100644 index 0000000000..4d21688a11 --- /dev/null +++ b/libs/mocks/src/router_message.rs @@ -0,0 +1,50 @@ +#[frame_support::pallet(dev_mode)] +pub mod pallet { + use cfg_traits::liquidity_pools::{MessageReceiver, MessageSender}; + use frame_support::pallet_prelude::*; + use mock_builder::{execute_call, register_call}; + + #[pallet::config] + pub trait Config: frame_system::Config { + type Middleware; + type Origin; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::storage] + type CallIds = StorageMap<_, _, String, mock_builder::CallId>; + + impl Pallet { + pub fn mock_receive( + f: impl Fn(T::Middleware, T::Origin, Vec) -> DispatchResult + 'static, + ) { + register_call!(move |(a, b, c)| f(a, b, c)); + } + + pub fn mock_send( + f: impl Fn(T::Middleware, T::Origin, Vec) -> DispatchResult + 'static, + ) { + register_call!(move |(a, b, c)| f(a, b, c)); + } + } + + impl MessageReceiver for Pallet { + type Middleware = T::Middleware; + type Origin = T::Origin; + + fn receive(a: Self::Middleware, b: Self::Origin, c: Vec) -> DispatchResult { + execute_call!((a, b, c)) + } + } + + impl MessageSender for Pallet { + type Middleware = T::Middleware; + type Origin = T::Origin; + + fn send(a: Self::Middleware, b: Self::Origin, c: Vec) -> DispatchResult { + execute_call!((a, b, c)) + } + } +} diff --git a/libs/traits/src/liquidity_pools.rs b/libs/traits/src/liquidity_pools.rs index 9ee729e4c6..0a90a072f9 100644 --- a/libs/traits/src/liquidity_pools.rs +++ b/libs/traits/src/liquidity_pools.rs @@ -11,10 +11,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, - weights::Weight, -}; +use frame_support::{dispatch::DispatchResult, weights::Weight}; use sp_runtime::DispatchError; use sp_std::vec::Vec; @@ -36,16 +33,38 @@ pub trait LPEncoding: Sized { fn empty() -> Self; } -/// The trait required for sending outbound messages. -pub trait Router { - /// The sender type of the outbound message. - type Sender; +pub trait RouterSupport: Sized { + /// Returns a list of routers supported for the given domain. + fn for_domain(domain: Domain) -> Vec; +} - /// Initialize the router. - fn init(&self) -> DispatchResult; +/// The behavior of an entity that can send messages +pub trait MessageSender { + /// The middleware by where this message is sent + type Middleware; - /// Send the message to the router's destination. - fn send(&self, sender: Self::Sender, message: Vec) -> DispatchResultWithPostInfo; + /// The originator of the message to be sent + type Origin; + + /// Sends a message for origin to destination + fn send(middleware: Self::Middleware, origin: Self::Origin, message: Vec) + -> DispatchResult; +} + +/// The behavior of an entity that can receive messages +pub trait MessageReceiver { + /// The middleware by where this message is received + type Middleware; + + /// The originator of the received message + type Origin; + + /// Sends a message for origin to destination + fn receive( + middleware: Self::Middleware, + origin: Self::Origin, + message: Vec, + ) -> DispatchResult; } /// The trait required for queueing messages. diff --git a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/Cargo.toml b/pallets/axelar-router/Cargo.toml similarity index 63% rename from pallets/liquidity-pools-gateway/axelar-gateway-precompile/Cargo.toml rename to pallets/axelar-router/Cargo.toml index 894b9b1a43..bf741a9784 100644 --- a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/Cargo.toml +++ b/pallets/axelar-router/Cargo.toml @@ -1,67 +1,78 @@ [package] -name = "axelar-gateway-precompile" -version = "0.1.0" -edition = "2021" +description = "Pallet to send and receive messages from axelar" +name = "pallet-axelar-router" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] [dependencies] -frame-support = { workspace = true } -frame-system = { workspace = true } hex = { workspace = true } parity-scale-codec = { workspace = true } scale-info = { workspace = true } + +frame-support = { workspace = true } +frame-system = { workspace = true } sp-core = { workspace = true } -sp-io = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } +cfg-traits = { workspace = true } +cfg-types = { workspace = true } +cfg-utils = { workspace = true } + ethabi = { workspace = true } fp-evm = { workspace = true } -pallet-evm = { workspace = true } precompile-utils = { workspace = true } -cfg-traits = { workspace = true } -cfg-types = { workspace = true } -cfg-utils = { workspace = true } -pallet-liquidity-pools-gateway = { workspace = true } +# Optionals for benchmarking +frame-benchmarking = { workspace = true, optional = true } + +[dev-dependencies] +cfg-mocks = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } [features] default = ["std"] std = [ "parity-scale-codec/std", + "scale-info/std", "hex/std", "frame-support/std", "frame-system/std", + "frame-benchmarking?/std", + "sp-runtime/std", "sp-std/std", "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "scale-info/std", - "fp-evm/std", - "precompile-utils/std", - "pallet-evm/std", - "pallet-liquidity-pools-gateway/std", - "cfg-types/std", "cfg-traits/std", + "cfg-types/std", "cfg-utils/std", + "fp-evm/std", + "precompile-utils/std", "ethabi/std", ] runtime-benchmarks = [ - "cfg-types/runtime-benchmarks", - "cfg-traits/runtime-benchmarks", - "cfg-utils/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", # Optional enabled "sp-runtime/runtime-benchmarks", - "pallet-evm/runtime-benchmarks", - "pallet-liquidity-pools-gateway/runtime-benchmarks", + "cfg-traits/runtime-benchmarks", + "cfg-types/runtime-benchmarks", + "cfg-utils/runtime-benchmarks", + "cfg-mocks/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", - "pallet-liquidity-pools-gateway/try-runtime", - "cfg-types/try-runtime", + "sp-runtime/try-runtime", "cfg-traits/try-runtime", + "cfg-types/try-runtime", "cfg-utils/try-runtime", - "pallet-evm/try-runtime", - "sp-runtime/try-runtime", + "cfg-mocks/try-runtime", ] diff --git a/pallets/axelar-router/src/lib.rs b/pallets/axelar-router/src/lib.rs new file mode 100644 index 0000000000..d18b96bd42 --- /dev/null +++ b/pallets/axelar-router/src/lib.rs @@ -0,0 +1,421 @@ +// Copyright 2021 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. +// +//! # Axelar Router +//! +//! Pallet that sends and receive message with other chains using Axelar. +#![cfg_attr(not(feature = "std"), no_std)] + +use cfg_traits::{ + ethereum::EthereumTransactor, + liquidity_pools::{MessageReceiver, MessageSender}, + PreConditions, +}; +use cfg_types::{domain_address::DomainAddress, EVMChainId}; +use ethabi::{Contract, Function, Param, ParamType, Token}; +use fp_evm::PrecompileHandle; +use frame_support::{ + pallet_prelude::*, + weights::{constants::RocksDbWeight, Weight}, + BoundedVec, +}; +use frame_system::pallet_prelude::*; +pub use pallet::*; +use precompile_utils::prelude::*; +use scale_info::prelude::{format, string::String}; +use sp_core::{H160, H256, U256}; +use sp_std::{boxed::Box, collections::btree_map::BTreeMap, vec, vec::Vec}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +/// Maximum size allowed for a byte representation of an Axelar EVM chain +/// string, as found below: +/// +/// +const MAX_AXELAR_EVM_CHAIN_SIZE: u32 = 16; + +const MAX_SOURCE_CHAIN_BYTES: u32 = 128; +// Ensure we allow enough to support a hex encoded address with the `0x` prefix. +const MAX_SOURCE_ADDRESS_BYTES: u32 = 42; +const MAX_TOKEN_SYMBOL_BYTES: u32 = 32; +const MAX_PAYLOAD_BYTES: u32 = 1024; +const EVM_ADDRESS_LEN: usize = 20; + +pub type ChainName = BoundedVec>; + +/// Type to represent the kind of message received by Axelar +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum AxelarId { + Evm(EVMChainId), +} + +/// Configuration for outbound messages though axelar +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct AxelarConfig { + /// Address of liquidity pool contract in the target chain + pub liquidity_pools_contract_address: H160, + + /// Configuration for executing the EVM call. + pub domain: DomainConfig, +} + +/// Specific domain configuration +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum DomainConfig { + Evm(EvmConfig), +} + +/// Data for validating and executing the internal EVM call. +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct EvmConfig { + /// Associated chain id + pub chain_id: EVMChainId, + + /// The address of the contract deployed in our EVM. + pub target_contract_address: H160, + + /// The `BlakeTwo256` hash of the target contract code. + /// + /// This is used during router initialization to ensure that the correct + /// contract code is used. + pub target_contract_hash: H256, + + /// The values used when executing the EVM call. + pub fee_values: FeeValues, +} + +/// The FeeValues holds all information related to the transaction costs. +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct FeeValues { + /// The value used when executing the EVM call. + pub value: U256, + + /// The gas price used when executing the EVM call. + pub gas_price: U256, + + /// The gas limit used when executing the EVM call. + pub gas_limit: U256, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The origin that is allowed to set the gateway address we accept + /// messages from + type AdminOrigin: EnsureOrigin; + + /// The target of the messages comming from other chains + type Receiver: MessageReceiver; + + /// Middleware used by the gateway + type Middleware: From; + + /// The target of the messages comming from this chain + type Transactor: EthereumTransactor; + + /// Checker to ensure an evm account code is registered + type EvmAccountCodeChecker: PreConditions<(H160, H256), Result = bool>; + } + + #[pallet::storage] + pub type Configuration = StorageMap<_, Twox64Concat, ChainName, AxelarConfig>; + + #[pallet::storage] + pub type ChainNameById = StorageMap<_, Twox64Concat, AxelarId, ChainName>; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + ConfigSet { + name: ChainName, + config: Box, + }, + } + + #[pallet::error] + pub enum Error { + /// Emit when the router configuration is not found. + RouterConfigurationNotFound, + + /// Emit when the evm account code is not registered + ContractCodeMismatch, + + /// Emit when the source chain is too big + SourceChainTooLong, + + /// Emit when the source address can not be recognized + InvalidSourceAddress, + + /// Emit when a message is received from a non LP caller + ContractCallerMismatch, + } + + #[pallet::call] + impl Pallet { + #[pallet::weight(Weight::from_parts(50_000_000, 512).saturating_add(RocksDbWeight::get().writes(2)))] + #[pallet::call_index(0)] + pub fn set_config( + origin: OriginFor, + chain_name: ChainName, + config: Box, + ) -> DispatchResult { + T::AdminOrigin::ensure_origin(origin)?; + + match &config.domain { + DomainConfig::Evm(evm_config) => { + ensure!( + T::EvmAccountCodeChecker::check(( + evm_config.target_contract_address, + evm_config.target_contract_hash, + )), + Error::::ContractCodeMismatch + ); + + ChainNameById::::insert( + AxelarId::Evm(evm_config.chain_id), + chain_name.clone(), + ); + } + } + + Configuration::::insert(chain_name.clone(), config.clone()); + + Self::deposit_event(Event::::ConfigSet { + name: chain_name, + config, + }); + + Ok(()) + } + } + + impl Pallet { + pub fn receive( + caller: H160, + source_chain: &[u8], + source_address: &[u8], + payload: &[u8], + ) -> DispatchResult { + let chain_name: ChainName = source_chain + .to_vec() + .try_into() + .map_err(|_| Error::::SourceChainTooLong)?; + + let config = Configuration::::get(chain_name) + .ok_or(Error::::RouterConfigurationNotFound)?; + + ensure!( + caller == config.liquidity_pools_contract_address, + Error::::ContractCallerMismatch, + ); + + match config.domain { + DomainConfig::Evm(EvmConfig { chain_id, .. }) => { + let source_address_bytes = + cfg_utils::decode_var_source::(source_address) + .ok_or(Error::::InvalidSourceAddress)?; + + T::Receiver::receive( + AxelarId::Evm(chain_id).into(), + DomainAddress::EVM(chain_id, source_address_bytes), + payload.to_vec(), + ) + } + } + } + } + + #[precompile_utils::precompile] + impl Pallet { + // Mimics: + // + // function execute( + // bytes32 commandId, + // string calldata sourceChain, + // string calldata sourceAddress, + // bytes calldata payload + // ) external { bytes32 payloadHash = keccak256(payload); + // if ( + // !gateway.validateContractCall( + // commandId, + // sourceChain, + // sourceAddress, + // payloadHash) + // ) revert NotApprovedByGateway(); + // + // _execute(sourceChain, sourceAddress, payload); + // } + #[precompile::public("execute(bytes32,string,string,bytes)")] + fn execute( + handle: &mut impl PrecompileHandle, + _command_id: H256, + source_chain: BoundedString>, + source_address: BoundedString>, + payload: BoundedBytes>, + ) -> EvmResult { + Self::receive( + handle.context().caller, + source_chain.as_bytes(), + source_address.as_bytes(), + payload.as_bytes(), + ) + .map_err(|e| TryDispatchError::Substrate(e).into()) + } + + // Mimics: + // + // function executeWithToken( + // bytes32 commandId, + // string calldata sourceChain, + // string calldata sourceAddress, + // bytes calldata payload, + // string calldata tokenSymbol, + // uint256 amount + // ) external { ... + // } + // + // Note: NOT SUPPORTED + // + #[precompile::public("executeWithToken(bytes32,string,string,bytes,string,uint256)")] + fn execute_with_token( + _handle: &mut impl PrecompileHandle, + _command_id: H256, + _source_chain: BoundedString>, + _source_address: BoundedString>, + _payload: BoundedBytes>, + _token_symbol: BoundedString>, + _amount: U256, + ) -> EvmResult { + // TODO: Check whether this is enough or if we should error out + Ok(()) + } + } + + impl MessageSender for Pallet { + type Middleware = AxelarId; + type Origin = DomainAddress; + + fn send(axelar_id: AxelarId, origin: Self::Origin, message: Vec) -> DispatchResult { + let chain_name = ChainNameById::::get(axelar_id) + .ok_or(Error::::RouterConfigurationNotFound)?; + let config = Configuration::::get(&chain_name) + .ok_or(Error::::RouterConfigurationNotFound)?; + + match config.domain { + DomainConfig::Evm(evm_config) => { + let sender_evm_address = H160::from_slice(&origin.address()[0..20]); + + let message = wrap_into_axelar_msg( + message, + chain_name.into_inner(), + config.liquidity_pools_contract_address, + ) + .map_err(DispatchError::Other)?; + + T::Transactor::call( + sender_evm_address, + evm_config.target_contract_address, + message.as_slice(), + evm_config.fee_values.value, + evm_config.fee_values.gas_price, + evm_config.fee_values.gas_limit, + ) + .map(|_| ()) + .map_err(|e| e.error) + } + } + } + } +} + +/// Encodes the provided message into the format required for submitting it +/// to the Axelar contract which in turn calls the LiquidityPools +/// contract with the serialized LP message as `payload`. +/// +/// Axelar contract call: +/// +/// +/// LiquidityPools contract call: +/// +pub fn wrap_into_axelar_msg( + serialized_msg: Vec, + target_chain: Vec, + target_contract: H160, +) -> Result, &'static str> { + const AXELAR_FUNCTION_NAME: &str = "callContract"; + const AXELAR_DESTINATION_CHAIN_PARAM: &str = "destinationChain"; + const AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM: &str = "destinationContractAddress"; + const AXELAR_PAYLOAD_PARAM: &str = "payload"; + + #[allow(deprecated)] + let encoded_axelar_contract = Contract { + constructor: None, + functions: BTreeMap::>::from([( + AXELAR_FUNCTION_NAME.into(), + vec![Function { + name: AXELAR_FUNCTION_NAME.into(), + inputs: vec![ + Param { + name: AXELAR_DESTINATION_CHAIN_PARAM.into(), + kind: ParamType::String, + internal_type: None, + }, + Param { + name: AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM.into(), + kind: ParamType::String, + internal_type: None, + }, + Param { + name: AXELAR_PAYLOAD_PARAM.into(), + kind: ParamType::Bytes, + internal_type: None, + }, + ], + outputs: vec![], + constant: Some(false), + state_mutability: Default::default(), + }], + )]), + events: Default::default(), + errors: Default::default(), + receive: false, + fallback: false, + } + .function(AXELAR_FUNCTION_NAME) + .map_err(|_| "cannot retrieve Axelar contract function")? + .encode_input(&[ + Token::String( + String::from_utf8(target_chain).map_err(|_| "target chain conversion error")?, + ), + // Ensure that the target contract is correctly converted to hex. + // + // The `to_string` method on the H160 is returning a string containing an ellipsis, such + // as: 0x1234…7890 + Token::String(format!("0x{}", hex::encode(target_contract.0))), + Token::Bytes(serialized_msg), + ]) + .map_err(|_| "cannot encode input for Axelar contract function")?; + + Ok(encoded_axelar_contract) +} diff --git a/pallets/axelar-router/src/mock.rs b/pallets/axelar-router/src/mock.rs new file mode 100644 index 0000000000..958889f340 --- /dev/null +++ b/pallets/axelar-router/src/mock.rs @@ -0,0 +1,58 @@ +use cfg_types::domain_address::DomainAddress; +use frame_support::{derive_impl, traits::EitherOfDiverse}; +use frame_system::{EnsureRoot, EnsureSigned}; +use sp_core::{H160, H256}; +use sp_io::TestExternalities; + +use crate::{pallet as pallet_axelar_router, AxelarId}; + +pub type AccountId = u64; + +#[derive(Debug, PartialEq)] +pub struct Middleware(pub AxelarId); + +impl From for Middleware { + fn from(id: AxelarId) -> Self { + Middleware(id) + } +} + +frame_support::construct_runtime!( + pub enum Runtime { + System: frame_system, + Receiver: cfg_mocks::router_message::pallet, + Transactor: cfg_mocks::ethereum_transactor::pallet, + AccountCodeChecker: cfg_mocks::pre_conditions::pallet, + Router: pallet_axelar_router, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Runtime { + type Block = frame_system::mocking::MockBlock; +} + +impl cfg_mocks::router_message::pallet::Config for Runtime { + type Middleware = Middleware; + type Origin = DomainAddress; +} + +impl cfg_mocks::ethereum_transactor::pallet::Config for Runtime {} + +impl cfg_mocks::pre_conditions::pallet::Config for Runtime { + type Conditions = (H160, H256); + type Result = bool; +} + +impl pallet_axelar_router::Config for Runtime { + type AdminOrigin = EitherOfDiverse, EnsureSigned>; + type EvmAccountCodeChecker = AccountCodeChecker; + type Middleware = Middleware; + type Receiver = Receiver; + type RuntimeEvent = RuntimeEvent; + type Transactor = Transactor; +} + +pub fn new_test_ext() -> TestExternalities { + System::externalities() +} diff --git a/pallets/axelar-router/src/tests.rs b/pallets/axelar-router/src/tests.rs new file mode 100644 index 0000000000..376f2bd7ef --- /dev/null +++ b/pallets/axelar-router/src/tests.rs @@ -0,0 +1,229 @@ +use frame_support::{assert_err, assert_noop, assert_ok}; +use sp_core::U256; + +use crate::{mock::*, *}; + +const CHAIN_NAME: &str = "CHAIN_1"; +const CHAIN_ID: EVMChainId = 1; +const LP_CONTRACT_ADDRESS: H160 = H160::repeat_byte(1); +const AXELAR_CONTRACT_ADDRESS: H160 = H160::repeat_byte(2); +const SOURCE_ADDRESS: H160 = H160::repeat_byte(3); +const AXELAR_CONTRACT_HASH: H256 = H256::repeat_byte(42); +const SENDER: DomainAddress = DomainAddress::Centrifuge([0; 32]); +const MESSAGE: &[u8] = &[1, 2, 3]; +const FEE_VALUE: U256 = U256::zero(); +const GAS_LIMIT: U256 = U256::one(); +const GAS_PRICE: U256 = U256::max_value(); + +fn config() -> AxelarConfig { + AxelarConfig { + liquidity_pools_contract_address: LP_CONTRACT_ADDRESS, + domain: DomainConfig::Evm(EvmConfig { + chain_id: CHAIN_ID, + target_contract_address: AXELAR_CONTRACT_ADDRESS, + target_contract_hash: AXELAR_CONTRACT_HASH, + fee_values: FeeValues { + value: FEE_VALUE, + gas_limit: GAS_LIMIT, + gas_price: GAS_PRICE, + }, + }), + } +} + +fn correct_configuration() { + AccountCodeChecker::mock_check(|_| true); + + assert_ok!(Router::set_config( + RuntimeOrigin::root(), + CHAIN_NAME.as_bytes().to_vec().try_into().unwrap(), + Box::new(config()) + )); +} + +fn wrap_message(message: Vec) -> Vec { + wrap_into_axelar_msg(message, CHAIN_NAME.as_bytes().to_vec(), LP_CONTRACT_ADDRESS).unwrap() +} + +mod configuration { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + AccountCodeChecker::mock_check(|(address, hash)| { + assert_eq!(address, AXELAR_CONTRACT_ADDRESS); + assert_eq!(hash, AXELAR_CONTRACT_HASH); + true + }); + + assert_ok!(Router::set_config( + RuntimeOrigin::root(), + CHAIN_NAME.as_bytes().to_vec().try_into().unwrap(), + Box::new(config()) + )); + }); + } + + #[test] + fn without_correct_account_code() { + new_test_ext().execute_with(|| { + AccountCodeChecker::mock_check(|_| false); + + assert_noop!( + Router::set_config( + RuntimeOrigin::root(), + CHAIN_NAME.as_bytes().to_vec().try_into().unwrap(), + Box::new(config()) + ), + Error::::ContractCodeMismatch + ); + }); + } +} + +mod send { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + correct_configuration(); + + Transactor::mock_call(move |from, to, data, value, gas_price, gas_limit| { + assert_eq!(from, H160::from_slice(&SENDER.address()[0..20])); + assert_eq!(to, AXELAR_CONTRACT_ADDRESS); + assert_eq!(data, &wrap_message(MESSAGE.to_vec())); + assert_eq!(value, FEE_VALUE); + assert_eq!(gas_limit, GAS_LIMIT); + assert_eq!(gas_price, GAS_PRICE); + Ok(().into()) + }); + + assert_ok!(Router::send( + AxelarId::Evm(CHAIN_ID), + SENDER, + MESSAGE.to_vec() + )); + }); + } + + #[test] + fn without_configuration() { + new_test_ext().execute_with(|| { + assert_err!( + Router::send(AxelarId::Evm(CHAIN_ID), SENDER, MESSAGE.to_vec()), + Error::::RouterConfigurationNotFound, + ); + }); + } + + #[test] + fn with_ethereum_error() { + new_test_ext().execute_with(|| { + correct_configuration(); + + Transactor::mock_call(move |_, _, _, _, _, _| Err(DispatchError::Other("err").into())); + + assert_err!( + Router::send(AxelarId::Evm(CHAIN_ID), SENDER, MESSAGE.to_vec()), + DispatchError::Other("err") + ); + }); + } +} + +mod receive { + use super::*; + + #[test] + fn success() { + new_test_ext().execute_with(|| { + correct_configuration(); + + Receiver::mock_receive(|middleware, origin, message| { + assert_eq!(middleware, Middleware(AxelarId::Evm(CHAIN_ID))); + assert_eq!(origin, DomainAddress::EVM(CHAIN_ID, SOURCE_ADDRESS.0)); + assert_eq!(&message, MESSAGE); + Ok(()) + }); + + assert_ok!(Router::receive( + LP_CONTRACT_ADDRESS, + CHAIN_NAME.as_bytes(), + &SOURCE_ADDRESS.0, + MESSAGE + )); + }); + } + + #[test] + fn without_configuration() { + new_test_ext().execute_with(|| { + assert_err!( + Router::receive( + LP_CONTRACT_ADDRESS, + CHAIN_NAME.as_bytes(), + &SOURCE_ADDRESS.0, + MESSAGE + ), + Error::::RouterConfigurationNotFound + ); + }); + } + + #[test] + fn with_wrong_caller() { + new_test_ext().execute_with(|| { + correct_configuration(); + + assert_err!( + Router::receive( + H160::repeat_byte(23), + CHAIN_NAME.as_bytes(), + &SOURCE_ADDRESS.0, + MESSAGE + ), + Error::::ContractCallerMismatch + ); + }); + } + + #[test] + fn with_long_chain_name() { + new_test_ext().execute_with(|| { + correct_configuration(); + + let big_source_chain = (0..MAX_AXELAR_EVM_CHAIN_SIZE + 1) + .map(|_| 1) + .collect::>(); + + assert_err!( + Router::receive( + LP_CONTRACT_ADDRESS, + &big_source_chain, + &SOURCE_ADDRESS.0, + MESSAGE + ), + Error::::SourceChainTooLong + ); + }); + } + + #[test] + fn with_small_source_address() { + new_test_ext().execute_with(|| { + correct_configuration(); + + assert_err!( + Router::receive( + LP_CONTRACT_ADDRESS, + CHAIN_NAME.as_bytes(), + &[1, 2, 3], + MESSAGE + ), + Error::::InvalidSourceAddress + ); + }); + } +} diff --git a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/lib.rs b/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/lib.rs deleted file mode 100644 index d2fcec96b6..0000000000 --- a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/lib.rs +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2021 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. -// -//! # Axelar Gateway Precompile -//! -//! Pallet that serves as an EVM precompile for incoming Liquidity Pools -//! messages from the Axelar network. -#![cfg_attr(not(feature = "std"), no_std)] - -use cfg_types::domain_address::{Domain, DomainAddress}; -use fp_evm::{ExitError, PrecompileFailure, PrecompileHandle}; -use frame_support::ensure; -use precompile_utils::prelude::*; -use sp_core::{bounded::BoundedVec, ConstU32, H256, U256}; -use sp_runtime::traits::{BlakeTwo256, Hash}; - -pub use crate::weights::WeightInfo; - -pub const MAX_SOURCE_CHAIN_BYTES: u32 = 128; -// Ensure we allow enough to support a hex encoded address with the `0x` prefix. -pub const MAX_SOURCE_ADDRESS_BYTES: u32 = 42; -pub const MAX_TOKEN_SYMBOL_BYTES: u32 = 32; -pub const MAX_PAYLOAD_BYTES: u32 = 1024; -pub const PREFIX_CONTRACT_CALL_APPROVED: [u8; 32] = keccak256!("contract-call-approved"); -const EXPECTED_SOURCE_ADDRESS_SIZE: usize = 20; - -pub type String = BoundedString>; -pub type Bytes = BoundedBytes>; - -pub use pallet::*; - -pub mod weights; - -#[derive( - PartialEq, - Clone, - parity_scale_codec::Encode, - parity_scale_codec::Decode, - scale_info::TypeInfo, - parity_scale_codec::MaxEncodedLen, - frame_support::RuntimeDebugNoBound, -)] -pub struct SourceConverter { - pub domain: Domain, -} - -impl SourceConverter { - pub fn new(domain: Domain) -> Self { - Self { domain } - } -} - -impl SourceConverter { - pub fn try_convert(&self, maybe_address: &[u8]) -> Option { - match self.domain { - Domain::Centrifuge => Some(DomainAddress::Centrifuge(Self::try_into_32bytes( - maybe_address, - )?)), - Domain::EVM(id) => Some(DomainAddress::EVM( - id, - Self::try_into_20bytes(maybe_address)?, - )), - } - } - - fn try_into_32bytes(maybe_address: &[u8]) -> Option<[u8; 32]> { - if maybe_address.len() == 32 { - let mut address: [u8; 32] = [0u8; 32]; - address.copy_from_slice(maybe_address); - - Some(address) - } else { - None - } - } - - fn try_into_20bytes(maybe_address: &[u8]) -> Option<[u8; 20]> { - if maybe_address.len() == 20 { - let mut address: [u8; 20] = [0u8; 20]; - address.copy_from_slice(maybe_address); - - Some(address) - } else { - None - } - } -} - -#[frame_support::pallet] -pub mod pallet { - // Import various types used to declare pallet in scope. - use frame_support::{pallet_prelude::*, DefaultNoBound}; - use frame_system::pallet_prelude::*; - use sp_core::{H160, H256}; - - use super::SourceConverter; - use crate::weights::WeightInfo; - - // Simple declaration of the `Pallet` type. It is placeholder we use to - // implement traits and method. - #[pallet::pallet] - - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_evm::Config + pallet_liquidity_pools_gateway::Config - { - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - /// The origin that is allowed to set the gateway address we accept - /// messages from - type AdminOrigin: EnsureOrigin<::RuntimeOrigin>; - - type WeightInfo: WeightInfo; - } - - #[pallet::storage] - pub type GatewayContract = StorageValue<_, H160, ValueQuery>; - - /// `SourceConversion` is a `hash_of(Vec)` where the `Vec` is the - /// blake256-hash of the source-chain identifier used by the Axelar network. - #[pallet::storage] - pub type SourceConversion = StorageMap<_, Twox64Concat, H256, SourceConverter>; - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig { - pub gateway: H160, - _phantom: core::marker::PhantomData, - } - - #[pallet::genesis_build] - impl BuildGenesisConfig for GenesisConfig { - fn build(&self) { - GatewayContract::::set(self.gateway) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - GatewaySet { - address: H160, - }, - ConverterSet { - id_hash: H256, - converter: SourceConverter, - }, - } - - #[pallet::error] - pub enum Error { - /// The given domain is not yet allowlisted, as we have no converter yet - NoConverterForSource, - /// A given domain expects a given structure for account bytes and it - /// was not given here. - AccountBytesMismatchForDomain, - } - - #[pallet::call] - impl Pallet { - #[pallet::weight(::WeightInfo::set_gateway())] - #[pallet::call_index(0)] - pub fn set_gateway(origin: OriginFor, address: H160) -> DispatchResult { - ::AdminOrigin::ensure_origin(origin)?; - - GatewayContract::::set(address); - - Self::deposit_event(Event::::GatewaySet { address }); - - Ok(()) - } - - #[pallet::weight(::WeightInfo::set_converter())] - #[pallet::call_index(1)] - pub fn set_converter( - origin: OriginFor, - id_hash: H256, - converter: SourceConverter, - ) -> DispatchResult { - ::AdminOrigin::ensure_origin(origin)?; - - SourceConversion::::insert(id_hash, converter.clone()); - - Self::deposit_event(Event::::ConverterSet { id_hash, converter }); - - Ok(()) - } - } -} - -#[precompile_utils::precompile] -impl Pallet -where - T: frame_system::Config, - ::RuntimeOrigin: From, -{ - // Mimics: - // - // function execute( - // bytes32 commandId, - // string calldata sourceChain, - // string calldata sourceAddress, - // bytes calldata payload - // ) external { bytes32 payloadHash = keccak256(payload); - // if ( - // !gateway.validateContractCall( - // commandId, - // sourceChain, - // sourceAddress, - // payloadHash) - // ) revert NotApprovedByGateway(); - // - // _execute(sourceChain, sourceAddress, payload); - // } - // - // Note: The _execute logic in this case will forward all calls to the - // liquidity-pools-gateway with a special runtime local origin - #[precompile::public("execute(bytes32,string,string,bytes)")] - fn execute( - handle: &mut impl PrecompileHandle, - _command_id: H256, - source_chain: String, - source_address: String, - payload: Bytes, - ) -> EvmResult { - ensure!( - handle.context().caller == GatewayContract::::get(), - PrecompileFailure::Error { - exit_status: ExitError::Other("gateway contract address mismatch".into()), - } - ); - - let msg = BoundedVec::< - u8, - ::MaxIncomingMessageSize, - >::try_from(payload.as_bytes().to_vec()) - .map_err(|_| PrecompileFailure::Error { - exit_status: ExitError::Other("payload conversion".into()), - })?; - - let domain_converter = SourceConversion::::get(BlakeTwo256::hash( - source_chain.as_bytes(), - )) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::Other("converter for source not found".into()), - })?; - - let source_address_bytes = - cfg_utils::decode_var_source::(source_address.as_bytes()) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::Other("invalid source address".into()), - })?; - - let domain_address = domain_converter - .try_convert(source_address_bytes.as_slice()) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::Other("account bytes mismatch for domain".into()), - })?; - - pallet_liquidity_pools_gateway::Pallet::::receive_message( - pallet_liquidity_pools_gateway::GatewayOrigin::Domain(domain_address).into(), - msg, - ) - .map(|_| ()) - .map_err(TryDispatchError::Substrate) - .map_err(Into::into) - } - - // Mimics: - // - // function executeWithToken( - // bytes32 commandId, - // string calldata sourceChain, - // string calldata sourceAddress, - // bytes calldata payload, - // string calldata tokenSymbol, - // uint256 amount - // ) external { ... - // } - // - // Note: NOT SUPPORTED - // - #[precompile::public("executeWithToken(bytes32,string,string,bytes,string,uint256)")] - fn execute_with_token( - _handle: &mut impl PrecompileHandle, - _command_id: H256, - _source_chain: String, - _source_address: String, - _payload: Bytes, - _token_symbol: String, - _amount: U256, - ) -> EvmResult { - // TODO: Check whether this is enough or if we should error out - Ok(()) - } -} diff --git a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/weights.rs b/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/weights.rs deleted file mode 100644 index 5a2c0fe873..0000000000 --- a/pallets/liquidity-pools-gateway/axelar-gateway-precompile/src/weights.rs +++ /dev/null @@ -1,42 +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 frame_support::weights::{constants::RocksDbWeight, Weight}; - -pub trait WeightInfo { - fn set_gateway() -> Weight; - fn set_converter() -> Weight; -} - -impl WeightInfo for () { - fn set_gateway() -> Weight { - // TODO: BENCHMARK CORRECTLY - // - // NOTE: Reasonable weight taken from `PoolSystem::set_max_reserve` - // This one has one read and one write for sure and possible one - // read for `AdminOrigin` - Weight::from_parts(30_117_000, 5991) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } - - fn set_converter() -> Weight { - // TODO: BENCHMARK CORRECTLY - // - // NOTE: Reasonable weight taken from `PoolSystem::set_max_reserve` - // This one has one read and one write for sure and possible one - // read for `AdminOrigin` - Weight::from_parts(30_117_000, 5991) - .saturating_add(RocksDbWeight::get().reads(2)) - .saturating_add(RocksDbWeight::get().writes(1)) - } -} diff --git a/pallets/liquidity-pools-gateway/routers/Cargo.toml b/pallets/liquidity-pools-gateway/routers/Cargo.toml deleted file mode 100644 index 0edfe84975..0000000000 --- a/pallets/liquidity-pools-gateway/routers/Cargo.toml +++ /dev/null @@ -1,98 +0,0 @@ -[package] -authors = ["Centrifuge "] -description = "Centrifuge Liquidity Pools Gateway Routers" -edition = "2021" -license = "LGPL-3.0" -name = "liquidity-pools-gateway-routers" -repository = "https://github.com/centrifuge/centrifuge-chain" -version = "0.0.1" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -frame-support = { workspace = true } -frame-system = { workspace = true } -hex = { workspace = true } -parity-scale-codec = { workspace = true } -scale-info = { workspace = true } -sp-std = { workspace = true } - -# Substrate -sp-core = { workspace = true } -sp-runtime = { workspace = true } - -# EVM -ethabi = { workspace = true } -pallet-ethereum = { workspace = true } -pallet-evm = { workspace = true } - -# Custom crates -cfg-traits = { workspace = true } - -# Local pallets -pallet-ethereum-transaction = { workspace = true } - -[dev-dependencies] -cfg-types = { workspace = true, default-features = true } -lazy_static = { workspace = true } - -cumulus-primitives-core = { workspace = true, default-features = true } - -pallet-evm-chain-id = { workspace = true, default-features = true } -pallet-evm-precompile-simple = { workspace = true, default-features = true } -pallet-timestamp = { workspace = true, default-features = true } - -sp-core = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } - -orml-traits = { workspace = true, default-features = true } - -cfg-mocks = { workspace = true, default-features = true } -cfg-primitives = { workspace = true, default-features = true } -pallet-balances = { workspace = true, default-features = true } - -[features] -default = ["std"] -std = [ - "parity-scale-codec/std", - "cfg-traits/std", - "cfg-mocks/std", - "hex/std", - "frame-support/std", - "frame-system/std", - "sp-std/std", - "sp-core/std", - "pallet-ethereum/std", - "pallet-ethereum-transaction/std", - "ethabi/std", - "scale-info/std", - "pallet-evm/std", - "sp-runtime/std", -] -runtime-benchmarks = [ - "cfg-traits/runtime-benchmarks", - "cfg-types/runtime-benchmarks", - "cfg-mocks/runtime-benchmarks", - "cfg-primitives/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-ethereum/runtime-benchmarks", - "pallet-ethereum-transaction/runtime-benchmarks", - "pallet-evm/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "cfg-traits/try-runtime", - "cfg-primitives/try-runtime", - "cfg-types/try-runtime", - "cfg-mocks/try-runtime", - "pallet-ethereum/try-runtime", - "pallet-ethereum-transaction/try-runtime", - "pallet-evm/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/pallets/liquidity-pools-gateway/routers/src/lib.rs b/pallets/liquidity-pools-gateway/routers/src/lib.rs deleted file mode 100644 index a731009fbb..0000000000 --- a/pallets/liquidity-pools-gateway/routers/src/lib.rs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2021 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. -// -//! # Liquidity Pools Gateway Routers -//! -//! This crate contains the `DomainRouters` used by the Liquidity Pools Gateway -//! pallet. -//! -//! The routers can be used to communicate with: -//! -//! Axelar - via EVM and XCM (through Moonbeam). -//! -//! Moonbeam - via XCM. -#![cfg_attr(not(feature = "std"), no_std)] - -use cfg_traits::{ethereum::EthereumTransactor, liquidity_pools::Router}; -use frame_support::{ - ensure, - pallet_prelude::{DispatchError, DispatchResult, DispatchResultWithPostInfo}, -}; -use frame_system::pallet_prelude::OriginFor; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::{H160, H256, U256}; -use sp_runtime::traits::{BlakeTwo256, Hash}; -use sp_std::{marker::PhantomData, vec::Vec}; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -pub mod routers { - pub mod axelar_evm; -} - -pub use routers::axelar_evm::AxelarEVMRouter; - -/// Maximum size allowed for a byte representation of an Axelar EVM chain -/// string, as found below: -/// -/// -pub const MAX_AXELAR_EVM_CHAIN_SIZE: u32 = 16; - -const AXELAR_FUNCTION_NAME: &str = "callContract"; -const AXELAR_DESTINATION_CHAIN_PARAM: &str = "destinationChain"; -const AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM: &str = "destinationContractAddress"; -const AXELAR_PAYLOAD_PARAM: &str = "payload"; - -/// The routers used for outgoing messages. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub enum DomainRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - T::AccountId: AsRef<[u8; 32]>, - OriginFor: - From + Into>>, -{ - AxelarEVM(AxelarEVMRouter), -} - -impl Router for DomainRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - T::AccountId: AsRef<[u8; 32]>, - OriginFor: - From + Into>>, -{ - type Sender = T::AccountId; - - fn init(&self) -> DispatchResult { - match self { - DomainRouter::AxelarEVM(r) => r.do_init(), - } - } - - fn send(&self, sender: Self::Sender, message: Vec) -> DispatchResultWithPostInfo { - match self { - DomainRouter::AxelarEVM(r) => r.do_send(sender, message), - } - } -} - -/// A generic router used for executing EVM calls. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct EVMRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - OriginFor: - From + Into>>, -{ - pub evm_domain: EVMDomain, - pub _marker: PhantomData, -} - -impl EVMRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - OriginFor: - From + Into>>, -{ - pub fn new(evm_domain: EVMDomain) -> Self { - Self { - evm_domain, - _marker: Default::default(), - } - } -} - -impl EVMRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - T::AccountId: AsRef<[u8; 32]>, - OriginFor: - From + Into>>, -{ - /// Performs an extra check to ensure that the actual contract is deployed - /// at the provided address and that the contract code hash matches. - pub fn do_init(&self) -> DispatchResult { - let code = pallet_evm::AccountCodes::::get(self.evm_domain.target_contract_address); - - ensure!( - BlakeTwo256::hash_of(&code) == self.evm_domain.target_contract_hash, - DispatchError::Other("Target contract code does not match"), - ); - - Ok(()) - } - - /// NOTE - the sender account ID provided here will be converted to an EVM - /// address via truncating. When the call is processed by the underlying EVM - /// pallet, this EVM address will be converted back into a substrate account - /// which will be charged for the transaction. This converted substrate - /// account is not the same as the original account. - pub fn do_send(&self, sender: T::AccountId, msg: Vec) -> DispatchResultWithPostInfo { - let sender_evm_address = H160::from_slice(&sender.as_ref()[0..20]); - - as EthereumTransactor>::call( - sender_evm_address, - self.evm_domain.target_contract_address, - msg.as_slice(), - self.evm_domain.fee_values.value, - self.evm_domain.fee_values.gas_price, - self.evm_domain.fee_values.gas_limit, - ) - } -} - -/// The EVMDomain holds all relevant information for validating and executing -/// the EVM call. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct EVMDomain { - /// The address of the contract deployed in our EVM. - pub target_contract_address: H160, - - /// The `BlakeTwo256` hash of the target contract code. - /// - /// This is used during router initialization to ensure that the correct - /// contract code is used. - pub target_contract_hash: H256, - - /// The values used when executing the EVM call. - pub fee_values: FeeValues, -} - -/// The FeeValues holds all information related to the transaction costs. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct FeeValues { - /// The value used when executing the EVM call. - pub value: U256, - - /// The gas price used when executing the EVM call. - pub gas_price: U256, - - /// The gas limit used when executing the EVM call. - pub gas_limit: U256, -} diff --git a/pallets/liquidity-pools-gateway/routers/src/mock.rs b/pallets/liquidity-pools-gateway/routers/src/mock.rs deleted file mode 100644 index 40ab5abbb3..0000000000 --- a/pallets/liquidity-pools-gateway/routers/src/mock.rs +++ /dev/null @@ -1,191 +0,0 @@ -use std::str::FromStr; - -use cfg_mocks::{pallet_mock_liquidity_pools, pallet_mock_routers}; -use cfg_primitives::{BLOCK_STORAGE_LIMIT, MAX_POV_SIZE}; -use cfg_types::domain_address::DomainAddress; -use frame_support::{derive_impl, parameter_types, traits::FindAuthor, weights::Weight}; -use pallet_ethereum::{IntermediateStateRoot, PostLogContent}; -use pallet_evm::{ - runner::stack::Runner, AddressMapping, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, - FixedGasWeightMapping, IsPrecompileResult, Precompile, PrecompileHandle, PrecompileResult, - PrecompileSet, SubstrateBlockHashMapping, -}; -use sp_core::{crypto::AccountId32, ByteArray, ConstU32, H160, U256}; -use sp_runtime::{traits::IdentityLookup, ConsensusEngineId}; - -pub type Balance = u128; - -frame_support::construct_runtime!( - pub enum Runtime { - System: frame_system, - Balances: pallet_balances, - MockLiquidityPools: pallet_mock_liquidity_pools, - EVM: pallet_evm, - Timestamp: pallet_timestamp, - Ethereum: pallet_ethereum, - EthereumTransaction: pallet_ethereum_transaction, - } -); - -frame_support::parameter_types! { - pub const MaxIncomingMessageSize: u32 = 1024; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for Runtime { - type AccountData = pallet_balances::AccountData; - type AccountId = AccountId32; - type Block = frame_system::mocking::MockBlock; - type Lookup = IdentityLookup; -} - -parameter_types! { - // the minimum fee for an anchor is 500,000ths of a CFG. - // This is set to a value so you can still get some return without getting your account removed. - pub const ExistentialDeposit: Balance = 1 * cfg_primitives::MICRO_CFG; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] -impl pallet_balances::Config for Runtime { - type AccountStore = System; - type Balance = Balance; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type RuntimeHoldReason = (); -} - -impl pallet_mock_liquidity_pools::Config for Runtime { - type DomainAddress = DomainAddress; - type Message = (); -} - -impl pallet_ethereum_transaction::Config for Runtime {} - -impl pallet_mock_routers::Config for Runtime {} - -parameter_types! { - pub const MinimumPeriod: u64 = 1000; -} - -impl pallet_timestamp::Config for Runtime { - type MinimumPeriod = MinimumPeriod; - type Moment = u64; - type OnTimestampSet = (); - type WeightInfo = (); -} - -/////////////////////// -// EVM pallet mocks. // -/////////////////////// - -pub struct FixedGasPrice; -impl FeeCalculator for FixedGasPrice { - fn min_gas_price() -> (U256, Weight) { - // Return some meaningful gas price and weight - (1_000_000_000u128.into(), Weight::from_parts(7u64, 0)) - } -} - -/// Identity address mapping. -pub struct IdentityAddressMapping; - -impl AddressMapping for IdentityAddressMapping { - fn into_account_id(address: H160) -> AccountId32 { - let tag = b"EVM"; - let mut bytes = [0; 32]; - bytes[0..20].copy_from_slice(address.as_bytes()); - bytes[20..28].copy_from_slice(&1u64.to_be_bytes()); - bytes[28..31].copy_from_slice(tag); - - AccountId32::from_slice(bytes.as_slice()).unwrap() - } -} - -pub struct FindAuthorTruncated; -impl FindAuthor for FindAuthorTruncated { - fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, - { - Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) - } -} - -pub struct MockPrecompileSet; - -impl PrecompileSet for MockPrecompileSet { - /// Tries to execute a precompile in the precompile set. - /// If the provided address is not a precompile, returns None. - fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - let address = handle.code_address(); - - if address == H160::from_low_u64_be(1) { - return Some(pallet_evm_precompile_simple::Identity::execute(handle)); - } - - None - } - - /// Check if the given address is a precompile. Should only be called to - /// perform the check while not executing the precompile afterward, since - /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160, _remaining_gas: u64) -> IsPrecompileResult { - IsPrecompileResult::Answer { - is_precompile: address == H160::from_low_u64_be(1), - extra_cost: 0, - } - } -} - -parameter_types! { - pub BlockGasLimit: U256 = U256::max_value(); - pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); - pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet; - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; - pub GasLimitStorageGrowthRatio: u64 = - BlockGasLimit::get().min(u64::MAX.into()).low_u64().saturating_div(BLOCK_STORAGE_LIMIT); -} - -impl pallet_evm::Config for Runtime { - type AddressMapping = IdentityAddressMapping; - type BlockGasLimit = BlockGasLimit; - type BlockHashMapping = SubstrateBlockHashMapping; - type CallOrigin = EnsureAddressRoot; - type ChainId = (); - type Currency = Balances; - type FeeCalculator = FixedGasPrice; - type FindAuthor = FindAuthorTruncated; - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; - type GasWeightMapping = FixedGasWeightMapping; - type OnChargeTransaction = (); - type OnCreate = (); - type PrecompilesType = MockPrecompileSet; - type PrecompilesValue = MockPrecompiles; - type Runner = Runner; - type RuntimeEvent = RuntimeEvent; - type SuicideQuickClearLimit = ConstU32<0>; - type Timestamp = Timestamp; - type WeightInfo = (); - type WeightPerGas = WeightPerGas; - type WithdrawOrigin = EnsureAddressNever; -} - -parameter_types! { - pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; - pub const ExtraDataLength: u32 = 30; -} - -impl pallet_ethereum::Config for Runtime { - type ExtraDataLength = ExtraDataLength; - type PostLogContent = PostBlockAndTxnHashes; - type RuntimeEvent = RuntimeEvent; - type StateRoot = IntermediateStateRoot; -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - System::externalities() -} diff --git a/pallets/liquidity-pools-gateway/routers/src/routers/axelar_evm.rs b/pallets/liquidity-pools-gateway/routers/src/routers/axelar_evm.rs deleted file mode 100644 index 670bb8b4dd..0000000000 --- a/pallets/liquidity-pools-gateway/routers/src/routers/axelar_evm.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2021 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 ethabi::{Contract, Function, Param, ParamType, Token}; -use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, - pallet_prelude::DispatchError, -}; -use frame_system::pallet_prelude::OriginFor; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::{ - prelude::{format, string::String}, - TypeInfo, -}; -use sp_core::{bounded::BoundedVec, ConstU32, H160}; -use sp_std::{collections::btree_map::BTreeMap, vec, vec::Vec}; - -use crate::{ - EVMRouter, AXELAR_DESTINATION_CHAIN_PARAM, AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM, - AXELAR_FUNCTION_NAME, AXELAR_PAYLOAD_PARAM, MAX_AXELAR_EVM_CHAIN_SIZE, -}; - -/// The router used for executing the LiquidityPools contract via Axelar. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct AxelarEVMRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - OriginFor: - From + Into>>, -{ - pub router: EVMRouter, - pub evm_chain: BoundedVec>, - pub liquidity_pools_contract_address: H160, -} - -impl AxelarEVMRouter -where - T: pallet_ethereum_transaction::Config + pallet_evm::Config, - T::AccountId: AsRef<[u8; 32]>, - OriginFor: - From + Into>>, -{ - pub fn new( - router: EVMRouter, - evm_chain: BoundedVec>, - liquidity_pools_contract_address: H160, - ) -> Self { - Self { - router, - evm_chain, - liquidity_pools_contract_address, - } - } - - /// Calls the init function on the EVM router. - pub fn do_init(&self) -> DispatchResult { - self.router.do_init() - } - - /// Executes the EVM call using the generic EVM router. - pub fn do_send(&self, sender: T::AccountId, msg: Vec) -> DispatchResultWithPostInfo { - let eth_msg = get_axelar_encoded_msg( - msg, - self.evm_chain.clone().into_inner(), - self.liquidity_pools_contract_address, - ) - .map_err(DispatchError::Other)?; - - self.router.do_send(sender, eth_msg) - } -} - -/// Encodes the provided message into the format required for submitting it -/// to the Axelar contract which in turn calls the LiquidityPools -/// contract with the serialized LP message as `payload`. -/// -/// Axelar contract call: -/// -/// -/// LiquidityPools contract call: -/// -pub(crate) fn get_axelar_encoded_msg( - serialized_msg: Vec, - target_chain: Vec, - target_contract: H160, -) -> Result, &'static str> { - #[allow(deprecated)] - let encoded_axelar_contract = Contract { - constructor: None, - functions: BTreeMap::>::from([( - AXELAR_FUNCTION_NAME.into(), - vec![Function { - name: AXELAR_FUNCTION_NAME.into(), - inputs: vec![ - Param { - name: AXELAR_DESTINATION_CHAIN_PARAM.into(), - kind: ParamType::String, - internal_type: None, - }, - Param { - name: AXELAR_DESTINATION_CONTRACT_ADDRESS_PARAM.into(), - kind: ParamType::String, - internal_type: None, - }, - Param { - name: AXELAR_PAYLOAD_PARAM.into(), - kind: ParamType::Bytes, - internal_type: None, - }, - ], - outputs: vec![], - constant: Some(false), - state_mutability: Default::default(), - }], - )]), - events: Default::default(), - errors: Default::default(), - receive: false, - fallback: false, - } - .function(AXELAR_FUNCTION_NAME) - .map_err(|_| "cannot retrieve Axelar contract function")? - .encode_input(&[ - Token::String( - String::from_utf8(target_chain).map_err(|_| "target chain conversion error")?, - ), - // Ensure that the target contract is correctly converted to hex. - // - // The `to_string` method on the H160 is returning a string containing an ellipsis, such - // as: 0x1234…7890 - Token::String(format!("0x{}", hex::encode(target_contract.0))), - Token::Bytes(serialized_msg), - ]) - .map_err(|_| "cannot encode input for Axelar contract function")?; - - Ok(encoded_axelar_contract) -} diff --git a/pallets/liquidity-pools-gateway/routers/src/tests.rs b/pallets/liquidity-pools-gateway/routers/src/tests.rs deleted file mode 100644 index 02c165205a..0000000000 --- a/pallets/liquidity-pools-gateway/routers/src/tests.rs +++ /dev/null @@ -1,351 +0,0 @@ -use cfg_primitives::CFG; -use cfg_traits::liquidity_pools::Router; -use frame_support::{assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec}; -use lazy_static::lazy_static; -use pallet_evm::AddressMapping; -use sp_core::{crypto::AccountId32, H160, H256, U256}; -use sp_runtime::{ - traits::{BlakeTwo256, ConstU32, Hash}, - DispatchError, -}; - -use super::mock::*; -use crate::*; - -lazy_static! { - static ref TEST_EVM_CHAIN: BoundedVec> = - BoundedVec::>::try_from( - "ethereum".as_bytes().to_vec() - ) - .unwrap(); -} - -mod evm_router { - use util::*; - - use super::*; - - mod util { - use super::*; - - pub struct EVMRouterTestData { - pub test_contract_address: H160, - pub test_contract_code: Vec, - pub test_contract_hash: H256, - pub evm_domain: EVMDomain, - pub sender: AccountId32, - pub sender_h160: H160, - pub derived_sender: AccountId32, - pub msg: Vec, - } - - pub fn get_test_data() -> EVMRouterTestData { - let test_contract_address = H160::from_low_u64_be(1); - let test_contract_code = [0; 32].to_vec(); - let test_contract_hash = BlakeTwo256::hash_of(&test_contract_code); - - let evm_domain = EVMDomain { - target_contract_address: test_contract_address, - target_contract_hash: test_contract_hash, - fee_values: FeeValues { - value: U256::from(0), - gas_limit: U256::from(10), - gas_price: U256::from(10), - }, - }; - - let sender: AccountId32 = [0; 32].into(); - let sender_h160: H160 = - H160::from_slice(&>::as_ref(&sender)[0..20]); - let derived_sender = IdentityAddressMapping::into_account_id(sender_h160); - - let msg = vec![0, 1, 2]; - - EVMRouterTestData { - test_contract_address, - test_contract_code, - test_contract_hash, - evm_domain, - sender, - sender_h160, - derived_sender, - msg, - } - } - } - - mod init { - use super::*; - - #[test] - fn success() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - pallet_evm::AccountCodes::::insert( - test_data.test_contract_address, - test_data.test_contract_code, - ); - - let router = EVMRouter:: { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }; - - assert_ok!(router.do_init()); - }); - } - - #[test] - fn failure() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - let router = EVMRouter:: { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }; - - assert_noop!( - router.do_init(), - DispatchError::Other("Target contract code does not match") - ); - - pallet_evm::AccountCodes::::insert( - test_data.test_contract_address, - [1; 32].to_vec(), - ); - - assert_noop!( - router.do_init(), - DispatchError::Other("Target contract code does not match") - ); - }); - } - } - - mod send { - use super::*; - - #[test] - fn success() { - new_test_ext().execute_with(|| { - let mut test_data = get_test_data(); - - Balances::mint_into(&test_data.derived_sender.into(), 1_000_000 * CFG).unwrap(); - - let transaction_call_cost = - ::config().gas_transaction_call; - - test_data.evm_domain.fee_values.gas_limit = - U256::from(transaction_call_cost + 10_000); - - let router = EVMRouter:: { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }; - - assert_ok!(router.do_send(test_data.sender, test_data.msg)); - }); - } - - #[test] - fn insufficient_balance() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - let router = EVMRouter:: { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }; - - let res = router.do_send(test_data.sender, test_data.msg); - - assert_eq!( - res.err().unwrap().error, - pallet_evm::Error::::BalanceLow.into() - ); - }); - } - } -} - -mod axelar_evm { - use util::*; - - use super::*; - - mod util { - use super::*; - - pub struct AxelarEVMTestData { - pub axelar_contract_address: H160, - pub axelar_contract_code: Vec, - pub axelar_contract_hash: H256, - pub liquidity_pools_contract_address: H160, - pub evm_domain: EVMDomain, - pub sender: AccountId32, - pub sender_h160: H160, - pub derived_sender: AccountId32, - pub msg: Vec, - } - - pub fn get_test_data() -> AxelarEVMTestData { - let axelar_contract_address = H160::from_low_u64_be(1); - let axelar_contract_code = [0; 32].to_vec(); - let axelar_contract_hash = BlakeTwo256::hash_of(&axelar_contract_code); - let liquidity_pools_contract_address = H160::from_low_u64_be(2); - - let evm_domain = EVMDomain { - target_contract_address: axelar_contract_address, - target_contract_hash: axelar_contract_hash, - fee_values: FeeValues { - value: U256::from(0), - gas_limit: U256::from(10), - gas_price: U256::from(10), - }, - }; - - let sender: AccountId32 = [0; 32].into(); - let sender_h160: H160 = - H160::from_slice(&>::as_ref(&sender)[0..20]); - let derived_sender = IdentityAddressMapping::into_account_id(sender_h160); - - let msg = vec![0x42]; - - AxelarEVMTestData { - axelar_contract_address, - axelar_contract_code, - axelar_contract_hash, - liquidity_pools_contract_address, - evm_domain, - sender, - sender_h160, - derived_sender, - msg, - } - } - } - - mod init { - use super::*; - - #[test] - fn success() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - pallet_evm::AccountCodes::::insert( - test_data.axelar_contract_address, - test_data.axelar_contract_code, - ); - - let domain_router = - DomainRouter::::AxelarEVM(AxelarEVMRouter:: { - router: EVMRouter { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }, - evm_chain: TEST_EVM_CHAIN.clone(), - liquidity_pools_contract_address: test_data - .liquidity_pools_contract_address, - }); - - assert_ok!(domain_router.init()); - }); - } - - #[test] - fn failure() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - let domain_router = - DomainRouter::::AxelarEVM(AxelarEVMRouter:: { - router: EVMRouter { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }, - evm_chain: TEST_EVM_CHAIN.clone(), - liquidity_pools_contract_address: test_data - .liquidity_pools_contract_address, - }); - - assert_noop!( - domain_router.init(), - DispatchError::Other("Target contract code does not match") - ); - - pallet_evm::AccountCodes::::insert( - test_data.axelar_contract_address, - [1; 32].to_vec(), - ); - - assert_noop!( - domain_router.init(), - DispatchError::Other("Target contract code does not match") - ); - }); - } - } - - mod send { - use super::*; - - #[test] - fn success() { - new_test_ext().execute_with(|| { - let mut test_data = get_test_data(); - - assert_ok!(Balances::mint_into( - &test_data.derived_sender.clone(), - 1_000_000 * CFG - )); - - let transaction_call_cost = - ::config().gas_transaction_call; - - test_data.evm_domain.fee_values.gas_limit = - U256::from(transaction_call_cost + 10_000); - - let domain_router = - DomainRouter::::AxelarEVM(AxelarEVMRouter:: { - router: EVMRouter { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }, - evm_chain: TEST_EVM_CHAIN.clone(), - liquidity_pools_contract_address: test_data - .liquidity_pools_contract_address, - }); - - assert_ok!(domain_router.send(test_data.sender, test_data.msg)); - }); - } - - #[test] - fn insufficient_balance() { - new_test_ext().execute_with(|| { - let test_data = get_test_data(); - - let domain_router = - DomainRouter::::AxelarEVM(AxelarEVMRouter:: { - router: EVMRouter { - evm_domain: test_data.evm_domain, - _marker: Default::default(), - }, - evm_chain: TEST_EVM_CHAIN.clone(), - liquidity_pools_contract_address: test_data - .liquidity_pools_contract_address, - }); - - let res = domain_router.send(test_data.sender, test_data.msg); - - assert_eq!( - res.err().unwrap().error, - pallet_evm::Error::::BalanceLow.into() - ); - }); - } - } -} diff --git a/pallets/liquidity-pools-gateway/src/lib.rs b/pallets/liquidity-pools-gateway/src/lib.rs index 2dfc01b85e..f1b1c62336 100644 --- a/pallets/liquidity-pools-gateway/src/lib.rs +++ b/pallets/liquidity-pools-gateway/src/lib.rs @@ -30,8 +30,8 @@ use core::fmt::Debug; use cfg_primitives::LP_DEFENSIVE_WEIGHT; use cfg_traits::liquidity_pools::{ - InboundMessageHandler, LPEncoding, MessageProcessor, MessageQueue, OutboundMessageHandler, - Router as DomainRouter, + InboundMessageHandler, LPEncoding, MessageProcessor, MessageQueue, MessageReceiver, + MessageSender, OutboundMessageHandler, RouterSupport, }; use cfg_types::domain_address::{Domain, DomainAddress}; use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; @@ -39,8 +39,8 @@ use frame_system::pallet_prelude::{ensure_signed, OriginFor}; use message::GatewayMessage; use orml_traits::GetByKey; pub use pallet::*; -use parity_scale_codec::{EncodeLike, FullCodec}; -use sp_std::convert::TryInto; +use parity_scale_codec::FullCodec; +use sp_std::{convert::TryInto, vec::Vec}; use crate::weights::WeightInfo; @@ -93,15 +93,11 @@ pub mod pallet { /// The Liquidity Pools message type. type Message: LPEncoding + Clone + Debug + PartialEq + MaxEncodedLen + TypeInfo + FullCodec; - /// The message router type that is stored for each domain. - type Router: DomainRouter - + Clone - + Debug - + MaxEncodedLen - + TypeInfo - + FullCodec - + EncodeLike - + PartialEq; + /// The target of of the messages comming from this chain + type MessageSender: MessageSender; + + /// An identification of a router + type RouterId: RouterSupport + Parameter; /// The type that processes inbound messages. type InboundMessageHandler: InboundMessageHandler< @@ -118,18 +114,15 @@ pub mod pallet { /// The sender account that will be used in the OutboundQueue /// implementation. #[pallet::constant] - type Sender: Get; + type Sender: Get; /// Type used for queueing messages. - type MessageQueue: MessageQueue>; + type MessageQueue: MessageQueue>; } #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { - /// The router for a given domain was set. - DomainRouterSet { domain: Domain, router: T::Router }, - /// An instance was added to a domain. InstanceAdded { instance: DomainAddress }, @@ -143,13 +136,6 @@ pub mod pallet { }, } - /// Storage for domain routers. - /// - /// This can only be set by an admin. - #[pallet::storage] - #[pallet::getter(fn domain_routers)] - pub type DomainRouters = StorageMap<_, Blake2_128Concat, Domain, T::Router>; - /// Storage that contains a limited number of whitelisted instances of /// deployed liquidity pools for a particular domain. /// @@ -177,9 +163,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// Router initialization failed. - RouterInitFailed, - /// The origin of the message to be processed is invalid. InvalidMessageOrigin, @@ -196,7 +179,7 @@ pub mod pallet { UnknownInstance, /// Router not found. - RouterNotFound, + RouterConfigurationNotFound, /// Emitted when you call `start_batch_messages()` but that was already /// called. You should finalize the message with `end_batch_messages()` @@ -209,27 +192,6 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Set a domain's router, - #[pallet::weight(T::WeightInfo::set_domain_router())] - #[pallet::call_index(0)] - pub fn set_domain_router( - origin: OriginFor, - domain: Domain, - router: T::Router, - ) -> DispatchResult { - T::AdminOrigin::ensure_origin(origin)?; - - ensure!(domain != Domain::Centrifuge, Error::::DomainNotSupported); - - router.init().map_err(|_| Error::::RouterInitFailed)?; - - >::insert(domain.clone(), router.clone()); - - Self::deposit_event(Event::DomainRouterSet { domain, router }); - - Ok(()) - } - /// Add a known instance of a deployed liquidity pools integration for a /// specific domain. #[pallet::weight(T::WeightInfo::add_instance())] @@ -277,6 +239,7 @@ pub mod pallet { #[pallet::call_index(5)] pub fn receive_message( origin: OriginFor, + router_id: T::RouterId, msg: BoundedVec, ) -> DispatchResult { let GatewayOrigin::Domain(origin_address) = T::LocalEVMOrigin::ensure_origin(origin)?; @@ -285,17 +248,7 @@ pub mod pallet { return Err(Error::::InvalidMessageOrigin.into()); } - ensure!( - Allowlist::::contains_key(origin_address.domain(), origin_address.clone()), - Error::::UnknownInstance, - ); - - let gateway_message = GatewayMessage::::Inbound { - domain_address: origin_address, - message: T::Message::deserialize(&msg)?, - }; - - T::MessageQueue::submit(gateway_message) + Self::receive(router_id, origin_address, msg.into()) } /// Set the address of the domain hook @@ -379,28 +332,32 @@ pub mod pallet { /// message using the router, and calculates and returns the required /// weight for these operations in the `DispatchResultWithPostInfo`. fn process_outbound_message( - sender: T::AccountId, + sender: DomainAddress, domain: Domain, message: T::Message, ) -> (DispatchResult, Weight) { - let read_weight = T::DbWeight::get().reads(1); + let router_ids = T::RouterId::for_domain(domain); - let Some(router) = DomainRouters::::get(domain) else { - return (Err(Error::::RouterNotFound.into()), read_weight); - }; + // TODO handle router ids logic - let (result, router_weight) = match router.send(sender, message.serialize()) { - Ok(dispatch_info) => (Ok(()), dispatch_info.actual_weight), - Err(e) => (Err(e.error), e.post_info.actual_weight), - }; + let mut count = 0; + let bytes = message.serialize(); - (result, router_weight.unwrap_or(read_weight)) + for router_id in router_ids { + count += 1; + if let Err(e) = T::MessageSender::send(router_id, sender.clone(), bytes.clone()) { + return (Err(e), LP_DEFENSIVE_WEIGHT.saturating_mul(count)); + } + } + + // TODO: Should we fix weights? + (Ok(()), LP_DEFENSIVE_WEIGHT.saturating_mul(count)) } fn queue_message(destination: Domain, message: T::Message) -> DispatchResult { // We are using the sender specified in the pallet config so that we can // ensure that the account is funded - let gateway_message = GatewayMessage::::Outbound { + let gateway_message = GatewayMessage::::Outbound { sender: T::Sender::get(), destination, message, @@ -439,7 +396,7 @@ pub mod pallet { } impl MessageProcessor for Pallet { - type Message = GatewayMessage; + type Message = GatewayMessage; fn process(msg: Self::Message) -> (DispatchResult, Weight) { match msg { @@ -465,4 +422,29 @@ pub mod pallet { } } } + + impl MessageReceiver for Pallet { + type Middleware = T::RouterId; + type Origin = DomainAddress; + + fn receive( + _router_id: T::RouterId, + origin_address: DomainAddress, + message: Vec, + ) -> DispatchResult { + // TODO handle router ids logic with votes and session_id + + ensure!( + Allowlist::::contains_key(origin_address.domain(), origin_address.clone()), + Error::::UnknownInstance, + ); + + let gateway_message = GatewayMessage::::Inbound { + domain_address: origin_address, + message: T::Message::deserialize(&message)?, + }; + + T::MessageQueue::submit(gateway_message) + } + } } diff --git a/pallets/liquidity-pools-gateway/src/message.rs b/pallets/liquidity-pools-gateway/src/message.rs index cf0bbb1a17..505e075b0f 100644 --- a/pallets/liquidity-pools-gateway/src/message.rs +++ b/pallets/liquidity-pools-gateway/src/message.rs @@ -3,19 +3,19 @@ use frame_support::pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}; /// Message type used by the LP gateway. #[derive(Debug, Encode, Decode, Clone, Eq, MaxEncodedLen, PartialEq, TypeInfo)] -pub enum GatewayMessage { +pub enum GatewayMessage { Inbound { domain_address: DomainAddress, message: Message, }, Outbound { - sender: AccountId, + sender: DomainAddress, destination: Domain, message: Message, }, } -impl Default for GatewayMessage { +impl Default for GatewayMessage { fn default() -> Self { GatewayMessage::Inbound { domain_address: Default::default(), diff --git a/pallets/liquidity-pools-gateway/src/mock.rs b/pallets/liquidity-pools-gateway/src/mock.rs index 348ccf8d91..1ea75c1885 100644 --- a/pallets/liquidity-pools-gateway/src/mock.rs +++ b/pallets/liquidity-pools-gateway/src/mock.rs @@ -1,9 +1,6 @@ -use cfg_mocks::{ - pallet_mock_liquidity_pools, pallet_mock_liquidity_pools_gateway_queue, pallet_mock_routers, - RouterMock, -}; -use cfg_traits::liquidity_pools::LPEncoding; -use cfg_types::domain_address::DomainAddress; +use cfg_mocks::{pallet_mock_liquidity_pools, pallet_mock_liquidity_pools_gateway_queue}; +use cfg_traits::liquidity_pools::{LPEncoding, RouterSupport}; +use cfg_types::domain_address::{Domain, DomainAddress}; use frame_support::{derive_impl, weights::constants::RocksDbWeight}; use frame_system::EnsureRoot; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -76,12 +73,21 @@ impl LPEncoding for Message { } } +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct RouterId(u32); + +impl RouterSupport for RouterId { + fn for_domain(_domain: Domain) -> Vec { + vec![] // TODO + } +} + frame_support::construct_runtime!( pub enum Runtime { System: frame_system, MockLiquidityPools: pallet_mock_liquidity_pools, MockLiquidityPoolsGatewayQueue: pallet_mock_liquidity_pools_gateway_queue, - MockRouters: pallet_mock_routers, + MockMessageSender: cfg_mocks::router_message::pallet, LiquidityPoolsGateway: pallet_liquidity_pools_gateway, } ); @@ -99,14 +105,17 @@ impl pallet_mock_liquidity_pools::Config for Runtime { type Message = Message; } -impl pallet_mock_routers::Config for Runtime {} - impl pallet_mock_liquidity_pools_gateway_queue::Config for Runtime { - type Message = GatewayMessage; + type Message = GatewayMessage; +} + +impl cfg_mocks::router_message::pallet::Config for Runtime { + type Middleware = RouterId; + type Origin = DomainAddress; } frame_support::parameter_types! { - pub Sender: AccountId32 = AccountId32::from(H256::from_low_u64_be(1).to_fixed_bytes()); + pub Sender: DomainAddress = DomainAddress::Centrifuge(AccountId32::from(H256::from_low_u64_be(1).to_fixed_bytes()).into()); pub const MaxIncomingMessageSize: u32 = 1024; pub const LpAdminAccount: AccountId32 = LP_ADMIN_ACCOUNT; } @@ -118,13 +127,16 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { type MaxIncomingMessageSize = MaxIncomingMessageSize; type Message = Message; type MessageQueue = MockLiquidityPoolsGatewayQueue; - type Router = RouterMock; + type MessageSender = MockMessageSender; + type RouterId = RouterId; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type Sender = Sender; type WeightInfo = (); } +/* pub fn new_test_ext() -> sp_io::TestExternalities { System::externalities() } +*/ diff --git a/pallets/liquidity-pools-gateway/src/tests.rs b/pallets/liquidity-pools-gateway/src/tests.rs index 13afa3a3bc..61e54fde63 100644 --- a/pallets/liquidity-pools-gateway/src/tests.rs +++ b/pallets/liquidity-pools-gateway/src/tests.rs @@ -1,3 +1,4 @@ +/* use cfg_mocks::*; use cfg_primitives::LP_DEFENSIVE_WEIGHT; use cfg_traits::liquidity_pools::{LPEncoding, MessageProcessor, OutboundMessageHandler}; @@ -849,3 +850,4 @@ mod batches { }); } } +*/ diff --git a/runtime/altair/Cargo.toml b/runtime/altair/Cargo.toml index d4353693d6..ea5009dd4e 100644 --- a/runtime/altair/Cargo.toml +++ b/runtime/altair/Cargo.toml @@ -68,11 +68,9 @@ cfg-primitives = { workspace = true } cfg-traits = { workspace = true } cfg-types = { workspace = true } cfg-utils = { workspace = true } -liquidity-pools-gateway-routers = { workspace = true } runtime-common = { workspace = true } # Pallet list -axelar-gateway-precompile = { workspace = true } chainbridge = { workspace = true } cumulus-pallet-aura-ext = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } @@ -85,6 +83,7 @@ orml-xtokens = { workspace = true } pallet-anchors = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } +pallet-axelar-router = { workspace = true } pallet-balances = { workspace = true } pallet-base-fee = { workspace = true } pallet-block-rewards = { workspace = true } @@ -200,9 +199,7 @@ std = [ "cfg-traits/std", "cfg-types/std", "runtime-common/std", - "liquidity-pools-gateway-routers/std", # Pallet list - "axelar-gateway-precompile/std", "chainbridge/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -215,6 +212,7 @@ std = [ "pallet-anchors/std", "pallet-aura/std", "pallet-authorship/std", + "pallet-axelar-router/std", "pallet-balances/std", "pallet-base-fee/std", "pallet-block-rewards/std", @@ -295,9 +293,7 @@ runtime-benchmarks = [ "cfg-traits/runtime-benchmarks", "cfg-types/runtime-benchmarks", "runtime-common/runtime-benchmarks", - "liquidity-pools-gateway-routers/runtime-benchmarks", # Pallet list - "axelar-gateway-precompile/runtime-benchmarks", "chainbridge/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -305,6 +301,7 @@ runtime-benchmarks = [ "orml-tokens/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", "pallet-anchors/runtime-benchmarks", + "pallet-axelar-router/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-block-rewards/runtime-benchmarks", "pallet-bridge/runtime-benchmarks", @@ -375,9 +372,7 @@ try-runtime = [ "cfg-traits/try-runtime", "cfg-types/try-runtime", "runtime-common/try-runtime", - "liquidity-pools-gateway-routers/try-runtime", # Pallet list - "axelar-gateway-precompile/try-runtime", "chainbridge/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-parachain-system/try-runtime", @@ -390,6 +385,7 @@ try-runtime = [ "pallet-anchors/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", + "pallet-axelar-router/try-runtime", "pallet-balances/try-runtime", "pallet-base-fee/try-runtime", "pallet-block-rewards/try-runtime", diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 62d6ac27e2..5b68b3c593 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -31,6 +31,7 @@ use cfg_primitives::{ }; use cfg_traits::{investments::OrderManager, Millis, PoolUpdateGuard, Seconds}; use cfg_types::{ + domain_address::DomainAddress, fee_keys::{Fee, FeeKey}, fixed_point::{Quantity, Rate, Ratio}, investments::InvestmentPortfolio, @@ -116,6 +117,7 @@ use runtime_common::{ permissions::{IsUnfrozenTrancheInvestor, PoolAdminCheck}, remarks::Remark, rewards::SingleCurrencyMovement, + routing::{EvmAccountCodeChecker, RouterDispatcher, RouterId}, transfer_filter::{PreLpTransfer, PreNativeTransfer}, xcm::AccountIdToLocation, xcm_transactor, AllowanceDeposit, CurrencyED, @@ -1756,7 +1758,7 @@ impl pallet_liquidity_pools::Config for Runtime { parameter_types! { pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); + pub Sender: DomainAddress = gateway::get_gateway_account::(); } impl pallet_liquidity_pools_gateway::Config for Runtime { @@ -1766,7 +1768,8 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { type MaxIncomingMessageSize = MaxIncomingMessageSize; type Message = pallet_liquidity_pools::Message; type MessageQueue = LiquidityPoolsGatewayQueue; - type Router = liquidity_pools_gateway_routers::DomainRouter; + type MessageSender = RouterDispatcher; + type RouterId = RouterId; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type Sender = Sender; @@ -1774,7 +1777,7 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { } impl pallet_liquidity_pools_gateway_queue::Config for Runtime { - type Message = GatewayMessage; + type Message = GatewayMessage; type MessageNonce = LPGatewayQueueMessageNonce; type MessageProcessor = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; @@ -1884,10 +1887,13 @@ impl pallet_ethereum::Config for Runtime { impl pallet_ethereum_transaction::Config for Runtime {} -impl axelar_gateway_precompile::Config for Runtime { - type AdminOrigin = EnsureRootOr; +impl pallet_axelar_router::Config for Runtime { + type AdminOrigin = EnsureRoot; + type EvmAccountCodeChecker = EvmAccountCodeChecker; + type Middleware = RouterId; + type Receiver = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); + type Transactor = EthereumTransaction; } impl pallet_conviction_voting::Config for Runtime { @@ -2109,7 +2115,7 @@ construct_runtime!( BaseFee: pallet_base_fee::{Pallet, Call, Config, Storage, Event} = 162, Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 163, EthereumTransaction: pallet_ethereum_transaction::{Pallet, Storage} = 164, - LiquidityPoolsAxelarGateway: axelar_gateway_precompile::{Pallet, Call, Storage, Event} = 165, + AxelarRouter: pallet_axelar_router::{Pallet, Call, Storage, Event} = 165, // Our pallets (part 2) // Removed: Migration = 199 diff --git a/runtime/centrifuge/Cargo.toml b/runtime/centrifuge/Cargo.toml index a113cbd6b2..0a4f83b50f 100644 --- a/runtime/centrifuge/Cargo.toml +++ b/runtime/centrifuge/Cargo.toml @@ -66,11 +66,9 @@ fp-self-contained = { workspace = true } cfg-primitives = { workspace = true } cfg-traits = { workspace = true } cfg-types = { workspace = true } -liquidity-pools-gateway-routers = { workspace = true } runtime-common = { workspace = true } # Pallet list -axelar-gateway-precompile = { workspace = true } chainbridge = { workspace = true } cumulus-pallet-aura-ext = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } @@ -83,6 +81,7 @@ orml-xtokens = { workspace = true } pallet-anchors = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } +pallet-axelar-router = { workspace = true } pallet-balances = { workspace = true } pallet-base-fee = { workspace = true } pallet-block-rewards = { workspace = true } @@ -193,9 +192,7 @@ std = [ "cfg-traits/std", "cfg-types/std", "runtime-common/std", - "liquidity-pools-gateway-routers/std", # Pallet list - "axelar-gateway-precompile/std", "chainbridge/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -208,6 +205,7 @@ std = [ "pallet-anchors/std", "pallet-aura/std", "pallet-authorship/std", + "pallet-axelar-router/std", "pallet-balances/std", "pallet-base-fee/std", "pallet-block-rewards/std", @@ -284,9 +282,7 @@ runtime-benchmarks = [ "cfg-traits/runtime-benchmarks", "cfg-types/runtime-benchmarks", "runtime-common/runtime-benchmarks", - "liquidity-pools-gateway-routers/runtime-benchmarks", # Pallet list - "axelar-gateway-precompile/runtime-benchmarks", "chainbridge/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -294,6 +290,7 @@ runtime-benchmarks = [ "orml-tokens/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", "pallet-anchors/runtime-benchmarks", + "pallet-axelar-router/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-block-rewards/runtime-benchmarks", "pallet-bridge/runtime-benchmarks", @@ -360,9 +357,7 @@ try-runtime = [ "cfg-traits/try-runtime", "cfg-types/try-runtime", "runtime-common/try-runtime", - "liquidity-pools-gateway-routers/try-runtime", # Pallet list - "axelar-gateway-precompile/try-runtime", "chainbridge/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-parachain-system/try-runtime", @@ -375,6 +370,7 @@ try-runtime = [ "pallet-anchors/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", + "pallet-axelar-router/try-runtime", "pallet-balances/try-runtime", "pallet-base-fee/try-runtime", "pallet-block-rewards/try-runtime", diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index cf6b00b7b0..86feb75fcb 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -34,6 +34,7 @@ use cfg_traits::{ Seconds, }; use cfg_types::{ + domain_address::DomainAddress, fee_keys::{Fee, FeeKey}, fixed_point::{Quantity, Rate, Ratio}, investments::InvestmentPortfolio, @@ -116,6 +117,7 @@ use runtime_common::{ }, permissions::{IsUnfrozenTrancheInvestor, PoolAdminCheck}, rewards::SingleCurrencyMovement, + routing::{EvmAccountCodeChecker, RouterDispatcher, RouterId}, transfer_filter::{PreLpTransfer, PreNativeTransfer}, xcm::AccountIdToLocation, xcm_transactor, AllowanceDeposit, CurrencyED, @@ -1839,7 +1841,7 @@ impl pallet_liquidity_pools::Config for Runtime { parameter_types! { pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); + pub Sender: DomainAddress = gateway::get_gateway_account::(); } parameter_types! { @@ -1865,7 +1867,8 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { type MaxIncomingMessageSize = MaxIncomingMessageSize; type Message = pallet_liquidity_pools::Message; type MessageQueue = LiquidityPoolsGatewayQueue; - type Router = liquidity_pools_gateway_routers::DomainRouter; + type MessageSender = RouterDispatcher; + type RouterId = RouterId; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type Sender = Sender; @@ -1873,7 +1876,7 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { } impl pallet_liquidity_pools_gateway_queue::Config for Runtime { - type Message = GatewayMessage; + type Message = GatewayMessage; type MessageNonce = LPGatewayQueueMessageNonce; type MessageProcessor = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; @@ -1983,10 +1986,13 @@ impl pallet_ethereum::Config for Runtime { impl pallet_ethereum_transaction::Config for Runtime {} -impl axelar_gateway_precompile::Config for Runtime { +impl pallet_axelar_router::Config for Runtime { type AdminOrigin = EnsureAccountOrRootOr; + type EvmAccountCodeChecker = EvmAccountCodeChecker; + type Middleware = RouterId; + type Receiver = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); + type Transactor = EthereumTransaction; } /// Block type as expected by this runtime. @@ -2111,7 +2117,7 @@ construct_runtime!( BaseFee: pallet_base_fee::{Pallet, Call, Config, Storage, Event} = 162, Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 163, EthereumTransaction: pallet_ethereum_transaction::{Pallet, Storage} = 164, - LiquidityPoolsAxelarGateway: axelar_gateway_precompile::{Pallet, Call, Storage, Event} = 165, + AxelarRouter: pallet_axelar_router::{Pallet, Call, Storage, Event} = 165, // Synced pallets across all runtimes - Range: 180-240 // WHY: * integrations like fireblocks will need to know the index in the enum diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index b4b4cdad74..d566344efb 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -56,7 +56,6 @@ cfg-types = { workspace = true } cfg-utils = { workspace = true } # Pallets in the export list -axelar-gateway-precompile = { workspace = true } chainbridge = { workspace = true } cumulus-pallet-aura-ext = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } @@ -70,6 +69,7 @@ orml-xtokens = { workspace = true } pallet-anchors = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } +pallet-axelar-router = { workspace = true } pallet-balances = { workspace = true } pallet-base-fee = { workspace = true } pallet-block-rewards = { workspace = true } @@ -174,7 +174,6 @@ std = [ "cfg-types/std", "cfg-utils/std", # Pallet exporting list - "axelar-gateway-precompile/std", "chainbridge/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -187,6 +186,7 @@ std = [ "pallet-anchors/std", "pallet-aura/std", "pallet-authorship/std", + "pallet-axelar-router/std", "pallet-balances/std", "pallet-base-fee/std", "pallet-block-rewards/std", @@ -259,7 +259,6 @@ runtime-benchmarks = [ "cfg-utils/runtime-benchmarks", "cfg-mocks/runtime-benchmarks", # Pallet exporting list - "axelar-gateway-precompile/runtime-benchmarks", "chainbridge/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -267,6 +266,7 @@ runtime-benchmarks = [ "orml-tokens/runtime-benchmarks", "orml-xtokens/runtime-benchmarks", "pallet-anchors/runtime-benchmarks", + "pallet-axelar-router/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-block-rewards/runtime-benchmarks", "pallet-bridge/runtime-benchmarks", @@ -326,7 +326,6 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", # Pallet exporting list - "axelar-gateway-precompile/try-runtime", "chainbridge/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-parachain-system/try-runtime", @@ -339,6 +338,7 @@ try-runtime = [ "pallet-anchors/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", + "pallet-axelar-router/try-runtime", "pallet-balances/try-runtime", "pallet-base-fee/try-runtime", "pallet-block-rewards/try-runtime", diff --git a/runtime/common/src/evm/precompile.rs b/runtime/common/src/evm/precompile.rs index df6df7617e..99b563f113 100644 --- a/runtime/common/src/evm/precompile.rs +++ b/runtime/common/src/evm/precompile.rs @@ -76,7 +76,7 @@ pub type RuntimePrecompilesAt = ( // Centrifuge specific precompiles: PrecompileAt< AddressU64, - axelar_gateway_precompile::Pallet, + pallet_axelar_router::Pallet, CallableByContract, >, ); diff --git a/runtime/common/src/gateway.rs b/runtime/common/src/gateway.rs index 99f06d651a..d958b2dab5 100644 --- a/runtime/common/src/gateway.rs +++ b/runtime/common/src/gateway.rs @@ -10,7 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_primitives::AccountId; +use cfg_types::domain_address::DomainAddress; use polkadot_parachain_primitives::primitives::Sibling; use sp_core::{crypto::AccountId32, H160}; use sp_runtime::traits::AccountIdConversion; @@ -24,6 +24,8 @@ pub fn get_gateway_h160_account() -> H160 { } pub fn get_gateway_account( -) -> AccountId { - AccountConverter::evm_address_to_account::(get_gateway_h160_account::()) +) -> DomainAddress { + DomainAddress::Centrifuge( + AccountConverter::evm_address_to_account::(get_gateway_h160_account::()).into(), + ) } diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 7acc49d58f..73b3a24066 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -43,6 +43,7 @@ pub mod origins; pub mod permissions; pub mod pool; pub mod remarks; +pub mod routing; pub mod transfer_filter; pub mod xcm; diff --git a/runtime/common/src/routing.rs b/runtime/common/src/routing.rs new file mode 100644 index 0000000000..9b9e345d3e --- /dev/null +++ b/runtime/common/src/routing.rs @@ -0,0 +1,75 @@ +use cfg_traits::{ + liquidity_pools::{MessageSender, RouterSupport}, + PreConditions, +}; +use cfg_types::domain_address::{Domain, DomainAddress}; +use frame_support::{ + dispatch::DispatchResult, + pallet_prelude::{Decode, Encode, MaxEncodedLen, TypeInfo}, +}; +use pallet_axelar_router::AxelarId; +use sp_core::{H160, H256}; +use sp_runtime::traits::{BlakeTwo256, Hash}; +use sp_std::{marker::PhantomData, vec, vec::Vec}; + +/// Identification of the router where the messages are sent and received. +/// +/// NOTE: `RouterId` is more specific than `Domain`. `Domain` identifies the +/// source and destination of the message, but `RouterId` also identifies how +/// to reach them. +#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum RouterId { + /// The message must be sent/received by EVM using Axelar + Axelar(AxelarId), +} + +impl From for RouterId { + fn from(axelar_id: AxelarId) -> Self { + RouterId::Axelar(axelar_id) + } +} + +impl From for Domain { + fn from(router_id: RouterId) -> Self { + match router_id { + RouterId::Axelar(AxelarId::Evm(chain_id)) => Domain::EVM(chain_id), + } + } +} + +impl RouterSupport for RouterId { + fn for_domain(domain: Domain) -> Vec { + match domain { + Domain::EVM(chain_id) => vec![RouterId::Axelar(AxelarId::Evm(chain_id))], + Domain::Centrifuge => vec![], + } + } +} + +/// This type choose the correct router implementation given a router id +pub struct RouterDispatcher(PhantomData); +impl MessageSender for RouterDispatcher +where + Routers: pallet_axelar_router::Config, +{ + type Middleware = RouterId; + type Origin = DomainAddress; + + fn send(router_id: RouterId, origin: Self::Origin, message: Vec) -> DispatchResult { + match router_id { + RouterId::Axelar(axelar_id) => { + pallet_axelar_router::Pallet::::send(axelar_id, origin, message) + } + } + } +} + +pub struct EvmAccountCodeChecker(PhantomData); +impl PreConditions<(H160, H256)> for EvmAccountCodeChecker { + type Result = bool; + + fn check((contract_address, contract_hash): (H160, H256)) -> bool { + let code = pallet_evm::AccountCodes::::get(contract_address); + BlakeTwo256::hash_of(&code) == contract_hash + } +} diff --git a/runtime/development/Cargo.toml b/runtime/development/Cargo.toml index b1a6719608..ac92c94e31 100644 --- a/runtime/development/Cargo.toml +++ b/runtime/development/Cargo.toml @@ -68,11 +68,9 @@ cfg-primitives = { workspace = true } cfg-traits = { workspace = true } cfg-types = { workspace = true } cfg-utils = { workspace = true } -liquidity-pools-gateway-routers = { workspace = true } runtime-common = { workspace = true } # Pallet list -axelar-gateway-precompile = { workspace = true } chainbridge = { workspace = true } cumulus-pallet-aura-ext = { workspace = true } cumulus-pallet-parachain-system = { workspace = true } @@ -86,6 +84,7 @@ pallet-anchors = { workspace = true } pallet-anchors-v2 = { workspace = true } pallet-aura = { workspace = true } pallet-authorship = { workspace = true } +pallet-axelar-router = { workspace = true } pallet-balances = { workspace = true } pallet-base-fee = { workspace = true } pallet-block-rewards = { workspace = true } @@ -201,9 +200,7 @@ std = [ "cfg-traits/std", "cfg-types/std", "runtime-common/std", - "liquidity-pools-gateway-routers/std", # Pallet list - "axelar-gateway-precompile/std", "chainbridge/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -217,6 +214,7 @@ std = [ "pallet-anchors-v2/std", "pallet-aura/std", "pallet-authorship/std", + "pallet-axelar-router/std", "pallet-balances/std", "pallet-base-fee/std", "pallet-block-rewards/std", @@ -296,9 +294,7 @@ runtime-benchmarks = [ "cfg-traits/runtime-benchmarks", "cfg-types/runtime-benchmarks", "runtime-common/runtime-benchmarks", - "liquidity-pools-gateway-routers/runtime-benchmarks", # Pallet list - "axelar-gateway-precompile/runtime-benchmarks", "chainbridge/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -307,6 +303,7 @@ runtime-benchmarks = [ "orml-xtokens/runtime-benchmarks", "pallet-anchors/runtime-benchmarks", "pallet-anchors-v2/runtime-benchmarks", + "pallet-axelar-router/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-block-rewards/runtime-benchmarks", "pallet-bridge/runtime-benchmarks", @@ -377,9 +374,7 @@ try-runtime = [ "cfg-traits/try-runtime", "cfg-types/try-runtime", "runtime-common/try-runtime", - "liquidity-pools-gateway-routers/try-runtime", # Pallet list - "axelar-gateway-precompile/try-runtime", "chainbridge/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-parachain-system/try-runtime", @@ -393,6 +388,7 @@ try-runtime = [ "pallet-anchors-v2/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", + "pallet-axelar-router/try-runtime", "pallet-balances/try-runtime", "pallet-base-fee/try-runtime", "pallet-block-rewards/try-runtime", diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index af7272944d..4df2d41bbe 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -34,6 +34,7 @@ use cfg_traits::{ Seconds, }; use cfg_types::{ + domain_address::DomainAddress, fee_keys::{Fee, FeeKey}, fixed_point::{Quantity, Rate, Ratio}, investments::InvestmentPortfolio, @@ -124,6 +125,7 @@ use runtime_common::{ permissions::{IsUnfrozenTrancheInvestor, PoolAdminCheck}, remarks::Remark, rewards::SingleCurrencyMovement, + routing::{EvmAccountCodeChecker, RouterDispatcher, RouterId}, transfer_filter::{PreLpTransfer, PreNativeTransfer}, xcm::AccountIdToLocation, xcm_transactor, AllowanceDeposit, CurrencyED, @@ -1861,7 +1863,7 @@ impl pallet_liquidity_pools::Config for Runtime { parameter_types! { pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); + pub Sender: DomainAddress = gateway::get_gateway_account::(); } impl pallet_liquidity_pools_gateway::Config for Runtime { @@ -1871,7 +1873,8 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { type MaxIncomingMessageSize = MaxIncomingMessageSize; type Message = pallet_liquidity_pools::Message; type MessageQueue = LiquidityPoolsGatewayQueue; - type Router = liquidity_pools_gateway_routers::DomainRouter; + type MessageSender = RouterDispatcher; + type RouterId = RouterId; type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; type Sender = Sender; @@ -1879,7 +1882,7 @@ impl pallet_liquidity_pools_gateway::Config for Runtime { } impl pallet_liquidity_pools_gateway_queue::Config for Runtime { - type Message = GatewayMessage; + type Message = GatewayMessage; type MessageNonce = LPGatewayQueueMessageNonce; type MessageProcessor = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; @@ -1990,10 +1993,13 @@ impl pallet_ethereum::Config for Runtime { impl pallet_ethereum_transaction::Config for Runtime {} -impl axelar_gateway_precompile::Config for Runtime { +impl pallet_axelar_router::Config for Runtime { type AdminOrigin = EnsureRoot; + type EvmAccountCodeChecker = EvmAccountCodeChecker; + type Middleware = RouterId; + type Receiver = LiquidityPoolsGateway; type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); + type Transactor = EthereumTransaction; } impl pallet_conviction_voting::Config for Runtime { @@ -2219,7 +2225,7 @@ construct_runtime!( BaseFee: pallet_base_fee::{Pallet, Call, Config, Storage, Event} = 162, Ethereum: pallet_ethereum::{Pallet, Config, Call, Storage, Event, Origin} = 163, EthereumTransaction: pallet_ethereum_transaction::{Pallet, Storage} = 164, - LiquidityPoolsAxelarGateway: axelar_gateway_precompile::{Pallet, Call, Storage, Event} = 165, + AxelarRouter: pallet_axelar_router::{Pallet, Call, Storage, Event} = 165, // Removed: Migration = 199 // admin stuff diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index fc28961991..5740d4e194 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -14,7 +14,7 @@ ethabi = { workspace = true, features = ["std", "full-serde"] } ethereum = { workspace = true, features = ["std"] } fudge = { workspace = true } fudge-core = { workspace = true } -hex = { workspace = true } +hex = { workspace = true, features = ["std"] } hex-literal = { workspace = true } lazy_static = { workspace = true } parity-scale-codec = { workspace = true } @@ -80,12 +80,10 @@ cfg-traits = { workspace = true, features = ["std"] } cfg-types = { workspace = true, features = ["std"] } cfg-utils = { workspace = true, features = ["std"] } development-runtime = { workspace = true, features = ["std"] } -liquidity-pools-gateway-routers = { workspace = true, features = ["std"] } runtime-common = { workspace = true, features = ["std"] } runtime-integration-tests-proc-macro = { workspace = true } # Pallet list -axelar-gateway-precompile = { workspace = true, features = ["std"] } chainbridge = { workspace = true, features = ["std"] } cumulus-pallet-aura-ext = { workspace = true, features = ["std"] } cumulus-pallet-parachain-system = { workspace = true, features = ["std"] } @@ -98,6 +96,7 @@ orml-xtokens = { workspace = true, features = ["std"] } pallet-anchors = { workspace = true, features = ["std"] } pallet-aura = { workspace = true, features = ["std"] } pallet-authorship = { workspace = true, features = ["std"] } +pallet-axelar-router = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-base-fee = { workspace = true, features = ["std"] } pallet-block-rewards = { workspace = true, features = ["std"] } diff --git a/runtime/integration-tests/src/cases.rs b/runtime/integration-tests/src/cases.rs index 811cabf589..38f6212f63 100644 --- a/runtime/integration-tests/src/cases.rs +++ b/runtime/integration-tests/src/cases.rs @@ -10,7 +10,6 @@ mod liquidity_pools_gateway_queue; mod loans; mod lp; mod oracles; -mod precompile; mod proxy; mod restricted_transfers; mod routers; diff --git a/runtime/integration-tests/src/cases/liquidity_pools.rs b/runtime/integration-tests/src/cases/liquidity_pools.rs index a1b51cc8ca..ddc227f779 100644 --- a/runtime/integration-tests/src/cases/liquidity_pools.rs +++ b/runtime/integration-tests/src/cases/liquidity_pools.rs @@ -280,7 +280,10 @@ pub mod utils { assert_ok!(orml_tokens::Pallet::::set_balance( ::RuntimeOrigin::root(), - ::Sender::get().into(), + AccountId::from( + ::Sender::get().address() + ) + .into(), GLMR_CURRENCY_ID, DEFAULT_BALANCE_GLMR, 0, diff --git a/runtime/integration-tests/src/cases/liquidity_pools_gateway_queue.rs b/runtime/integration-tests/src/cases/liquidity_pools_gateway_queue.rs index db308d759f..7034f18ecb 100644 --- a/runtime/integration-tests/src/cases/liquidity_pools_gateway_queue.rs +++ b/runtime/integration-tests/src/cases/liquidity_pools_gateway_queue.rs @@ -1,4 +1,4 @@ -use cfg_traits::liquidity_pools::MessageQueue as MessageQueueT; +use cfg_traits::liquidity_pools::MessageQueue; use cfg_types::domain_address::{Domain, DomainAddress}; use frame_support::assert_ok; use pallet_liquidity_pools::Message; @@ -29,11 +29,9 @@ fn inbound() { message: Message::Invalid, }; - assert_ok!( - as MessageQueueT>::submit( - message.clone() - ) - ); + assert_ok!(pallet_liquidity_pools_gateway_queue::Pallet::::submit( + message.clone() + )); pallet_liquidity_pools_gateway_queue::Event::::MessageExecutionFailure { nonce, @@ -57,21 +55,19 @@ fn outbound() { let expected_event = env.parachain_state_mut(|| { let nonce = ::MessageNonce::one(); let message = GatewayMessage::Outbound { - sender: [1; 32].into(), + sender: DomainAddress::Centrifuge([1; 32]), destination: Domain::EVM(1), message: Message::Invalid, }; - assert_ok!( - as MessageQueueT>::submit( - message.clone() - ) - ); + assert_ok!(pallet_liquidity_pools_gateway_queue::Pallet::::submit( + message.clone() + )); pallet_liquidity_pools_gateway_queue::Event::::MessageExecutionFailure { nonce, message, - error: pallet_liquidity_pools_gateway::Error::::RouterNotFound.into(), + error: pallet_axelar_router::Error::::RouterConfigurationNotFound.into(), } }); diff --git a/runtime/integration-tests/src/cases/lp/mod.rs b/runtime/integration-tests/src/cases/lp/mod.rs index acfa20336a..1c6896a9bf 100644 --- a/runtime/integration-tests/src/cases/lp/mod.rs +++ b/runtime/integration-tests/src/cases/lp/mod.rs @@ -10,7 +10,6 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use axelar_gateway_precompile::SourceConverter; use cfg_primitives::{Balance, PoolId, CFG, SECONDS_PER_HOUR, SECONDS_PER_YEAR}; use cfg_traits::Seconds; use cfg_types::{ @@ -22,14 +21,10 @@ use ethabi::{ ethereum_types::{H160, U128, U256}, FixedBytes, Token, Uint, }; -use frame_support::{ - assert_ok, dispatch::RawOrigin, pallet_prelude::ConstU32, traits::OriginTrait, BoundedVec, -}; +use frame_support::{assert_ok, dispatch::RawOrigin, traits::OriginTrait}; use frame_system::pallet_prelude::OriginFor; use hex_literal::hex; -use liquidity_pools_gateway_routers::{ - AxelarEVMRouter, DomainRouter, EVMDomain, EVMRouter, FeeValues, MAX_AXELAR_EVM_CHAIN_SIZE, -}; +use pallet_axelar_router::{AxelarConfig, DomainConfig, EvmConfig, FeeValues}; use pallet_evm::FeeCalculator; use runtime_common::account_conversion::AccountConverter; pub use setup_lp::*; diff --git a/runtime/integration-tests/src/cases/lp/setup_lp.rs b/runtime/integration-tests/src/cases/lp/setup_lp.rs index 684cdd83ab..efc3ad0299 100644 --- a/runtime/integration-tests/src/cases/lp/setup_lp.rs +++ b/runtime/integration-tests/src/cases/lp/setup_lp.rs @@ -41,10 +41,7 @@ pub fn setup as EnvEvmExtension>::E evm.load_contracts(); // Fund gateway sender - give_balance::( - ::Sender::get(), - DEFAULT_BALANCE * CFG, - ); + give_balance::(T::Sender::get().address().into(), DEFAULT_BALANCE * CFG); // Register general local pool-currency register_currency::(LocalUSDC, |_| {}); @@ -67,49 +64,31 @@ pub fn setup as EnvEvmExtension>::E // Create router let (base_fee, _) = ::FeeCalculator::min_gas_price(); - let evm_domain = EVMDomain { - target_contract_address: evm.deployed(names::ADAPTER).address(), - target_contract_hash: BlakeTwo256::hash_of( - &evm.deployed(names::ADAPTER).deployed_bytecode, - ), - fee_values: FeeValues { - value: sp_core::U256::zero(), - gas_limit: sp_core::U256::from(500_000), - gas_price: sp_core::U256::from(base_fee), - }, + let axelar_evm_config = AxelarConfig { + liquidity_pools_contract_address: evm.deployed(names::ADAPTER).address(), + domain: DomainConfig::Evm(EvmConfig { + chain_id: EVM_DOMAIN_CHAIN_ID, + target_contract_address: evm.deployed(names::ADAPTER).address(), + target_contract_hash: BlakeTwo256::hash_of( + &evm.deployed(names::ADAPTER).deployed_bytecode, + ), + fee_values: FeeValues { + value: sp_core::U256::zero(), + gas_limit: sp_core::U256::from(500_000), + gas_price: sp_core::U256::from(base_fee), + }, + }), }; - let axelar_evm_router = AxelarEVMRouter::::new( - EVMRouter::new(evm_domain), - BoundedVec::>::try_from( - EVM_DOMAIN_STR.as_bytes().to_vec(), - ) - .unwrap(), - evm.deployed(names::ADAPTER).address(), - ); - - assert_ok!( - pallet_liquidity_pools_gateway::Pallet::::set_domain_router( - RawOrigin::Root.into(), - Domain::EVM(EVM_DOMAIN_CHAIN_ID), - DomainRouter::::AxelarEVM(axelar_evm_router), - ) - ); - - assert_ok!(pallet_liquidity_pools_gateway::Pallet::::add_instance( + assert_ok!(pallet_axelar_router::Pallet::::set_config( RawOrigin::Root.into(), - DomainAddress::evm(EVM_DOMAIN_CHAIN_ID, EVM_LP_INSTANCE) + EVM_DOMAIN_STR.as_bytes().to_vec().try_into().unwrap(), + Box::new(axelar_evm_config) )); - assert_ok!(axelar_gateway_precompile::Pallet::::set_gateway( - RawOrigin::Root.into(), - evm.deployed(names::ADAPTER).address() - )); - - assert_ok!(axelar_gateway_precompile::Pallet::::set_converter( + assert_ok!(pallet_liquidity_pools_gateway::Pallet::::add_instance( RawOrigin::Root.into(), - BlakeTwo256::hash(EVM_DOMAIN_STR.as_bytes()), - SourceConverter::new(EVM_DOMAIN), + DomainAddress::evm(EVM_DOMAIN_CHAIN_ID, EVM_LP_INSTANCE) )); assert_ok!( diff --git a/runtime/integration-tests/src/cases/lp/utils.rs b/runtime/integration-tests/src/cases/lp/utils.rs index 191f8ddb16..e8229d5c17 100644 --- a/runtime/integration-tests/src/cases/lp/utils.rs +++ b/runtime/integration-tests/src/cases/lp/utils.rs @@ -20,7 +20,7 @@ use frame_support::traits::{OriginTrait, PalletInfo}; use frame_system::pallet_prelude::OriginFor; use pallet_evm::ExecutionInfo; use pallet_liquidity_pools_gateway::message::GatewayMessage; -use sp_core::{ByteArray, Get}; +use sp_core::Get; use sp_runtime::{ traits::{Convert, EnsureAdd}, DispatchError, @@ -99,7 +99,7 @@ pub fn verify_outbound_failure_on_lp(to: H160) { // The sender is the sender account on the gateway assert_eq!( status.from.0, - ::Sender::get().as_slice()[0..20] + ::Sender::get().address()[0..20] ); assert_eq!(status.to.unwrap().0, to.0); assert!(!receipt_ok(receipt)); diff --git a/runtime/integration-tests/src/cases/precompile.rs b/runtime/integration-tests/src/cases/precompile.rs deleted file mode 100644 index c9401c76f9..0000000000 --- a/runtime/integration-tests/src/cases/precompile.rs +++ /dev/null @@ -1,137 +0,0 @@ -use axelar_gateway_precompile::SourceConverter; -use cfg_primitives::CFG; -use cfg_traits::liquidity_pools::LPEncoding; -use cfg_types::domain_address::{Domain, DomainAddress}; -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::{ - config::Runtime, - env::{Blocks, Env, EnvEvmExtension}, - envs::runtime_env::RuntimeEnv, - utils::{ - currency::{usd18, CurrencyInfo, Usd18}, - evm, - genesis::{self, Genesis}, - }, -}; - -#[test_runtimes(all)] -fn axelar_precompile_execute() { - let mut env = RuntimeEnv::::from_parachain_storage( - Genesis::default() - .add(genesis::assets::([(Usd18.id(), &Usd18.metadata())])) - .storage(), - ); - - 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_receiver_account = - env.state(|_| T::AddressMapping::into_account_id(receiver_address)); - - env.parachain_state_mut(|| { - 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::TransferAssets { - currency: general_currency_id, - 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: Some(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::Root.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(), - )); - }); - - env.pass(Blocks::ByNumber(1)); - - env.state(|_| { - assert_eq!( - orml_tokens::Pallet::::free_balance(Usd18.id(), &derived_receiver_account), - transfer_amount - ); - }); -} diff --git a/runtime/integration-tests/src/cases/restricted_transfers.rs b/runtime/integration-tests/src/cases/restricted_transfers.rs index e6217da721..bb1cfe7466 100644 --- a/runtime/integration-tests/src/cases/restricted_transfers.rs +++ b/runtime/integration-tests/src/cases/restricted_transfers.rs @@ -21,7 +21,6 @@ use cfg_types::{ }; use cumulus_primitives_core::WeightLimit; use frame_support::{assert_noop, assert_ok, dispatch::RawOrigin, traits::PalletInfo}; -use pallet_liquidity_pools_gateway_queue::MessageNonceStore; use runtime_common::remarks::Remark; use sp_runtime::traits::Zero; use staging_xcm::{ @@ -358,7 +357,6 @@ mod xcm { mod eth_address { use super::*; - use crate::utils::last_event; const TRANSFER: u32 = 10; const CHAIN_ID: u64 = 1; @@ -425,22 +423,6 @@ mod eth_address { curr_contract, curr.val(TRANSFER), )); - - assert_ok!( - pallet_liquidity_pools_gateway_queue::Pallet::::process_message( - RawOrigin::Signed(Keyring::Alice.into()).into(), - MessageNonceStore::::get(), - ) - ); - - let last_event = last_event::>(); - assert!(matches!( - last_event, - pallet_liquidity_pools_gateway_queue::Event::::MessageExecutionFailure { - error, - .. - } if error == pallet_liquidity_pools_gateway::Error::::RouterNotFound.into() - )); }); } } diff --git a/runtime/integration-tests/src/cases/routers.rs b/runtime/integration-tests/src/cases/routers.rs index bdaef239f7..e4c13f0d55 100644 --- a/runtime/integration-tests/src/cases/routers.rs +++ b/runtime/integration-tests/src/cases/routers.rs @@ -1,94 +1,190 @@ use cfg_primitives::Balance; -use cfg_traits::liquidity_pools::MessageProcessor; -use cfg_types::domain_address::Domain; -use frame_support::{assert_ok, dispatch::RawOrigin}; -use liquidity_pools_gateway_routers::{ - AxelarEVMRouter, DomainRouter, EVMDomain, EVMRouter, FeeValues, +use cfg_traits::liquidity_pools::{LPEncoding, MessageProcessor}; +use cfg_types::{ + domain_address::{Domain, DomainAddress}, + EVMChainId, }; +use ethabi::{Function, Param, ParamType, Token}; +use frame_support::{assert_ok, dispatch::RawOrigin}; +use orml_traits::MultiCurrency; +use pallet_axelar_router::{AxelarConfig, DomainConfig, EvmConfig, FeeValues}; use pallet_liquidity_pools::Message; use pallet_liquidity_pools_gateway::message::GatewayMessage; -use polkadot_core_primitives::BlakeTwo256; -use runtime_common::gateway::get_gateway_h160_account; -use sp_core::{Get, H160, U256}; -use sp_runtime::traits::Hash; +use runtime_common::{ + account_conversion::AccountConverter, evm::precompile::LP_AXELAR_GATEWAY, + gateway::get_gateway_h160_account, +}; +use sp_core::{Get, H160, H256, U256}; +use sp_runtime::traits::{BlakeTwo256, Hash}; use crate::{ config::Runtime, - env::Env, + env::{Blocks, Env}, envs::runtime_env::RuntimeEnv, - utils::{self, accounts::Keyring, currency::cfg, genesis, genesis::Genesis}, + utils::{ + self, + currency::{cfg, usd18, CurrencyInfo, Usd18}, + genesis, + genesis::Genesis, + }, }; -const INITIAL: Balance = 100; -const TEST_DOMAIN: Domain = Domain::EVM(1); +mod axelar_evm { + use super::*; -const AXELAR_CONTRACT_ADDRESS: H160 = H160::repeat_byte(1); -const AXELAR_CONTRACT_CODE: &[u8] = &[0, 0, 0]; + const CHAIN_NAME: &str = "Ethereum"; + const INITIAL: Balance = 100; + const CHAIN_ID: EVMChainId = 1; + const TEST_DOMAIN: Domain = Domain::EVM(CHAIN_ID); + const AXELAR_CONTRACT_CODE: &[u8] = &[0, 0, 0]; + const AXELAR_CONTRACT_ADDRESS: H160 = H160::repeat_byte(1); + const LP_CONTRACT_ADDRESS: H160 = H160::repeat_byte(2); + const SOURCE_ADDRESS: H160 = H160::repeat_byte(3); + const RECEIVER_ADDRESS: H160 = H160::repeat_byte(4); + const TRANSFER_AMOUNT: Balance = usd18(100); -fn environment_for_evm() -> RuntimeEnv { - let mut env = RuntimeEnv::::from_parachain_storage( - Genesis::default() - .add(genesis::balances::(cfg(1_000))) - .storage(), - ); + fn base_config() -> AxelarConfig { + AxelarConfig { + liquidity_pools_contract_address: LP_CONTRACT_ADDRESS, + domain: DomainConfig::Evm(EvmConfig { + chain_id: CHAIN_ID, + target_contract_address: AXELAR_CONTRACT_ADDRESS, + target_contract_hash: BlakeTwo256::hash_of(&AXELAR_CONTRACT_CODE), + fee_values: FeeValues { + value: U256::from(0), + gas_limit: U256::from(T::config().gas_transaction_call + 1_000_000), + gas_price: U256::from(10), + }, + }), + } + } - env.parachain_state_mut(|| { - pallet_evm::AccountCodes::::insert(AXELAR_CONTRACT_ADDRESS, AXELAR_CONTRACT_CODE); + fn send_ethereum_message_through_axelar_to_centrifuge(message: Message) { + #[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: Some(false), + state_mutability: Default::default(), + } + .encode_input(&[ + Token::FixedBytes(H256::from_low_u64_be(5678).0.to_vec()), + Token::String(CHAIN_NAME.into()), + Token::String(String::from_utf8(SOURCE_ADDRESS.as_fixed_bytes().to_vec()).unwrap()), + Token::Bytes(message.serialize()), + ]) + .expect("cannot encode input for test contract function"); - utils::evm::mint_balance_into_derived_account::(AXELAR_CONTRACT_ADDRESS, cfg(1)); - utils::evm::mint_balance_into_derived_account::(get_gateway_h160_account::(), cfg(1)); - }); + assert_ok!(pallet_evm::Pallet::::call( + RawOrigin::Root.into(), + LP_CONTRACT_ADDRESS, + H160::from_low_u64_be(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(), + )); + } - env -} + #[test_runtimes(all)] + fn send() { + let mut env = RuntimeEnv::::default(); -fn check_submission(mut env: impl Env, domain_router: DomainRouter) { - env.parachain_state_mut(|| { - assert_ok!( - pallet_liquidity_pools_gateway::Pallet::::set_domain_router( + env.parachain_state_mut(|| { + pallet_evm::AccountCodes::::insert(AXELAR_CONTRACT_ADDRESS, AXELAR_CONTRACT_CODE); + + utils::evm::mint_balance_into_derived_account::(AXELAR_CONTRACT_ADDRESS, cfg(1)); + utils::evm::mint_balance_into_derived_account::( + get_gateway_h160_account::(), + cfg(1), + ); + + assert_ok!(pallet_axelar_router::Pallet::::set_config( RawOrigin::Root.into(), - TEST_DOMAIN, - domain_router, - ) - ); + Vec::from(CHAIN_NAME).try_into().unwrap(), + Box::new(base_config::()), + )); - let msg = Message::TransferAssets { - currency: 0, - receiver: Keyring::Bob.into(), - amount: 1_000, - }; + let gateway_message = GatewayMessage::Outbound { + sender: T::Sender::get(), + destination: TEST_DOMAIN, + message: Message::Invalid, + }; - let gateway_message = GatewayMessage::Outbound { - sender: ::Sender::get(), - destination: TEST_DOMAIN, - message: msg.clone(), - }; + assert_ok!(pallet_liquidity_pools_gateway::Pallet::::process(gateway_message).0); + }); + } - let (res, _) = as MessageProcessor>::process( - gateway_message, + #[test_runtimes(all)] + fn receive() { + let mut env = RuntimeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::assets::([(Usd18.id(), &Usd18.metadata())])) + .storage(), ); - assert_ok!(res); - }); -} -#[test_runtimes(all)] -fn submit_by_axelar_evm() { - let router = DomainRouter::AxelarEVM(AxelarEVMRouter:: { - router: EVMRouter { - evm_domain: EVMDomain { - target_contract_address: AXELAR_CONTRACT_ADDRESS, - target_contract_hash: BlakeTwo256::hash_of(&AXELAR_CONTRACT_CODE), - fee_values: FeeValues { - value: U256::from(0), - gas_limit: U256::from(T::config().gas_transaction_call + 1_000_000), - gas_price: U256::from(10), - }, - }, - _marker: Default::default(), - }, - evm_chain: Vec::from(b"ethereum").try_into().unwrap(), - liquidity_pools_contract_address: H160::from_low_u64_be(2), - }); - - check_submission(environment_for_evm::(), router); + let derived_receiver_account = + env.parachain_state(|| AccountConverter::evm_address_to_account::(RECEIVER_ADDRESS)); + + env.parachain_state_mut(|| { + pallet_evm::AccountCodes::::insert(AXELAR_CONTRACT_ADDRESS, AXELAR_CONTRACT_CODE); + + utils::evm::mint_balance_into_derived_account::(LP_CONTRACT_ADDRESS, cfg(1)); + + assert_ok!(pallet_axelar_router::Pallet::::set_config( + RawOrigin::Root.into(), + Vec::from(CHAIN_NAME).try_into().unwrap(), + Box::new(base_config::()), + )); + + let message = Message::TransferAssets { + currency: pallet_liquidity_pools::Pallet::::try_get_general_index(Usd18.id()) + .unwrap(), + receiver: derived_receiver_account.clone().into(), + amount: TRANSFER_AMOUNT, + }; + + pallet_liquidity_pools_gateway::Pallet::::add_instance( + RawOrigin::Root.into(), + DomainAddress::EVM(CHAIN_ID, SOURCE_ADDRESS.0), + ) + .unwrap(); + + send_ethereum_message_through_axelar_to_centrifuge::(message); + }); + + env.pass(Blocks::ByNumber(1)); + + env.parachain_state(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(Usd18.id(), &derived_receiver_account), + TRANSFER_AMOUNT + ); + }); + } } diff --git a/runtime/integration-tests/src/config.rs b/runtime/integration-tests/src/config.rs index 3fa6da2632..6873cbcbc8 100644 --- a/runtime/integration-tests/src/config.rs +++ b/runtime/integration-tests/src/config.rs @@ -20,7 +20,6 @@ use frame_support::{ traits::{IsSubType, IsType, OriginTrait}, Parameter, }; -use liquidity_pools_gateway_routers::DomainRouter; use pallet_liquidity_pools::Message; use pallet_liquidity_pools_gateway::message::GatewayMessage; use pallet_transaction_payment::CurrencyAdapter; @@ -34,6 +33,7 @@ use runtime_common::{ oracle::Feeder, remarks::Remark, rewards::SingleCurrencyMovement, + routing::RouterId, }; use sp_core::{sr25519::Public, H256}; use sp_runtime::{ @@ -136,8 +136,8 @@ pub trait Runtime: PoolId = PoolId, TrancheId = TrancheId, BalanceRatio = Ratio, - > + pallet_liquidity_pools_gateway::Config, Message = Message> - + pallet_liquidity_pools_gateway_queue::Config> + > + pallet_liquidity_pools_gateway::Config + + pallet_liquidity_pools_gateway_queue::Config> + pallet_xcm_transactor::Config + pallet_ethereum::Config + pallet_ethereum_transaction::Config @@ -183,7 +183,7 @@ pub trait Runtime: CurrencyId = CurrencyId, Balance = Balance, Rewards = pallet_rewards::Pallet, - > + axelar_gateway_precompile::Config + > + pallet_axelar_router::Config + pallet_token_mux::Config< BalanceIn = Balance, BalanceOut = Balance, diff --git a/runtime/integration-tests/src/env.rs b/runtime/integration-tests/src/env.rs index 3c55821356..ef78f6ca2c 100644 --- a/runtime/integration-tests/src/env.rs +++ b/runtime/integration-tests/src/env.rs @@ -125,7 +125,7 @@ pub trait Env: Default { if let Blocks::UntilEvent { event, limit } = blocks.clone() { if !found_event { - panic!("Event {event:?} was not found producing {limit} blocks"); + panic!("The event:\n{event:#?}\nwas not found producing {limit} blocks"); } } }