diff --git a/Cargo.lock b/Cargo.lock index 08b1b93..e7e80ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7469,6 +7469,7 @@ dependencies = [ "pallet-order", "pallet-pot", "pallet-transaction-payment", + "pallet-utility", "parity-scale-codec", "scale-info", "smallvec", @@ -8403,6 +8404,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-utility", "pallet-xcm 1.0.0", "parachain-info", "parachains-common", diff --git a/pallets/liquidation/Cargo.toml b/pallets/liquidation/Cargo.toml index 308dde1..e4884c7 100644 --- a/pallets/liquidation/Cargo.toml +++ b/pallets/liquidation/Cargo.toml @@ -34,6 +34,7 @@ sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk", branch frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false, optional = true} +pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} [features] default = ['std'] @@ -42,6 +43,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", ] std = [ "sp-core/std", @@ -54,10 +56,12 @@ std = [ "pallet-order/std", "pallet-pot/std", "mp-system/std", + "pallet-utility/std", "frame-benchmarking/std", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "sp-runtime/try-runtime", + "pallet-utility/try-runtime", ] diff --git a/pallets/liquidation/src/lib.rs b/pallets/liquidation/src/lib.rs index 7a7bfc6..dbfc77d 100644 --- a/pallets/liquidation/src/lib.rs +++ b/pallets/liquidation/src/lib.rs @@ -15,7 +15,11 @@ use frame_support::{ use frame_system::pallet_prelude::BlockNumberFor; use mp_system::BASE_ACCOUNT; pub use pallet::*; -use sp_runtime::{traits::Zero, AccountId32, Perbill, Saturating}; +use sp_runtime::{ + traits::{StaticLookup, Zero}, + AccountId32, Perbill, Saturating, +}; +use sp_std::{prelude::*, vec}; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -26,7 +30,9 @@ pub type Balance = u128; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; + use pallet_balances; use pallet_order::OrderGasCost; + use pallet_utility; #[pallet::pallet] #[pallet::without_storage_info] @@ -34,7 +40,13 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config + pallet_order::Config + pallet_pot::Config { + pub trait Config: + frame_system::Config + + pallet_order::Config + + pallet_pot::Config + + pallet_balances::Config + + pallet_utility::Config + { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -109,39 +121,20 @@ pub mod pallet { /// block has been handled include weight and fee BlockProcessed(BlockNumberFor, BalanceOf, Balance, T::AccountId), - /// parameters. [accountId, balance] - ///transfer profit from BaseAccount to SystemAccount - SystemAccountProfit(T::AccountId, BalanceOf), + /// liquidation and distribution finished + DistributionFinished, - /// parameters. [accountId, balance] - ///transfer total fee from BaseAccount to SystemAccount + /// parameters. [baseAccount, systemAccount, balance] + ///transfer total block fee from BaseAccount to SystemAccount TransferBaseToSystem(T::AccountId, T::AccountId, BalanceOf), - /// parameters. [accountId, balance] - ///transfer profit from BaseAccount to TreasuryAccount - TreasuryAccountProfit(T::AccountId, BalanceOf), - - /// parameters. [accountId, balance] - ///transfer profit from BaseAccount to operationAccount - OperationAccountProfit(T::AccountId, BalanceOf), - - /// parameters. [accountId, balance] - ///transfer profit from BaseAccount to collators account - CollatorProfit(T::AccountId, BalanceOf), - - /// parameters. [accountId, balance] - ///transfer principal from BaseAccount to system account if deficit - CollatorPrincipal(T::AccountId, BalanceOf), - - /// parameters. [accountId, balance] - ///transfer from SystemAccount to collators account for compensate - CollatorCompensate(T::AccountId, BalanceOf), - + ///parameters. [totalFee, totalCost] /// profit distributed succeed - ProfitDistributed, + ProfitDistributed(Balance, Balance), + ///parameters. [totalFee, totalCost] /// collators compensated - CollatorsCompensated, + CollatorsCompensated(Balance, Balance), /// error occurred Error(Error), @@ -154,10 +147,10 @@ pub mod pallet { FailedToFetchRealGasCost, /// internal errors - InternalError, + TransferBaseToSystemError, ///get pot account errors - PotAccountError, + GetPotAccountError, ///failed to process liquidation ProcessLiquidationError, @@ -167,6 +160,8 @@ pub mod pallet { impl Hooks> for Pallet where T::AccountId: From, + ::RuntimeCall: From>, + ::Balance: From>, { fn on_finalize(n: BlockNumberFor) { let base_account = ::from(BASE_ACCOUNT); @@ -180,30 +175,39 @@ pub mod pallet { }, }; - CollatorRealGasCosts::::mutate(collator.clone(), |cost| { - *cost = cost.saturating_add(real_gas_cost); - }); - - let reserved_balance: BalanceOf = - T::ExistentialDeposit::get().try_into().unwrap_or_else(|_| Zero::zero()); + let reserved_balance: BalanceOf = ::ExistentialDeposit::get() + .try_into() + .unwrap_or_else(|_| Zero::zero()); let block_fee_except_ed = base_account_balance.saturating_sub(reserved_balance); let current_block_fee_u128: Balance = block_fee_except_ed.try_into().unwrap_or_else(|_| 0); - let base_account = ::from(BASE_ACCOUNT); + TotalCost::::mutate(|cost| *cost = cost.saturating_add(real_gas_cost)); + CollatorRealGasCosts::::mutate(collator.clone(), |cost| { + *cost = cost.saturating_add(real_gas_cost); + }); + + let mut count = DistributionBlockCount::::get(); + count = count.saturating_add(1u32.into()); + DistributionBlockCount::::put(count); + let system_account = match pallet_pot::Pallet::::ensure_pot(T::SystemAccountName::get()) { Ok(account) => account, Err(err) => { log::error!("get system account err:{:?}", err); - Self::deposit_event(Event::Error(Error::::InternalError.into())); + Self::deposit_event(Event::Error(Error::::GetPotAccountError.into())); return; }, }; - match Self::transfer_funds(&base_account, &system_account, block_fee_except_ed.clone()) - { + match ::Currency::transfer( + &base_account, + &system_account, + block_fee_except_ed.clone(), + ExistenceRequirement::KeepAlive, + ) { Ok(_) => { Self::deposit_event(Event::TransferBaseToSystem( base_account.clone(), @@ -213,7 +217,7 @@ pub mod pallet { }, Err(err) => { log::error!("Transfer to system account failed: {:?}", err); - Self::deposit_event(Event::Error(Error::::InternalError.into())); + Self::deposit_event(Event::Error(Error::::TransferBaseToSystemError.into())); return; }, } @@ -221,21 +225,12 @@ pub mod pallet { TotalIncome::::mutate(|income| { *income = income.saturating_add(current_block_fee_u128) }); - TotalCost::::mutate(|cost| *cost = cost.saturating_add(real_gas_cost)); - let mut count = DistributionBlockCount::::get(); - count = count.saturating_add(1u32.into()); - DistributionBlockCount::::put(count); if count % T::ProfitDistributionCycle::get() == Zero::zero() { DistributionBlockCount::::put(BlockNumberFor::::zero()); match Self::distribute_profit() { Ok(_) => { - Self::deposit_event(Event::BlockProcessed( - n, - block_fee_except_ed.clone(), - real_gas_cost, - collator, - )); + Self::deposit_event(Event::DistributionFinished); }, Err(err) => { log::error!("process liquidation failed: {:?}", err); @@ -245,85 +240,112 @@ pub mod pallet { }, } } + + Self::deposit_event(Event::BlockProcessed( + n, + block_fee_except_ed.clone(), + real_gas_cost, + collator, + )); } } impl Pallet where T::AccountId: From, + ::RuntimeCall: From>, + ::Balance: From>, { - fn transfer_funds( - source: &T::AccountId, - to: &T::AccountId, - amount: BalanceOf, - ) -> DispatchResult { - ensure!( - ::Currency::free_balance(source) >= amount, - "Not enough balance" - ); - ::Currency::transfer( - source, - to, - amount, - ExistenceRequirement::KeepAlive, - )?; + fn execute_batch_transfers( + transfers: Vec<(T::AccountId, BalanceOf)>, + ) -> DispatchResultWithPostInfo { + let system_account = + match pallet_pot::Pallet::::ensure_pot(T::SystemAccountName::get()) { + Ok(account) => account, + Err(err) => { + log::error!("get system account err:{:?}", err); + Err(Error::::GetPotAccountError)? + }, + }; + + let mut calls: Vec<::RuntimeCall> = vec![]; + + for (recipient, amount) in transfers { + if amount.is_zero() { + continue; + } + + let transfer_call = pallet_balances::Call::::transfer { + dest: T::Lookup::unlookup(recipient), + value: amount.into(), + }; - Ok(()) + let utility_call: ::RuntimeCall = transfer_call.into(); + calls.push(utility_call); + } + + pallet_utility::Pallet::::batch( + frame_system::RawOrigin::Signed(system_account).into(), + calls, + ) + .map_err(|err| { + log::error!("Batch transfer failed: {:?}", err); + err + })?; + + Ok(().into()) } - fn distribute_profit() -> DispatchResult { + fn distribute_profit() -> DispatchResultWithPostInfo { let total_income = TotalIncome::::get(); let total_cost = TotalCost::::get(); if total_income > total_cost { Self::distribute_positive_profit()?; - Self::deposit_event(Event::ProfitDistributed); + Self::deposit_event(Event::ProfitDistributed( + total_income.clone(), + total_cost.clone(), + )); } else { Self::compensate_collators()?; - Self::deposit_event(Event::CollatorsCompensated); + Self::deposit_event(Event::CollatorsCompensated( + total_income.clone(), + total_cost.clone(), + )); } let _ = >::clear(u32::max_value(), None); TotalIncome::::put(0u128); TotalCost::::put(0u128); - Ok(()) + Ok(().into()) } #[cfg(test)] - pub fn test_distribute_profit() -> DispatchResult { + pub fn test_distribute_profit() -> DispatchResultWithPostInfo { Self::distribute_profit() } - fn distribute_positive_profit() -> DispatchResult { + fn distribute_positive_profit() -> DispatchResultWithPostInfo { let total_income = TotalIncome::::get(); let total_cost = TotalCost::::get(); let total_profit = total_income.saturating_sub(total_cost); - let system_account = - match pallet_pot::Pallet::::ensure_pot(T::SystemAccountName::get()) { - Ok(account) => account, - Err(err) => { - log::error!("get system account err:{:?}", err); - Err(Error::::PotAccountError)? - }, - }; - let treasury_account = - match pallet_pot::Pallet::::ensure_pot(T::TreasuryAccountName::get()) { - Ok(account) => account, - Err(err) => { - log::error!("get treasury account err:{:?}", err); - Err(Error::::PotAccountError)? - }, - }; - let operation_account = - match pallet_pot::Pallet::::ensure_pot(T::OperationAccountName::get()) { - Ok(account) => account, - Err(err) => { - log::error!("get maintenance account err:{:?}", err); - Err(Error::::PotAccountError)? - }, - }; + let treasury_account = pallet_pot::Pallet::::ensure_pot( + T::TreasuryAccountName::get(), + ) + .map_err(|err| { + log::error!("get treasury account err:{:?}", err); + Error::::GetPotAccountError + })?; + + let operation_account = pallet_pot::Pallet::::ensure_pot( + T::OperationAccountName::get(), + ) + .map_err(|err| { + log::error!("get maintenance account err:{:?}", err); + Error::::GetPotAccountError + })?; let system_ratio = T::SystemRatio::get(); let treasury_ratio = T::TreasuryRatio::get(); @@ -335,103 +357,40 @@ pub mod pallet { let total_collators_profit = total_profit.saturating_sub(treasury_amount + operation_amount + system_amount); + let mut transfers = Vec::new(); + let treasury_account_profit = treasury_amount.try_into().unwrap_or_else(|_| Zero::zero()); - match Self::transfer_funds(&system_account, &treasury_account, treasury_account_profit) - { - Ok(_) => { - Self::deposit_event(Event::TreasuryAccountProfit( - treasury_account.clone(), - treasury_account_profit, - )); - }, - Err(err) => { - log::error!("Transfer to treasury account failed: {:?}", err); - }, - } + transfers.push((treasury_account, treasury_account_profit)); let operation_account_profit = operation_amount.try_into().unwrap_or_else(|_| Zero::zero()); - match Self::transfer_funds( - &system_account, - &operation_account, - operation_account_profit, - ) { - Ok(_) => { - Self::deposit_event(Event::OperationAccountProfit( - operation_account.clone(), - operation_account_profit, - )); - }, - Err(err) => { - log::error!("Transfer to maintenance account failed: {:?}", err); - }, - } + transfers.push((operation_account, operation_account_profit)); - // distribute profit and compensate cost to every collator for (collator, collator_cost) in CollatorRealGasCosts::::iter() { let collator_ratio = Perbill::from_rational(collator_cost, total_cost); let collator_profit = collator_ratio * total_collators_profit; let collator_addr_profit = collator_profit.try_into().unwrap_or_else(|_| Zero::zero()); - match Self::transfer_funds(&system_account, &collator.clone(), collator_addr_profit) - { - Ok(_) => { - Self::deposit_event(Event::CollatorProfit( - collator.clone(), - collator_addr_profit, - )); - }, - Err(err) => { - log::error!("Transfer profit to collator account failed: {:?}", err); - }, - } - let collator_addr_cost = collator_cost.try_into().unwrap_or_else(|_| Zero::zero()); - match Self::transfer_funds(&system_account, &collator.clone(), collator_addr_cost) { - Ok(_) => { - Self::deposit_event(Event::CollatorCompensate( - collator.clone(), - collator_addr_cost, - )); - }, - Err(err) => { - log::error!("Transfer principal to collator account failed: {:?}", err); - }, - } + + transfers.push((collator.clone(), collator_addr_profit)); + transfers.push((collator.clone(), collator_addr_cost)); } - Ok(()) + Self::execute_batch_transfers(transfers) } - fn compensate_collators() -> DispatchResult { - let system_account = - match pallet_pot::Pallet::::ensure_pot(T::SystemAccountName::get()) { - Ok(account) => account, - Err(err) => { - log::error!("get system account err:{:?}", err); - Err(Error::::PotAccountError)? - }, - }; + fn compensate_collators() -> DispatchResultWithPostInfo { + let mut transfers = Vec::new(); - // compensate for every collator for (collator, collator_cost) in CollatorRealGasCosts::::iter() { let collator_addr_cost = collator_cost.try_into().unwrap_or_else(|_| Zero::zero()); - match Self::transfer_funds(&system_account, &collator.clone(), collator_addr_cost) { - Ok(_) => { - Self::deposit_event(Event::CollatorCompensate( - collator, - collator_addr_cost, - )); - }, - Err(err) => { - log::error!("Transfer principal to collator account failed: {:?}", err); - }, - } - } - Ok(()) + transfers.push((collator.clone(), collator_addr_cost)); + } + Self::execute_batch_transfers(transfers) } } } diff --git a/pallets/liquidation/src/mock.rs b/pallets/liquidation/src/mock.rs index 5bbb437..f9c22d2 100644 --- a/pallets/liquidation/src/mock.rs +++ b/pallets/liquidation/src/mock.rs @@ -35,6 +35,7 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, OrderPallet: pallet_order::{Pallet, Storage, Event}, Pot: pallet_pot::{Pallet, Call, Storage, Event}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, Liquidation: pallet_liquidation::{Pallet, Storage, Event}, } ); @@ -112,6 +113,13 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); } +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + parameter_types! { pub const SlotWidth: u32 = 2; pub const OrderMaxAmount:Balance = 200000000; @@ -218,5 +226,8 @@ impl ExtBuilder { } pub(crate) fn expected_event(event: &LiquidationEvent) -> bool { - matches!(event, LiquidationEvent::ProfitDistributed | LiquidationEvent::CollatorsCompensated) + matches!( + event, + LiquidationEvent::ProfitDistributed(_, _) | LiquidationEvent::CollatorsCompensated(_, _) + ) } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b13ebcf..e0cc84d 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -66,6 +66,7 @@ sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", bran sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } pallet-society ={ git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} +pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false} @@ -190,7 +191,8 @@ std = [ "pallet-order/std", "sp-trie/std", "cumulus-primitives-timestamp/std", - "pallet-liquidation/std", + "pallet-liquidation/std", + "pallet-utility/std", "pallet-pot/std", "pallet-assurance/std", "pallet-preimage/std", @@ -228,7 +230,8 @@ runtime-benchmarks = [ "pallet-society/runtime-benchmarks", "pallet-evm/runtime-benchmarks", "pallet-ethereum/runtime-benchmarks", - "pallet-liquidation/runtime-benchmarks", + "pallet-liquidation/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", "pallet-pot/runtime-benchmarks", "pallet-assurance/runtime-benchmarks", "pallet-preimage/runtime-benchmarks", @@ -267,7 +270,7 @@ try-runtime = [ "pallet-evm-utils/try-runtime", "pallet-precompile-substrate-utils/try-runtime", "pallet-order/try-runtime", - "pallet-liquidation/try-runtime", + "pallet-liquidation/try-runtime", "pallet-assets/try-runtime", "pallet-assets-bridge/try-runtime", "pallet-collective/try-runtime", @@ -280,6 +283,7 @@ try-runtime = [ "pallet-hotfix-sufficients/try-runtime", "pallet-pot/try-runtime", "pallet-assurance/try-runtime", + "pallet-utility/try-runtime", "pallet-assurance/try-runtime", "pallet-preimage/try-runtime", "pallet-referenda/try-runtime", diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 06486b9..88d9908 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -891,6 +891,13 @@ impl pallet_assurance::Config for Runtime { type DefaultLiquidateThreshold = ConstU128<0>; } +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = pallet_utility::weights::SubstrateWeight; +} + parameter_types! { pub const SystemRatio: Perbill = Perbill::from_percent(20); // 20% for system pub const TreasuryRatio: Perbill = Perbill::from_percent(33); // 33% for treasury @@ -1110,6 +1117,7 @@ construct_runtime!( ParachainSystem: cumulus_pallet_parachain_system = 1, Timestamp: pallet_timestamp = 2, ParachainInfo: parachain_info = 3, + Utility: pallet_utility = 4, // Monetary stuff. Balances: pallet_balances = 10,