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: manual oracle activation #782

Merged
merged 23 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ed0cb7a
manual_oracle_activation
Roznovjak Mar 5, 2024
74c7bda
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 5, 2024
644a614
fix name of the config param
Roznovjak Mar 5, 2024
e802938
rebenchmark ema oracle pallet
Roznovjak Mar 5, 2024
4e3067b
bump crate versions
Roznovjak Mar 5, 2024
01c948d
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 6, 2024
f38be99
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 12, 2024
4d38a39
bump crate versions
Roznovjak Mar 12, 2024
84b22f7
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 14, 2024
37a090c
bump crate version
Roznovjak Mar 14, 2024
1497db0
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 17, 2024
cfedd19
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 18, 2024
0b2ceb5
bump crate versions
Roznovjak Mar 18, 2024
50a81f0
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 19, 2024
a4e35c0
Merge branch 'master' into manual_oracle_activation
Roznovjak Mar 19, 2024
1177201
bump crate versions
Roznovjak Mar 19, 2024
1fd258e
Merge branch 'master' into manual_oracle_activation
Roznovjak Apr 8, 2024
781a31d
rename events
Roznovjak Apr 8, 2024
5d6eda8
remove_oracle removes entries from the storage
Roznovjak Apr 8, 2024
1a6c318
refactor oracle whitelist
Roznovjak Apr 8, 2024
e4b3176
bump crate versions
Roznovjak Apr 9, 2024
4e48e74
Merge branch 'master' into manual_oracle_activation
mrq1911 Apr 9, 2024
249a797
bump crate versions
Roznovjak Apr 9, 2024
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
11 changes: 6 additions & 5 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.11"
version = "1.19.12"
description = "Integration tests"
authors = ["GalacticCouncil"]
edition = "2021"
Expand Down
54 changes: 54 additions & 0 deletions integration-tests/src/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,57 @@ fn xyk_trades_with_insufficient_asset_are_not_tracked_by_oracle() {
}
});
}

#[test]
fn xyk_trades_with_insufficient_asset_are_tracked_by_oracle_when_asset_is_whitelisted() {
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!(EmaOracle::add_oracle(
RuntimeOrigin::root(),
XYK_SOURCE,
(HDX, INSUFFICIENT_ASSET)
));

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!(EmaOracle::get_price(HDX, INSUFFICIENT_ASSET, *supported_period, XYK_SOURCE).is_ok(),);
}
for unsupported_period in UNSUPPORTED_PERIODS {
assert_eq!(
EmaOracle::get_price(HDX, INSUFFICIENT_ASSET, *unsupported_period, XYK_SOURCE),
Err(OracleError::NotPresent)
);
}
});
}
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.4.1"
version = "1.4.2"
description = 'A pallet to manage DCA scheduling'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
3 changes: 2 additions & 1 deletion pallets/dca/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,14 @@ parameter_types! {

impl pallet_ema_oracle::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type AuthorityOrigin = EnsureRoot<AccountId>;
type BlockNumberProvider = MockBlockNumberProvider;
type SupportedPeriods = SupportedPeriods;
type OracleWhitelist = Everything;
type MaxUniqueEntries = ConstU32<20>;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
type WeightInfo = ();
}

