From 3769a0ba4d6c5feeea5b57d83a94fe8e372874c8 Mon Sep 17 00:00:00 2001 From: Frederik Gartenmeister Date: Thu, 6 Jun 2024 16:42:48 +0200 Subject: [PATCH] Feat: Liquidity pools inter domain tranche token transfer (#1860) --- pallets/liquidity-pools/src/inbound.rs | 30 ++++-- pallets/liquidity-pools/src/lib.rs | 11 +- runtime/altair/src/lib.rs | 1 + runtime/altair/src/liquidity_pools.rs | 99 ------------------ runtime/centrifuge/src/lib.rs | 1 + runtime/centrifuge/src/liquidity_pools.rs | 116 --------------------- runtime/common/src/account_conversion.rs | 21 +++- runtime/development/src/lib.rs | 1 + runtime/development/src/liquidity_pools.rs | 101 ------------------ 9 files changed, 51 insertions(+), 330 deletions(-) delete mode 100644 runtime/altair/src/liquidity_pools.rs delete mode 100644 runtime/centrifuge/src/liquidity_pools.rs delete mode 100644 runtime/development/src/liquidity_pools.rs diff --git a/pallets/liquidity-pools/src/inbound.rs b/pallets/liquidity-pools/src/inbound.rs index 573bce07c1..3386fddff4 100644 --- a/pallets/liquidity-pools/src/inbound.rs +++ b/pallets/liquidity-pools/src/inbound.rs @@ -20,7 +20,7 @@ use cfg_types::{ }; use frame_support::{ ensure, - traits::{fungibles::Mutate, tokens::Preservation}, + traits::{fungibles::Mutate, tokens::Preservation, OriginTrait}, }; use sp_core::Get; use sp_runtime::{ @@ -32,7 +32,7 @@ use crate::{pallet::Error, Config, GeneralCurrencyIndexOf, Message, MessageOf, P impl Pallet where - T::AccountId: Into<[u8; 32]>, + ::AccountId: From<[u8; 32]> + Into<[u8; 32]>, { /// Executes a transfer from another domain exclusively for /// non-tranche-tokens. @@ -59,16 +59,19 @@ where pub fn handle_tranche_tokens_transfer( pool_id: T::PoolId, tranche_id: T::TrancheId, - sending_domain: DomainAddress, - receiver: T::AccountId, + sending_domain: Domain, + receiver: DomainAddress, amount: ::Balance, ) -> DispatchResult { ensure!(!amount.is_zero(), Error::::InvalidTransferAmount); + let local_representation_of_receiver = + T::DomainAddressToAccountId::convert(receiver.clone()); + ensure!( T::Permission::has( PermissionScope::Pool(pool_id), - receiver.clone(), + local_representation_of_receiver.clone(), Role::PoolRole(PoolRole::TrancheInvestor(tranche_id, T::Time::now())), ), Error::::UnauthorizedTransfer @@ -78,12 +81,25 @@ where T::Tokens::transfer( invest_id.into(), - &Domain::convert(sending_domain.domain()), - &receiver, + &Domain::convert(sending_domain), + &local_representation_of_receiver, amount, Preservation::Expendable, )?; + // If the receiver is not on the Centrifuge domain we need to forward it now + // to the right domain from the holdings of the receiver we just transferred + // them to. + if receiver.domain() != Domain::Centrifuge { + Pallet::::transfer_tranche_tokens( + T::RuntimeOrigin::signed(local_representation_of_receiver), + pool_id, + tranche_id, + receiver, + amount, + )?; + } + Ok(()) } diff --git a/pallets/liquidity-pools/src/lib.rs b/pallets/liquidity-pools/src/lib.rs index 1041ca87c4..bb39c1e95a 100644 --- a/pallets/liquidity-pools/src/lib.rs +++ b/pallets/liquidity-pools/src/lib.rs @@ -258,9 +258,13 @@ pub mod pallet { /// The converter from a DomainAddress to a Substrate AccountId. type DomainAddressToAccountId: Convert; - /// The converter from a Domain 32 byte array to Substrate AccountId. + /// The converter from a Domain and 32 byte array to Substrate + /// AccountId. type DomainAccountToAccountId: Convert<(Domain, [u8; 32]), Self::AccountId>; + /// The converter from a Domain and a 32 byte array to DomainAddress. + type DomainAccountToDomainAddress: Convert<(Domain, [u8; 32]), DomainAddress>; + /// The type for processing outgoing messages. type OutboundQueue: OutboundQueue< Sender = Self::AccountId, @@ -973,14 +977,15 @@ pub mod pallet { Message::TransferTrancheTokens { pool_id, tranche_id, + domain, receiver, amount, .. } => Self::handle_tranche_tokens_transfer( pool_id, tranche_id, - sender, - receiver.into(), + sender.domain(), + T::DomainAccountToDomainAddress::convert((domain, receiver)), amount, ), Message::IncreaseInvestOrder { diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index ae3ddb8fe4..98f08d9eda 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -1726,6 +1726,7 @@ impl pallet_liquidity_pools::Config for Runtime { type BalanceRatio = Ratio; type CurrencyId = CurrencyId; type DomainAccountToAccountId = AccountConverter; + type DomainAccountToDomainAddress = AccountConverter; type DomainAddressToAccountId = AccountConverter; type ForeignInvestment = ForeignInvestments; type GeneralCurrencyPrefix = GeneralCurrencyPrefix; diff --git a/runtime/altair/src/liquidity_pools.rs b/runtime/altair/src/liquidity_pools.rs deleted file mode 100644 index cd3aea4087..0000000000 --- a/runtime/altair/src/liquidity_pools.rs +++ /dev/null @@ -1,99 +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 cfg_primitives::{ - liquidity_pools::GeneralCurrencyPrefix, AccountId, Balance, OutboundMessageNonce, PalletIndex, - PoolId, TrancheId, -}; -use cfg_types::{ - fixed_point::Ratio, - tokens::{CurrencyId, TrancheCurrency}, -}; -use frame_support::{parameter_types, traits::PalletInfoAccess}; -use frame_system::EnsureRoot; -use pallet_liquidity_pools::hooks::{ - CollectedForeignInvestmentHook, CollectedForeignRedemptionHook, DecreasedForeignInvestOrderHook, -}; -use runtime_common::{ - account_conversion::AccountConverter, gateway, liquidity_pools::LiquidityPoolsMessage, - transfer_filter::PreLpTransfer, -}; - -use crate::{ - ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsGateway, LocationToAccountId, - OrmlAssetRegistry, Permissions, PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, Swaps, - Timestamp, Tokens, TransferAllowList, TreasuryAccount, -}; - -impl pallet_foreign_investments::Config for Runtime { - type CollectedForeignInvestmentHook = CollectedForeignInvestmentHook; - type CollectedForeignRedemptionHook = CollectedForeignRedemptionHook; - type CurrencyId = CurrencyId; - type DecreasedForeignInvestOrderHook = DecreasedForeignInvestOrderHook; - type ForeignBalance = Balance; - type Investment = Investments; - type InvestmentId = TrancheCurrency; - type PoolBalance = Balance; - type PoolInspect = PoolSystem; - type SwapBalance = Balance; - type Swaps = Swaps; - type TrancheBalance = Balance; -} - -parameter_types! { - pub LiquidityPoolsPalletIndex: PalletIndex = ::index() as u8; -} - -impl pallet_liquidity_pools::Config for Runtime { - type AdminOrigin = EnsureRoot; - type AssetRegistry = OrmlAssetRegistry; - type Balance = Balance; - type BalanceRatio = Ratio; - type CurrencyId = CurrencyId; - type DomainAccountToAccountId = AccountConverter; - type DomainAddressToAccountId = AccountConverter; - type ForeignInvestment = ForeignInvestments; - type GeneralCurrencyPrefix = GeneralCurrencyPrefix; - type OutboundQueue = LiquidityPoolsGateway; - type Permission = Permissions; - type PoolId = PoolId; - type PoolInspect = PoolSystem; - type PreTransferFilter = PreLpTransfer; - type RuntimeEvent = RuntimeEvent; - type Time = Timestamp; - type Tokens = Tokens; - type TrancheCurrency = TrancheCurrency; - type TrancheId = TrancheId; - type TrancheTokenPrice = PoolSystem; - type TreasuryAccount = TreasuryAccount; - type WeightInfo = (); -} - -parameter_types! { - pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); -} - -impl pallet_liquidity_pools_gateway::Config for Runtime { - type AdminOrigin = EnsureRoot; - type InboundQueue = crate::LiquidityPools; - type LocalEVMOrigin = pallet_liquidity_pools_gateway::EnsureLocal; - type MaxIncomingMessageSize = MaxIncomingMessageSize; - type Message = LiquidityPoolsMessage; - type OriginRecovery = crate::LiquidityPoolsAxelarGateway; - type OutboundMessageNonce = OutboundMessageNonce; - type Router = liquidity_pools_gateway_routers::DomainRouter; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type Sender = Sender; - type WeightInfo = (); -} diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 66e9996c31..33135524d4 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -1824,6 +1824,7 @@ impl pallet_liquidity_pools::Config for Runtime { type BalanceRatio = Ratio; type CurrencyId = CurrencyId; type DomainAccountToAccountId = AccountConverter; + type DomainAccountToDomainAddress = AccountConverter; type DomainAddressToAccountId = AccountConverter; type ForeignInvestment = ForeignInvestments; type GeneralCurrencyPrefix = GeneralCurrencyPrefix; diff --git a/runtime/centrifuge/src/liquidity_pools.rs b/runtime/centrifuge/src/liquidity_pools.rs deleted file mode 100644 index 066fd0cd0b..0000000000 --- a/runtime/centrifuge/src/liquidity_pools.rs +++ /dev/null @@ -1,116 +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 cfg_primitives::{ - liquidity_pools::GeneralCurrencyPrefix, AccountId, Balance, EnsureRootOr, OutboundMessageNonce, - PalletIndex, PoolId, TrancheId, TwoThirdOfCouncil, -}; -use cfg_types::{ - fixed_point::Ratio, - tokens::{CurrencyId, TrancheCurrency}, -}; -use frame_support::{parameter_types, traits::PalletInfoAccess}; -use pallet_liquidity_pools::hooks::{ - CollectedForeignInvestmentHook, CollectedForeignRedemptionHook, DecreasedForeignInvestOrderHook, -}; -use runtime_common::{ - account_conversion::AccountConverter, gateway, liquidity_pools::LiquidityPoolsMessage, - origin::EnsureAccountOrRootOr, transfer_filter::PreLpTransfer, -}; - -use crate::{ - ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsAxelarGateway, - LiquidityPoolsGateway, LocationToAccountId, OrmlAssetRegistry, Permissions, PoolSystem, - Runtime, RuntimeEvent, RuntimeOrigin, Swaps, Timestamp, Tokens, TransferAllowList, - TreasuryAccount, -}; - -impl pallet_foreign_investments::Config for Runtime { - type CollectedForeignInvestmentHook = CollectedForeignInvestmentHook; - type CollectedForeignRedemptionHook = CollectedForeignRedemptionHook; - type CurrencyId = CurrencyId; - type DecreasedForeignInvestOrderHook = DecreasedForeignInvestOrderHook; - type ForeignBalance = Balance; - type Investment = Investments; - type InvestmentId = TrancheCurrency; - type PoolBalance = Balance; - type PoolInspect = PoolSystem; - type SwapBalance = Balance; - type Swaps = Swaps; - type TrancheBalance = Balance; -} - -parameter_types! { - // To be used if we want to register a particular asset in the chain spec, when running the chain locally. - pub LiquidityPoolsPalletIndex: PalletIndex = ::index() as u8; -} - -impl pallet_liquidity_pools::Config for Runtime { - type AdminOrigin = EnsureRootOr; - type AssetRegistry = OrmlAssetRegistry; - type Balance = Balance; - type BalanceRatio = Ratio; - type CurrencyId = CurrencyId; - type DomainAccountToAccountId = AccountConverter; - type DomainAddressToAccountId = AccountConverter; - type ForeignInvestment = ForeignInvestments; - type GeneralCurrencyPrefix = GeneralCurrencyPrefix; - type OutboundQueue = LiquidityPoolsGateway; - type Permission = Permissions; - type PoolId = PoolId; - type PoolInspect = PoolSystem; - type PreTransferFilter = PreLpTransfer; - type RuntimeEvent = RuntimeEvent; - type Time = Timestamp; - type Tokens = Tokens; - type TrancheCurrency = TrancheCurrency; - type TrancheId = TrancheId; - type TrancheTokenPrice = PoolSystem; - type TreasuryAccount = TreasuryAccount; - type WeightInfo = (); -} - -parameter_types! { - pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); -} - -parameter_types! { - // A temporary admin account for the LP logic - // This is a multi-sig controlled pure proxy on mainnet - // - address: "4eEqmbQMbFfNUg6bQnqi9zgUvQvSpNbUgstEM64Xq9FW58Xv" (on Centrifuge) - // (pub key 0x80339e91a87b9c082705fd1a6d39b3e00b46e445ad8c80c127f6a56941c6aa57) - // - // This account is besides Root and 2/3-council able to - // - add valid relayer contracts - // - rm valid relayer contracts - // - add valid LP instance contracts - // - rm valid LP instance contracts - // - add conversions from Axelar `sourceChain` strings to `DomainAddress` - // - set the Axelar gateway contract in the Axelar gateway precompile - pub LpAdminAccount: AccountId = AccountId::new(hex_literal::hex!("80339e91a87b9c082705fd1a6d39b3e00b46e445ad8c80c127f6a56941c6aa57")); -} - -impl pallet_liquidity_pools_gateway::Config for Runtime { - type AdminOrigin = EnsureAccountOrRootOr; - type InboundQueue = LiquidityPools; - type LocalEVMOrigin = pallet_liquidity_pools_gateway::EnsureLocal; - type MaxIncomingMessageSize = MaxIncomingMessageSize; - type Message = LiquidityPoolsMessage; - type OriginRecovery = LiquidityPoolsAxelarGateway; - type OutboundMessageNonce = OutboundMessageNonce; - type Router = liquidity_pools_gateway_routers::DomainRouter; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type Sender = Sender; - type WeightInfo = (); -} diff --git a/runtime/common/src/account_conversion.rs b/runtime/common/src/account_conversion.rs index 99061a6073..cdc209d040 100644 --- a/runtime/common/src/account_conversion.rs +++ b/runtime/common/src/account_conversion.rs @@ -13,7 +13,7 @@ use cfg_primitives::AccountId; use cfg_types::domain_address::{Domain, DomainAddress}; use pallet_evm::AddressMapping; -use sp_core::{Get, H160}; +use sp_core::{crypto::AccountId32, Get, H160}; use sp_runtime::traits::Convert; use staging_xcm::v3; use staging_xcm_executor::traits::ConvertLocation; @@ -77,10 +77,23 @@ impl Convert for AccountConverter { } } -impl Convert<(Domain, [u8; 32]), AccountId> for AccountConverter { - fn convert((domain, account): (Domain, [u8; 32])) -> AccountId { +impl Convert<(Domain, [u8; 32]), DomainAddress> for AccountConverter { + fn convert((domain, account): (Domain, [u8; 32])) -> DomainAddress { match domain { - Domain::Centrifuge => AccountId::new(account), + Domain::Centrifuge => DomainAddress::Centrifuge(account), + Domain::EVM(chain_id) => { + let mut bytes20 = [0; 20]; + bytes20.copy_from_slice(&account[..20]); + DomainAddress::EVM(chain_id, bytes20) + } + } + } +} + +impl Convert<(Domain, [u8; 32]), AccountId32> for AccountConverter { + fn convert((domain, account): (Domain, [u8; 32])) -> AccountId32 { + match domain { + Domain::Centrifuge => AccountId32::new(account), // EVM AccountId20 addresses are right-padded to 32 bytes Domain::EVM(chain_id) => { let mut bytes20 = [0; 20]; diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index f062a08f47..4d2205c972 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -1795,6 +1795,7 @@ impl pallet_liquidity_pools::Config for Runtime { type BalanceRatio = Ratio; type CurrencyId = CurrencyId; type DomainAccountToAccountId = AccountConverter; + type DomainAccountToDomainAddress = AccountConverter; type DomainAddressToAccountId = AccountConverter; type ForeignInvestment = ForeignInvestments; type GeneralCurrencyPrefix = GeneralCurrencyPrefix; diff --git a/runtime/development/src/liquidity_pools.rs b/runtime/development/src/liquidity_pools.rs deleted file mode 100644 index 34802614d2..0000000000 --- a/runtime/development/src/liquidity_pools.rs +++ /dev/null @@ -1,101 +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 cfg_primitives::{ - liquidity_pools::GeneralCurrencyPrefix, AccountId, Balance, EnsureRootOr, HalfOfCouncil, - OutboundMessageNonce, PalletIndex, PoolId, TrancheId, -}; -use cfg_types::{ - fixed_point::Ratio, - tokens::{CurrencyId, TrancheCurrency}, -}; -use frame_support::{parameter_types, traits::PalletInfoAccess}; -use frame_system::EnsureRoot; -use pallet_liquidity_pools::hooks::{ - CollectedForeignInvestmentHook, CollectedForeignRedemptionHook, DecreasedForeignInvestOrderHook, -}; -use runtime_common::{ - account_conversion::AccountConverter, gateway, liquidity_pools::LiquidityPoolsMessage, - transfer_filter::PreLpTransfer, -}; - -use crate::{ - ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsAxelarGateway, - LiquidityPoolsGateway, LocationToAccountId, OrmlAssetRegistry, Permissions, PoolSystem, - Runtime, RuntimeEvent, RuntimeOrigin, Swaps, Timestamp, Tokens, TransferAllowList, - TreasuryAccount, -}; - -impl pallet_foreign_investments::Config for Runtime { - type CollectedForeignInvestmentHook = CollectedForeignInvestmentHook; - type CollectedForeignRedemptionHook = CollectedForeignRedemptionHook; - type CurrencyId = CurrencyId; - type DecreasedForeignInvestOrderHook = DecreasedForeignInvestOrderHook; - type ForeignBalance = Balance; - type Investment = Investments; - type InvestmentId = TrancheCurrency; - type PoolBalance = Balance; - type PoolInspect = PoolSystem; - type SwapBalance = Balance; - type Swaps = Swaps; - type TrancheBalance = Balance; -} - -parameter_types! { - // To be used if we want to register a particular asset in the chain spec, when running the chain locally. - pub LiquidityPoolsPalletIndex: PalletIndex = ::index() as u8; -} - -impl pallet_liquidity_pools::Config for Runtime { - type AdminOrigin = EnsureRoot; - type AssetRegistry = OrmlAssetRegistry; - type Balance = Balance; - type BalanceRatio = Ratio; - type CurrencyId = CurrencyId; - type DomainAccountToAccountId = AccountConverter; - type DomainAddressToAccountId = AccountConverter; - type ForeignInvestment = ForeignInvestments; - type GeneralCurrencyPrefix = GeneralCurrencyPrefix; - type OutboundQueue = LiquidityPoolsGateway; - type Permission = Permissions; - type PoolId = PoolId; - type PoolInspect = PoolSystem; - type PreTransferFilter = PreLpTransfer; - type RuntimeEvent = RuntimeEvent; - type Time = Timestamp; - type Tokens = Tokens; - type TrancheCurrency = TrancheCurrency; - type TrancheId = TrancheId; - type TrancheTokenPrice = PoolSystem; - type TreasuryAccount = TreasuryAccount; - type WeightInfo = (); -} - -parameter_types! { - pub const MaxIncomingMessageSize: u32 = 1024; - pub Sender: AccountId = gateway::get_gateway_account::(); -} - -impl pallet_liquidity_pools_gateway::Config for Runtime { - type AdminOrigin = EnsureRootOr; - type InboundQueue = LiquidityPools; - type LocalEVMOrigin = pallet_liquidity_pools_gateway::EnsureLocal; - type MaxIncomingMessageSize = MaxIncomingMessageSize; - type Message = LiquidityPoolsMessage; - type OriginRecovery = LiquidityPoolsAxelarGateway; - type OutboundMessageNonce = OutboundMessageNonce; - type Router = liquidity_pools_gateway_routers::DomainRouter; - type RuntimeEvent = RuntimeEvent; - type RuntimeOrigin = RuntimeOrigin; - type Sender = Sender; - type WeightInfo = (); -}