From 9c057fabf577112b46a45f694aea84f9aabbbad8 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 11 Oct 2023 17:59:37 +0200 Subject: [PATCH 01/17] initial force position withdraw --- pallets/omnipool/src/lib.rs | 125 ++++++++++++++++++++++++++++++++ pallets/omnipool/src/weights.rs | 17 +++++ 2 files changed, 142 insertions(+) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 586ff528e..635c59f12 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -390,6 +390,12 @@ pub mod pallet { InvalidWithdrawalFee, /// More than allowed amount of fee has been transferred. FeeOverdraft, + /// + SharesLeft, + /// + OwnerNotFound, + /// + PositionsLeft, } #[pallet::call] @@ -1496,6 +1502,125 @@ pub mod pallet { Self::deposit_event(Event::TVLCapUpdated { cap }); Ok(()) } + + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::force_withdraw_position())] + #[transactional] + pub fn force_withdraw_position(origin: OriginFor, + position_id: T::PositionItemId ) -> DispatchResult { + ensure_root(origin.clone())?; + + let position = Positions::::get(position_id).ok_or(Error::::PositionNotFound)?; + + let owner = T::NFTHandler::owner(&T::NFTCollectionId::get(), &position_id).ok_or(Error::::OwnerNotFound)?; + + let asset_id = position.asset_id; + let asset_state = Self::load_asset_state(asset_id)?; + let current_imbalance = >::get(); + let current_hub_asset_liquidity = + T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); + + let amount = position.shares; + + let state_changes = hydra_dx_math::omnipool::calculate_remove_liquidity_state_changes( + &(&asset_state).into(), + amount, + &(&position).into(), + I129 { + value: current_imbalance.value, + negative: current_imbalance.negative, + }, + current_hub_asset_liquidity, + FixedU128::zero(), + ) + .ok_or(ArithmeticError::Overflow)?; + + let new_asset_state = asset_state + .clone() + .delta_update(&state_changes.asset) + .ok_or(ArithmeticError::Overflow)?; + + // Update position state + let updated_position = position + .delta_update( + &state_changes.delta_position_reserve, + &state_changes.delta_position_shares, + ) + .ok_or(ArithmeticError::Overflow)?; + + T::Currency::transfer( + asset_id, + &Self::protocol_account(), + &owner, + *state_changes.asset.delta_reserve, + )?; + + Self::update_imbalance(state_changes.delta_imbalance)?; + + // burn only difference between delta hub and lp hub amount. + Self::update_hub_asset_liquidity( + &state_changes + .asset + .delta_hub_reserve + .merge(BalanceUpdate::Increase(state_changes.lp_hub_amount)) + .ok_or(ArithmeticError::Overflow)?, + )?; + + // LP receives some hub asset + if state_changes.lp_hub_amount > Balance::zero() { + T::Currency::transfer( + T::HubAssetId::get(), + &Self::protocol_account(), + &owner, + state_changes.lp_hub_amount, + )?; + } + + ensure!(updated_position.shares == Balance::zero(), Error::::SharesLeft); + + >::remove(position_id); + T::NFTHandler::burn(&T::NFTCollectionId::get(), &position_id, Some(&owner))?; + + Self::deposit_event(Event::PositionDestroyed { + position_id, + owner: owner.clone(), + }); + + // Callback hook info + let info: AssetInfo = + AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset); + + Self::set_asset_state(asset_id, new_asset_state); + + Self::deposit_event(Event::LiquidityRemoved { + who: owner, + position_id, + asset_id, + shares_removed: amount, + fee: FixedU128::zero(), + }); + + T::OmnipoolHooks::on_liquidity_changed(origin, info)?; + + + Ok(()) + } + + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::withdraw_protocol_shares())] + #[transactional] + pub fn withdraw_protocol_shares(origin: OriginFor, + asset_id: T::AssetId) -> DispatchResult { + ensure_root(origin.clone())?; + + let asset_state = Self::load_asset_state(asset_id)?; + + ensure!(asset_state.shares == asset_state.protocol_shares, Error::::PositionsLeft); + + + Ok(()) + } + } #[pallet::hooks] diff --git a/pallets/omnipool/src/weights.rs b/pallets/omnipool/src/weights.rs index bb6597f5c..8ef63aa82 100644 --- a/pallets/omnipool/src/weights.rs +++ b/pallets/omnipool/src/weights.rs @@ -60,6 +60,8 @@ pub trait WeightInfo { fn set_asset_weight_cap() -> Weight; fn router_execution_sell(c: u32, e: u32) -> Weight; fn router_execution_buy(c: u32, e: u32) -> Weight; + fn force_withdraw_position() -> Weight; + fn withdraw_protocol_shares() -> Weight; } /// Weights for pallet_omnipool using the hydraDX node and recommended hardware. @@ -417,6 +419,14 @@ impl WeightInfo for HydraWeight { .saturating_add(T::DbWeight::get().reads(24 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } + + fn force_withdraw_position() -> Weight { + Weight::zero() + } + + fn withdraw_protocol_shares() -> Weight { + Weight::zero() + } } // For backwards compatibility and tests @@ -772,4 +782,11 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(24 as u64)) .saturating_add(RocksDbWeight::get().writes(15 as u64)) } + fn force_withdraw_position() -> Weight { + Weight::zero() + } + + fn withdraw_protocol_shares() -> Weight { + Weight::zero() + } } From dabbee193fa1d19089490c2b4ed57713c3eb8170 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 12 Oct 2023 13:19:06 +0200 Subject: [PATCH 02/17] add withdraw pol --- pallets/omnipool/src/lib.rs | 93 +++++++++++++++++++++++++++++++-- pallets/omnipool/src/weights.rs | 6 +-- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 635c59f12..4b825bab4 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -267,6 +267,13 @@ pub mod pallet { shares_removed: Balance, fee: FixedU128, }, + /// Liquidity of an asset was removed to Omnipool. + ProtocolLiquidityRemoved { + who: T::AccountId, + asset_id: T::AssetId, + shares_removed: Balance, + hub_amount: Balance, + }, /// Sell trade executed. SellExecuted { who: T::AccountId, @@ -1607,17 +1614,95 @@ pub mod pallet { } #[pallet::call_index(12)] - #[pallet::weight(::WeightInfo::withdraw_protocol_shares())] + #[pallet::weight(::WeightInfo::withdraw_protocol_liquidity())] #[transactional] - pub fn withdraw_protocol_shares(origin: OriginFor, - asset_id: T::AssetId) -> DispatchResult { + pub fn withdraw_protocol_liquidity(origin: OriginFor, + asset_id: T::AssetId, + amount: Balance, + price: (Balance, Balance), + dest: T::AccountId, + ) -> DispatchResult { ensure_root(origin.clone())?; let asset_state = Self::load_asset_state(asset_id)?; + ensure!(amount >= asset_state.protocol_shares, Error::::InsufficientShares); + + let current_imbalance = >::get(); + let current_hub_asset_liquidity = + T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); + + // dev note: as we no longer have the position details for sacrificed one, we just need to + // construct temporary position. + // Note that amount is ok to set to zero in this case. Although the remove liquidity calculation + // calculates the delta for this field, it does not make any difference afterwards. + let position = hydra_dx_math::omnipool::types::Position:: { + amount: 0, + price, + shares: amount, + }; + + let state_changes = hydra_dx_math::omnipool::calculate_remove_liquidity_state_changes( + &(&asset_state).into(), + amount, + &position, + I129 { + value: current_imbalance.value, + negative: current_imbalance.negative, + }, + current_hub_asset_liquidity, + FixedU128::zero(), + ) + .ok_or(ArithmeticError::Overflow)?; - ensure!(asset_state.shares == asset_state.protocol_shares, Error::::PositionsLeft); + let mut new_asset_state = asset_state + .clone() + .delta_update(&state_changes.asset) + .ok_or(ArithmeticError::Overflow)?; + new_asset_state.protocol_shares = new_asset_state.protocol_shares.saturating_sub(amount); + T::Currency::transfer( + asset_id, + &Self::protocol_account(), + &dest, + *state_changes.asset.delta_reserve, + )?; + + Self::update_imbalance(state_changes.delta_imbalance)?; + + // burn only difference between delta hub and lp hub amount. + Self::update_hub_asset_liquidity( + &state_changes + .asset + .delta_hub_reserve + .merge(BalanceUpdate::Increase(state_changes.lp_hub_amount)) + .ok_or(ArithmeticError::Overflow)?, + )?; + + // LP receives some hub asset + if state_changes.lp_hub_amount > Balance::zero() { + T::Currency::transfer( + T::HubAssetId::get(), + &Self::protocol_account(), + &dest, + state_changes.lp_hub_amount, + )?; + } + + // Callback hook info + let info: AssetInfo = + AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset); + + Self::set_asset_state(asset_id, new_asset_state); + + Self::deposit_event(Event::ProtocolLiquidityRemoved { + who: dest, + asset_id, + shares_removed: amount, + hub_amount: state_changes.lp_hub_amount, + }); + + T::OmnipoolHooks::on_liquidity_changed(origin, info)?; Ok(()) } diff --git a/pallets/omnipool/src/weights.rs b/pallets/omnipool/src/weights.rs index 8ef63aa82..c15e2fcb5 100644 --- a/pallets/omnipool/src/weights.rs +++ b/pallets/omnipool/src/weights.rs @@ -61,7 +61,7 @@ pub trait WeightInfo { fn router_execution_sell(c: u32, e: u32) -> Weight; fn router_execution_buy(c: u32, e: u32) -> Weight; fn force_withdraw_position() -> Weight; - fn withdraw_protocol_shares() -> Weight; + fn withdraw_protocol_liquidity() -> Weight; } /// Weights for pallet_omnipool using the hydraDX node and recommended hardware. @@ -424,7 +424,7 @@ impl WeightInfo for HydraWeight { Weight::zero() } - fn withdraw_protocol_shares() -> Weight { + fn withdraw_protocol_liquidity() -> Weight { Weight::zero() } } @@ -786,7 +786,7 @@ impl WeightInfo for () { Weight::zero() } - fn withdraw_protocol_shares() -> Weight { + fn withdraw_protocol_liquidity() -> Weight { Weight::zero() } } From 1ba594cd52b138814f2fb46264b528c434f9f10b Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 12 Oct 2023 17:37:11 +0200 Subject: [PATCH 03/17] add force withdraw position tests --- pallets/omnipool/src/lib.rs | 11 ++- .../omnipool/src/tests/remove_liquidity.rs | 89 +++++++++++++++++++ 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 4b825bab4..d5b03fa15 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1513,13 +1513,13 @@ pub mod pallet { #[pallet::call_index(11)] #[pallet::weight(::WeightInfo::force_withdraw_position())] #[transactional] - pub fn force_withdraw_position(origin: OriginFor, - position_id: T::PositionItemId ) -> DispatchResult { + pub fn force_withdraw_position(origin: OriginFor, position_id: T::PositionItemId) -> DispatchResult { ensure_root(origin.clone())?; let position = Positions::::get(position_id).ok_or(Error::::PositionNotFound)?; - let owner = T::NFTHandler::owner(&T::NFTCollectionId::get(), &position_id).ok_or(Error::::OwnerNotFound)?; + let owner = + T::NFTHandler::owner(&T::NFTCollectionId::get(), &position_id).ok_or(Error::::OwnerNotFound)?; let asset_id = position.asset_id; let asset_state = Self::load_asset_state(asset_id)?; @@ -1609,14 +1609,14 @@ pub mod pallet { T::OmnipoolHooks::on_liquidity_changed(origin, info)?; - Ok(()) } #[pallet::call_index(12)] #[pallet::weight(::WeightInfo::withdraw_protocol_liquidity())] #[transactional] - pub fn withdraw_protocol_liquidity(origin: OriginFor, + pub fn withdraw_protocol_liquidity( + origin: OriginFor, asset_id: T::AssetId, amount: Balance, price: (Balance, Balance), @@ -1705,7 +1705,6 @@ pub mod pallet { T::OmnipoolHooks::on_liquidity_changed(origin, info)?; Ok(()) } - } #[pallet::hooks] diff --git a/pallets/omnipool/src/tests/remove_liquidity.rs b/pallets/omnipool/src/tests/remove_liquidity.rs index 85d28c58b..64151073d 100644 --- a/pallets/omnipool/src/tests/remove_liquidity.rs +++ b/pallets/omnipool/src/tests/remove_liquidity.rs @@ -605,3 +605,92 @@ fn remove_liquidity_should_apply_correct_fee_when_price_is_different() { assert_eq!(position, expected); }); } + +#[test] +fn force_withdraw_position_should_work_correctly() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + assert_balance!(LP1, 1_000, 4600 * ONE); + + assert_ok!(Omnipool::force_withdraw_position( + RuntimeOrigin::root(), + current_position_id, + )); + + assert_asset_state!( + 1_000, + AssetReserveState { + reserve: 2000000000000000, + hub_reserve: 1300000000000000, + shares: 2000000000000000, + protocol_shares: Balance::zero(), + cap: DEFAULT_WEIGHT_CAP, + tradable: Tradability::default(), + } + ); + + let position = Positions::::get(current_position_id); + assert!(position.is_none()); + + assert_balance!(LP1, 1_000, 5000000000000000); + assert_balance!(LP1, LRNA, 0); + }); +} + +#[test] +fn force_withdraw_position_should_transfer_lrna() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + assert_ok!(Omnipool::buy( + RuntimeOrigin::signed(LP2), + 1_000, + DAI, + 200 * ONE, + 500000 * ONE + )); + + assert_ok!(Omnipool::force_withdraw_position( + RuntimeOrigin::root(), + current_position_id, + )); + + let position = Positions::::get(current_position_id); + assert!(position.is_none()); + + assert_balance!(LP1, 1_000, 4966666666666666); + assert_balance!(LP1, LRNA, 24617495711835); + }); +} From c4c23f9f9eb7e6881913e6d7fd4594b0c551d9d2 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 13 Oct 2023 09:39:41 +0200 Subject: [PATCH 04/17] add withdraw pol tests --- .../omnipool/src/tests/remove_liquidity.rs | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/pallets/omnipool/src/tests/remove_liquidity.rs b/pallets/omnipool/src/tests/remove_liquidity.rs index 64151073d..fde00e0b0 100644 --- a/pallets/omnipool/src/tests/remove_liquidity.rs +++ b/pallets/omnipool/src/tests/remove_liquidity.rs @@ -694,3 +694,143 @@ fn force_withdraw_position_should_transfer_lrna() { assert_balance!(LP1, LRNA, 24617495711835); }); } + +#[test] +fn withdraw_protocol_liquidity_should_work_correctly() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + let position = Positions::::get(current_position_id).unwrap(); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + assert_asset_state!( + 1_000, + AssetReserveState { + reserve: 2400000000000000, + hub_reserve: 1560000000000000, + shares: 2400000000000000, + protocol_shares: 400000000000000, + cap: DEFAULT_WEIGHT_CAP, + tradable: Tradability::default(), + } + ); + + assert_ok!(Omnipool::withdraw_protocol_liquidity( + RuntimeOrigin::root(), + 1000, + position.shares, + position.price, + 1234, + )); + + let position = Positions::::get(current_position_id); + assert!(position.is_none()); + + assert_balance!(1234, 1_000, 400 * ONE); + assert_balance!(1234, LRNA, 0); + assert_asset_state!( + 1_000, + AssetReserveState { + reserve: 2000000000000000, + hub_reserve: 1300000000000000, + shares: 2000000000000000, + protocol_shares: 0, + cap: DEFAULT_WEIGHT_CAP, + tradable: Tradability::default(), + } + ); + }); +} + +#[test] +fn withdraw_protocol_liquidity_should_transfer_lrna_when_price_is_different() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + let position = Positions::::get(current_position_id).unwrap(); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + assert_asset_state!( + 1_000, + AssetReserveState { + reserve: 2400000000000000, + hub_reserve: 1560000000000000, + shares: 2400000000000000, + protocol_shares: 400000000000000, + cap: DEFAULT_WEIGHT_CAP, + tradable: Tradability::default(), + } + ); + + assert_ok!(Omnipool::buy( + RuntimeOrigin::signed(LP2), + 1_000, + DAI, + 200 * ONE, + 500000 * ONE + )); + + assert_ok!(Omnipool::withdraw_protocol_liquidity( + RuntimeOrigin::root(), + 1000, + position.shares, + position.price, + 1234, + )); + + let position = Positions::::get(current_position_id); + assert!(position.is_none()); + + assert_balance!(1234, 1_000, 366666666666666); + assert_balance!(1234, LRNA, 24617495711835); + assert_asset_state!( + 1_000, + AssetReserveState { + reserve: 1833_333_333_333_334, + hub_reserve: 1418181818181819, + shares: 2000000000000000, + protocol_shares: 0, + cap: DEFAULT_WEIGHT_CAP, + tradable: Tradability::default(), + } + ); + }); +} From 001e2d15d80f78128f04d08be4feac0252d76866 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 13 Oct 2023 17:53:57 +0200 Subject: [PATCH 05/17] burn lrna below ed --- pallets/omnipool/src/lib.rs | 46 ++++++++++++------------- runtime/hydradx/src/weights/omnipool.rs | 8 +++++ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index d5b03fa15..b9d8e337a 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -917,14 +917,7 @@ pub mod pallet { )?; // LP receives some hub asset - if state_changes.lp_hub_amount > Balance::zero() { - T::Currency::transfer( - T::HubAssetId::get(), - &Self::protocol_account(), - &who, - state_changes.lp_hub_amount, - )?; - } + Self::process_hub_amount(state_changes.lp_hub_amount, &who)?; if updated_position.shares == Balance::zero() { // All liquidity removed, remove position and burn NFT instance @@ -1555,6 +1548,7 @@ pub mod pallet { ) .ok_or(ArithmeticError::Overflow)?; + /* T::Currency::transfer( asset_id, &Self::protocol_account(), @@ -1562,6 +1556,8 @@ pub mod pallet { *state_changes.asset.delta_reserve, )?; + */ + Self::update_imbalance(state_changes.delta_imbalance)?; // burn only difference between delta hub and lp hub amount. @@ -1574,14 +1570,7 @@ pub mod pallet { )?; // LP receives some hub asset - if state_changes.lp_hub_amount > Balance::zero() { - T::Currency::transfer( - T::HubAssetId::get(), - &Self::protocol_account(), - &owner, - state_changes.lp_hub_amount, - )?; - } + Self::process_hub_amount(state_changes.lp_hub_amount, &owner)?; ensure!(updated_position.shares == Balance::zero(), Error::::SharesLeft); @@ -1680,14 +1669,7 @@ pub mod pallet { )?; // LP receives some hub asset - if state_changes.lp_hub_amount > Balance::zero() { - T::Currency::transfer( - T::HubAssetId::get(), - &Self::protocol_account(), - &dest, - state_changes.lp_hub_amount, - )?; - } + Self::process_hub_amount(state_changes.lp_hub_amount, &dest)?; // Callback hook info let info: AssetInfo = @@ -2155,4 +2137,20 @@ impl Pallet { ); Ok(()) } + + pub fn process_hub_amount(amount: Balance, dest: &T::AccountId) -> DispatchResult { + if amount > Balance::zero() { + if amount < 400_000_000u128 { + T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), amount)?; + }else{ + T::Currency::transfer( + T::HubAssetId::get(), + &Self::protocol_account(), + dest, + amount, + )?; + } + } + Ok(()) + } } diff --git a/runtime/hydradx/src/weights/omnipool.rs b/runtime/hydradx/src/weights/omnipool.rs index 1273b734f..d0fb26064 100644 --- a/runtime/hydradx/src/weights/omnipool.rs +++ b/runtime/hydradx/src/weights/omnipool.rs @@ -403,4 +403,12 @@ impl WeightInfo for HydraWeight { .saturating_add(T::DbWeight::get().reads(24 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } + + fn force_withdraw_position() -> Weight { + Weight::zero() + } + + fn withdraw_protocol_liquidity() -> Weight { + Weight::zero() + } } From f3942cfb4a5276f9856702b01e226d9beb8553ec Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 18 Oct 2023 11:32:35 +0200 Subject: [PATCH 06/17] add remove token, skip circuit breaker if safe withdrawal --- integration-tests/src/omnipool_init.rs | 91 +++++++ integration-tests/src/polkadot_test_net.rs | 1 + pallets/omnipool/src/lib.rs | 239 ++++++++---------- .../omnipool/src/tests/remove_liquidity.rs | 119 +++++++-- pallets/omnipool/src/traits.rs | 3 + pallets/omnipool/src/types.rs | 6 + pallets/omnipool/src/weights.rs | 6 +- runtime/adapters/src/lib.rs | 24 +- runtime/hydradx/src/weights/omnipool.rs | 2 +- 9 files changed, 314 insertions(+), 177 deletions(-) diff --git a/integration-tests/src/omnipool_init.rs b/integration-tests/src/omnipool_init.rs index 186744fcb..da18be5c8 100644 --- a/integration-tests/src/omnipool_init.rs +++ b/integration-tests/src/omnipool_init.rs @@ -6,6 +6,7 @@ use frame_support::{assert_noop, assert_ok}; use orml_traits::currency::MultiCurrency; use orml_traits::MultiCurrencyExtended; +use sp_runtime::FixedPointNumber; use sp_runtime::{FixedU128, Permill}; use xcm_emulator::TestExt; @@ -227,3 +228,93 @@ fn add_liquidity_should_fail_when_price_changes_across_multiple_block() { ); }); } + +#[test] +fn withdraw_pool_liquidity_should_work_when_withdrawing_all() { + TestNet::reset(); + + Hydra::execute_with(|| { + init_omnipool(); + let bob_account = AccountId::from(UNKNOWN); + let stable_price = FixedU128::from_inner(45_000_000_000); + let state = pallet_omnipool::Pallet::::load_asset_state(DAI).unwrap(); + let dai_reserve = state.reserve; + + assert_ok!(hydradx_runtime::Omnipool::withdraw_protocol_liquidity( + hydradx_runtime::RuntimeOrigin::root(), + DAI, + state.protocol_shares, + (stable_price.into_inner(), FixedU128::DIV), + bob_account.clone() + )); + + let state = pallet_omnipool::Pallet::::load_asset_state(DAI).unwrap(); + assert_eq!(state.reserve, 0); + assert_eq!(state.hub_reserve, 0); + assert_eq!(state.shares, 0); + assert_eq!(state.protocol_shares, 0); + + let dai_balance = hydradx_runtime::Tokens::free_balance(DAI, &bob_account); + assert!(dai_balance <= dai_reserve); + let lrna_balance = hydradx_runtime::Tokens::free_balance(LRNA, &bob_account); + assert_eq!(lrna_balance, 0); + }); +} + +use pallet_omnipool::types::Tradability; +#[test] +fn removing_token_should_work_when_no_shares_remaining() { + TestNet::reset(); + + Hydra::execute_with(|| { + init_omnipool(); + let bob_account = AccountId::from(UNKNOWN); + let dot_amount = 87_719_298_250_000_u128; + + let token_price = FixedU128::from_inner(25_650_000_000_000_000_000); + assert_ok!(hydradx_runtime::Omnipool::add_token( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + token_price, + Permill::from_percent(100), + bob_account.clone(), + )); + + hydra_run_to_block(10); + + assert_ok!(hydradx_runtime::Omnipool::set_asset_tradable_state( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY + )); + + let position = + pallet_omnipool::Pallet::::load_position(0, bob_account.clone()).unwrap(); + assert_ok!(hydradx_runtime::Omnipool::remove_liquidity( + hydradx_runtime::RuntimeOrigin::signed(UNKNOWN.into()), + 0, + position.shares, + )); + + let state = pallet_omnipool::Pallet::::load_asset_state(DOT).unwrap(); + let dot_balance = hydradx_runtime::Tokens::free_balance(DOT, &bob_account); + assert!(dot_balance <= dot_amount); + let lrna_balance = hydradx_runtime::Tokens::free_balance(LRNA, &bob_account); + assert_eq!(lrna_balance, 0); + + assert_ok!(hydradx_runtime::Omnipool::set_asset_tradable_state( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + Tradability::FROZEN + )); + assert_ok!(hydradx_runtime::Omnipool::remove_token( + hydradx_runtime::RuntimeOrigin::root(), + DOT, + bob_account.clone(), + )); + let dot_balance = hydradx_runtime::Tokens::free_balance(DOT, &bob_account); + assert!(dot_balance == dot_amount); + let lrna_balance = hydradx_runtime::Tokens::free_balance(LRNA, &bob_account); + assert_eq!(lrna_balance, 0); + }); +} diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index 3a7b8e2d4..c7fe25598 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -24,6 +24,7 @@ pub const ALICE: [u8; 32] = [4u8; 32]; pub const BOB: [u8; 32] = [5u8; 32]; pub const CHARLIE: [u8; 32] = [6u8; 32]; pub const DAVE: [u8; 32] = [7u8; 32]; +pub const UNKNOWN: [u8; 32] = [8u8; 32]; pub const UNITS: Balance = 1_000_000_000_000; diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index b9d8e337a..b3a4cc596 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -252,6 +252,8 @@ pub mod pallet { initial_amount: Balance, initial_price: Price, }, + /// An asset was removed from Omnipool + TokenRemoved { asset_id: T::AssetId }, /// Liquidity of an asset was added to Omnipool. LiquidityAdded { who: T::AccountId, @@ -397,12 +399,10 @@ pub mod pallet { InvalidWithdrawalFee, /// More than allowed amount of fee has been transferred. FeeOverdraft, - /// - SharesLeft, - /// - OwnerNotFound, - /// - PositionsLeft, + /// Token cannot be removed from Omnipool due to some shares are remaining owned by other users. + SharesRemaining, + /// Token cannot be removed from Omnipool because asset is not frozen. + AssetNotFrozen, } #[pallet::call] @@ -627,7 +627,7 @@ pub mod pallet { }; T::OmnipoolHooks::on_liquidity_changed( origin, - AssetInfo::new(asset, &AssetReserveState::default(), &reserve_state, &changes), + AssetInfo::new(asset, &AssetReserveState::default(), &reserve_state, &changes, false), )?; >::insert(asset, state); @@ -771,7 +771,7 @@ pub mod pallet { // Callback hook info let info: AssetInfo = - AssetInfo::new(asset, &asset_state, &new_asset_state, &state_changes.asset); + AssetInfo::new(asset, &asset_state, &new_asset_state, &state_changes.asset, false); Self::update_imbalance(state_changes.delta_imbalance)?; @@ -840,24 +840,22 @@ pub mod pallet { Error::::NotAllowed ); - T::PriceBarrier::ensure_price( - &who, - T::HubAssetId::get(), - asset_id, - EmaPrice::new(asset_state.hub_reserve, asset_state.reserve), - ) - .map_err(|_| Error::::PriceDifferenceTooHigh)?; - - let current_imbalance = >::get(); - let current_hub_asset_liquidity = - T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); - + let safe_withdrawal = asset_state.tradable.is_safe_withdrawal(); + // Skip price check if safe withdrawal - trading disabled. + if !safe_withdrawal { + T::PriceBarrier::ensure_price( + &who, + T::HubAssetId::get(), + asset_id, + EmaPrice::new(asset_state.hub_reserve, asset_state.reserve), + ) + .map_err(|_| Error::::PriceDifferenceTooHigh)?; + } let ext_asset_price = T::ExternalPriceOracle::get_price(T::HubAssetId::get(), asset_id)?; if ext_asset_price.is_zero() { return Err(Error::::InvalidOraclePrice.into()); } - let withdrawal_fee = hydra_dx_math::omnipool::calculate_withdrawal_fee( asset_state.price().ok_or(ArithmeticError::DivisionByZero)?, FixedU128::checked_from_rational(ext_asset_price.n, ext_asset_price.d) @@ -865,6 +863,10 @@ pub mod pallet { T::MinWithdrawalFee::get(), ); + let current_imbalance = >::get(); + let current_hub_asset_liquidity = + T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); + // // calculate state changes of remove liquidity // @@ -897,7 +899,6 @@ pub mod pallet { // // Post - update states // - T::Currency::transfer( asset_id, &Self::protocol_account(), @@ -945,8 +946,13 @@ pub mod pallet { } // Callback hook info - let info: AssetInfo = - AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset); + let info: AssetInfo = AssetInfo::new( + asset_id, + &asset_state, + &new_asset_state, + &state_changes.asset, + safe_withdrawal, + ); Self::set_asset_state(asset_id, new_asset_state); @@ -1158,14 +1164,20 @@ pub mod pallet { }; // Callback hook info - let info_in: AssetInfo = - AssetInfo::new(asset_in, &asset_in_state, &new_asset_in_state, &state_changes.asset_in); + let info_in: AssetInfo = AssetInfo::new( + asset_in, + &asset_in_state, + &new_asset_in_state, + &state_changes.asset_in, + false, + ); let info_out: AssetInfo = AssetInfo::new( asset_out, &asset_out_state, &new_asset_out_state, &state_changes.asset_out, + false, ); Self::update_imbalance(state_changes.delta_imbalance)?; @@ -1344,14 +1356,20 @@ pub mod pallet { }; // Callback hook info - let info_in: AssetInfo = - AssetInfo::new(asset_in, &asset_in_state, &new_asset_in_state, &state_changes.asset_in); + let info_in: AssetInfo = AssetInfo::new( + asset_in, + &asset_in_state, + &new_asset_in_state, + &state_changes.asset_in, + false, + ); let info_out: AssetInfo = AssetInfo::new( asset_out, &asset_out_state, &new_asset_out_state, &state_changes.asset_out, + false, ); Self::update_imbalance(state_changes.delta_imbalance)?; @@ -1504,104 +1522,6 @@ pub mod pallet { } #[pallet::call_index(11)] - #[pallet::weight(::WeightInfo::force_withdraw_position())] - #[transactional] - pub fn force_withdraw_position(origin: OriginFor, position_id: T::PositionItemId) -> DispatchResult { - ensure_root(origin.clone())?; - - let position = Positions::::get(position_id).ok_or(Error::::PositionNotFound)?; - - let owner = - T::NFTHandler::owner(&T::NFTCollectionId::get(), &position_id).ok_or(Error::::OwnerNotFound)?; - - let asset_id = position.asset_id; - let asset_state = Self::load_asset_state(asset_id)?; - let current_imbalance = >::get(); - let current_hub_asset_liquidity = - T::Currency::free_balance(T::HubAssetId::get(), &Self::protocol_account()); - - let amount = position.shares; - - let state_changes = hydra_dx_math::omnipool::calculate_remove_liquidity_state_changes( - &(&asset_state).into(), - amount, - &(&position).into(), - I129 { - value: current_imbalance.value, - negative: current_imbalance.negative, - }, - current_hub_asset_liquidity, - FixedU128::zero(), - ) - .ok_or(ArithmeticError::Overflow)?; - - let new_asset_state = asset_state - .clone() - .delta_update(&state_changes.asset) - .ok_or(ArithmeticError::Overflow)?; - - // Update position state - let updated_position = position - .delta_update( - &state_changes.delta_position_reserve, - &state_changes.delta_position_shares, - ) - .ok_or(ArithmeticError::Overflow)?; - - /* - T::Currency::transfer( - asset_id, - &Self::protocol_account(), - &owner, - *state_changes.asset.delta_reserve, - )?; - - */ - - Self::update_imbalance(state_changes.delta_imbalance)?; - - // burn only difference between delta hub and lp hub amount. - Self::update_hub_asset_liquidity( - &state_changes - .asset - .delta_hub_reserve - .merge(BalanceUpdate::Increase(state_changes.lp_hub_amount)) - .ok_or(ArithmeticError::Overflow)?, - )?; - - // LP receives some hub asset - Self::process_hub_amount(state_changes.lp_hub_amount, &owner)?; - - ensure!(updated_position.shares == Balance::zero(), Error::::SharesLeft); - - >::remove(position_id); - T::NFTHandler::burn(&T::NFTCollectionId::get(), &position_id, Some(&owner))?; - - Self::deposit_event(Event::PositionDestroyed { - position_id, - owner: owner.clone(), - }); - - // Callback hook info - let info: AssetInfo = - AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset); - - Self::set_asset_state(asset_id, new_asset_state); - - Self::deposit_event(Event::LiquidityRemoved { - who: owner, - position_id, - asset_id, - shares_removed: amount, - fee: FixedU128::zero(), - }); - - T::OmnipoolHooks::on_liquidity_changed(origin, info)?; - - Ok(()) - } - - #[pallet::call_index(12)] #[pallet::weight(::WeightInfo::withdraw_protocol_liquidity())] #[transactional] pub fn withdraw_protocol_liquidity( @@ -1673,7 +1593,7 @@ pub mod pallet { // Callback hook info let info: AssetInfo = - AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset); + AssetInfo::new(asset_id, &asset_state, &new_asset_state, &state_changes.asset, true); Self::set_asset_state(asset_id, new_asset_state); @@ -1687,6 +1607,35 @@ pub mod pallet { T::OmnipoolHooks::on_liquidity_changed(origin, info)?; Ok(()) } + + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::remove_token())] + #[transactional] + pub fn remove_token(origin: OriginFor, asset_id: T::AssetId, beneficiary: T::AccountId) -> DispatchResult { + ensure_root(origin)?; + let asset_state = Self::load_asset_state(asset_id)?; + + // Allow only if no shares owned by LPs and asset is frozen. + ensure!(asset_state.shares == Balance::zero(), Error::::SharesRemaining); + ensure!(asset_state.tradable == Tradability::FROZEN, Error::::AssetNotFrozen); + + T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), asset_state.hub_reserve)?; + + /* + T::Currency::transfer( + T::HubAssetId::get(), + &Self::protocol_account(), + &beneficiary, + asset_state.hub_reserve, + )?; + + */ + T::Currency::transfer(asset_id, &Self::protocol_account(), &beneficiary, asset_state.reserve)?; + >::remove(asset_id); + + Self::deposit_event(Event::TokenRemoved { asset_id }); + Ok(()) + } } #[pallet::hooks] @@ -1775,8 +1724,13 @@ impl Pallet { ..Default::default() }; - let info: AssetInfo = - AssetInfo::new(T::HdxAssetId::get(), &hdx_state, &updated_hdx_state, &delta_changes); + let info: AssetInfo = AssetInfo::new( + T::HdxAssetId::get(), + &hdx_state, + &updated_hdx_state, + &delta_changes, + false, + ); T::OmnipoolHooks::on_liquidity_changed(origin, info)?; } @@ -1905,8 +1859,13 @@ impl Pallet { *state_changes.asset.delta_reserve, )?; - let info: AssetInfo = - AssetInfo::new(asset_out, &asset_state, &new_asset_out_state, &state_changes.asset); + let info: AssetInfo = AssetInfo::new( + asset_out, + &asset_state, + &new_asset_out_state, + &state_changes.asset, + false, + ); Self::update_imbalance(state_changes.delta_imbalance)?; @@ -2006,8 +1965,13 @@ impl Pallet { *state_changes.asset.delta_reserve, )?; - let info: AssetInfo = - AssetInfo::new(asset_out, &asset_state, &new_asset_out_state, &state_changes.asset); + let info: AssetInfo = AssetInfo::new( + asset_out, + &asset_state, + &new_asset_out_state, + &state_changes.asset, + false, + ); Self::update_imbalance(state_changes.delta_imbalance)?; @@ -2142,13 +2106,8 @@ impl Pallet { if amount > Balance::zero() { if amount < 400_000_000u128 { T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), amount)?; - }else{ - T::Currency::transfer( - T::HubAssetId::get(), - &Self::protocol_account(), - dest, - amount, - )?; + } else { + T::Currency::transfer(T::HubAssetId::get(), &Self::protocol_account(), dest, amount)?; } } Ok(()) diff --git a/pallets/omnipool/src/tests/remove_liquidity.rs b/pallets/omnipool/src/tests/remove_liquidity.rs index fde00e0b0..624c8a919 100644 --- a/pallets/omnipool/src/tests/remove_liquidity.rs +++ b/pallets/omnipool/src/tests/remove_liquidity.rs @@ -607,7 +607,7 @@ fn remove_liquidity_should_apply_correct_fee_when_price_is_different() { } #[test] -fn force_withdraw_position_should_work_correctly() { +fn safe_withdrawal_should_work_correctly_when_trading_is_disabled() { ExtBuilder::default() .with_endowed_accounts(vec![ (Omnipool::protocol_account(), DAI, 1000 * ONE), @@ -621,40 +621,45 @@ fn force_withdraw_position_should_work_correctly() { .build() .execute_with(|| { let liq_added = 400 * ONE; - let current_position_id = >::get(); - assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); - assert_balance!(LP1, 1_000, 4600 * ONE); - - assert_ok!(Omnipool::force_withdraw_position( + assert_ok!(Omnipool::set_asset_tradable_state( RuntimeOrigin::root(), + 1_000, + Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY + )); + + let position = Positions::::get(current_position_id).unwrap(); + + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), current_position_id, + position.shares, )); assert_asset_state!( 1_000, AssetReserveState { - reserve: 2000000000000000, - hub_reserve: 1300000000000000, + reserve: 2004000000000000, + hub_reserve: 1302600000000000, shares: 2000000000000000, protocol_shares: Balance::zero(), cap: DEFAULT_WEIGHT_CAP, - tradable: Tradability::default(), + tradable: Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY } ); let position = Positions::::get(current_position_id); assert!(position.is_none()); - assert_balance!(LP1, 1_000, 5000000000000000); + assert_balance!(LP1, 1_000, 4996000000000000); assert_balance!(LP1, LRNA, 0); }); } #[test] -fn force_withdraw_position_should_transfer_lrna() { +fn safe_withdrawal_should_transfer_lrna() { ExtBuilder::default() .with_endowed_accounts(vec![ (Omnipool::protocol_account(), DAI, 1000 * ONE), @@ -681,17 +686,25 @@ fn force_withdraw_position_should_transfer_lrna() { 200 * ONE, 500000 * ONE )); - - assert_ok!(Omnipool::force_withdraw_position( + assert_ok!(Omnipool::set_asset_tradable_state( RuntimeOrigin::root(), + 1_000, + Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY + )); + + let position = Positions::::get(current_position_id).unwrap(); + + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), current_position_id, + position.shares, )); let position = Positions::::get(current_position_id); assert!(position.is_none()); - assert_balance!(LP1, 1_000, 4966666666666666); - assert_balance!(LP1, LRNA, 24617495711835); + assert_balance!(LP1, 1_000, 4962999999999999); + assert_balance!(LP1, LRNA, 24371320754716); }); } @@ -711,17 +724,13 @@ fn withdraw_protocol_liquidity_should_work_correctly() { .build() .execute_with(|| { let liq_added = 400 * ONE; - let current_position_id = >::get(); - assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); - let position = Positions::::get(current_position_id).unwrap(); assert_ok!(Omnipool::sacrifice_position( RuntimeOrigin::signed(LP1), current_position_id )); - assert_asset_state!( 1_000, AssetReserveState { @@ -741,10 +750,6 @@ fn withdraw_protocol_liquidity_should_work_correctly() { position.price, 1234, )); - - let position = Positions::::get(current_position_id); - assert!(position.is_none()); - assert_balance!(1234, 1_000, 400 * ONE); assert_balance!(1234, LRNA, 0); assert_asset_state!( @@ -834,3 +839,71 @@ fn withdraw_protocol_liquidity_should_transfer_lrna_when_price_is_different() { ); }); } + +#[test] +fn remove_liquidity_should_skip_price_check_when_price_is_higher_and_is_safe_to_withdraw() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_max_allowed_price_difference(Permill::from_percent(1)) + .build() + .execute_with(|| { + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, 400 * ONE)); + + EXT_PRICE_ADJUSTMENT.with(|v| { + *v.borrow_mut() = (3, 100, false); + }); + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY + )); + + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), + current_position_id, + 200 * ONE, + ),); + }); +} + +#[test] +fn remove_liquidity_should_skip_price_check_when_price_is_lower_and_is_safe_to_withdraw() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_max_allowed_price_difference(Permill::from_percent(1)) + .build() + .execute_with(|| { + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, 400 * ONE)); + + EXT_PRICE_ADJUSTMENT.with(|v| { + *v.borrow_mut() = (3, 100, true); + }); + + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY + )); + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), + current_position_id, + 200 * ONE, + ),); + }); +} diff --git a/pallets/omnipool/src/traits.rs b/pallets/omnipool/src/traits.rs index e31c1f3a1..7e3d5f56e 100644 --- a/pallets/omnipool/src/traits.rs +++ b/pallets/omnipool/src/traits.rs @@ -16,6 +16,7 @@ where pub before: AssetReserveState, pub after: AssetReserveState, pub delta_changes: AssetStateChange, + pub safe_withdrawal: bool, } impl AssetInfo @@ -27,12 +28,14 @@ where before_state: &AssetReserveState, after_state: &AssetReserveState, delta_changes: &AssetStateChange, + safe_withdrawal: bool, ) -> Self { Self { asset_id, before: (*before_state).clone(), after: (*after_state).clone(), delta_changes: (*delta_changes).clone(), + safe_withdrawal, } } } diff --git a/pallets/omnipool/src/types.rs b/pallets/omnipool/src/types.rs index 573a79aae..757a3d395 100644 --- a/pallets/omnipool/src/types.rs +++ b/pallets/omnipool/src/types.rs @@ -34,6 +34,12 @@ impl Default for Tradability { } } +impl Tradability { + pub(crate) fn is_safe_withdrawal(&self) -> bool { + *self == Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY || *self == Tradability::REMOVE_LIQUIDITY + } +} + #[derive(Clone, Default, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct AssetState { /// Quantity of Hub Asset matching this asset diff --git a/pallets/omnipool/src/weights.rs b/pallets/omnipool/src/weights.rs index c15e2fcb5..17e1ab844 100644 --- a/pallets/omnipool/src/weights.rs +++ b/pallets/omnipool/src/weights.rs @@ -60,8 +60,8 @@ pub trait WeightInfo { fn set_asset_weight_cap() -> Weight; fn router_execution_sell(c: u32, e: u32) -> Weight; fn router_execution_buy(c: u32, e: u32) -> Weight; - fn force_withdraw_position() -> Weight; fn withdraw_protocol_liquidity() -> Weight; + fn remove_token() -> Weight; } /// Weights for pallet_omnipool using the hydraDX node and recommended hardware. @@ -420,7 +420,7 @@ impl WeightInfo for HydraWeight { .saturating_add(T::DbWeight::get().writes(15 as u64)) } - fn force_withdraw_position() -> Weight { + fn remove_token() -> Weight { Weight::zero() } @@ -782,7 +782,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(24 as u64)) .saturating_add(RocksDbWeight::get().writes(15 as u64)) } - fn force_withdraw_position() -> Weight { + fn remove_token() -> Weight { Weight::zero() } diff --git a/runtime/adapters/src/lib.rs b/runtime/adapters/src/lib.rs index 4bfb7fb5d..12c4ddf84 100644 --- a/runtime/adapters/src/lib.rs +++ b/runtime/adapters/src/lib.rs @@ -351,19 +351,23 @@ where .map_err(|(_, e)| e)?; match asset.delta_changes.delta_reserve { - BalanceUpdate::Increase(amount) => pallet_circuit_breaker::Pallet::::ensure_add_liquidity_limit( - origin, - asset.asset_id.into(), - asset.before.reserve.into(), - amount.into(), - )?, - BalanceUpdate::Decrease(amount) => { - pallet_circuit_breaker::Pallet::::ensure_remove_liquidity_limit( + BalanceUpdate::Increase(amount) => { + pallet_circuit_breaker::Pallet::::ensure_add_liquidity_limit( origin, asset.asset_id.into(), asset.before.reserve.into(), amount.into(), - )? + )?; + } + BalanceUpdate::Decrease(amount) => { + if !asset.safe_withdrawal { + pallet_circuit_breaker::Pallet::::ensure_remove_liquidity_limit( + origin, + asset.asset_id.into(), + asset.before.reserve.into(), + amount.into(), + )?; + } } }; @@ -474,7 +478,7 @@ where fn get_price(asset_a: AssetId, asset_b: AssetId) -> Result { let (price, _) = pallet_ema_oracle::Pallet::::get_price(asset_a, asset_b, Period::get(), OMNIPOOL_SOURCE) - .map_err(|_| pallet_omnipool::Error::::PriceDifferenceTooHigh)?; + .map_err(|_| pallet_omnipool::Error::::InvalidOraclePrice)?; Ok(price) } diff --git a/runtime/hydradx/src/weights/omnipool.rs b/runtime/hydradx/src/weights/omnipool.rs index d0fb26064..3b0ed4991 100644 --- a/runtime/hydradx/src/weights/omnipool.rs +++ b/runtime/hydradx/src/weights/omnipool.rs @@ -404,7 +404,7 @@ impl WeightInfo for HydraWeight { .saturating_add(T::DbWeight::get().writes(15 as u64)) } - fn force_withdraw_position() -> Weight { + fn remove_token() -> Weight { Weight::zero() } From 891fdc84a7fe3b8423ae95e7bfe382f9c54c8e54 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 18 Oct 2023 17:47:27 +0200 Subject: [PATCH 07/17] fix condition --- pallets/omnipool/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index b3a4cc596..699b7475b 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1534,7 +1534,7 @@ pub mod pallet { ensure_root(origin.clone())?; let asset_state = Self::load_asset_state(asset_id)?; - ensure!(amount >= asset_state.protocol_shares, Error::::InsufficientShares); + ensure!(amount <= asset_state.protocol_shares, Error::::InsufficientShares); let current_imbalance = >::get(); let current_hub_asset_liquidity = From 7656fcc730ec4cf3d668313cd4a8c2993ad0c020 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 Oct 2023 11:22:48 +0200 Subject: [PATCH 08/17] add remove token tests --- pallets/omnipool/src/lib.rs | 36 +-- pallets/omnipool/src/tests/mod.rs | 1 + .../omnipool/src/tests/remove_liquidity.rs | 85 ++++++- pallets/omnipool/src/tests/remove_token.rs | 228 ++++++++++++++++++ 4 files changed, 335 insertions(+), 15 deletions(-) create mode 100644 pallets/omnipool/src/tests/remove_token.rs diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 699b7475b..ce41411ec 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -253,7 +253,11 @@ pub mod pallet { initial_price: Price, }, /// An asset was removed from Omnipool - TokenRemoved { asset_id: T::AssetId }, + TokenRemoved { + asset_id: T::AssetId, + amount: Balance, + hub_withdrawn: Balance, + }, /// Liquidity of an asset was added to Omnipool. LiquidityAdded { who: T::AccountId, @@ -403,6 +407,8 @@ pub mod pallet { SharesRemaining, /// Token cannot be removed from Omnipool because asset is not frozen. AssetNotFrozen, + /// Configured stable asset cannot be removed from Omnipool. + StableAssetCannotBeRemoved, } #[pallet::call] @@ -1613,27 +1619,29 @@ pub mod pallet { #[transactional] pub fn remove_token(origin: OriginFor, asset_id: T::AssetId, beneficiary: T::AccountId) -> DispatchResult { ensure_root(origin)?; + + ensure!( + asset_id != T::StableCoinAssetId::get(), + Error::::StableAssetCannotBeRemoved + ); + let asset_state = Self::load_asset_state(asset_id)?; // Allow only if no shares owned by LPs and asset is frozen. - ensure!(asset_state.shares == Balance::zero(), Error::::SharesRemaining); ensure!(asset_state.tradable == Tradability::FROZEN, Error::::AssetNotFrozen); + ensure!( + asset_state.shares == asset_state.protocol_shares, + Error::::SharesRemaining + ); T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), asset_state.hub_reserve)?; - - /* - T::Currency::transfer( - T::HubAssetId::get(), - &Self::protocol_account(), - &beneficiary, - asset_state.hub_reserve, - )?; - - */ T::Currency::transfer(asset_id, &Self::protocol_account(), &beneficiary, asset_state.reserve)?; >::remove(asset_id); - - Self::deposit_event(Event::TokenRemoved { asset_id }); + Self::deposit_event(Event::TokenRemoved { + asset_id, + amount: asset_state.reserve, + hub_withdrawn: asset_state.hub_reserve, + }); Ok(()) } } diff --git a/pallets/omnipool/src/tests/mod.rs b/pallets/omnipool/src/tests/mod.rs index 9f070ed26..66175136c 100644 --- a/pallets/omnipool/src/tests/mod.rs +++ b/pallets/omnipool/src/tests/mod.rs @@ -16,6 +16,7 @@ mod init_pool; pub(crate) mod mock; mod positions; mod refund; +mod remove_token; mod tradability; mod tvl; mod types; diff --git a/pallets/omnipool/src/tests/remove_liquidity.rs b/pallets/omnipool/src/tests/remove_liquidity.rs index 624c8a919..42d5f2c2f 100644 --- a/pallets/omnipool/src/tests/remove_liquidity.rs +++ b/pallets/omnipool/src/tests/remove_liquidity.rs @@ -2,6 +2,7 @@ use super::*; use crate::types::Tradability; use frame_support::assert_noop; use sp_runtime::traits::One; +use sp_runtime::DispatchError::BadOrigin; #[test] fn remove_liquidity_works() { @@ -829,7 +830,7 @@ fn withdraw_protocol_liquidity_should_transfer_lrna_when_price_is_different() { assert_asset_state!( 1_000, AssetReserveState { - reserve: 1833_333_333_333_334, + reserve: 1_833_333_333_333_334, hub_reserve: 1418181818181819, shares: 2000000000000000, protocol_shares: 0, @@ -840,6 +841,88 @@ fn withdraw_protocol_liquidity_should_transfer_lrna_when_price_is_different() { }); } +#[test] +fn withdraw_protocol_liquidity_fail_when_not_root() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + let position = Positions::::get(current_position_id).unwrap(); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + assert_noop!( + Omnipool::withdraw_protocol_liquidity( + RuntimeOrigin::signed(LP1), + 1000, + position.shares, + position.price, + 1234, + ), + BadOrigin + ); + }); +} + +#[test] +fn withdraw_protocol_liquidity_fail_when_withdrawing_more_protocol_shares() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + + let position = Positions::::get(current_position_id).unwrap(); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + let state = Assets::::get(1_000).unwrap(); + + assert_noop!( + Omnipool::withdraw_protocol_liquidity( + RuntimeOrigin::root(), + 1000, + state.protocol_shares + 1, + position.price, + 1234, + ), + Error::::InsufficientShares + ); + }); +} + #[test] fn remove_liquidity_should_skip_price_check_when_price_is_higher_and_is_safe_to_withdraw() { ExtBuilder::default() diff --git a/pallets/omnipool/src/tests/remove_token.rs b/pallets/omnipool/src/tests/remove_token.rs new file mode 100644 index 000000000..c1bff86c3 --- /dev/null +++ b/pallets/omnipool/src/tests/remove_token.rs @@ -0,0 +1,228 @@ +use super::*; +use crate::types::Tradability; +use frame_support::assert_noop; +use sp_runtime::DispatchError::BadOrigin; + +#[test] +fn remove_token_should_fail_when_not_root() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + assert_noop!(Omnipool::remove_token(RuntimeOrigin::signed(LP1), 1000, LP1), BadOrigin,); + }); +} + +#[test] +fn remove_token_should_fail_when_removing_configured_stable_asset() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + assert_noop!( + Omnipool::remove_token(RuntimeOrigin::root(), 2, LP1), + Error::::StableAssetCannotBeRemoved + ); + }); +} + +#[test] +fn remove_token_should_fail_when_asset_is_not_frozen() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + + assert_noop!( + Omnipool::remove_token(RuntimeOrigin::root(), 1000, LP1), + Error::::AssetNotFrozen + ); + }); +} + +#[test] +fn remove_token_should_fail_when_lp_shares_remaining() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::FROZEN + )); + assert_noop!( + Omnipool::remove_token(RuntimeOrigin::root(), 1000, LP1), + Error::::SharesRemaining + ); + }); +} + +#[test] +fn remove_token_should_remove_asset_from_omnipool() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::FROZEN + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP2), + current_position_id - 1, + )); + assert_ok!(Omnipool::remove_token(RuntimeOrigin::root(), 1000, LP1),); + + assert!(Assets::::get(1000).is_none()); + }); +} + +#[test] +fn remove_token_should_transfer_remaining_asset_to_beneficiary_account() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::FROZEN + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP2), + current_position_id - 1, + )); + let reserve = Tokens::free_balance(1000, &Omnipool::protocol_account()); + assert_ok!(Omnipool::remove_token(RuntimeOrigin::root(), 1000, 1234),); + assert_balance!(1234, 1_000, reserve); + assert_balance!(Omnipool::protocol_account(), 1_000, 0); + assert_balance!(1234, LRNA, 0); + }); +} + +#[test] +fn remove_token_should_burn_hub_asset() { + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP2, 1_000, 2000 * ONE), + (LP2, DAI, 2000 * ONE), + (LP1, 1_000, 5000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(1_000, FixedU128::from_float(0.65), LP2, 2000 * ONE) + .with_min_withdrawal_fee(Permill::from_float(0.01)) + .build() + .execute_with(|| { + let liq_added = 400 * ONE; + let current_position_id = >::get(); + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), 1_000, liq_added)); + assert_ok!(Omnipool::set_asset_tradable_state( + RuntimeOrigin::root(), + 1_000, + Tradability::FROZEN + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP1), + current_position_id + )); + assert_ok!(Omnipool::sacrifice_position( + RuntimeOrigin::signed(LP2), + current_position_id - 1, + )); + let state = Assets::::get(1000).unwrap(); + let lrna_reserve = Tokens::free_balance(LRNA, &Omnipool::protocol_account()); + let lrna_issuance = Tokens::total_issuance(LRNA); + assert_ok!(Omnipool::remove_token(RuntimeOrigin::root(), 1000, 1234),); + let lrna_issuance_after = Tokens::total_issuance(LRNA); + assert_balance!(Omnipool::protocol_account(), LRNA, lrna_reserve - state.hub_reserve); + assert_eq!(lrna_issuance_after, lrna_issuance - state.hub_reserve); + }); +} From c7166a7d508432f5a8b35100fa01c38fa0834a81 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 Oct 2023 11:50:19 +0200 Subject: [PATCH 09/17] bump versions --- Cargo.lock | 8 ++++---- integration-tests/Cargo.toml | 2 +- pallets/omnipool/Cargo.toml | 2 +- runtime/adapters/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c27a34c6f..9003fed85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3786,7 +3786,7 @@ dependencies = [ [[package]] name = "hydradx-adapters" -version = "0.6.3" +version = "0.6.4" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -3831,7 +3831,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "183.0.0" +version = "184.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", @@ -7118,7 +7118,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "3.2.3" +version = "3.3.0" dependencies = [ "bitflags", "frame-benchmarking", @@ -10220,7 +10220,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.13.0" +version = "1.14.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 427cb6e53..dcc8170dd 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.13.0" +version = "1.14.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index 7bf8a892d..dd64f492b 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "3.2.3" +version = "3.3.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/runtime/adapters/Cargo.toml b/runtime/adapters/Cargo.toml index d2129fde7..1b001889f 100644 --- a/runtime/adapters/Cargo.toml +++ b/runtime/adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-adapters" -version = "0.6.3" +version = "0.6.4" description = "Structs and other generic types for building runtimes." authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 4d36357fe..bcbe3e3a5 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "183.0.0" +version = "184.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index c7ffe6dd6..50436df0c 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -94,7 +94,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 183, + spec_version: 184, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 4a5bb84746e9b7f1b2bd62d1ea836a738b49fbd2 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 Oct 2023 17:46:14 +0200 Subject: [PATCH 10/17] add withdraw pol benchmark --- runtime/hydradx/src/benchmarking/omnipool.rs | 63 +++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index f7b57c573..e41f20650 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -201,14 +201,16 @@ runtime_benchmarks! { update_balance(stable_id, &buyer, 500_000_000_000_000_u128); Omnipool::buy(RawOrigin::Signed(buyer).into(), token_id, stable_id, 100_000_000_000_u128, 100_000_000_000_000_u128)?; + let hub_id = ::HubAssetId::get(); + let hub_issuance = ::Currency::total_issuance(hub_id); }: {Omnipool::remove_liquidity(RawOrigin::Signed(lp_provider.clone()).into(), current_position_id, liquidity_added)? } verify { // Ensure NFT instance was burned assert!(Omnipool::positions(current_position_id).is_none()); - // Ensure lp provider received LRNA - let hub_id = ::HubAssetId::get(); - assert!(::Currency::free_balance(hub_id, &lp_provider) > Balance::zero()); + // Ensure LRNA was burned + let hub_issuance_after = ::Currency::total_issuance(hub_id); + assert!(hub_issuance_after < hub_issuance); } sell { @@ -436,6 +438,61 @@ runtime_benchmarks! { assert!(asset_state.cap == 100_000_000_000_000_000u128); } + withdraw_protocol_liquidity { + + // Initialize pool + let stable_amount: Balance = 1_000_000_000_000_000u128; + let native_amount: Balance = 1_000_000_000_000_000u128; + let stable_price: FixedU128= FixedU128::from((1,2)); + let native_price: FixedU128= FixedU128::from(1); + + let acc = Omnipool::protocol_account(); + let native_id = ::HdxAssetId::get(); + let stable_id = ::StableCoinAssetId::get(); + + Omnipool::set_tvl_cap(RawOrigin::Root.into(), TVL_CAP)?; + + update_balance(stable_id, &acc, stable_amount); + update_balance(native_id, &acc, native_amount); + + Omnipool::initialize_pool(RawOrigin::Root.into(), stable_price, native_price, Permill::from_percent(100), Permill::from_percent(100))?; + + // Register new asset in asset registry + let token_id = AssetRegistry::create_asset(&b"FCK".to_vec(), Balance::one())?; + + // Create account for token provider and set balance + let owner: AccountId = account("owner", 0, 1); + + let token_price = FixedU128::from((1,5)); + let token_amount = 200_000_000_000_000_u128; + + update_balance(token_id, &acc, token_amount); + + // Add the token to the pool + Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price,Permill::from_percent(100), owner)?; + + // Create LP provider account with correct balance + let lp_provider: AccountId = account("provider", 1, 1); + update_balance(token_id, &lp_provider, 500_000_000_000_000_u128); + + let liquidity_added = 1_000_000_000_000_u128; + + let current_position_id = Omnipool::next_position_id(); + + run_to_block(10); + Omnipool::add_liquidity(RawOrigin::Signed(lp_provider.clone()).into(), token_id, liquidity_added)?; + + let position = Omnipool::positions(current_position_id).unwrap(); + + Omnipool::sacrifice_position(RawOrigin::Signed(lp_provider.clone()).into(), current_position_id)?; + + let beneficiary: AccountId = account("beneficiary", 1, 1); + + }: { Omnipool::withdraw_protocol_liquidity(RawOrigin::Root.into(), token_id, position.shares, position.price, beneficiary.clone())? } + verify { + assert_eq!(::Currency::free_balance(token_id, &beneficiary), liquidity_added); + } + router_execution_sell { let c in 0..1; // if c == 1, calculate_sell is executed let e in 0..1; // if e == 1, execute_sell is executed From 042ca9533e57ee917fb9ec7931106399d9e5e2db Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 19 Oct 2023 17:58:22 +0200 Subject: [PATCH 11/17] add remove token benchmark --- runtime/hydradx/src/benchmarking/omnipool.rs | 51 +++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index e41f20650..f127c3f85 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -439,7 +439,6 @@ runtime_benchmarks! { } withdraw_protocol_liquidity { - // Initialize pool let stable_amount: Balance = 1_000_000_000_000_000u128; let native_amount: Balance = 1_000_000_000_000_000u128; @@ -493,6 +492,56 @@ runtime_benchmarks! { assert_eq!(::Currency::free_balance(token_id, &beneficiary), liquidity_added); } + remove_token{ + // Initialize pool + let stable_amount: Balance = 1_000_000_000_000_000u128; + let native_amount: Balance = 1_000_000_000_000_000u128; + let stable_price: FixedU128= FixedU128::from((1,2)); + let native_price: FixedU128= FixedU128::from(1); + + let acc = Omnipool::protocol_account(); + let native_id = ::HdxAssetId::get(); + let stable_id = ::StableCoinAssetId::get(); + + Omnipool::set_tvl_cap(RawOrigin::Root.into(), TVL_CAP)?; + + update_balance(stable_id, &acc, stable_amount); + update_balance(native_id, &acc, native_amount); + + Omnipool::initialize_pool(RawOrigin::Root.into(), stable_price, native_price, Permill::from_percent(100), Permill::from_percent(100))?; + + // Register new asset in asset registry + let token_id = AssetRegistry::create_asset(&b"FCK".to_vec(), Balance::one())?; + + // Create account for token provider and set balance + let owner: AccountId = account("owner", 0, 1); + + let token_price = FixedU128::from((1,5)); + let token_amount = 200_000_000_000_000_u128; + + update_balance(token_id, &acc, token_amount); + + let current_position_id = Omnipool::next_position_id(); + // Add the token to the pool + Omnipool::add_token(RawOrigin::Root.into(), token_id, token_price,Permill::from_percent(100), owner.clone())?; + + // Create LP provider account with correct balance + let lp_provider: AccountId = account("provider", 1, 1); + update_balance(token_id, &lp_provider, 500_000_000_000_000_u128); + + let liquidity_added = 1_000_000_000_000_u128; + + run_to_block(10); + Omnipool::sacrifice_position(RawOrigin::Signed(owner).into(), current_position_id)?; + + Omnipool::set_asset_tradable_state(RawOrigin::Root.into(), token_id, Tradability::FROZEN)?; + + let beneficiary: AccountId = account("beneficiary", 1, 1); + }: { Omnipool::remove_token(RawOrigin::Root.into(), token_id, beneficiary.clone())? } + verify { + assert_eq!(::Currency::free_balance(token_id, &beneficiary), token_amount); + } + router_execution_sell { let c in 0..1; // if c == 1, calculate_sell is executed let e in 0..1; // if e == 1, execute_sell is executed From e4dcbf092ab5ff73cbfe393f8d7a5d8fb7082c03 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 20 Oct 2023 09:39:17 +0200 Subject: [PATCH 12/17] burn lrna only if transfer fails --- pallets/omnipool/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index ce41411ec..340e9be44 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -2112,10 +2112,13 @@ impl Pallet { pub fn process_hub_amount(amount: Balance, dest: &T::AccountId) -> DispatchResult { if amount > Balance::zero() { - if amount < 400_000_000u128 { - T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), amount)?; - } else { - T::Currency::transfer(T::HubAssetId::get(), &Self::protocol_account(), dest, amount)?; + // If transfers fails and the amount is less than ED, it failed due to ED limit, so we simply burn it + if let Err(e) = T::Currency::transfer(T::HubAssetId::get(), &Self::protocol_account(), dest, amount) { + if amount < 400_000_000u128 { + T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), amount)?; + } else { + return Err(e) + } } } Ok(()) From bfbe3b8b1da8e6d08d76d8f6add4a37a961aa624 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 20 Oct 2023 10:29:29 +0200 Subject: [PATCH 13/17] add tests of remove liquidity lrna burn --- pallets/omnipool/src/lib.rs | 2 +- pallets/omnipool/src/tests/mock.rs | 10 ++- .../omnipool/src/tests/remove_liquidity.rs | 81 +++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index 340e9be44..deef31131 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -2117,7 +2117,7 @@ impl Pallet { if amount < 400_000_000u128 { T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), amount)?; } else { - return Err(e) + return Err(e); } } } diff --git a/pallets/omnipool/src/tests/mock.rs b/pallets/omnipool/src/tests/mock.rs index 637d50797..5d44fc1f6 100644 --- a/pallets/omnipool/src/tests/mock.rs +++ b/pallets/omnipool/src/tests/mock.rs @@ -132,8 +132,12 @@ impl pallet_balances::Config for Test { } parameter_type_with_key! { - pub ExistentialDeposits: |_currency_id: AssetId| -> Balance { - 0 + pub ExistentialDeposits: |currency_id: AssetId| -> Balance { + if *currency_id == LRNA{ + 400_000_000 + }else{ + 0 + } }; } @@ -145,7 +149,7 @@ impl orml_tokens::Config for Test { type WeightInfo = (); type ExistentialDeposits = ExistentialDeposits; type MaxLocks = (); - type DustRemovalWhitelist = Everything; + type DustRemovalWhitelist = (); type MaxReserves = (); type ReserveIdentifier = (); type CurrencyHooks = (); diff --git a/pallets/omnipool/src/tests/remove_liquidity.rs b/pallets/omnipool/src/tests/remove_liquidity.rs index 42d5f2c2f..e32b78d72 100644 --- a/pallets/omnipool/src/tests/remove_liquidity.rs +++ b/pallets/omnipool/src/tests/remove_liquidity.rs @@ -1,6 +1,7 @@ use super::*; use crate::types::Tradability; use frame_support::assert_noop; +use orml_traits::MultiCurrencyExtended; use sp_runtime::traits::One; use sp_runtime::DispatchError::BadOrigin; @@ -246,6 +247,86 @@ fn lp_receives_lrna_when_price_is_higher() { }); } +#[test] +fn remove_liquiduity_should_burn_lrna_when_amount_is_below_ed() { + let asset_id = 1_000; + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP3, asset_id, 500 * ONE), + (LP1, asset_id, 2 * ONE), + (LP2, DAI, 50000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(asset_id, FixedU128::from_float(0.65), LP3, 500 * ONE) + .build() + .execute_with(|| { + let liq_added = 2 * ONE; + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), asset_id, liq_added)); + + assert_ok!(Omnipool::buy( + RuntimeOrigin::signed(LP2), + asset_id, + DAI, + 100_000_000_000, + 500000 * ONE + )); + + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), + current_position_id, + liq_added + )); + assert_balance!(LP1, LRNA, 0); + let lrna_issuance = Tokens::total_issuance(LRNA); + assert!(lrna_issuance < 10826000000025799); // this value is when lrna is transferred + }); +} + +#[test] +fn remove_liquiduity_should_transfer_lrna_below_ed_when_lp_has_sufficient_lrna_amount() { + let asset_id = 1_000; + ExtBuilder::default() + .with_endowed_accounts(vec![ + (Omnipool::protocol_account(), DAI, 1000 * ONE), + (Omnipool::protocol_account(), HDX, NATIVE_AMOUNT), + (LP3, asset_id, 500 * ONE), + (LP1, asset_id, 2 * ONE), + (LP2, DAI, 50000 * ONE), + ]) + .with_initial_pool(FixedU128::from_float(0.5), FixedU128::from(1)) + .with_token(asset_id, FixedU128::from_float(0.65), LP3, 500 * ONE) + .build() + .execute_with(|| { + let liq_added = 2 * ONE; + let current_position_id = >::get(); + + assert_ok!(Omnipool::add_liquidity(RuntimeOrigin::signed(LP1), asset_id, liq_added)); + + assert_ok!(Omnipool::buy( + RuntimeOrigin::signed(LP2), + asset_id, + DAI, + 100_000_000_000, + 500000 * ONE + )); + + Tokens::update_balance(LRNA, &LP1, ONE as i128).unwrap(); + + assert_ok!(Omnipool::remove_liquidity( + RuntimeOrigin::signed(LP1), + current_position_id, + liq_added + )); + assert_balance!(LP1, LRNA, 1000259041538); + let lrna_issuance = Tokens::total_issuance(LRNA); + assert_eq!(lrna_issuance, 10826000000025799); + }); +} + #[test] fn protocol_shares_should_update_when_removing_asset_liquidity_after_price_change() { let asset_a: AssetId = 1_000; From 90ae2f850092cd5184b4cbaee9876394b9c8f1c6 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 20 Oct 2023 10:47:16 +0200 Subject: [PATCH 14/17] happy clippy happy life --- runtime/hydradx/src/benchmarking/omnipool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/hydradx/src/benchmarking/omnipool.rs b/runtime/hydradx/src/benchmarking/omnipool.rs index f127c3f85..7c076a7ee 100644 --- a/runtime/hydradx/src/benchmarking/omnipool.rs +++ b/runtime/hydradx/src/benchmarking/omnipool.rs @@ -483,7 +483,7 @@ runtime_benchmarks! { let position = Omnipool::positions(current_position_id).unwrap(); - Omnipool::sacrifice_position(RawOrigin::Signed(lp_provider.clone()).into(), current_position_id)?; + Omnipool::sacrifice_position(RawOrigin::Signed(lp_provider).into(), current_position_id)?; let beneficiary: AccountId = account("beneficiary", 1, 1); From 7c8e50fc2c8ffaf22db2b9aed142020e3ae3eba6 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 20 Oct 2023 11:16:47 +0200 Subject: [PATCH 15/17] remove token updates imbalance --- pallets/omnipool/src/lib.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index deef31131..b5e7a5c31 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -277,8 +277,9 @@ pub mod pallet { ProtocolLiquidityRemoved { who: T::AccountId, asset_id: T::AssetId, - shares_removed: Balance, + amount: Balance, hub_amount: Balance, + shares_removed: Balance, }, /// Sell trade executed. SellExecuted { @@ -1606,8 +1607,9 @@ pub mod pallet { Self::deposit_event(Event::ProtocolLiquidityRemoved { who: dest, asset_id, - shares_removed: amount, + amount: *state_changes.asset.delta_reserve, hub_amount: state_changes.lp_hub_amount, + shares_removed: amount, }); T::OmnipoolHooks::on_liquidity_changed(origin, info)?; @@ -1633,6 +1635,19 @@ pub mod pallet { asset_state.shares == asset_state.protocol_shares, Error::::SharesRemaining ); + // Imbalance update + let imbalance = >::get(); + let hub_asset_liquidity = Self::get_hub_asset_balance_of_protocol_account(); + let delta_imbalance = hydra_dx_math::omnipool::calculate_delta_imbalance( + asset_state.hub_reserve, + I129 { + value: imbalance.value, + negative: imbalance.negative, + }, + hub_asset_liquidity, + ) + .ok_or(ArithmeticError::Overflow)?; + Self::update_imbalance(BalanceUpdate::Increase(delta_imbalance))?; T::Currency::withdraw(T::HubAssetId::get(), &Self::protocol_account(), asset_state.hub_reserve)?; T::Currency::transfer(asset_id, &Self::protocol_account(), &beneficiary, asset_state.reserve)?; From 2981a3d1c96112ead76d70c2f42275a702df9245 Mon Sep 17 00:00:00 2001 From: Martin Hloska Date: Tue, 24 Oct 2023 08:34:05 +0200 Subject: [PATCH 16/17] new weights --- runtime/hydradx/src/weights/omnipool.rs | 130 ++++++++++++++++-------- 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/runtime/hydradx/src/weights/omnipool.rs b/runtime/hydradx/src/weights/omnipool.rs index 3b0ed4991..cb121c187 100644 --- a/runtime/hydradx/src/weights/omnipool.rs +++ b/runtime/hydradx/src/weights/omnipool.rs @@ -18,23 +18,25 @@ //! Autogenerated weights for pallet_omnipool //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-06, STEPS: 10, REPEAT: 30, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2023-10-24, STEPS: 5, REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/hydradx // benchmark // pallet -// --chain=dev -// --steps=10 -// --repeat=30 +// --pallet=pallet-omnipool // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --template=.maintain/pallet-weight-template-no-back.hbs -// --pallet=pallet-omnipool -// --output=omnipool.rs +// --chain=dev // --extrinsic=* +// --steps=5 +// --repeat=20 +// --output +// omnipool.rs +// --template +// .maintain/pallet-weight-template-no-back.hbs #![allow(unused_parens)] #![allow(unused_imports)] @@ -73,8 +75,8 @@ impl WeightInfo for HydraWeight { // Storage: Omnipool HubAssetTradability (r:0 w:1) // Proof: Omnipool HubAssetTradability (max_values: Some(1), max_size: Some(1), added: 496, mode: MaxEncodedLen) fn initialize_pool() -> Weight { - // Minimum execution time: 149_531 nanoseconds. - Weight::from_ref_time(150_484_000 as u64) + // Minimum execution time: 142_482 nanoseconds. + Weight::from_ref_time(143_649_000 as u64) .saturating_add(T::DbWeight::get().reads(11 as u64)) .saturating_add(T::DbWeight::get().writes(8 as u64)) } @@ -105,8 +107,8 @@ impl WeightInfo for HydraWeight { // Storage: Omnipool Positions (r:0 w:1) // Proof: Omnipool Positions (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) fn add_token() -> Weight { - // Minimum execution time: 148_641 nanoseconds. - Weight::from_ref_time(150_420_000 as u64) + // Minimum execution time: 150_622 nanoseconds. + Weight::from_ref_time(151_024_000 as u64) .saturating_add(T::DbWeight::get().reads(15 as u64)) .saturating_add(T::DbWeight::get().writes(10 as u64)) } @@ -149,8 +151,8 @@ impl WeightInfo for HydraWeight { // Storage: Omnipool Positions (r:0 w:1) // Proof: Omnipool Positions (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) fn add_liquidity() -> Weight { - // Minimum execution time: 233_542 nanoseconds. - Weight::from_ref_time(235_489_000 as u64) + // Minimum execution time: 228_878 nanoseconds. + Weight::from_ref_time(235_865_000 as u64) .saturating_add(T::DbWeight::get().reads(23 as u64)) .saturating_add(T::DbWeight::get().writes(14 as u64)) } @@ -193,8 +195,8 @@ impl WeightInfo for HydraWeight { // Storage: Uniques ItemPriceOf (r:0 w:1) // Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(113), added: 2588, mode: MaxEncodedLen) fn remove_liquidity() -> Weight { - // Minimum execution time: 295_371 nanoseconds. - Weight::from_ref_time(297_465_000 as u64) + // Minimum execution time: 287_519 nanoseconds. + Weight::from_ref_time(288_641_000 as u64) .saturating_add(T::DbWeight::get().reads(23 as u64)) .saturating_add(T::DbWeight::get().writes(16 as u64)) } @@ -229,8 +231,8 @@ impl WeightInfo for HydraWeight { // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn sell() -> Weight { - // Minimum execution time: 258_297 nanoseconds. - Weight::from_ref_time(260_217_000 as u64) + // Minimum execution time: 252_767 nanoseconds. + Weight::from_ref_time(254_421_000 as u64) .saturating_add(T::DbWeight::get().reads(23 as u64)) .saturating_add(T::DbWeight::get().writes(14 as u64)) } @@ -265,16 +267,16 @@ impl WeightInfo for HydraWeight { // Storage: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (r:1 w:0) // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) fn buy() -> Weight { - // Minimum execution time: 279_260 nanoseconds. - Weight::from_ref_time(280_777_000 as u64) + // Minimum execution time: 272_781 nanoseconds. + Weight::from_ref_time(274_844_000 as u64) .saturating_add(T::DbWeight::get().reads(24 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } // Storage: Omnipool Assets (r:1 w:1) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) fn set_asset_tradable_state() -> Weight { - // Minimum execution time: 33_892 nanoseconds. - Weight::from_ref_time(34_292_000 as u64) + // Minimum execution time: 33_941 nanoseconds. + Weight::from_ref_time(34_430_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -291,8 +293,8 @@ impl WeightInfo for HydraWeight { // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) fn refund_refused_asset() -> Weight { - // Minimum execution time: 109_440 nanoseconds. - Weight::from_ref_time(110_207_000 as u64) + // Minimum execution time: 106_574 nanoseconds. + Weight::from_ref_time(107_433_000 as u64) .saturating_add(T::DbWeight::get().reads(8 as u64)) .saturating_add(T::DbWeight::get().writes(5 as u64)) } @@ -309,19 +311,65 @@ impl WeightInfo for HydraWeight { // Storage: Uniques ItemPriceOf (r:0 w:1) // Proof: Uniques ItemPriceOf (max_values: None, max_size: Some(113), added: 2588, mode: MaxEncodedLen) fn sacrifice_position() -> Weight { - // Minimum execution time: 77_870 nanoseconds. - Weight::from_ref_time(78_533_000 as u64) + // Minimum execution time: 76_552 nanoseconds. + Weight::from_ref_time(77_448_000 as u64) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(6 as u64)) } // Storage: Omnipool Assets (r:1 w:1) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) fn set_asset_weight_cap() -> Weight { - // Minimum execution time: 34_229 nanoseconds. - Weight::from_ref_time(34_689_000 as u64) + // Minimum execution time: 34_304 nanoseconds. + Weight::from_ref_time(34_615_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } + // Storage: Omnipool Assets (r:1 w:1) + // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + // Storage: Tokens Accounts (r:3 w:3) + // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + // Storage: Omnipool HubAssetImbalance (r:1 w:1) + // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) + // Storage: AssetRegistry Assets (r:2 w:0) + // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:1) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:1 w:0) + // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) + // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: Tokens TotalIssuance (r:1 w:1) + // Proof: Tokens TotalIssuance (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: EmaOracle Accumulator (r:1 w:1) + // Proof: EmaOracle Accumulator (max_values: Some(1), max_size: Some(5921), added: 6416, mode: MaxEncodedLen) + fn withdraw_protocol_liquidity() -> Weight { + // Minimum execution time: 160_117 nanoseconds. + Weight::from_ref_time(161_104_000 as u64) + .saturating_add(T::DbWeight::get().reads(13 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } + // Storage: Omnipool Assets (r:1 w:1) + // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) + // Storage: Tokens Accounts (r:4 w:3) + // Proof: Tokens Accounts (max_values: None, max_size: Some(108), added: 2583, mode: MaxEncodedLen) + // Storage: Omnipool HubAssetImbalance (r:1 w:1) + // Proof: Omnipool HubAssetImbalance (max_values: Some(1), max_size: Some(17), added: 512, mode: MaxEncodedLen) + // Storage: AssetRegistry Assets (r:2 w:0) + // Proof: AssetRegistry Assets (max_values: None, max_size: Some(87), added: 2562, mode: MaxEncodedLen) + // Storage: Tokens TotalIssuance (r:1 w:1) + // Proof: Tokens TotalIssuance (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + // Storage: System Account (r:2 w:2) + // Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AccountCurrencyMap (r:2 w:0) + // Proof: MultiTransactionPayment AccountCurrencyMap (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + // Storage: MultiTransactionPayment AcceptedCurrencies (r:1 w:0) + // Proof: MultiTransactionPayment AcceptedCurrencies (max_values: None, max_size: Some(28), added: 2503, mode: MaxEncodedLen) + fn remove_token() -> Weight { + // Minimum execution time: 158_937 nanoseconds. + Weight::from_ref_time(162_234_000 as u64) + .saturating_add(T::DbWeight::get().reads(14 as u64)) + .saturating_add(T::DbWeight::get().writes(8 as u64)) + } // Storage: Omnipool Assets (r:3 w:3) // Proof: Omnipool Assets (max_values: None, max_size: Some(85), added: 2560, mode: MaxEncodedLen) // Storage: Tokens Accounts (r:4 w:4) @@ -355,11 +403,11 @@ impl WeightInfo for HydraWeight { /// The range of component `c` is `[0, 1]`. /// The range of component `e` is `[0, 1]`. fn router_execution_sell(c: u32, e: u32) -> Weight { - // Minimum execution time: 54_153 nanoseconds. - Weight::from_ref_time(40_444_373 as u64) // Standard Error: 79_703 - .saturating_add(Weight::from_ref_time(14_755_626 as u64).saturating_mul(c as u64)) - // Standard Error: 79_703 - .saturating_add(Weight::from_ref_time(219_012_766 as u64).saturating_mul(e as u64)) + // Minimum execution time: 53_821 nanoseconds. + Weight::from_ref_time(41_181_125 as u64) // Standard Error: 379_584 + .saturating_add(Weight::from_ref_time(13_415_900 as u64).saturating_mul(c as u64)) + // Standard Error: 379_584 + .saturating_add(Weight::from_ref_time(214_332_825 as u64).saturating_mul(e as u64)) .saturating_add(T::DbWeight::get().reads(7 as u64)) .saturating_add(T::DbWeight::get().reads((16 as u64).saturating_mul(e as u64))) .saturating_add(T::DbWeight::get().writes((14 as u64).saturating_mul(e as u64))) @@ -396,19 +444,13 @@ impl WeightInfo for HydraWeight { // Proof: CircuitBreaker AllowedRemoveLiquidityAmountPerAsset (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) /// The range of component `c` is `[1, 2]`. /// The range of component `e` is `[0, 1]`. - fn router_execution_buy(c: u32, _e: u32) -> Weight { - // Minimum execution time: 277_855 nanoseconds. - Weight::from_ref_time(269_412_275 as u64) // Standard Error: 112_640 - .saturating_add(Weight::from_ref_time(12_219_983 as u64).saturating_mul(c as u64)) + fn router_execution_buy(c: u32, e: u32) -> Weight { + // Minimum execution time: 271_496 nanoseconds. + Weight::from_ref_time(266_903_925 as u64) // Standard Error: 491_298 + .saturating_add(Weight::from_ref_time(9_205_000 as u64).saturating_mul(c as u64)) + // Standard Error: 491_298 + .saturating_add(Weight::from_ref_time(862_825 as u64).saturating_mul(e as u64)) .saturating_add(T::DbWeight::get().reads(24 as u64)) .saturating_add(T::DbWeight::get().writes(15 as u64)) } - - fn remove_token() -> Weight { - Weight::zero() - } - - fn withdraw_protocol_liquidity() -> Weight { - Weight::zero() - } } From 722b03873a7543888a781a9877e6f0e183d1d675 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 31 Oct 2023 08:56:13 +0100 Subject: [PATCH 17/17] use authority origin instead of ensure root --- pallets/omnipool/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/omnipool/src/lib.rs b/pallets/omnipool/src/lib.rs index b5e7a5c31..bebeaf2b6 100644 --- a/pallets/omnipool/src/lib.rs +++ b/pallets/omnipool/src/lib.rs @@ -1538,7 +1538,7 @@ pub mod pallet { price: (Balance, Balance), dest: T::AccountId, ) -> DispatchResult { - ensure_root(origin.clone())?; + T::AuthorityOrigin::ensure_origin(origin.clone())?; let asset_state = Self::load_asset_state(asset_id)?; ensure!(amount <= asset_state.protocol_shares, Error::::InsufficientShares); @@ -1620,7 +1620,7 @@ pub mod pallet { #[pallet::weight(::WeightInfo::remove_token())] #[transactional] pub fn remove_token(origin: OriginFor, asset_id: T::AssetId, beneficiary: T::AccountId) -> DispatchResult { - ensure_root(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; ensure!( asset_id != T::StableCoinAssetId::get(),