impl BlockNumberProvider for MockBlockNumberProvider {
Expand Down
3 changes: 2 additions & 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.2.1'
version = '1.3.0'
description = 'Exponential moving average oracle for AMM pools'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down Expand Up @@ -35,6 +35,7 @@ pretty_assertions = "1.3.0"
proptest = "1.0.0"
rug = { version = "1.17.0", features = ["num-traits"] }
sp-io = { workspace = true }
test-utils = { workspace = true }

[features]
default = ['std']
Expand Down
49 changes: 48 additions & 1 deletion pallets/ema-oracle/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub const HDX: AssetId = 1_000;
pub const DOT: AssetId = 2_000;

use frame_benchmarking::benchmarks;
use frame_support::{assert_ok, traits::Hooks};
use frame_support::{assert_ok, dispatch::RawOrigin, traits::Hooks};

#[cfg(test)]
use pretty_assertions::assert_eq;
Expand All @@ -33,7 +33,36 @@ use crate::Pallet as EmaOracle;
/// Default oracle source.
const SOURCE: Source = *b"dummysrc";

fn fill_whitelist_storage<T: Config>(n: u32) {
for i in 0..n {
assert_ok!(EmaOracle::<T>::add_oracle(RawOrigin::Root.into(), SOURCE, (HDX, i)));
}
}
benchmarks! {
add_oracle {
let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries - 1);

assert_eq!(WhitelistedAssets::<T>::get().len(), (max_entries - 1) as usize);

}: _(RawOrigin::Root, SOURCE, (HDX, DOT))
verify {
assert!(WhitelistedAssets::<T>::get().contains(&(SOURCE, (HDX, DOT))));
}

remove_oracle {
let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries - 1);

assert_ok!(EmaOracle::<T>::add_oracle(RawOrigin::Root.into(), SOURCE, (HDX, DOT)));

assert_eq!(WhitelistedAssets::<T>::get().len(), max_entries as usize);

}: _(RawOrigin::Root, SOURCE, (HDX, DOT))
verify {
assert!(!WhitelistedAssets::<T>::get().contains(&(SOURCE, (HDX, DOT))));
}

