diff --git a/pallets/order-book/src/lib.rs b/pallets/order-book/src/lib.rs index b5ad7daf8c..2bbed407ac 100644 --- a/pallets/order-book/src/lib.rs +++ b/pallets/order-book/src/lib.rs @@ -362,9 +362,27 @@ pub mod pallet { price: T::SellRatio, ) -> DispatchResult { let account_id = ensure_signed(origin)?; - Self::place_order( - account_id, asset_in, asset_out, buy_amount, price, buy_amount, + + Self::inner_place_order( + account_id, + asset_in, + asset_out, + buy_amount, + price, + buy_amount, + |order| { + let min_amount = TradingPair::::get(&asset_in, &asset_out)?; + Self::is_valid_order( + order.asset_in_id, + order.asset_out_id, + order.buy_amount, + order.max_sell_rate, + order.min_fullfillment_amount, + min_amount, + ) + }, )?; + Ok(()) } @@ -378,13 +396,29 @@ pub mod pallet { price: T::SellRatio, ) -> DispatchResult { let account_id = ensure_signed(origin)?; - let order = >::get(order_id)?; - ensure!( - account_id == order.placing_account, - Error::::Unauthorised - ); - >::update_order( - account_id, order_id, buy_amount, price, buy_amount, + Self::inner_update_order( + account_id.clone(), + order_id, + buy_amount, + price, + buy_amount, + |order| { + ensure!( + account_id == order.placing_account, + Error::::Unauthorised + ); + + let min_amount = + TradingPair::::get(&order.asset_in_id, &order.asset_out_id)?; + Self::is_valid_order( + order.asset_in_id, + order.asset_out_id, + order.buy_amount, + order.max_sell_rate, + order.min_fullfillment_amount, + min_amount, + ) + }, ) } @@ -584,27 +618,15 @@ pub mod pallet { } } - impl TokenSwaps for Pallet - where - ::Hash: PartialEq<::Hash>, - { - type Balance = T::Balance; - type CurrencyId = T::AssetCurrencyId; - type OrderId = T::OrderIdNonce; - type SellRatio = T::SellRatio; - - /// Creates an order. - /// Verify funds available in, and reserve for both chains fee currency - /// for storage fee, and amount of outgoing currency as determined by - /// the buy amount and price. - fn place_order( - account: T::AccountId, + impl Pallet { + fn is_valid_order( currency_in: T::AssetCurrencyId, currency_out: T::AssetCurrencyId, buy_amount: T::Balance, sell_rate_limit: T::SellRatio, min_fullfillment_amount: T::Balance, - ) -> Result { + min_order_amount: T::Balance, + ) -> DispatchResult { ensure!(currency_in != currency_out, Error::::ConflictingAssetIds); ensure!( @@ -621,107 +643,31 @@ pub mod pallet { Error::::InvalidMaxPrice ); - >::try_mutate(|n| { - *n = n.ensure_add(T::OrderIdNonce::one())?; - Ok::<_, DispatchError>(()) - })?; - ensure!( buy_amount >= min_fullfillment_amount, Error::::InvalidBuyAmount ); - Self::is_valid_min_order(currency_in, currency_out, buy_amount)?; - - let max_sell_amount = - Self::convert_with_ratio(currency_in, currency_out, sell_rate_limit, buy_amount)?; - - T::TradeableAsset::hold(currency_out, &account, max_sell_amount)?; - - let order_id = >::get(); - let new_order = Order { - order_id, - placing_account: account.clone(), - asset_in_id: currency_in, - asset_out_id: currency_out, - buy_amount, - max_sell_rate: sell_rate_limit, - initial_buy_amount: buy_amount, - min_fullfillment_amount, - max_sell_amount, - }; - - >::try_mutate(currency_in, currency_out, |orders| { - orders - .try_push(order_id) - .map_err(|_| Error::::AssetPairOrdersOverflow) - })?; - - >::insert(order_id, new_order.clone()); - >::insert(&account, order_id, new_order); - Self::deposit_event(Event::OrderCreated { - creator_account: account, - sell_rate_limit, - order_id, - buy_amount, - currency_in, - currency_out, - min_fullfillment_amount, - }); - Ok(order_id) - } - - /// Cancel an existing order. - /// Unreserve currency reserved for trade as well storage fee. - fn cancel_order(order: Self::OrderId) -> DispatchResult { - let order = >::get(order)?; - let account_id = order.placing_account.clone(); - - Self::unreserve_order(&order)?; - Self::remove_order(order.order_id)?; - Self::deposit_event(Event::OrderCancelled { - account: account_id, - order_id: order.order_id, - }); + ensure!( + buy_amount >= min_order_amount, + Error::::InsufficientOrderSize + ); Ok(()) } - /// Update an existing order. - /// Update outgoing asset currency reserved to match new amount or price - /// if either have changed. - fn update_order( + fn inner_update_order( account: T::AccountId, - order_id: Self::OrderId, + order_id: T::OrderIdNonce, buy_amount: T::Balance, sell_rate_limit: T::SellRatio, min_fullfillment_amount: T::Balance, + validate: impl FnOnce(&OrderOf) -> DispatchResult, ) -> DispatchResult { - ensure!( - buy_amount != T::Balance::zero(), - Error::::InvalidBuyAmount - ); - - ensure!( - sell_rate_limit != T::SellRatio::zero(), - Error::::InvalidMaxPrice - ); - - ensure!( - min_fullfillment_amount != ::zero(), - Error::::InvalidMinimumFulfillment - ); - - ensure!( - buy_amount >= min_fullfillment_amount, - Error::::InvalidBuyAmount - ); - let max_sell_amount = >::try_mutate_exists( order_id, |maybe_order| -> Result { let mut order = maybe_order.as_mut().ok_or(Error::::OrderNotFound)?; - Self::is_valid_min_order(order.asset_in_id, order.asset_out_id, buy_amount)?; let max_sell_amount = Self::convert_with_ratio( order.asset_in_id, @@ -758,6 +704,8 @@ pub mod pallet { order.min_fullfillment_amount = min_fullfillment_amount; order.max_sell_amount = max_sell_amount; + validate(order)?; + Ok(max_sell_amount) }, )?; @@ -785,6 +733,155 @@ pub mod pallet { Ok(()) } + fn inner_place_order( + account: T::AccountId, + currency_in: T::AssetCurrencyId, + currency_out: T::AssetCurrencyId, + buy_amount: T::Balance, + sell_rate_limit: T::SellRatio, + min_fullfillment_amount: T::Balance, + validate: impl FnOnce(&OrderOf) -> DispatchResult, + ) -> Result { + >::try_mutate(|n| { + *n = n.ensure_add(T::OrderIdNonce::one())?; + Ok::<_, DispatchError>(()) + })?; + + let max_sell_amount = + Self::convert_with_ratio(currency_in, currency_out, sell_rate_limit, buy_amount)?; + + T::TradeableAsset::hold(currency_out, &account, max_sell_amount)?; + + let order_id = >::get(); + let new_order = Order { + order_id, + placing_account: account.clone(), + asset_in_id: currency_in, + asset_out_id: currency_out, + buy_amount, + max_sell_rate: sell_rate_limit, + initial_buy_amount: buy_amount, + min_fullfillment_amount, + max_sell_amount, + }; + + validate(&new_order)?; + + >::try_mutate(currency_in, currency_out, |orders| { + orders + .try_push(order_id) + .map_err(|_| Error::::AssetPairOrdersOverflow) + })?; + + >::insert(order_id, new_order.clone()); + >::insert(&account, order_id, new_order); + Self::deposit_event(Event::OrderCreated { + creator_account: account, + sell_rate_limit, + order_id, + buy_amount, + currency_in, + currency_out, + min_fullfillment_amount, + }); + + Ok(order_id) + } + } + + impl TokenSwaps for Pallet + where + ::Hash: PartialEq<::Hash>, + { + type Balance = T::Balance; + type CurrencyId = T::AssetCurrencyId; + type OrderId = T::OrderIdNonce; + type SellRatio = T::SellRatio; + + /// Creates an order. + /// Verify funds available in, and reserve for both chains fee currency + /// for storage fee, and amount of outgoing currency as determined by + /// the buy amount and price. + fn place_order( + account: T::AccountId, + currency_in: T::AssetCurrencyId, + currency_out: T::AssetCurrencyId, + buy_amount: T::Balance, + sell_rate_limit: T::SellRatio, + min_fullfillment_amount: T::Balance, + ) -> Result { + Self::inner_place_order( + account, + currency_in, + currency_out, + buy_amount, + sell_rate_limit, + min_fullfillment_amount, + |order| { + // We only check if the trading pair exists not if the minimum amount is + // reached. + let _min_amount = TradingPair::::get(¤cy_in, ¤cy_out)?; + Self::is_valid_order( + order.asset_in_id, + order.asset_out_id, + order.buy_amount, + order.max_sell_rate, + order.min_fullfillment_amount, + T::Balance::zero(), + ) + }, + ) + } + + /// Cancel an existing order. + /// Unreserve currency reserved for trade as well storage fee. + fn cancel_order(order: Self::OrderId) -> DispatchResult { + let order = >::get(order)?; + let account_id = order.placing_account.clone(); + + Self::unreserve_order(&order)?; + Self::remove_order(order.order_id)?; + Self::deposit_event(Event::OrderCancelled { + account: account_id, + order_id: order.order_id, + }); + + Ok(()) + } + + /// Update an existing order. + /// Update outgoing asset currency reserved to match new amount or price + /// if either have changed. + fn update_order( + account: T::AccountId, + order_id: Self::OrderId, + buy_amount: T::Balance, + sell_rate_limit: T::SellRatio, + min_fullfillment_amount: T::Balance, + ) -> DispatchResult { + Self::inner_update_order( + account, + order_id, + buy_amount, + sell_rate_limit, + min_fullfillment_amount, + |order| { + // We only check if the trading pair exists not if the minimum amount is + // reached. + let _min_amount = + TradingPair::::get(&order.asset_in_id, &order.asset_out_id)?; + Self::is_valid_order( + order.asset_in_id, + order.asset_out_id, + order.buy_amount, + order.max_sell_rate, + order.min_fullfillment_amount, + T::Balance::zero(), + ) + }, + ) + } + /// Check whether an order is active. fn is_active(order: Self::OrderId) -> bool { >::contains_key(order) diff --git a/pallets/order-book/src/tests.rs b/pallets/order-book/src/tests.rs index 0a6799dd1b..f020452040 100644 --- a/pallets/order-book/src/tests.rs +++ b/pallets/order-book/src/tests.rs @@ -528,16 +528,29 @@ fn ensure_nonce_updates_order_correctly() { } #[test] -fn place_order_requires_min_buy() { +fn place_order_requires_no_min_buy() { + new_test_ext().execute_with(|| { + assert_ok!(OrderBook::place_order( + ACCOUNT_0, + DEV_AUSD_CURRENCY_ID, + DEV_USDT_CURRENCY_ID, + 1 * CURRENCY_AUSD_DECIMALS, + Rate::checked_from_rational(3u32, 2u32).unwrap(), + 1 * CURRENCY_AUSD_DECIMALS, + ),); + }) +} + +#[test] +fn create_order_requires_min_buy() { new_test_ext().execute_with(|| { assert_err!( - OrderBook::place_order( - ACCOUNT_0, + OrderBook::create_order( + RuntimeOrigin::signed(ACCOUNT_0), DEV_AUSD_CURRENCY_ID, DEV_USDT_CURRENCY_ID, 1 * CURRENCY_AUSD_DECIMALS, Rate::checked_from_rational(3u32, 2u32).unwrap(), - 1 * CURRENCY_AUSD_DECIMALS, ), Error::::InsufficientOrderSize ); @@ -890,7 +903,29 @@ fn update_order_works_with_order_decrease() { } #[test] -fn update_order_requires_min_buy() { +fn update_order_requires_no_min_buy() { + new_test_ext().execute_with(|| { + assert_ok!(OrderBook::place_order( + ACCOUNT_0, + DEV_AUSD_CURRENCY_ID, + DEV_USDT_CURRENCY_ID, + 15 * CURRENCY_AUSD_DECIMALS, + Rate::checked_from_rational(3u32, 2u32).unwrap(), + 5 * CURRENCY_AUSD_DECIMALS + )); + let (order_id, _) = get_account_orders(ACCOUNT_0).unwrap()[0]; + assert_ok!(OrderBook::update_order( + ACCOUNT_0, + order_id, + 1 * CURRENCY_AUSD_DECIMALS, + Rate::checked_from_integer(1u32).unwrap(), + 1 * CURRENCY_AUSD_DECIMALS + ),); + }) +} + +#[test] +fn user_update_order_requires_min_buy() { new_test_ext().execute_with(|| { assert_ok!(OrderBook::place_order( ACCOUNT_0, @@ -902,12 +937,11 @@ fn update_order_requires_min_buy() { )); let (order_id, _) = get_account_orders(ACCOUNT_0).unwrap()[0]; assert_err!( - OrderBook::update_order( - ACCOUNT_0, + OrderBook::user_update_order( + RuntimeOrigin::signed(ACCOUNT_0), order_id, 1 * CURRENCY_AUSD_DECIMALS, Rate::checked_from_integer(1u32).unwrap(), - 1 * CURRENCY_AUSD_DECIMALS ), Error::::InsufficientOrderSize ); diff --git a/runtime/altair/src/evm.rs b/runtime/altair/src/evm.rs index 347e58c406..3276d60d25 100644 --- a/runtime/altair/src/evm.rs +++ b/runtime/altair/src/evm.rs @@ -10,9 +10,8 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_primitives::{AccountId, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO}; +use cfg_primitives::{EnsureRootOr, HalfOfCouncil, MAXIMUM_BLOCK_WEIGHT, NORMAL_DISPATCH_RATIO}; use frame_support::{parameter_types, traits::FindAuthor, weights::Weight, ConsensusEngineId}; -use frame_system::EnsureRoot; use pallet_evm::{EnsureAddressRoot, EnsureAddressTruncated}; use runtime_common::{ account_conversion::AccountConverter, @@ -22,7 +21,7 @@ use sp_core::{crypto::ByteArray, H160, U256}; use sp_runtime::Permill; use sp_std::marker::PhantomData; -use crate::{Aura, Runtime, RuntimeEvent}; +use crate::{Aura, LocationToAccountId, Runtime, RuntimeEvent}; /// To create valid Ethereum-compatible blocks, we need a 20-byte /// "author" for the block. Since that author is purely informational, @@ -48,7 +47,7 @@ parameter_types! { } impl pallet_evm::Config for Runtime { - type AddressMapping = AccountConverter; + type AddressMapping = AccountConverter; type BlockGasLimit = BlockGasLimit; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; type CallOrigin = EnsureAddressRoot; @@ -91,6 +90,6 @@ impl pallet_ethereum_transaction::Config for Runtime { } impl axelar_gateway_precompile::Config for Runtime { - type AdminOrigin = EnsureRoot; + type AdminOrigin = EnsureRootOr; type RuntimeEvent = RuntimeEvent; } diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 1b80d731d6..b0398f15ba 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -19,10 +19,11 @@ // Allow things like `1 * CFG` #![allow(clippy::identity_op)] +use ::xcm::v3::{MultiAsset, MultiLocation}; pub use cfg_primitives::{constants::*, types::*}; use cfg_traits::{ OrderManager, Permissions as PermissionsT, PoolNAV, PoolUpdateGuard, PreConditions, - TrancheCurrency as _, + TrancheCurrency as _, TryConvert, }; pub use cfg_types::tokens::CurrencyId; use cfg_types::{ @@ -72,8 +73,11 @@ pub use pallet_timestamp::Call as TimestampCall; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo}; use polkadot_runtime_common::{prod_or_fast, BlockHashCount, SlowAdjustingFeeUpdate}; -use runtime_common::fees::{DealWithFees, WeightToFee}; pub use runtime_common::*; +use runtime_common::{ + account_conversion::AccountConverter, + fees::{DealWithFees, WeightToFee}, +}; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_core::{OpaqueMetadata, H160, H256, U256}; @@ -1385,7 +1389,7 @@ impl pallet_xcm_transactor::Config for Runtime { } impl pallet_liquidity_pools::Config for Runtime { - type AccountConverter = AccountConverter; + type AccountConverter = AccountConverter; type AdminOrigin = EnsureRoot; type AssetRegistry = OrmlAssetRegistry; type Balance = Balance; @@ -1972,7 +1976,7 @@ mod __runtime_api_use { #[cfg(not(feature = "disable-runtime-api"))] use __runtime_api_use::*; use cfg_types::domain_address::Domain; -use runtime_common::{account_conversion::AccountConverter, xcm::AccountIdToMultiLocation}; +use runtime_common::xcm::AccountIdToMultiLocation; #[cfg(not(feature = "disable-runtime-api"))] impl_runtime_apis! { @@ -2184,6 +2188,11 @@ impl_runtime_apis! { } } + impl runtime_common::apis::AccountConversionApi for Runtime { + fn conversion_of(location: MultiLocation) -> Option { + AccountConverter::::try_convert(location).ok() + } + } // Frontier APIs impl fp_rpc::EthereumRuntimeRPCApi for Runtime { diff --git a/runtime/centrifuge/src/evm.rs b/runtime/centrifuge/src/evm.rs index 7ad5f52b67..acae21fcf9 100644 --- a/runtime/centrifuge/src/evm.rs +++ b/runtime/centrifuge/src/evm.rs @@ -22,7 +22,7 @@ use sp_core::{crypto::ByteArray, H160, U256}; use sp_runtime::Permill; use sp_std::marker::PhantomData; -use crate::Aura; +use crate::{Aura, LocationToAccountId}; // To create valid Ethereum-compatible blocks, we need a 20-byte // "author" for the block. Since that author is purely informational, @@ -48,7 +48,7 @@ parameter_types! { } impl pallet_evm::Config for crate::Runtime { - type AddressMapping = AccountConverter; + type AddressMapping = AccountConverter; type BlockGasLimit = BlockGasLimit; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; type CallOrigin = EnsureAddressRoot; diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index bf75d84b6b..5128f93ac8 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -23,7 +23,7 @@ pub use cfg_primitives::{constants::*, types::*}; use cfg_traits::{ liquidity_pools::{InboundQueue, OutboundQueue}, OrderManager, Permissions as PermissionsT, PoolNAV, PoolUpdateGuard, PreConditions, - TrancheCurrency as _, + TrancheCurrency as _, TryConvert, }; use cfg_types::{ consts::pools::{MaxTrancheNameLengthBytes, MaxTrancheSymbolLengthBytes}, @@ -434,7 +434,7 @@ parameter_types! { } impl pallet_liquidity_pools::Config for Runtime { - type AccountConverter = AccountConverter; + type AccountConverter = AccountConverter; type AdminOrigin = EnsureRootOr; type AssetRegistry = OrmlAssetRegistry; type Balance = Balance; @@ -2047,6 +2047,7 @@ impl fp_rpc::ConvertTransaction for TransactionConv #[cfg(not(feature = "disable-runtime-api"))] mod __runtime_api_use { pub use pallet_loans::entities::loans::ActiveLoanInfo; + pub use runtime_common::account_conversion::AccountConverter; } #[cfg(not(feature = "disable-runtime-api"))] @@ -2262,6 +2263,12 @@ impl_runtime_apis! { } } + impl runtime_common::apis::AccountConversionApi for Runtime { + fn conversion_of(location: ::xcm::v3::MultiLocation) -> Option { + AccountConverter::::try_convert(location).ok() + } + } + // Frontier APIs impl fp_rpc::EthereumRuntimeRPCApi for Runtime { fn chain_id() -> u64 { diff --git a/runtime/common/src/account_conversion.rs b/runtime/common/src/account_conversion.rs index 4a0ef9afc0..0cf2fdc65d 100644 --- a/runtime/common/src/account_conversion.rs +++ b/runtime/common/src/account_conversion.rs @@ -11,16 +11,18 @@ // GNU General Public License for more details. use cfg_primitives::AccountId; +use cfg_traits::TryConvert; use cfg_types::domain_address::DomainAddress; use pallet_evm::AddressMapping; use sp_core::{Get, H160}; use sp_runtime::traits::Convert; +use xcm::*; /// Common converter code for translating accounts across different /// domains and chains. -pub struct AccountConverter(core::marker::PhantomData); +pub struct AccountConverter(core::marker::PhantomData<(R, XcmConverter)>); -impl AccountConverter { +impl AccountConverter { /// Converts an EVM address from a given chain into a local AccountId pub fn convert_evm_address(chain_id: u64, address: [u8; 20]) -> AccountId { // We use a custom encoding here rather than relying on @@ -40,7 +42,7 @@ impl AccountConverter { } // Implement EVM account conversion using our shared conversion code -impl AddressMapping for AccountConverter +impl AddressMapping for AccountConverter where R: pallet_evm_chain_id::Config, { @@ -51,7 +53,7 @@ where } // Implement liquidityPools account conversion using our shared conversion code -impl Convert for AccountConverter { +impl Convert for AccountConverter { fn convert(domain_address: DomainAddress) -> AccountId { match domain_address { DomainAddress::Centrifuge(addr) => AccountId::new(addr), @@ -60,6 +62,34 @@ impl Convert for AccountConverter { } } +impl TryConvert for AccountConverter +where + XcmConverter: xcm_executor::traits::Convert, +{ + type Error = v3::MultiLocation; + + fn try_convert(location: v3::MultiLocation) -> Result { + // Try xcm logic first + match XcmConverter::convert_ref(location).ok() { + Some(acc) => Ok(acc), + None => { + // match EVM logic + match location { + v3::MultiLocation { + parents: 0, + interior: + v3::Junctions::X1(v3::Junction::AccountKey20 { + network: Some(v3::NetworkId::Ethereum { chain_id }), + key, + }), + } => Ok(Self::convert_evm_address(chain_id, key)), + _ => Err(location), + } + } + } + } +} + #[cfg(test)] mod tests { use hex_literal::hex; @@ -71,7 +101,7 @@ mod tests { let address = [0x42; 20]; let chain_id = 0xDADB0D; let domain_address = DomainAddress::EVM(chain_id, address); - let account: AccountId = AccountConverter::<()>::convert(domain_address); + let account: AccountId = AccountConverter::<(), ()>::convert(domain_address); let expected = AccountId::new(hex![ "42424242424242424242424242424242424242420000000000DADB0D45564d00" ]); @@ -84,7 +114,7 @@ mod tests { let address = [0x42; 32]; let expected = AccountId::new(address); let domain_address = DomainAddress::Centrifuge(address); - let account: AccountId = AccountConverter::<()>::convert(domain_address); + let account: AccountId = AccountConverter::<(), ()>::convert(domain_address); assert_eq!(account, expected); } diff --git a/runtime/common/src/apis/account_conversion.rs b/runtime/common/src/apis/account_conversion.rs new file mode 100644 index 0000000000..d89499c419 --- /dev/null +++ b/runtime/common/src/apis/account_conversion.rs @@ -0,0 +1,25 @@ +// 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 codec::Codec; +use sp_api::decl_runtime_apis; +use xcm::v3::MultiLocation; + +decl_runtime_apis! { + /// Runtime Api for the pallet-anchors, to be implemented + /// by and for a specific runtime that uses that pallet. + pub trait AccountConversionApi + where + AccountId: Codec + { + fn conversion_of(location: MultiLocation) -> Option; + } +} diff --git a/runtime/common/src/apis/mod.rs b/runtime/common/src/apis/mod.rs index 810bd7b330..58fda594d7 100644 --- a/runtime/common/src/apis/mod.rs +++ b/runtime/common/src/apis/mod.rs @@ -11,12 +11,14 @@ // GNU General Public License for more details. //! Runtime apis useful in the Centrifuge ecosystem +pub use account_conversion::*; pub use anchors::*; pub use investments::*; pub use loans::*; pub use pools::*; pub use rewards::*; +mod account_conversion; mod anchors; mod investments; mod loans; diff --git a/runtime/development/src/evm.rs b/runtime/development/src/evm.rs index 7e526efbd5..5546d1803f 100644 --- a/runtime/development/src/evm.rs +++ b/runtime/development/src/evm.rs @@ -22,7 +22,7 @@ use sp_core::{crypto::ByteArray, H160, U256}; use sp_runtime::Permill; use sp_std::marker::PhantomData; -use crate::{Aura, Runtime, RuntimeEvent}; +use crate::{Aura, LocationToAccountId, Runtime, RuntimeEvent}; // To create valid Ethereum-compatible blocks, we need a 20-byte // "author" for the block. Since that author is purely informational, @@ -48,7 +48,7 @@ parameter_types! { } impl pallet_evm::Config for Runtime { - type AddressMapping = AccountConverter; + type AddressMapping = AccountConverter; type BlockGasLimit = BlockGasLimit; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; type CallOrigin = EnsureAddressTruncated; diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 728e566627..1c7378d0c1 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -25,7 +25,7 @@ pub use cfg_primitives::{ }; use cfg_traits::{ OrderManager, Permissions as PermissionsT, PoolNAV, PoolUpdateGuard, PreConditions, - TrancheCurrency as _, + TrancheCurrency as _, TryConvert as _, }; use cfg_types::{ consts::pools::*, @@ -1549,7 +1549,7 @@ parameter_types! { } impl pallet_liquidity_pools::Config for Runtime { - type AccountConverter = AccountConverter; + type AccountConverter = AccountConverter; type AdminOrigin = EnsureRoot; type AssetRegistry = OrmlAssetRegistry; type Balance = Balance; @@ -2330,6 +2330,12 @@ impl_runtime_apis! { } } + impl runtime_common::apis::AccountConversionApi for Runtime { + fn conversion_of(location: MultiLocation) -> Option { + AccountConverter::::try_convert(location).ok() + } + } + // Frontier APIs impl fp_rpc::EthereumRuntimeRPCApi for Runtime { fn chain_id() -> u64 { diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools.rs index d7b6080914..b19d2371a9 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/liquidity_pools.rs @@ -47,9 +47,9 @@ use cfg_types::{ }; use codec::Encode; use development_runtime::{ - Balances, Investments, LiquidityPools, LiquidityPoolsGateway, Loans, OrmlAssetRegistry, - OrmlTokens, Permissions, PoolSystem, Runtime as DevelopmentRuntime, RuntimeOrigin, System, - TreasuryAccount, XTokens, XcmTransactor, + Balances, Investments, LiquidityPools, LiquidityPoolsGateway, Loans, LocationToAccountId, + OrmlAssetRegistry, OrmlTokens, Permissions, PoolSystem, Runtime as DevelopmentRuntime, + RuntimeOrigin, System, TreasuryAccount, XTokens, XcmTransactor, }; use frame_support::{ assert_noop, assert_ok, @@ -252,7 +252,9 @@ fn update_member() { assert_ok!(Permissions::add( RuntimeOrigin::signed(ALICE.into()), Role::PoolRole(PoolRole::InvestorAdmin), - AccountConverter::::convert(new_member.clone()), + AccountConverter::::convert( + new_member.clone() + ), PermissionScope::Pool(pool_id), Role::PoolRole(PoolRole::TrancheInvestor( default_tranche_id(pool_id), @@ -263,7 +265,9 @@ fn update_member() { // Verify the Investor role was set as expected in Permissions assert!(Permissions::has( PermissionScope::Pool(pool_id), - AccountConverter::::convert(new_member.clone()), + AccountConverter::::convert( + new_member.clone() + ), Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, valid_until)), )); @@ -550,7 +554,9 @@ fn transfer_tranche_tokens_from_local() { assert_ok!(Permissions::add( RuntimeOrigin::signed(receiver.into()), Role::PoolRole(PoolRole::InvestorAdmin), - AccountConverter::::convert(dest_address.clone()), + AccountConverter::::convert( + dest_address.clone() + ), PermissionScope::Pool(pool_id), Role::PoolRole(PoolRole::TrancheInvestor( default_tranche_id(pool_id), @@ -738,14 +744,18 @@ fn transferring_invalid_tranche_tokens_should_fail() { assert_ok!(Permissions::add( RuntimeOrigin::signed(BOB.into()), Role::PoolRole(PoolRole::InvestorAdmin), - AccountConverter::::convert(dest_address.clone()), + AccountConverter::::convert( + dest_address.clone() + ), PermissionScope::Pool(invalid_pool_id), Role::PoolRole(PoolRole::TrancheInvestor(valid_tranche_id, valid_until)), )); assert_ok!(Permissions::add( RuntimeOrigin::signed(BOB.into()), Role::PoolRole(PoolRole::InvestorAdmin), - AccountConverter::::convert(dest_address.clone()), + AccountConverter::::convert( + dest_address.clone() + ), PermissionScope::Pool(valid_pool_id), Role::PoolRole(PoolRole::TrancheInvestor(invalid_tranche_id, valid_until)), )); @@ -2129,7 +2139,9 @@ mod utils { assert_eq!( OrmlTokens::free_balance( investment_id(pool_id, default_tranche_id(pool_id)).into(), - &AccountConverter::::convert(DEFAULT_OTHER_DOMAIN_ADDRESS) + &AccountConverter::::convert( + DEFAULT_OTHER_DOMAIN_ADDRESS + ) ), 0 ); diff --git a/runtime/integration-tests/src/utils/evm.rs b/runtime/integration-tests/src/utils/evm.rs index 53520ce156..57bccefb0d 100644 --- a/runtime/integration-tests/src/utils/evm.rs +++ b/runtime/integration-tests/src/utils/evm.rs @@ -29,7 +29,7 @@ pub fn mint_balance_into_derived_account(env: &mut TestEnv, address: H160, balan .unwrap(); let derived_account = - AccountConverter::::convert_evm_address(chain_id, address.to_fixed_bytes()); + AccountConverter::::convert_evm_address(chain_id, address.to_fixed_bytes()); env.with_mut_state(Chain::Para(PARA_ID), || { Balances::mint_into(&derived_account.into(), balance).unwrap() @@ -45,7 +45,7 @@ pub fn deploy_contract(env: &mut TestEnv, address: H160, code: Vec) { .unwrap(); let derived_address = - AccountConverter::::convert_evm_address(chain_id, address.to_fixed_bytes()); + AccountConverter::::convert_evm_address(chain_id, address.to_fixed_bytes()); let transaction_create_cost = env .with_state(Chain::Para(PARA_ID), || { diff --git a/src/chain_spec.rs b/src/chain_spec.rs index 05b38e8ceb..94e79f9af9 100644 --- a/src/chain_spec.rs +++ b/src/chain_spec.rs @@ -554,7 +554,7 @@ fn centrifuge_genesis( endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| { let chain_id = id.unwrap_or_else(|| chain_id.into()); - AccountConverter::::convert_evm_address(chain_id, addr) + AccountConverter::::convert_evm_address(chain_id, addr) })); let num_endowed_accounts = endowed_accounts.len(); @@ -684,7 +684,7 @@ fn altair_genesis( endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| { let chain_id = id.unwrap_or_else(|| chain_id.into()); - AccountConverter::::convert_evm_address(chain_id, addr) + AccountConverter::::convert_evm_address(chain_id, addr) })); let num_endowed_accounts = endowed_accounts.len(); @@ -800,7 +800,7 @@ fn development_genesis( endowed_accounts.extend(endowed_evm_accounts.into_iter().map(|(addr, id)| { let chain_id = id.unwrap_or_else(|| chain_id.into()); - AccountConverter::::convert_evm_address(chain_id, addr) + AccountConverter::::convert_evm_address(chain_id, addr) })); let num_endowed_accounts = endowed_accounts.len();