Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: disable oracle for insufficient assets #773

Merged
merged 6 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "runtime-integration-tests"
version = "1.19.4"
version = "1.19.5"
description = "Integration tests"
authors = ["GalacticCouncil"]
edition = "2021"
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/src/cross_chain_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ fn hydra_should_receive_asset_when_transferred_from_acala_to_eth_address() {
));
// Assert
assert_eq!(
hydradx_runtime::Balances::free_balance(&AccountId::from(ALICE)),
hydradx_runtime::Balances::free_balance(AccountId::from(ALICE)),
ALICE_INITIAL_NATIVE_BALANCE - amount
);
});
Expand Down Expand Up @@ -259,7 +259,7 @@ fn hydra_should_receive_asset_when_transferred_from_acala_to_same_address_repres

// Assert
assert_eq!(
hydradx_runtime::Balances::free_balance(&AccountId::from(ALICE)),
hydradx_runtime::Balances::free_balance(AccountId::from(ALICE)),
ALICE_INITIAL_NATIVE_BALANCE - 2 * amount
);
});
Expand Down
4 changes: 2 additions & 2 deletions integration-tests/src/dca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3398,11 +3398,11 @@ pub fn init_stableswap_with_three_assets_having_different_decimals(
initial.push(AssetAmount::new(asset_id, initial_liquidity));
added_liquidity.push(AssetAmount::new(asset_id, liquidity_added));
}
let pool_id = AssetRegistry::register_insufficient_asset(
let pool_id = AssetRegistry::register_sufficient_asset(
None,
Some(b"pool".to_vec().try_into().unwrap()),
AssetKind::Token,
Some(1u128),
1u128,
None,
None,
None,
Expand Down
1 change: 0 additions & 1 deletion integration-tests/src/exchange_asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use xcm_emulator::TestExt;
pub const SELL: bool = true;
pub const BUY: bool = false;

pub const HDX: u32 = CORE_ASSET_ID;
pub const ACA: u32 = 1234;
pub const GLMR: u32 = 4567;
pub const IBTC: u32 = 7890;
Expand Down
53 changes: 52 additions & 1 deletion integration-tests/src/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use hydradx_traits::{
};

use pallet_ema_oracle::OracleError;
use primitives::constants::chain::OMNIPOOL_SOURCE;
use primitives::constants::chain::{OMNIPOOL_SOURCE, XYK_SOURCE};
use xcm_emulator::TestExt;

pub fn hydradx_run_to_block(to: BlockNumber) {
Expand Down Expand Up @@ -141,3 +141,54 @@ fn omnipool_hub_asset_trades_are_ingested_into_oracle() {
}
});
}

#[test]
fn xyk_trades_with_insufficient_asset_are_not_tracked_by_oracle() {
TestNet::reset();

Hydra::execute_with(|| {
// arrange
hydradx_run_to_next_block();

assert_ok!(hydradx_runtime::Tokens::mint_into(
INSUFFICIENT_ASSET,
&ALICE.into(),
200 * UNITS,
));

assert_ok!(hydradx_runtime::XYK::create_pool(
RuntimeOrigin::signed(ALICE.into()),
HDX,
100 * UNITS,
INSUFFICIENT_ASSET,
100 * UNITS,
));

assert_ok!(hydradx_runtime::XYK::buy(
RuntimeOrigin::signed(ALICE.into()),
HDX,
INSUFFICIENT_ASSET,
2 * UNITS,
200 * UNITS,
false,
));

// act
// will store the data received in the sell as oracle values
hydradx_run_to_next_block();

// assert
for supported_period in SUPPORTED_PERIODS {
assert_eq!(
EmaOracle::get_price(HDX, INSUFFICIENT_ASSET, *supported_period, XYK_SOURCE),
Err(OracleError::NotPresent)
);
}
for unsupported_period in UNSUPPORTED_PERIODS {
assert_eq!(
EmaOracle::get_price(HDX, INSUFFICIENT_ASSET, *unsupported_period, XYK_SOURCE),
Err(OracleError::NotPresent)
);
}
});
}
2 changes: 2 additions & 0 deletions integration-tests/src/polkadot_test_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub const BTC: AssetId = 5;
pub const ACA: AssetId = 6;
pub const WETH: AssetId = 20;
pub const PEPE: AssetId = 420;
pub const INSUFFICIENT_ASSET: AssetId = 500;

pub const NOW: Moment = 1689844300000; // unix time in milliseconds