on_finalize_no_entry {
let block_num: u32 = 5;
}: { EmaOracle::<T>::on_finalize(block_num.into()); }
Expand All @@ -42,6 +71,9 @@ benchmarks! {

#[extra]
on_finalize_insert_one_token {
let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let block_num: BlockNumberFor<T> = 5u32.into();
let prev_block = block_num.saturating_sub(One::one());

Expand Down Expand Up @@ -78,6 +110,9 @@ benchmarks! {

#[extra]
on_finalize_update_one_token {
let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let initial_data_block: BlockNumberFor<T> = 5u32.into();
// higher update time difference might make exponentiation more expensive
let block_num = initial_data_block.saturating_add(1_000_000u32.into());
Expand Down Expand Up @@ -119,6 +154,9 @@ benchmarks! {
on_finalize_multiple_tokens {
let b in 1 .. (T::MaxUniqueEntries::get() - 1);

let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let initial_data_block: BlockNumberFor<T> = 5u32.into();
let block_num = initial_data_block.saturating_add(1_000_000u32.into());

Expand Down Expand Up @@ -167,6 +205,9 @@ benchmarks! {
on_trade_multiple_tokens {
let b in 1 .. (T::MaxUniqueEntries::get() - 1);

let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let initial_data_block: BlockNumberFor<T> = 5u32.into();
let block_num = initial_data_block.saturating_add(1_000_000u32.into());

Expand Down Expand Up @@ -229,6 +270,9 @@ benchmarks! {
on_liquidity_changed_multiple_tokens {
let b in 1 .. (T::MaxUniqueEntries::get() - 1);

let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let initial_data_block: BlockNumberFor<T> = 5u32.into();
let block_num = initial_data_block.saturating_add(1_000_000u32.into());

Expand Down Expand Up @@ -295,6 +339,9 @@ benchmarks! {
}

get_entry {
let max_entries = <<T as Config>::MaxUniqueEntries as Get<u32>>::get();
fill_whitelist_storage::<T>(max_entries);

let initial_data_block: BlockNumberFor<T> = 5u32.into();
let oracle_age: BlockNumberFor<T> = 999_999u32.into();
let block_num = initial_data_block.saturating_add(oracle_age.saturating_add(One::one()));
Expand Down
66 changes: 60 additions & 6 deletions pallets/ema-oracle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ impl BenchmarkHelper<AssetId> for () {
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::BoundedBTreeMap;
use frame_system::pallet_prelude::BlockNumberFor;
use frame_support::{BoundedBTreeMap, BoundedBTreeSet};
use frame_system::pallet_prelude::{BlockNumberFor, OriginFor};

#[pallet::pallet]
pub struct Pallet<T>(_);
Expand All @@ -128,6 +128,9 @@ pub mod pallet {
/// Weight information for the extrinsics.
type WeightInfo: WeightInfo;

/// Origin that can enable oracle for assets that would be rejected by `OracleWhitelist` otherwise.
type AuthorityOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Provider for the current block number.
type BlockNumberProvider: BlockNumberProvider<BlockNumber = BlockNumberFor<Self>>;

Expand All @@ -149,10 +152,17 @@ pub mod pallet {
pub enum Error<T> {
TooManyUniqueEntries,
OnTradeValueZero,
OracleNotFound,
}

#[pallet::event]
pub enum Event<T: Config> {}
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
/// Oracle was added to the whitelist.
OracleAdded { source: Source, assets: (AssetId, AssetId) },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you add event like this, i would expect it being fired every time oracle is created, not just when you modify whitelist

/// Oracle was removed from the whitelist.
OracleRemoved { source: Source, assets: (AssetId, AssetId) },
}

/// Accumulator for oracle data in current block that will be recorded at the end of the block.
#[pallet::storage]
Expand All @@ -163,7 +173,7 @@ pub mod pallet {
ValueQuery,
>;

/// Orace storage keyed by data source, involved asset ids and the period length of the oracle.
/// Oracle storage keyed by data source, involved asset ids and the period length of the oracle.
///
/// Stores the data entry as well as the block number when the oracle was first initialized.
#[pallet::storage]
Expand All @@ -179,6 +189,11 @@ pub mod pallet {
OptionQuery,
>;

/// Assets that are whitelisted and tracked by the pallet.
#[pallet::storage]
pub type WhitelistedAssets<T: Config> =
StorageValue<_, BoundedBTreeSet<(Source, (AssetId, AssetId)), T::MaxUniqueEntries>, ValueQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
Expand Down Expand Up @@ -232,7 +247,43 @@ pub mod pallet {
}

#[pallet::call]
impl<T: Config> Pallet<T> {}
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(<T as Config>::WeightInfo::add_oracle())]
pub fn add_oracle(origin: OriginFor<T>, source: Source, assets: (AssetId, AssetId)) -> DispatchResult {
T::AuthorityOrigin::ensure_origin(origin)?;

let assets = ordered_pair(assets.0, assets.1);

WhitelistedAssets::<T>::mutate(|list| {
list.try_insert((source, (assets)))
.map_err(|_| Error::<T>::TooManyUniqueEntries)
})?;

Self::deposit_event(Event::OracleAdded { source, assets });

Ok(())
}

#[pallet::call_index(1)]
#[pallet::weight(<T as Config>::WeightInfo::remove_oracle())]
pub fn remove_oracle(origin: OriginFor<T>, source: Source, assets: (AssetId, AssetId)) -> DispatchResult {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would expect this function would remove actual oracle as well, not just item from whitelist

T::AuthorityOrigin::ensure_origin(origin)?;

let assets = ordered_pair(assets.0, assets.1);

WhitelistedAssets::<T>::mutate(|list| {
let was_removed = list.remove(&(source, (assets)));
ensure!(was_removed, Error::<T>::OracleNotFound);

Ok::<(), DispatchError>(())
})?;

Self::deposit_event(Event::OracleRemoved { source, assets });

Ok(())
}
}
}

impl<T: Config> Pallet<T> {
Expand All @@ -243,7 +294,10 @@ impl<T: Config> Pallet<T> {
assets: (AssetId, AssetId),
oracle_entry: OracleEntry<BlockNumberFor<T>>,
) -> Result<(), ()> {
if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) {
if !(T::OracleWhitelist::contains(&(src, assets.0, assets.1))
|| WhitelistedAssets::<T>::get().contains(&(src, (assets.0, assets.1))))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont like the way this is tacked on, from configuration of the pallet i would expect that only OracleWhitelist controls it, nothing else, therefore this list should be only contains implementation (default ()) so if you override it in config, it wont be utilized

Copy link
Member

@mrq1911 mrq1911 Mar 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so for our case you would basically implement extension in the runtime config (extend sufficient asset filter), not other way around

{
// if we don't track oracle for given asset pair, don't throw error
return Ok(());
}

Expand Down
Loading
Loading