Skip to content

Commit

Permalink
Merge pull request #777 from galacticcouncil/fix/dca-retry-invalid-route
Browse files Browse the repository at this point in the history
fix: dca retry on invalid route
  • Loading branch information
mrq1911 authored Mar 4, 2024
2 parents 27160ca + 973bd3f commit c9afe26
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 80 additions & 0 deletions integration-tests/src/dca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use hydradx_traits::router::Trade;
use orml_traits::MultiCurrency;
use orml_traits::MultiReservableCurrency;
use pallet_dca::types::{Order, Schedule};
use pallet_omnipool::types::Tradability;
use pallet_stableswap::types::AssetAmount;
use pallet_stableswap::MAX_ASSETS_IN_POOL;
use primitives::{AssetId, Balance};
Expand Down Expand Up @@ -495,6 +496,85 @@ mod omnipool {
});
}

#[test]
fn sell_schedule_be_retried_when_route_is_invalid() {
TestNet::reset();
Hydra::execute_with(|| {
//Arrange
init_omnipool_with_oracle_for_block_10();

assert_ok!(Currencies::update_balance(
RuntimeOrigin::root(),
BOB.into(),
ETH,
1_000 * UNITS as i128,
));
let position_id = hydradx_runtime::Omnipool::next_position_id();

assert_ok!(hydradx_runtime::Omnipool::add_token(
hydradx_runtime::RuntimeOrigin::root(),
ETH,
FixedU128::from_rational(3, 10),
Permill::from_percent(60),
BOB.into(),
));

polkadot_run_to_block(11);

let alice_init_hdx_balance = 5000 * UNITS;
assert_ok!(Balances::force_set_balance(
RuntimeOrigin::root(),
ALICE.into(),
alice_init_hdx_balance,
));

let dca_budget = 1100 * UNITS;
let amount_to_sell = 100 * UNITS;
let schedule1 =
schedule_fake_with_sell_order(ALICE, PoolType::Omnipool, dca_budget, HDX, ETH, amount_to_sell);
create_schedule(ALICE, schedule1);

assert_balance!(ALICE.into(), HDX, alice_init_hdx_balance - dca_budget);
assert_balance!(ALICE.into(), DAI, ALICE_INITIAL_DAI_BALANCE);
assert_reserved_balance!(&ALICE.into(), HDX, dca_budget);
assert_balance!(&Treasury::account_id(), HDX, TREASURY_ACCOUNT_INIT_BALANCE);

//Act
//Remove ETH token resulting invalid route

assert_ok!(hydradx_runtime::Omnipool::set_asset_tradable_state(
hydradx_runtime::RuntimeOrigin::root(),
ETH,
Tradability::ADD_LIQUIDITY | Tradability::REMOVE_LIQUIDITY
));
let position =
pallet_omnipool::Pallet::<hydradx_runtime::Runtime>::load_position(position_id, BOB.into()).unwrap();
assert_ok!(hydradx_runtime::Omnipool::remove_liquidity(
hydradx_runtime::RuntimeOrigin::signed(BOB.into()),
position_id,
position.shares,
));

assert_ok!(hydradx_runtime::Omnipool::set_asset_tradable_state(
hydradx_runtime::RuntimeOrigin::root(),
ETH,
Tradability::FROZEN
));
assert_ok!(hydradx_runtime::Omnipool::remove_token(
hydradx_runtime::RuntimeOrigin::root(),
ETH,
BOB.into(),
));
polkadot_run_to_block(12);

//Assert
let schedule_id = 0;
let schedule = DCA::schedules(schedule_id);
assert!(schedule.is_some());
assert_eq!(DCA::retries_on_error(schedule_id), 1);
});
}

#[test]
fn sell_schedule_should_sell_remaining_in_next_trade_when_there_is_not_enough_left() {
TestNet::reset();
Expand Down
2 changes: 1 addition & 1 deletion pallets/dca/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = 'pallet-dca'
version = "1.3.6"
version = "1.4.0"
description = 'A pallet to manage DCA scheduling'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
5 changes: 5 additions & 0 deletions pallets/dca/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ pub const FEE_MULTIPLIER_FOR_MIN_TRADE_LIMIT: Balance = 20;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::traits::Contains;

use frame_support::weights::WeightToFee;

Expand Down Expand Up @@ -184,6 +185,7 @@ pub mod pallet {

if error != Error::<T>::TradeLimitReached.into()
&& error != Error::<T>::SlippageLimitReached.into()
&& !T::RetryOnError::contains(&error)
{
Self::terminate_schedule(schedule_id, &schedule, error);
} else if let Err(retry_error) =
Expand Down Expand Up @@ -242,6 +244,9 @@ pub mod pallet {
///Spot price provider to get the current price between two asset
type RouteProvider: RouteProvider<Self::AssetId>;

///Errors we want to explicitly retry on, in case of failing DCA
type RetryOnError: Contains<DispatchError>;

///Max price difference allowed between blocks
#[pallet::constant]
type MaxPriceDifferenceBetweenBlocks: Get<Permill>;
Expand Down
1 change: 1 addition & 0 deletions pallets/dca/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@ impl Config for Test {
type AmmTradeWeights = ();
type MinimumTradingLimit = MinTradeAmount;
type NativePriceOracle = NativePriceOracleMock;
type RetryOnError = ();
}

pub struct NativePriceOracleMock;
Expand Down
13 changes: 13 additions & 0 deletions runtime/hydradx/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,18 @@ parameter_types! {

}

pub struct RetryOnErrorForDca;

impl Contains<DispatchError> for RetryOnErrorForDca {
fn contains(t: &DispatchError) -> bool {
let errors: Vec<DispatchError> = vec![
pallet_omnipool::Error::<Runtime>::AssetNotFound.into(),
pallet_omnipool::Error::<Runtime>::NotAllowed.into(),
];
errors.contains(t)
}
}

impl pallet_dca::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type AssetId = AssetId;
Expand Down Expand Up @@ -712,6 +724,7 @@ impl pallet_dca::Config for Runtime {
MultiTransactionPayment,
DCAOraclePeriod,
>;
type RetryOnError = RetryOnErrorForDca;
}

// Provides weight info for the router. Router extrinsics can be executed with different AMMs, so we split the router weights into two parts:
Expand Down

0 comments on commit c9afe26

Please sign in to comment.