Expand Down Expand Up @@ -479,6 +480,7 @@ pub mod hydra {
None,
true,
),
(Some(INSUFFICIENT_ASSET), None, 1_000u128, None, None, None, false),
// workaround for next_asset_id() to return correct values
(
None,
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.5"
version = "1.3.6"
description = 'A pallet to manage DCA scheduling'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
30 changes: 30 additions & 0 deletions pallets/dca/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,41 @@ parameter_types! {
OraclePeriod::LastBlock, OraclePeriod::Short, OraclePeriod::TenMinutes]);
}

pub struct AssetInspectMock;

impl hydradx_traits::registry::Inspect for AssetInspectMock {
type AssetId = AssetId;
type Location = ();

fn is_sufficient(_id: Self::AssetId) -> bool {
true
}
fn exists(_id: Self::AssetId) -> bool {
unimplemented!()
}
fn decimals(_id: Self::AssetId) -> Option<u8> {
unimplemented!()
}
fn asset_type(_id: Self::AssetId) -> Option<AssetKind> {
unimplemented!()
}
fn is_banned(_id: Self::AssetId) -> bool {
unimplemented!()
}
fn asset_name(_id: Self::AssetId) -> Option<Vec<u8>> {
unimplemented!()
}
fn asset_symbol(_id: Self::AssetId) -> Option<Vec<u8>> {
unimplemented!()
}
}

impl pallet_ema_oracle::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type BlockNumberProvider = MockBlockNumberProvider;
type SupportedPeriods = SupportedPeriods;
type AssetInspect = AssetInspectMock;
type MaxUniqueEntries = ConstU32<20>;
}

Expand Down
2 changes: 1 addition & 1 deletion pallets/ema-oracle/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = 'pallet-ema-oracle'
version = '1.1.3'
version = '1.2.0'
description = 'Exponential moving average oracle for AMM pools'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
2 changes: 2 additions & 0 deletions pallets/ema-oracle/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use frame_support::{assert_ok, traits::Hooks};
use pretty_assertions::assert_eq;

use crate::Pallet as EmaOracle;
// type alias to simplify usage of the handler
type OnActivityHandler<Test> = super::OnActivityHandler<Test, AllowAll<Test>>;

