Skip to content

Commit

Permalink
LMP Enchancements and Automate OB withdrawals (#928)
Browse files Browse the repository at this point in the history
## Describe your changes

Enchancements to LMP and automating OB withdrawals.

## Issue ticket number and link

## Checklist before requesting a review
- [ ] I have performed a self-review of my code.
- [ ] If it is a core feature, I have added thorough tests.
- [ ] I removed all Clippy and Formatting Warnings. 
- [ ] I added required Copyrights.
  • Loading branch information
Gauthamastro authored Mar 14, 2024
2 parents 2de327a + fed4cf5 commit 2c8068f
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 182 deletions.
2 changes: 2 additions & 0 deletions pallets/liquidity-mining/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ parameter_types! {
pub const TresuryPalletId: PalletId = PalletId(*b"OCEX_TRE");
pub const LMPRewardsPalletId: PalletId = PalletId(*b"OCEX_TMP");
pub const MsPerDay: u64 = 86_400_000;
pub const OBWithdrawalLimit: u32 = 50;
}

impl crate::pallet::Config for Test {
Expand All @@ -142,6 +143,7 @@ impl ocex::Config for Test {
type AuthorityId = ocex::sr25519::AuthorityId;
type GovernanceOrigin = EnsureRoot<sp_runtime::AccountId32>;
type CrowdSourceLiqudityMining = LiqudityMining;
type OBWithdrawalLimit = OBWithdrawalLimit;
type WeightInfo = ocex::weights::WeightInfo<Test>;
}

Expand Down
8 changes: 7 additions & 1 deletion pallets/ocex/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ benchmarks! {
}: _(RawOrigin::Signed(main.clone()), x as u64, main.clone())
verify {
assert_last_event::<T>(Event::WithdrawalClaimed {
snapshot_id: x as u64,
main,
withdrawals: vec_withdrawals,
}.into());
Expand Down Expand Up @@ -572,7 +573,12 @@ fn create_trade_metrics<T: Config>() -> TradingPairMetricsMap<T::AccountId> {

fn get_dummy_snapshot<T: Config>() -> SnapshotSummary<T::AccountId> {
let mut withdrawals = Vec::new();
for _ in 0..20 {
let pallet_account = Ocex::<T>::get_pallet_account();
let _imbalance = T::NativeCurrency::deposit_creating(
&pallet_account,
(1000u128 * UNIT_BALANCE).saturated_into(),
);
for _ in 0..50 {
withdrawals.push(Withdrawal {
main_account: T::AccountId::decode(&mut &[0u8; 32][..]).unwrap(),
amount: Decimal::one(),
Expand Down
158 changes: 103 additions & 55 deletions pallets/ocex/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ pub mod pallet {
#[pallet::constant]
type LMPRewardsPalletId: Get<PalletId>;

/// Orderbook withdrawal Limit
#[pallet::constant]
type OBWithdrawalLimit: Get<u32>;

/// Balances Pallet
type NativeCurrency: Currency<Self::AccountId>
+ ReservableCurrency<Self::AccountId>
Expand Down Expand Up @@ -810,9 +814,6 @@ pub mod pallet {
// Anyone can claim the withdrawal for any user
// This is to build services that can enable free withdrawals similar to CEXes.
let _ = ensure_signed(origin)?;
// This vector will keep track of withdrawals processed already
let mut processed_withdrawals = vec![];
let mut failed_withdrawals = vec![];
ensure!(
<Withdrawals<T>>::contains_key(snapshot_id),
Error::<T>::InvalidWithdrawalIndex
Expand All @@ -822,50 +823,28 @@ pub mod pallet {
// return Err
<Withdrawals<T>>::mutate(snapshot_id, |btree_map| {
// Get mutable reference to the withdrawals vector
if let Some(withdrawal_vector) = btree_map.get_mut(&account) {
while !withdrawal_vector.is_empty() {
// Perform pop operation to ensure we do not leave any withdrawal left
// for a double spend
if let Some(withdrawal) = withdrawal_vector.pop() {
if Self::on_idle_withdrawal_processor(withdrawal.clone()) {
processed_withdrawals.push(withdrawal.to_owned());
} else {
// Storing the failed withdrawals back into the storage item
failed_withdrawals.push(withdrawal.to_owned());
Self::deposit_event(Event::WithdrawalFailed(withdrawal.to_owned()));
}
} else {
return Err(Error::<T>::InvalidWithdrawalAmount);
}
}
if let Some(withdrawal_vector) = btree_map.remove(&account) {
let (failed_withdrawals, processed_withdrawals) =
Self::do_withdraw(snapshot_id, withdrawal_vector);
// Not removing key from BtreeMap so that failed withdrawals can still be
// tracked
btree_map.insert(account.clone(), failed_withdrawals);

if !processed_withdrawals.is_empty() {
Self::deposit_event(Event::WithdrawalClaimed {
snapshot_id,
main: account.clone(),
withdrawals: processed_withdrawals.clone(),
});
}
Ok(())
} else {
// This allows us to ensure we do not have someone with an invalid account
Err(Error::<T>::InvalidWithdrawalIndex)
}
})?;
if !processed_withdrawals.is_empty() {
Self::deposit_event(Event::WithdrawalClaimed {
main: account.clone(),
withdrawals: processed_withdrawals.clone(),
});
<OnChainEvents<T>>::mutate(|onchain_events| {
onchain_events.push(
orderbook_primitives::ocex::OnChainEvents::OrderBookWithdrawalClaimed(
snapshot_id,
account.clone(),
processed_withdrawals,
),
);
});
Ok(Pays::No.into())
} else {
// If someone withdraws nothing successfully - should pay for such transaction
Ok(Pays::Yes.into())
}

Ok(Pays::Yes.into())
}

/// Allowlist Token
Expand Down Expand Up @@ -904,6 +883,7 @@ pub mod pallet {
_signatures: Vec<(u16, <T::AuthorityId as RuntimeAppPublic>::Signature)>,
) -> DispatchResult {
ensure_none(origin)?;
let snapshot_id = summary.snapshot_id;
// Update the trader's performance on-chain
if let Some(ref metrics) = summary.trader_metrics {
Self::update_lmp_scores(metrics)?;
Expand All @@ -912,17 +892,26 @@ pub mod pallet {
Self::process_egress_msg(summary.egress_messages.as_ref())?;
if !summary.withdrawals.is_empty() {
let withdrawal_map = Self::create_withdrawal_tree(&summary.withdrawals);
<Withdrawals<T>>::insert(summary.snapshot_id, withdrawal_map);
let mut failed_withdrawal_map = crate::pallet::WithdrawalsMap::<T>::new();
for (account, withdrawals) in withdrawal_map {
let (failed_withdraws, successful_withdraws) =
Self::do_withdraw(snapshot_id, withdrawals);
if !failed_withdraws.is_empty() {
failed_withdrawal_map.insert(account.clone(), failed_withdraws);
}
if !successful_withdraws.is_empty() {
Self::deposit_event(Event::WithdrawalClaimed {
snapshot_id,
main: account.clone(),
withdrawals: successful_withdraws.clone(),
});
}
}
if !failed_withdrawal_map.is_empty() {
<Withdrawals<T>>::insert(summary.snapshot_id, failed_withdrawal_map);
}
let fees = summary.get_fees();
Self::settle_withdrawal_fees(fees)?;
<OnChainEvents<T>>::mutate(|onchain_events| {
onchain_events.push(
orderbook_primitives::ocex::OnChainEvents::OrderbookWithdrawalProcessed(
summary.snapshot_id,
summary.withdrawals.clone(),
),
);
});
}
let id = summary.snapshot_id;
<SnapshotNonce<T>>::put(id);
Expand Down Expand Up @@ -1019,11 +1008,6 @@ pub mod pallet {
}
ensure!(config.verify(), Error::<T>::InvalidLMPConfig);
<ExpectedLMPConfig<T>>::put(config.clone());
let current_blk = frame_system::Pallet::<T>::current_block_number();
<IngressMessages<T>>::mutate(current_blk, |ingress_messages| {
ingress_messages
.push(orderbook_primitives::ingress::IngressMessages::LMPConfig(config))
});
Ok(())
}

Expand Down Expand Up @@ -1061,6 +1045,16 @@ pub mod pallet {
<Auction<T>>::put(auction_info);
Ok(())
}

/// Starts a new liquidity mining epoch
#[pallet::call_index(23)]
#[pallet::weight(< T as Config >::WeightInfo::set_fee_distribution())]
pub fn start_new_epoch_lmp(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
let current_blk = frame_system::Pallet::<T>::current_block_number();
Self::start_new_epoch(current_blk);
Ok(())
}
}

/// Events are a simple means of reporting specific conditions and
Expand Down Expand Up @@ -1103,6 +1097,7 @@ pub mod pallet {
EnclaveCleanup(Vec<T::AccountId>),
TradingPairIsNotOperational,
WithdrawalClaimed {
snapshot_id: u64,
main: T::AccountId,
withdrawals: Vec<Withdrawal<T::AccountId>>,
},
Expand All @@ -1118,8 +1113,8 @@ pub mod pallet {
TokenAllowlisted(AssetId),
/// AllowlistedTokenRemoved
AllowlistedTokenRemoved(AssetId),
/// Withdrawal failed
WithdrawalFailed(Withdrawal<T::AccountId>),
/// Withdrawal ready to claim
WithdrawalReady(u64, Withdrawal<T::AccountId>),
/// Exchange state has been updated
ExchangeStateUpdated(bool),
/// DisputePeriod has been updated
Expand All @@ -1145,6 +1140,13 @@ pub mod pallet {
},
/// LMP Scores updated
LMPScoresUpdated(u16),
/// LMP Reward Claimed
LMPRewardClaimed {
epoch: u16,
market: TradingPair,
main: T::AccountId,
reward: u128,
},
}

///Allowlisted tokens
Expand Down Expand Up @@ -1311,6 +1313,32 @@ pub mod pallet {
StorageValue<_, AuctionInfo<T::AccountId, BalanceOf<T>>, OptionQuery>;

impl<T: crate::pallet::Config> crate::pallet::Pallet<T> {
pub fn do_withdraw(
snapshot_id: u64,
mut withdrawal_vector: Vec<Withdrawal<T::AccountId>>,
) -> (Vec<Withdrawal<T::AccountId>>, Vec<Withdrawal<T::AccountId>>) {
let mut failed_withdrawals = Vec::new();
let mut processed_withdrawals = Vec::new();
while !withdrawal_vector.is_empty() {
// Perform pop operation to ensure we do not leave any withdrawal left
// for a double spend
if let Some(withdrawal) = withdrawal_vector.pop() {
if Self::on_idle_withdrawal_processor(withdrawal.clone()) {
processed_withdrawals.push(withdrawal.to_owned());
} else {
// Storing the failed withdrawals back into the storage item
failed_withdrawals.push(withdrawal.to_owned());
Self::deposit_event(Event::WithdrawalReady(
snapshot_id,
withdrawal.to_owned(),
));
}
}
}

(failed_withdrawals, processed_withdrawals)
}

pub fn do_claim_lmp_rewards(
main: T::AccountId,
epoch: u16,
Expand Down Expand Up @@ -1347,6 +1375,12 @@ pub mod pallet {
<TraderMetrics<T>>::mutate((epoch, market, main.clone()), |(_, _, is_claimed)| {
*is_claimed = true;
});
Self::deposit_event(Event::<T>::LMPRewardClaimed {
epoch,
main,
market,
reward: total_in_u128.saturated_into(),
});
Ok(total_in_u128)
}

Expand Down Expand Up @@ -1460,6 +1494,10 @@ pub mod pallet {
FEE_POT_PALLET_ID.into_account_truncating()
}

pub fn get_system_accounts() -> Vec<T::AccountId> {
vec![Self::get_pallet_account(), Self::get_pot_account()]
}

pub fn process_egress_msg(msgs: &Vec<EgressMessages<T::AccountId>>) -> DispatchResult {
for msg in msgs {
// Process egress messages
Expand Down Expand Up @@ -1803,12 +1841,18 @@ pub mod pallet {
),
DispatchError,
> {
let account_id =
let mut account_id =
<Accounts<T>>::iter().fold(vec![], |mut ids_accum, (acc, acc_info)| {
ids_accum.push((acc.clone(), acc_info.proxies));
ids_accum
});

let system_accounts = Self::get_system_accounts();

for account in system_accounts {
account_id.push((account, BoundedVec::new()));
}

let mut balances: BTreeMap<AccountAsset, Decimal> = BTreeMap::new();
let mut account_ids: BTreeMap<AccountId, Vec<AccountId>> = BTreeMap::new();
// all offchain balances for main accounts
Expand Down Expand Up @@ -2120,6 +2164,10 @@ impl<T: Config + frame_system::offchain::SendTransactionTypes<Call<T>>> Pallet<T
return InvalidTransaction::Custom(10).into();
}

if T::OBWithdrawalLimit::get() < snapshot_summary.withdrawals.len() as u32 {
return InvalidTransaction::Custom(13).into();
}

// Check if this validator was part of that authority set
let authorities = <Authorities<T>>::get(snapshot_summary.validator_set_id).validators;

Expand Down
14 changes: 12 additions & 2 deletions pallets/ocex/src/lmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,20 @@ pub fn get_fees_paid_by_main_account_in_quote(
}

/// Returns the LMP configuration from offchain work state trie
pub fn get_lmp_config(state: &mut OffchainState) -> Result<LMPConfig, &'static str> {
pub fn get_lmp_config(
state: &mut OffchainState,
current_on_chain_epoch: u16,
) -> Result<LMPConfig, &'static str> {
let key = LMP_CONFIG_KEY.encode();
Ok(match state.get(&key)? {
None => return Err("LMPConfigNotFound"),
None => {
if current_on_chain_epoch == 0 {
let config = LMPConfig { epoch: current_on_chain_epoch, index: 0 };
store_lmp_config(state, config);
return Ok(config);
}
return Err("LMPConfigNotFound");
},
Some(encoded_config) => LMPConfig::decode(&mut &encoded_config[..])
.map_err(|_| "Unable to decode LMP config")?,
})
Expand Down
2 changes: 2 additions & 0 deletions pallets/ocex/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ parameter_types! {
pub const TreasuryPalletId: PalletId = PalletId(*b"OCEX_TRS");
//pub const TreasuryPalletId: PalletId = PalletId(*b"OCEX_CRW");
pub const MsPerDay: u64 = 86_400_000;
pub const OBWithdrawalLimit: u32 = 50;
}

impl pallet_lmp::pallet::Config for Test {
Expand All @@ -147,6 +148,7 @@ impl Config for Test {
type GovernanceOrigin = EnsureRoot<sp_runtime::AccountId32>;
type CrowdSourceLiqudityMining = LiqudityMining;
type WeightInfo = crate::weights::WeightInfo<Test>;
type OBWithdrawalLimit = OBWithdrawalLimit;
}

parameter_types! {
Expand Down
6 changes: 4 additions & 2 deletions pallets/ocex/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ impl<T: Config> Pallet<T> {
}
current_epoch = current_epoch.saturating_add(1);
<LMPEpoch<T>>::put(current_epoch);
<LMPConfig<T>>::insert(current_epoch, config);
<LMPConfig<T>>::insert(current_epoch, config.clone());
// Notify Liquidity Crowd sourcing pallet about new epoch
T::CrowdSourceLiqudityMining::new_epoch(current_epoch);

<IngressMessages<T>>::mutate(n, |ingress_messages| {
ingress_messages.push(orderbook_primitives::ingress::IngressMessages::NewLMPEpoch(
current_epoch,
))
));
ingress_messages
.push(orderbook_primitives::ingress::IngressMessages::LMPConfig(config))
});
}
}
Expand Down
Loading

0 comments on commit 2c8068f

Please sign in to comment.