/// Default oracle source.
const SOURCE: Source = *b"dummysrc";
Expand Down
64 changes: 58 additions & 6 deletions pallets/ema-oracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ use frame_support::pallet_prelude::*;
use frame_support::sp_runtime::traits::{BlockNumberProvider, One, Zero};
use frame_system::pallet_prelude::BlockNumberFor;
use hydradx_traits::{
registry::Inspect,
AggregatedEntry, AggregatedOracle, AggregatedPriceOracle, Liquidity, OnCreatePoolHandler,
OnLiquidityChangedHandler, OnTradeHandler,
OraclePeriod::{self, *},
Expand Down Expand Up @@ -122,6 +123,9 @@ pub mod pallet {
/// The periods supported by the pallet. I.e. which oracles to track.
type SupportedPeriods: Get<BoundedVec<OraclePeriod, ConstU32<MAX_PERIODS>>>;

/// Provides info about sufficiency of registered assets.
type AssetInspect: Inspect<AssetId = AssetId>;

mrq1911 marked this conversation as resolved.
Show resolved Hide resolved
/// Maximum number of unique oracle entries expected in one block.
#[pallet::constant]
type MaxUniqueEntries: Get<u32>;
Expand Down Expand Up @@ -245,7 +249,7 @@ impl<T: Config> Pallet<T> {
assets: (AssetId, AssetId),
oracle_entry: OracleEntry<BlockNumberFor<T>>,
) -> Result<Weight, (Weight, DispatchError)> {
let weight = OnActivityHandler::<T>::on_trade_weight();
let weight = OnActivityHandler::<T, SufficientAssetsFilter<T>>::on_trade_weight();
Self::on_entry(src, assets, oracle_entry)
.map(|_| weight)
.map_err(|_| (weight, Error::<T>::TooManyUniqueEntries.into()))
Expand All @@ -258,7 +262,7 @@ impl<T: Config> Pallet<T> {
assets: (AssetId, AssetId),
oracle_entry: OracleEntry<BlockNumberFor<T>>,
) -> Result<Weight, (Weight, DispatchError)> {
let weight = OnActivityHandler::<T>::on_liquidity_changed_weight();
let weight = OnActivityHandler::<T, SufficientAssetsFilter<T>>::on_liquidity_changed_weight();
Self::on_entry(src, assets, oracle_entry)
.map(|_| weight)
.map_err(|_| (weight, Error::<T>::TooManyUniqueEntries.into()))
Expand Down Expand Up @@ -364,10 +368,39 @@ impl<T: Config> Pallet<T> {
}
}

/// Filter for assets that we don't want to have oracles for.
pub trait EntryFilter<AssetId> {
fn is_allowed(asset_a: AssetId, asset_b: AssetId) -> bool;
}

/// Fails if at least one of the assets is not sufficient.
pub struct SufficientAssetsFilter<T>(PhantomData<T>);
impl<T: Config> EntryFilter<AssetId> for SufficientAssetsFilter<T> {
fn is_allowed(asset_a: AssetId, asset_b: AssetId) -> bool {
T::AssetInspect::is_sufficient(asset_a) && T::AssetInspect::is_sufficient(asset_b)
}
}

#[cfg(feature = "runtime-benchmarks")]
// used in the benchmarks
pub struct AllowAll<T>(PhantomData<T>);
#[cfg(feature = "runtime-benchmarks")]
impl<T: Config> EntryFilter<AssetId> for AllowAll<T> {
fn is_allowed(asset_a: AssetId, asset_b: AssetId) -> bool {
// perform the same checks but ignore the result and continue in execution
let _ = SufficientAssetsFilter::<T>::is_allowed(asset_a, asset_b);
true
}
}

/// A callback handler for trading and liquidity activity that schedules oracle updates.
pub struct OnActivityHandler<T>(PhantomData<T>);
pub struct OnActivityHandler<T, EF>(PhantomData<(T, EF)>);

impl<T: Config> OnCreatePoolHandler<AssetId> for OnActivityHandler<T> {
impl<T, EF> OnCreatePoolHandler<AssetId> for OnActivityHandler<T, EF>
where
T: Config,
EF: EntryFilter<AssetId>,
{
// Nothing to do on pool creation. Oracles are created lazily.
fn on_create_pool(_asset_a: AssetId, _asset_b: AssetId) -> DispatchResult {
Ok(())
Expand All @@ -382,7 +415,11 @@ pub(crate) fn fractional_on_finalize_weight<T: Config>(max_entries: u32) -> Weig
.saturating_div(max_entries.into())
}

impl<T: Config> OnTradeHandler<AssetId, Balance, Price> for OnActivityHandler<T> {
impl<T, EF> OnTradeHandler<AssetId, Balance, Price> for OnActivityHandler<T, EF>
where
T: Config,
EF: EntryFilter<AssetId>,
{
fn on_trade(
source: Source,
asset_a: AssetId,
Expand All @@ -402,6 +439,11 @@ impl<T: Config> OnTradeHandler<AssetId, Balance, Price> for OnActivityHandler<T>
return Err((Self::on_trade_weight(), Error::<T>::OnTradeValueZero.into()));
}

// if the asset is not sufficient, we don't consider this as an error, so we return Ok
if !EF::is_allowed(asset_a, asset_b) {
return Ok(Self::on_trade_weight());
}

let price = determine_normalized_price(asset_a, asset_b, price);
let volume = determine_normalized_volume(asset_a, asset_b, amount_a, amount_b);
let liquidity = determine_normalized_liquidity(asset_a, asset_b, liquidity_a, liquidity_b);
Expand All @@ -424,7 +466,11 @@ impl<T: Config> OnTradeHandler<AssetId, Balance, Price> for OnActivityHandler<T>
}
}

impl<T: Config> OnLiquidityChangedHandler<AssetId, Balance, Price> for OnActivityHandler<T> {
impl<T, EF> OnLiquidityChangedHandler<AssetId, Balance, Price> for OnActivityHandler<T, EF>
where
T: Config,
EF: EntryFilter<AssetId>,
{
fn on_liquidity_changed(
source: Source,
asset_a: AssetId,
Expand All @@ -441,6 +487,12 @@ impl<T: Config> OnLiquidityChangedHandler<AssetId, Balance, Price> for OnActivit
"Liquidity is zero. Source: {source:?}, liquidity: ({liquidity_a},{liquidity_a})"
);
}

// if the asset is not sufficient, we don't consider this as an error, so we return Ok
if !EF::is_allowed(asset_a, asset_b) {
return Ok(Self::on_trade_weight());
}

let price = determine_normalized_price(asset_a, asset_b, price);
let liquidity = determine_normalized_liquidity(asset_a, asset_b, liquidity_a, liquidity_b);
let updated_at = T::BlockNumberProvider::current_block_number();
Expand Down
Loading
Loading