From 0e4da7a2c6037e5a1fb04eddb23035383819c0d0 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 17 Feb 2026 13:21:22 +0100 Subject: [PATCH 01/43] add set external oracle functionality --- integration-tests/src/driver/mod.rs | 8 + integration-tests/src/oracle.rs | 47 +- pallets/dca/src/tests/mock.rs | 11 +- pallets/ema-oracle/src/lib.rs | 215 ++++--- pallets/ema-oracle/src/migrations/mod.rs | 3 +- pallets/ema-oracle/src/migrations/v2.rs | 53 ++ .../ema-oracle/src/tests/external_oracle.rs | 526 ++++++++++++++++++ pallets/ema-oracle/src/tests/mock.rs | 13 +- pallets/ema-oracle/src/tests/mod.rs | 1 + .../src/tests/update_bifrost_oracle.rs | 103 ++-- pallets/ema-oracle/src/weights.rs | 25 + .../src/tests/mock.rs | 12 +- runtime/hydradx/src/assets.rs | 12 +- .../hydradx/src/benchmarking/ema_oracle.rs | 86 ++- .../hydradx/src/weights/pallet_ema_oracle.rs | 169 ++++-- 15 files changed, 1044 insertions(+), 240 deletions(-) create mode 100644 pallets/ema-oracle/src/migrations/v2.rs create mode 100644 pallets/ema-oracle/src/tests/external_oracle.rs diff --git a/integration-tests/src/driver/mod.rs b/integration-tests/src/driver/mod.rs index edf435fe6f..c3b776e9fc 100644 --- a/integration-tests/src/driver/mod.rs +++ b/integration-tests/src/driver/mod.rs @@ -132,6 +132,14 @@ impl HydrationTestDriver { price: (Balance, Balance), ) -> &Self { self.execute(|| { + // Ensure BIFROST_SOURCE is registered and bifrost_account is authorized + // We need this because we added support for multiple oracle sources which required migration + let _ = EmaOracle::register_external_source(RuntimeOrigin::root(), pallet_ema_oracle::BIFROST_SOURCE); + let _ = EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + pallet_ema_oracle::BIFROST_SOURCE, + bifrost_account(), + ); assert_ok!(EmaOracle::update_bifrost_oracle( RuntimeOrigin::signed(bifrost_account()), asset_a, diff --git a/integration-tests/src/oracle.rs b/integration-tests/src/oracle.rs index 94bfb6245d..f925dd80e8 100644 --- a/integration-tests/src/oracle.rs +++ b/integration-tests/src/oracle.rs @@ -30,7 +30,6 @@ use pallet_ema_oracle::BIFROST_SOURCE; use pallet_transaction_payment::ChargeTransactionPayment; use primitives::constants::chain::{OMNIPOOL_SOURCE, XYK_SOURCE}; use sp_runtime::traits::{DispatchTransaction, TransactionExtension}; -use sp_runtime::DispatchError::BadOrigin; use sp_runtime::DispatchResult; use sp_runtime::TransactionOutcome; use sp_std::collections::btree_map::BTreeMap; @@ -447,6 +446,17 @@ fn bifrost_oracle_should_be_updated() { let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); Hydra::execute_with(|| { + // Register BIFROST_SOURCE as external source and authorize bifrost account + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + bifrost_account(), + )); + assert_ok!(EmaOracle::add_oracle( RuntimeOrigin::root(), BIFROST_SOURCE, @@ -483,7 +493,18 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); Hydra::execute_with(|| { - // act + // Register BIFROST_SOURCE as external source and authorize bifrost account + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + bifrost_account(), + )); + + // act - no whitelist setup, external sources bypass whitelist assert_ok!(EmaOracle::update_bifrost_oracle( RuntimeOrigin::signed(bifrost_account()), asset_a, @@ -513,6 +534,17 @@ fn bifrost_oracle_update_should_return_fee() { let (_asset_a_id, _asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); let balance = 10 * UNITS; Hydra::execute_with(|| { + // Register BIFROST_SOURCE as external source and authorize bifrost account + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + bifrost_account(), + )); + assert_ok!(hydradx_runtime::Currencies::update_balance( hydradx_runtime::RuntimeOrigin::root(), bifrost_account(), @@ -564,6 +596,12 @@ fn bifrost_oracle_update_fail_should_charge_fee() { TestNet::reset(); let (_asset_a_id, _asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); Hydra::execute_with(|| { + // Register BIFROST_SOURCE but do NOT authorize ALICE + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + let balance = hydradx_runtime::Currencies::free_balance(0, &ALICE.into()); let oracle_call = hydradx_runtime::RuntimeCall::EmaOracle( pallet_ema_oracle::Call::::update_bifrost_oracle { @@ -586,7 +624,10 @@ fn bifrost_oracle_update_fail_should_charge_fee() { "fee should be withdrawn" ); let exec = EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(ALICE.into()), asset_a, asset_b, (50, 100)); - assert_noop!(exec.clone(), BadOrigin); + assert_noop!( + exec.clone(), + pallet_ema_oracle::Error::::NotAuthorized + ); let mut exec_err_post_info = exec.err().unwrap().post_info; assert_ok!(ChargeTransactionPayment::::post_dispatch( pre_data, diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index 5caadbcfd0..5e0027ca42 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -18,7 +18,7 @@ use crate as dca; use crate::{Config, Error, RandomnessProvider, RelayChainBlockHashProvider}; use cumulus_primitives_core::relay_chain::Hash; -use frame_support::traits::{Everything, Nothing, SortedMembers}; +use frame_support::traits::{Everything, Nothing}; use frame_support::weights::constants::ExtrinsicBaseWeight; use frame_support::weights::WeightToFeeCoefficient; use frame_support::weights::{IdentityFee, Weight}; @@ -139,15 +139,8 @@ parameter_types! { pub static MockBlockNumberProvider: u64 = 0; pub SupportedPeriods: BoundedVec> = BoundedVec::truncate_from(vec![ OraclePeriod::LastBlock, OraclePeriod::Short, OraclePeriod::TenMinutes]); - pub PriceDifference: Permill = Permill::from_percent(10); } -pub struct BifrostAcc; -impl SortedMembers for BifrostAcc { - fn sorted_members() -> Vec { - vec![ALICE] - } -} impl pallet_ema_oracle::Config for Test { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EnsureRoot; @@ -158,8 +151,6 @@ impl pallet_ema_oracle::Config for Test { type LocationToAssetIdConversion = (); #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); - type BifrostOrigin = frame_system::EnsureSignedBy; - type MaxAllowedPriceDifference = PriceDifference; type WeightInfo = (); } diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 1cd06662f6..c2b2fbf025 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -77,8 +77,6 @@ use hydradx_traits::{ OnLiquidityChangedHandler, OnTradeHandler, RawEntry, RawOracle, Volume, }; use sp_arithmetic::traits::Saturating; -use sp_arithmetic::FixedU128; -use sp_arithmetic::Permill; use sp_runtime::traits::Convert; use sp_std::collections::btree_map::BTreeMap; use sp_std::marker::PhantomData; @@ -99,6 +97,11 @@ pub const MAX_PERIODS: u32 = OraclePeriod::all_periods().len() as u32; pub const BIFROST_SOURCE: [u8; 8] = *b"bifrosto"; +/// Max external oracle entries per block based on proof_size budget: +/// 75% of MAX_POV_SIZE (5MB) / ~14,197 bytes per set_external_oracle call ≈ 275. +/// Rounded up to 300 for safety margin. +pub const MAX_EXTERNAL_ENTRIES_PER_BLOCK: u32 = 300; + const LOG_TARGET: &str = "runtime::ema-oracle"; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -119,7 +122,8 @@ impl BenchmarkHelper for () { #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{BoundedBTreeMap, BoundedBTreeSet}; + use frame_support::BoundedBTreeSet; + use frame_system::ensure_signed; use frame_system::pallet_prelude::{BlockNumberFor, OriginFor}; #[pallet::pallet] @@ -136,9 +140,6 @@ pub mod pallet { /// Origin that can enable oracle for assets that would be rejected by `OracleWhitelist` otherwise. type AuthorityOrigin: EnsureOrigin; - /// Origin that can update bifrost oracle via `update_bifrost_oracle` extrinsic. - type BifrostOrigin: EnsureOrigin; - /// Provider for the current block number. type BlockNumberProvider: BlockNumberProvider>; @@ -151,10 +152,6 @@ pub mod pallet { /// Location to Asset Id converter type LocationToAssetIdConversion: sp_runtime::traits::Convert>; - /// Maximum allowed percentage difference for bifrost oracle price update - #[pallet::constant] - type MaxAllowedPriceDifference: Get; - /// Maximum number of unique oracle entries expected in one block. #[pallet::constant] type MaxUniqueEntries: Get; @@ -170,8 +167,14 @@ pub mod pallet { OracleNotFound, /// Asset not found AssetNotFound, - ///The new price is outside the max allowed range - PriceOutsideAllowedRange, + /// The external source is already registered. + SourceAlreadyRegistered, + /// The external source was not found. + SourceNotFound, + /// The caller is not authorized for the given source. + NotAuthorized, + /// Price must not be zero. + PriceIsZero, } #[pallet::event] @@ -187,16 +190,22 @@ pub mod pallet { assets: (AssetId, AssetId), updates: BTreeMap, }, + /// An external oracle source was registered. + ExternalSourceRegistered { source: Source }, + /// An external oracle source was removed. + ExternalSourceRemoved { source: Source }, + /// An authorized account was added for an external source. + AuthorizedAccountAdded { source: Source, account: T::AccountId }, + /// An authorized account was removed for an external source. + AuthorizedAccountRemoved { source: Source, account: T::AccountId }, } /// Accumulator for oracle data in current block that will be recorded at the end of the block. #[pallet::storage] + #[pallet::unbounded] #[pallet::getter(fn accumulator)] - pub type Accumulator = StorageValue< - _, - BoundedBTreeMap<(Source, (AssetId, AssetId)), OracleEntry>, T::MaxUniqueEntries>, - ValueQuery, - >; + pub type Accumulator = + StorageValue<_, BTreeMap<(Source, (AssetId, AssetId)), OracleEntry>>, ValueQuery>; /// Oracle storage keyed by data source, involved asset ids and the period length of the oracle. /// @@ -220,6 +229,15 @@ pub mod pallet { pub type WhitelistedAssets = StorageValue<_, BoundedBTreeSet<(Source, (AssetId, AssetId)), T::MaxUniqueEntries>, ValueQuery>; + /// Registered external oracle sources. + #[pallet::storage] + pub type ExternalSources = StorageMap<_, Twox64Concat, Source, (), OptionQuery>; + + /// Authorized accounts per external oracle source. + #[pallet::storage] + pub type AuthorizedAccounts = + StorageDoubleMap<_, Twox64Concat, Source, Twox64Concat, T::AccountId, (), OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -319,8 +337,11 @@ pub mod pallet { Ok(()) } + /// DEPRECATED - Use `set_external_oracle` instead. + /// This is kept for backward compatibility with bifrost and will be removed in the future. #[pallet::call_index(2)] - #[pallet::weight(::WeightInfo::update_bifrost_oracle())] + #[pallet::weight(::WeightInfo::update_bifrost_oracle() + .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] pub fn update_bifrost_oracle( origin: OriginFor, //NOTE: these must be boxed becasue of https://github.com/paritytech/polkadot-sdk/blob/6875d36b2dba537f3254aad3db76ac7aa656b7ab/substrate/frame/utility/src/lib.rs#L150 @@ -328,45 +349,113 @@ pub mod pallet { asset_b: Box, price: (Balance, Balance), ) -> DispatchResultWithPostInfo { - T::BifrostOrigin::ensure_origin(origin)?; - - let asset_a = T::LocationToAssetIdConversion::convert(*asset_a).ok_or(Error::::AssetNotFound)?; - let asset_b = T::LocationToAssetIdConversion::convert(*asset_b).ok_or(Error::::AssetNotFound)?; - - let ordered_pair = ordered_pair(asset_a, asset_b); - let entry: OracleEntry> = { - let e = OracleEntry::new( - EmaPrice::new(price.0, price.1), - Volume::default(), - Liquidity::default(), - None, - T::BlockNumberProvider::current_block_number(), - ); - if ordered_pair == (asset_a, asset_b) { - e - } else { - e.inverted() - } - }; + let who = ensure_signed(origin)?; + Self::do_set_oracle(who, BIFROST_SOURCE, asset_a, asset_b, price) + } - if let Some(reference_entry) = Self::oracle((BIFROST_SOURCE, ordered_pair, OraclePeriod::TenMinutes)) { - if !Self::is_within_range(reference_entry.0.price.into(), price) { - log::error!( - target: LOG_TARGET, - "Updating bifrost oracle failed as the price is outside the allowed range" - ); - return Err(Error::::PriceOutsideAllowedRange.into()); - } - } + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::set_external_oracle() + .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] + pub fn set_external_oracle( + origin: OriginFor, + source: Source, + asset_a: Box, + asset_b: Box, + price: (Balance, Balance), + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + Self::do_set_oracle(who, source, asset_a, asset_b, price) + } - Self::on_entry(BIFROST_SOURCE, ordered_pair, entry).map_err(|_| Error::::TooManyUniqueEntries)?; + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::register_external_source())] + pub fn register_external_source(origin: OriginFor, source: Source) -> DispatchResult { + T::AuthorityOrigin::ensure_origin(origin)?; + ensure!( + !ExternalSources::::contains_key(source), + Error::::SourceAlreadyRegistered + ); + ExternalSources::::insert(source, ()); + Self::deposit_event(Event::ExternalSourceRegistered { source }); + Ok(()) + } - Ok(Pays::No.into()) + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::remove_external_source())] + pub fn remove_external_source(origin: OriginFor, source: Source) -> DispatchResult { + T::AuthorityOrigin::ensure_origin(origin)?; + ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); + ExternalSources::::remove(source); + let _ = AuthorizedAccounts::::clear_prefix(source, u32::MAX, None); + Self::deposit_event(Event::ExternalSourceRemoved { source }); + Ok(()) + } + + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::add_authorized_account())] + pub fn add_authorized_account(origin: OriginFor, source: Source, account: T::AccountId) -> DispatchResult { + T::AuthorityOrigin::ensure_origin(origin)?; + ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); + AuthorizedAccounts::::insert(source, &account, ()); + Self::deposit_event(Event::AuthorizedAccountAdded { source, account }); + Ok(()) + } + + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::remove_authorized_account())] + pub fn remove_authorized_account( + origin: OriginFor, + source: Source, + account: T::AccountId, + ) -> DispatchResult { + T::AuthorityOrigin::ensure_origin(origin)?; + ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); + AuthorizedAccounts::::remove(source, &account); + Self::deposit_event(Event::AuthorizedAccountRemoved { source, account }); + Ok(()) } } } impl Pallet { + fn do_set_oracle( + who: T::AccountId, + source: Source, + asset_a: Box, + asset_b: Box, + price: (Balance, Balance), + ) -> DispatchResultWithPostInfo { + ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); + ensure!( + AuthorizedAccounts::::contains_key(source, &who), + Error::::NotAuthorized + ); + ensure!(price.0 != 0 && price.1 != 0, Error::::PriceIsZero); + + let asset_a = T::LocationToAssetIdConversion::convert(*asset_a).ok_or(Error::::AssetNotFound)?; + let asset_b = T::LocationToAssetIdConversion::convert(*asset_b).ok_or(Error::::AssetNotFound)?; + + let ordered = ordered_pair(asset_a, asset_b); + let entry: OracleEntry> = { + let e = OracleEntry::new( + EmaPrice::new(price.0, price.1), + Volume::default(), + Liquidity::default(), + None, + T::BlockNumberProvider::current_block_number(), + ); + if ordered == (asset_a, asset_b) { + e + } else { + e.inverted() + } + }; + + Self::on_entry(source, ordered, entry).map_err(|_| Error::::TooManyUniqueEntries)?; + + Ok(Pays::No.into()) + } + /// Insert or update data in the accumulator from received entry. Aggregates volume and /// takes the most recent data for the rest. pub(crate) fn on_entry( @@ -374,7 +463,7 @@ impl Pallet { assets: (AssetId, AssetId), oracle_entry: OracleEntry>, ) -> Result<(), ()> { - if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && src.ne(&BIFROST_SOURCE) { + if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && !ExternalSources::::contains_key(src) { // if we don't track oracle for given asset pair, don't throw error return Ok(()); } @@ -384,10 +473,13 @@ impl Pallet { entry.accumulate_volume_and_update_from(&oracle_entry); Ok(()) } else { - accumulator - .try_insert((src, assets), oracle_entry) - .map(|_| ()) - .map_err(|_| ()) + if !ExternalSources::::contains_key(src) && accumulator.len() >= T::MaxUniqueEntries::get() as usize + { + //We have soft limit up to MaxUniqueEntries, only for AMM internal oracle updates + return Err(()); + } + accumulator.insert((src, assets), oracle_entry); + Ok(()) } }) } @@ -550,17 +642,6 @@ impl Pallet { (entry, init) }) } - - fn is_within_range(reference_price: (u128, u128), new_price: (u128, u128)) -> bool { - let reference = FixedU128::from_rational(reference_price.0, reference_price.1); - let new_value = FixedU128::from_rational(new_price.0, new_price.1); - - let percentage_difference = T::MaxAllowedPriceDifference::get(); - let lower_bound = reference.saturating_mul(FixedU128::one().saturating_sub(percentage_difference.into())); - let upper_bound = reference.saturating_mul(FixedU128::one().saturating_add(percentage_difference.into())); - - new_value >= lower_bound && new_value <= upper_bound - } } /// A callback handler for trading and liquidity activity that schedules oracle updates. @@ -621,7 +702,7 @@ impl OnTradeHandler for OnActivityHandler let max_entries = T::MaxUniqueEntries::get(); // on_trade + on_finalize / max_entries T::WeightInfo::on_trade_multiple_tokens(max_entries) - .saturating_add(fractional_on_finalize_weight::(max_entries)) + .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)) } } @@ -662,7 +743,7 @@ impl OnLiquidityChangedHandler for OnActivit let max_entries = T::MaxUniqueEntries::get(); // on_liquidity + on_finalize / max_entries T::WeightInfo::on_liquidity_changed_multiple_tokens(max_entries) - .saturating_add(fractional_on_finalize_weight::(max_entries)) + .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)) } } diff --git a/pallets/ema-oracle/src/migrations/mod.rs b/pallets/ema-oracle/src/migrations/mod.rs index ad59d2de4a..379e1d8b82 100644 --- a/pallets/ema-oracle/src/migrations/mod.rs +++ b/pallets/ema-oracle/src/migrations/mod.rs @@ -1,6 +1,7 @@ use frame_support::traits::StorageVersion; pub mod v1; +pub mod v2; /// The in-code storage version. -pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); diff --git a/pallets/ema-oracle/src/migrations/v2.rs b/pallets/ema-oracle/src/migrations/v2.rs new file mode 100644 index 0000000000..523459a3d1 --- /dev/null +++ b/pallets/ema-oracle/src/migrations/v2.rs @@ -0,0 +1,53 @@ +use crate::*; +use frame_support::traits::Get; +use frame_support::weights::Weight; +use frame_support::{migrations::VersionedMigration, traits::UncheckedOnRuntimeUpgrade}; + +mod unversioned { + use super::*; + + pub struct InnerMigrateV1ToV2>( + core::marker::PhantomData<(T, BifrostAccount)>, + ); +} + +impl> UncheckedOnRuntimeUpgrade + for unversioned::InnerMigrateV1ToV2 +{ + fn on_runtime_upgrade() -> Weight { + log::info!(target: "runtime::ema-oracle", "v1->v2 migration started"); + + // Register BIFROST_SOURCE as an external source + ExternalSources::::insert(BIFROST_SOURCE, ()); + + // Add the bifrost sovereign account as an authorized account for BIFROST_SOURCE + let bifrost_account = BifrostAccount::get(); + AuthorizedAccounts::::insert(BIFROST_SOURCE, &bifrost_account, ()); + + log::info!(target: "runtime::ema-oracle", "v1->v2 migration finished: registered BIFROST_SOURCE and authorized bifrost account"); + + T::DbWeight::get().reads_writes(0, 2) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_: Vec) -> Result<(), sp_runtime::DispatchError> { + assert!( + ExternalSources::::contains_key(BIFROST_SOURCE), + "BIFROST_SOURCE should be registered as external source" + ); + let bifrost_account = BifrostAccount::get(); + assert!( + AuthorizedAccounts::::contains_key(BIFROST_SOURCE, &bifrost_account), + "Bifrost account should be authorized for BIFROST_SOURCE" + ); + Ok(()) + } +} + +pub type MigrateV1ToV2 = VersionedMigration< + 1, + 2, + unversioned::InnerMigrateV1ToV2, + Pallet, + ::DbWeight, +>; diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs new file mode 100644 index 0000000000..22e5ff2c63 --- /dev/null +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -0,0 +1,526 @@ +// This file is part of pallet-ema-oracle. + +// Copyright (C) 2022-2023 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::mock::{self, EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; +use super::SOURCE; +use crate::pallet::{AuthorizedAccounts, ExternalSources}; +use crate::*; + +use frame_support::pallet_prelude::*; +use frame_support::{assert_noop, assert_ok}; +use pretty_assertions::assert_eq; + +const EXTERNAL_SOURCE: Source = *b"external"; +const ANOTHER_SOURCE: Source = *b"another_"; + +pub fn new_test_ext() -> sp_io::TestExternalities { + ExtBuilder::default().build() +} + +#[test] +fn register_external_source_works() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert!(ExternalSources::::contains_key(EXTERNAL_SOURCE)); + }); +} + +#[test] +fn register_duplicate_source_fails() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_noop!( + EmaOracle::register_external_source(RuntimeOrigin::root(), EXTERNAL_SOURCE), + Error::::SourceAlreadyRegistered + ); + }); +} + +#[test] +fn register_external_source_requires_authority() { + new_test_ext().execute_with(|| { + assert_noop!( + EmaOracle::register_external_source(RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn remove_external_source_works() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + assert_ok!(EmaOracle::remove_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert!(!ExternalSources::::contains_key(EXTERNAL_SOURCE)); + assert!(!AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + }); +} + +#[test] +fn remove_nonexistent_source_fails() { + new_test_ext().execute_with(|| { + assert_noop!( + EmaOracle::remove_external_source(RuntimeOrigin::root(), EXTERNAL_SOURCE), + Error::::SourceNotFound + ); + }); +} + +#[test] +fn add_authorized_account_works() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + assert!(AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + }); +} + +#[test] +fn add_account_for_nonexistent_source_fails() { + new_test_ext().execute_with(|| { + assert_noop!( + EmaOracle::add_authorized_account(RuntimeOrigin::root(), EXTERNAL_SOURCE, ALICE), + Error::::SourceNotFound + ); + }); +} + +#[test] +fn remove_authorized_account_works() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + assert_ok!(EmaOracle::remove_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + assert!(!AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + }); +} + +#[test] +fn set_external_oracle_happy_path() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + System::set_block_number(3); + + let res = EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 99), + ); + assert_eq!(res, Ok(Pays::No.into())); + + // Verify the entry is in the accumulator + let acc = Accumulator::::get(); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 5)))); + }); +} + +#[test] +fn set_external_oracle_unauthorized_rejected() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 99), + ), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn set_external_oracle_zero_price_rejected() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx.clone()), + Box::new(dot.clone()), + (0, 100), + ), + Error::::PriceIsZero + ); + + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 0), + ), + Error::::PriceIsZero + ); + }); +} + +#[test] +fn set_external_oracle_unregistered_source_rejected() { + new_test_ext().execute_with(|| { + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 99), + ), + Error::::SourceNotFound + ); + }); +} + +#[test] +fn external_sources_bypass_whitelist() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + // Use INSUFFICIENT_ASSET which is normally excluded by the whitelist + let asset_a_loc = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let asset_b_loc = polkadot_xcm::v5::Location::parent().into_versioned(); + + System::set_block_number(3); + + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(asset_a_loc), + Box::new(asset_b_loc), + (100, 99), + )); + + // Verify the entry is in the accumulator (bypasses whitelist) + let acc = Accumulator::::get(); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 5)))); + }); +} + +#[test] +fn multiple_sources_in_same_block() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + ANOTHER_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + ANOTHER_SOURCE, + BOB + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + System::set_block_number(3); + + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx.clone()), + Box::new(dot.clone()), + (100, 99), + )); + + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(BOB), + ANOTHER_SOURCE, + Box::new(hdx), + Box::new(dot), + (200, 99), + )); + + let acc = Accumulator::::get(); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 5)))); + assert!(acc.contains_key(&(ANOTHER_SOURCE, ordered_pair(0, 5)))); + }); +} + +// --- soft limit --- + +#[test] +fn amm_trades_are_limited_to_max_unique_entries() { + new_test_ext().execute_with(|| { + //Arrange + let max_entries = <::MaxUniqueEntries as Get>::get(); + + //Act - fill the accumulator to max + for i in 0..max_entries { + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + i, + i + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + } + + //Assert - accumulator is full, next AMM trade fails + assert_eq!(Accumulator::::get().len(), max_entries as usize); + assert_noop!( + OnActivityHandler::::on_trade( + SOURCE, + 2 * max_entries, + 2 * max_entries + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + ) + .map_err(|(_w, e)| e), + Error::::TooManyUniqueEntries + ); + }); +} + +#[test] +fn external_sources_can_add_entries_beyond_max_unique_entries() { + new_test_ext().execute_with(|| { + //Arrange - fill accumulator to max with AMM trades + let max_entries = <::MaxUniqueEntries as Get>::get(); + for i in 0..max_entries { + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + i, + i + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + } + + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + //Act - external source adds entry beyond the soft limit + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 99), + )); + + //Assert - accumulator has more entries than MaxUniqueEntries + assert_eq!(Accumulator::::get().len(), (max_entries + 1) as usize); + }); +} + +#[test] +fn soft_limit_only_for_non_external_sources() { + new_test_ext().execute_with(|| { + let max_entries = <::MaxUniqueEntries as Get>::get(); + + // Fill the accumulator with non-external entries + for i in 0..max_entries { + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + i, + i + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + } + + // Non-external source should fail + assert_noop!( + OnActivityHandler::::on_trade( + SOURCE, + 2 * max_entries, + 2 * max_entries + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + ) + .map_err(|(_w, e)| e), + Error::::TooManyUniqueEntries + ); + + // But external sources should still be able to insert + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + let hdx = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + // External source can still insert past the soft limit + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx), + Box::new(dot), + (100, 99), + )); + }); +} diff --git a/pallets/ema-oracle/src/tests/mock.rs b/pallets/ema-oracle/src/tests/mock.rs index 5c6423151c..8f6cb6a61a 100644 --- a/pallets/ema-oracle/src/tests/mock.rs +++ b/pallets/ema-oracle/src/tests/mock.rs @@ -25,7 +25,7 @@ use frame_support::sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; -use frame_support::traits::{Contains, Everything, SortedMembers}; +use frame_support::traits::{Contains, Everything}; use frame_support::BoundedVec; use frame_system::EnsureRoot; use hydradx_traits::OraclePeriod::{self, *}; @@ -33,7 +33,6 @@ use hydradx_traits::Source; use hydradx_traits::{Liquidity, Volume}; use polkadot_xcm::latest::{Junctions, Location}; use polkadot_xcm::prelude::GeneralIndex; -use sp_arithmetic::Permill; use sp_core::H256; use sp_runtime::traits::Convert; @@ -127,7 +126,6 @@ impl frame_system::Config for Test { parameter_types! { pub SupportedPeriods: BoundedVec> = bounded_vec![LastBlock, TenMinutes, Day, Week]; - pub PriceDifference: Permill = Permill::from_percent(10); } pub struct OracleWhitelist; @@ -137,13 +135,6 @@ impl Contains<(Source, AssetId, AssetId)> for OracleWhitelist { } } -pub struct BifrostAcc; -impl SortedMembers for BifrostAcc { - fn sorted_members() -> Vec { - vec![ALICE] - } -} - impl Config for Test { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EnsureRoot; @@ -153,10 +144,8 @@ impl Config for Test { type MaxUniqueEntries = ConstU32<45>; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); - type BifrostOrigin = frame_system::EnsureSignedBy; type WeightInfo = (); type LocationToAssetIdConversion = CurrencyIdConvertMock; - type MaxAllowedPriceDifference = PriceDifference; } pub struct CurrencyIdConvertMock; diff --git a/pallets/ema-oracle/src/tests/mod.rs b/pallets/ema-oracle/src/tests/mod.rs index a166e41078..34fc8c220d 100644 --- a/pallets/ema-oracle/src/tests/mod.rs +++ b/pallets/ema-oracle/src/tests/mod.rs @@ -16,6 +16,7 @@ // limitations under the License. mod add_and_remove_oracle; +mod external_oracle; mod invariants; mod mock; mod oracle_updated_event; diff --git a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs index 2031278ce2..f1c6eec258 100644 --- a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs +++ b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs @@ -29,12 +29,22 @@ pub fn new_test_ext() -> sp_io::TestExternalities { use crate::tests::mock::ALICE; use polkadot_xcm::v5::prelude::*; -use sp_runtime::DispatchError::BadOrigin; + +fn setup_bifrost_auth() { + assert_ok!(EmaOracle::register_external_source(RuntimeOrigin::root(), BIFROST_SOURCE)); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + ALICE + )); +} #[test] fn add_oracle_should_add_entry_to_storage() { new_test_ext().execute_with(|| { //Arrange + setup_bifrost_auth(); + let hdx = polkadot_xcm::v5::Location::new(0, polkadot_xcm::v5::Junctions::X1([GeneralIndex(0)].into())) .into_versioned(); @@ -70,6 +80,8 @@ fn add_oracle_should_add_entry_to_storage() { fn successful_oracle_update_shouldnt_pay_fee() { new_test_ext().execute_with(|| { //Arrange + setup_bifrost_auth(); + let hdx = polkadot_xcm::v5::Location::new(0, polkadot_xcm::v5::Junctions::X1([GeneralIndex(0)].into())) .into_versioned(); let dot = polkadot_xcm::v5::Location::parent().into_versioned(); @@ -78,7 +90,7 @@ fn successful_oracle_update_shouldnt_pay_fee() { let res = EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(ALICE), Box::new(hdx), Box::new(dot), (100, 99)); - // Assert + //Assert assert_eq!(res, Ok(Pays::No.into())); }); } @@ -87,6 +99,8 @@ fn successful_oracle_update_shouldnt_pay_fee() { fn add_oracle_should_add_entry_to_storage_with_inversed_pair() { new_test_ext().execute_with(|| { //Arrange + setup_bifrost_auth(); + let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); let dot = polkadot_xcm::v5::Location::parent().into_versioned(); @@ -118,9 +132,11 @@ fn add_oracle_should_add_entry_to_storage_with_inversed_pair() { } #[test] -fn bitfrost_oracle_should_not_be_updated_by_nonpriviliged_account() { +fn bifrost_oracle_should_not_be_updated_by_nonprivileged_account() { new_test_ext().execute_with(|| { //Arrange + setup_bifrost_auth(); + let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); let dot = polkadot_xcm::v5::Location::parent().into_versioned(); @@ -128,99 +144,52 @@ fn bitfrost_oracle_should_not_be_updated_by_nonpriviliged_account() { let asset_a = Box::new(hdx); let asset_b = Box::new(dot); - //Act System::set_block_number(3); + //Act & Assert assert_noop!( EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(BOB), asset_a, asset_b, (100, 99)), - BadOrigin + Error::::NotAuthorized ); }); } #[test] -fn should_fail_when_new_price_is_bigger_than_allowed() { +fn should_fail_when_price_is_zero() { new_test_ext().execute_with(|| { //Arrange - let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); + setup_bifrost_auth(); + let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - let asset_a = Box::new(hdx); - let asset_b = Box::new(dot); - System::set_block_number(3); - assert_ok!(EmaOracle::update_bifrost_oracle( - RuntimeOrigin::signed(ALICE), - asset_a.clone(), - asset_b.clone(), - (100, 100) - )); - - update_aggregated_oracles(); - - //Act + //Act & Assert assert_noop!( EmaOracle::update_bifrost_oracle( RuntimeOrigin::signed(ALICE), - asset_a.clone(), - asset_b.clone(), - (111, 100) + Box::new(hdx.clone()), + Box::new(dot.clone()), + (0, 100) ), - Error::::PriceOutsideAllowedRange + Error::::PriceIsZero ); assert_noop!( - EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(ALICE), asset_a, asset_b, (89, 100)), - Error::::PriceOutsideAllowedRange + EmaOracle::update_bifrost_oracle( + RuntimeOrigin::signed(ALICE), + Box::new(hdx), + Box::new(dot), + (100, 0) + ), + Error::::PriceIsZero ); }); } -#[test] -fn should_pass_when_new_price_is_still_within_range() { - new_test_ext().execute_with(|| { - //Arrange - let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); - - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - - let asset_a = Box::new(hdx); - let asset_b = Box::new(dot); - - System::set_block_number(3); - - assert_ok!(EmaOracle::update_bifrost_oracle( - RuntimeOrigin::signed(ALICE), - asset_a.clone(), - asset_b.clone(), - (100, 100) - )); - - update_aggregated_oracles(); - - //Act - assert_ok!(EmaOracle::update_bifrost_oracle( - RuntimeOrigin::signed(ALICE), - asset_a.clone(), - asset_b.clone(), - (110, 100) - ),); - - assert_ok!(EmaOracle::update_bifrost_oracle( - RuntimeOrigin::signed(ALICE), - asset_a, - asset_b, - (90, 100) - ),); - }); -} - pub fn update_aggregated_oracles() { EmaOracle::on_finalize(6); System::set_block_number(7); EmaOracle::on_initialize(7); } - -//TODO: add negative test when it is not called by bitfrost origni diff --git a/pallets/ema-oracle/src/weights.rs b/pallets/ema-oracle/src/weights.rs index c85fb7dd6d..0b354c666c 100644 --- a/pallets/ema-oracle/src/weights.rs +++ b/pallets/ema-oracle/src/weights.rs @@ -19,6 +19,11 @@ pub trait WeightInfo { fn on_trade_multiple_tokens(b: u32) -> Weight; fn on_liquidity_changed_multiple_tokens(b: u32) -> Weight; fn get_entry() -> Weight; + fn set_external_oracle() -> Weight; + fn register_external_source() -> Weight; + fn remove_external_source() -> Weight; + fn add_authorized_account() -> Weight; + fn remove_authorized_account() -> Weight; } /// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. @@ -54,6 +59,26 @@ impl WeightInfo for () { Weight::zero() } + fn set_external_oracle() -> Weight { + Weight::zero() + } + + fn register_external_source() -> Weight { + Weight::zero() + } + + fn remove_external_source() -> Weight { + Weight::zero() + } + + fn add_authorized_account() -> Weight { + Weight::zero() + } + + fn remove_authorized_account() -> Weight { + Weight::zero() + } + /// Storage: `EmaOracle::Accumulator` (r:1 w:0) /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(5921), added: 6416, mode: `MaxEncodedLen`) fn on_finalize_no_entry() -> Weight { diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index 4d087f2b06..35c771417d 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -26,7 +26,7 @@ use frame_support::BoundedVec; use hydradx_traits::liquidity_mining::PriceAdjustment; use pallet_omnipool; -use frame_support::traits::{ConstU128, Contains, Everything, SortedMembers}; +use frame_support::traits::{ConstU128, Contains, Everything}; use frame_support::{ assert_ok, construct_runtime, parameter_types, traits::{ConstU32, ConstU64}, @@ -296,17 +296,9 @@ parameter_types! { pub SupportedPeriods: BoundedVec> = BoundedVec::truncate_from(vec![ OraclePeriod::LastBlock, OraclePeriod::Short, OraclePeriod::TenMinutes]); - pub PriceDifference: Permill = Permill::from_percent(10); } -pub struct BifrostAcc; -impl SortedMembers for BifrostAcc { - fn sorted_members() -> Vec { - vec![ALICE] - } -} - impl pallet_ema_oracle::Config for Test { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EnsureRoot; @@ -314,9 +306,7 @@ impl pallet_ema_oracle::Config for Test { type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; type MaxUniqueEntries = ConstU32<20>; - type BifrostOrigin = frame_system::EnsureSignedBy; type LocationToAssetIdConversion = (); - type MaxAllowedPriceDifference = PriceDifference; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); type WeightInfo = (); diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index a2c61ee86c..095c37c631 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -32,7 +32,7 @@ use frame_support::{ sp_runtime::{FixedU128, Perbill, Permill}, traits::{ AsEnsureOriginWithArg, ConstU32, Contains, Currency, Defensive, EitherOf, EnsureOrigin, ExistenceRequirement, - Imbalance, LockIdentifier, NeverEnsureOrigin, OnUnbalanced, SortedMembers, + Imbalance, LockIdentifier, NeverEnsureOrigin, OnUnbalanced, }, BoundedVec, PalletId, }; @@ -641,7 +641,6 @@ parameter_types! { pub SupportedPeriods: BoundedVec> = BoundedVec::truncate_from(vec![ OraclePeriod::LastBlock, OraclePeriod::Short, OraclePeriod::TenMinutes]); - pub MaxAllowedPriceDifferenceForBifrostOracleUpdate: Permill = Permill::from_percent(10); } pub struct OracleWhitelist(PhantomData); @@ -660,17 +659,9 @@ where pub fn bifrost_account() -> AccountId { hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into() } -pub struct BifrostAcc; -impl SortedMembers for BifrostAcc { - fn sorted_members() -> Vec { - vec![bifrost_account()] - } -} - impl pallet_ema_oracle::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EitherOf, GeneralAdmin>; - type BifrostOrigin = frame_system::EnsureSignedBy; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. @@ -685,7 +676,6 @@ impl pallet_ema_oracle::Config for Runtime { /// Should take care of the overhead introduced by `OracleWhitelist`. type BenchmarkHelper = RegisterAsset; type LocationToAssetIdConversion = CurrencyIdConvert; - type MaxAllowedPriceDifference = MaxAllowedPriceDifferenceForBifrostOracleUpdate; } pub struct ExtendedDustRemovalWhitelist; diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index 5a567e7fe3..55870289d0 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -116,7 +116,7 @@ runtime_benchmarks! { shares_issuance: Some(shares_issuance), }; - assert_eq!(Accumulator::::get().into_inner(), [((SOURCE, pallet_ema_oracle::ordered_pair(HDX, DOT)), entry.clone())].into_iter().collect()); + assert_eq!(Accumulator::::get(), [((SOURCE, pallet_ema_oracle::ordered_pair(HDX, DOT)), entry.clone())].into_iter().collect()); }: { as frame_support::traits::OnFinalize>>::on_finalize(block_num); } verify { @@ -161,7 +161,7 @@ runtime_benchmarks! { shares_issuance: Some(shares_issuance), }; - assert_eq!(Accumulator::::get().into_inner(), [((SOURCE, ordered_pair(HDX, DOT)), entry.clone())].into_iter().collect()); + assert_eq!(Accumulator::::get(), [((SOURCE, ordered_pair(HDX, DOT)), entry.clone())].into_iter().collect()); }: { as frame_support::traits::OnFinalize>>::on_finalize(block_num); } verify { @@ -170,10 +170,11 @@ runtime_benchmarks! { } on_finalize_multiple_tokens { - let b in 1 .. (<::MaxUniqueEntries as Get>::get() - 1); + let b in 1 .. pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; - let max_entries = <::MaxUniqueEntries as Get>::get(); - fill_whitelist_storage(max_entries); + // Register an external source so on_trade bypasses the whitelist and soft limit + let external_source: Source = *b"benchext"; + EmaOracle::register_external_source(RawOrigin::Root.into(), external_source).expect("error when registering external source"); let initial_data_block: BlockNumberFor = 5u32; let block_num = initial_data_block.saturating_add(1_000_000u32); @@ -182,7 +183,7 @@ runtime_benchmarks! { as frame_support::traits::OnInitialize>>::on_initialize(initial_data_block); let (amount_in, amount_out) = (1_000_000_000_000, 2_000_000_000_000); let (liquidity_asset_in, liquidity_asset_out) = (1_000_000_000_000_000, 2_000_000_000_000_000); - let shares_issuance =1_000_000_000_000; + let shares_issuance = 1_000_000_000_000; for i in 0 .. b { let asset_a = (i + 1) * 1_000; let asset_b = asset_a + 500; @@ -190,7 +191,7 @@ runtime_benchmarks! { register_asset_with_id([b"AS2", asset_b.to_string().as_bytes()].concat(), asset_b).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; assert_ok!(OnActivityHandler::::on_trade( - SOURCE, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, + external_source, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); } as frame_support::traits::OnFinalize>>::on_finalize(initial_data_block); @@ -201,7 +202,7 @@ runtime_benchmarks! { let asset_a = (i + 1) * 1_000; let asset_b = asset_a + 500; assert_ok!(OnActivityHandler::::on_trade( - SOURCE, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, + external_source, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); } }: { as frame_support::traits::OnFinalize>>::on_finalize(block_num); } @@ -217,7 +218,7 @@ runtime_benchmarks! { for i in 0 .. b { let asset_a = (i + 1) * 1_000; let asset_b = asset_a + 500; - assert_eq!(pallet_ema_oracle::Pallet::::oracle((SOURCE, ordered_pair(asset_a, asset_b), OraclePeriod::LastBlock)).unwrap(), (entry.clone(), initial_data_block)); + assert_eq!(pallet_ema_oracle::Pallet::::oracle((external_source, ordered_pair(asset_a, asset_b), OraclePeriod::LastBlock)).unwrap(), (entry.clone(), initial_data_block)); } } @@ -285,7 +286,7 @@ runtime_benchmarks! { assert_ok!(*res.borrow()); entries.push(((SOURCE, ordered_pair(asset_a, asset_b)), entry.clone())); - assert_eq!(pallet_ema_oracle::Pallet::::accumulator().into_inner(), entries.into_iter().collect()); + assert_eq!(pallet_ema_oracle::Pallet::::accumulator(), entries.into_iter().collect()); } on_liquidity_changed_multiple_tokens { @@ -358,7 +359,7 @@ runtime_benchmarks! { }; entries.push(((SOURCE, ordered_pair(asset_a, asset_b)), liquidity_entry)); - assert_eq!(pallet_ema_oracle::Pallet::::accumulator().into_inner(), entries.into_iter().collect()); + assert_eq!(pallet_ema_oracle::Pallet::::accumulator(), entries.into_iter().collect()); } get_entry { @@ -411,7 +412,10 @@ runtime_benchmarks! { update_bifrost_oracle { let max_entries = <::MaxUniqueEntries as Get>::get(); fill_whitelist_storage(max_entries - 1); - EmaOracle::add_oracle(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE, (0, 3)).expect("error when adding oracle"); + + // Register BIFROST_SOURCE as external source and authorize bifrost account + EmaOracle::register_external_source(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE).expect("error when registering external source"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE, bifrost_account()).expect("error when adding authorized account"); let initial_data_block: BlockNumberFor = 5u32; let oracle_age: BlockNumberFor = 7u32; @@ -441,6 +445,64 @@ runtime_benchmarks! { let entry = pallet_ema_oracle::Pallet::::oracle((pallet_ema_oracle::BIFROST_SOURCE, pallet_ema_oracle::ordered_pair(0, 3), hydradx_traits::oracle::OraclePeriod::Short)); assert!(entry.is_some()); } + + set_external_oracle { + let max_entries = <::MaxUniqueEntries as Get>::get(); + fill_whitelist_storage(max_entries - 1); + + let external_source: Source = *b"external"; + EmaOracle::register_external_source(RawOrigin::Root.into(), external_source).expect("error when registering external source"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), external_source, bifrost_account()).expect("error when adding authorized account"); + + let initial_data_block: BlockNumberFor = 5u32; + frame_system::Pallet::::set_block_number(initial_data_block); + as frame_support::traits::OnInitialize>>::on_initialize(initial_data_block); + + let hdx_loc = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]); + let dot_loc = polkadot_xcm::v5::Location::new(1, [polkadot_xcm::v5::Junction::Parachain(1000), polkadot_xcm::v5::Junction::GeneralIndex(0)]); + + let dot_asset_loc = AssetLocation::try_from(dot_loc.clone()).unwrap(); + register_asset_with_id_and_loc(b"AS2".to_vec(), 3, dot_asset_loc).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + + let asset_a = Box::new(hdx_loc.into_versioned()); + let asset_b = Box::new(dot_loc.into_versioned()); + + }: _(RawOrigin::Signed(bifrost_account()), external_source, asset_a, asset_b, (100,99)) + verify { + assert!(!Accumulator::::get().is_empty()); + } + + register_external_source { + let source: Source = *b"newsrcxx"; + }: _(RawOrigin::Root, source) + verify { + assert!(pallet_ema_oracle::ExternalSources::::contains_key(source)); + } + + remove_external_source { + let source: Source = *b"newsrcxx"; + EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); + }: _(RawOrigin::Root, source) + verify { + assert!(!pallet_ema_oracle::ExternalSources::::contains_key(source)); + } + + add_authorized_account { + let source: Source = *b"newsrcxx"; + EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); + }: _(RawOrigin::Root, source, bifrost_account()) + verify { + assert!(pallet_ema_oracle::AuthorizedAccounts::::contains_key(source, bifrost_account())); + } + + remove_authorized_account { + let source: Source = *b"newsrcxx"; + EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, bifrost_account()).expect("error when adding authorized account"); + }: _(RawOrigin::Root, source, bifrost_account()) + verify { + assert!(!pallet_ema_oracle::AuthorizedAccounts::::contains_key(source, bifrost_account())); + } } #[cfg(test)] diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index cc5971bf4a..6dedf5f137 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -19,13 +19,13 @@ //! Autogenerated weights for `pallet_ema_oracle` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 48.0.0 -//! DATE: 2026-01-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-02-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `Mac.chello.hu`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./bin/hydradx +// ./target/release/hydradx // benchmark // pallet // --wasm-execution=compiled @@ -66,49 +66,49 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 27_702_000 picoseconds. - Weight::from_parts(28_298_000, 2126) + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(19_000_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `EmaOracle::Oracles` (r:0 w:3) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) fn remove_oracle() -> Weight { // Proof Size summary in bytes: // Measured: `1888` - // Estimated: `8086` - // Minimum execution time: 45_695_000 picoseconds. - Weight::from_parts(46_083_000, 8086) + // Estimated: `3373` + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(33_000_000, 3373) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } /// Storage: `EmaOracle::Accumulator` (r:1 w:0) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_finalize_no_entry() -> Weight { // Proof Size summary in bytes: // Measured: `742` - // Estimated: `8086` - // Minimum execution time: 4_170_000 picoseconds. - Weight::from_parts(4_281_000, 8086) + // Estimated: `2227` + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 2227) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Oracles` (r:117 w:117) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::Oracles` (r:900 w:900) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 39]`. + /// The range of component `b` is `[1, 300]`. fn on_finalize_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1195 + b * (891 ±0)` - // Estimated: `8086 + b * (8007 ±0)` - // Minimum execution time: 70_109_000 picoseconds. - Weight::from_parts(27_093_312, 8086) - // Standard Error: 29_899 - .saturating_add(Weight::from_parts(40_743_618, 0).saturating_mul(b.into())) + // Measured: `1218 + b * (813 ±0)` + // Estimated: `2700 + b * (8007 ±0)` + // Minimum execution time: 46_000_000 picoseconds. + Weight::from_parts(46_000_000, 2700) + // Standard Error: 16_044 + .saturating_add(Weight::from_parts(30_078_986, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -118,34 +118,40 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 39]`. fn on_trade_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1809 + b * (179 ±0)` - // Estimated: `8086` - // Minimum execution time: 33_625_000 picoseconds. - Weight::from_parts(34_742_168, 8086) - // Standard Error: 5_129 - .saturating_add(Weight::from_parts(632_108, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `6190 + b * (179 ±0)` + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(23_750_768, 6190) + // Standard Error: 4_879 + .saturating_add(Weight::from_parts(501_702, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) } /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 39]`. fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `1809 + b * (179 ±0)` - // Estimated: `8086` - // Minimum execution time: 33_706_000 picoseconds. - Weight::from_parts(34_835_424, 8086) - // Standard Error: 4_985 - .saturating_add(Weight::from_parts(629_384, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Estimated: `6190 + b * (179 ±0)` + // Minimum execution time: 23_000_000 picoseconds. + Weight::from_parts(23_443_865, 6190) + // Standard Error: 4_850 + .saturating_add(Weight::from_parts(506_256, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) } /// Storage: `EmaOracle::Oracles` (r:2 w:0) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) @@ -153,27 +159,98 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 31_469_000 picoseconds. - Weight::from_parts(31_854_000, 6328) + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(20_000_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Oracles` (r:1 w:0) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: Some(6601), added: 7096, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn update_bifrost_oracle() -> Weight { // Proof Size summary in bytes: - // Measured: `1924` - // Estimated: `8086` - // Minimum execution time: 51_877_000 picoseconds. - Weight::from_parts(52_696_000, 8086) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `2039` + // Estimated: `6190` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(38_000_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_external_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2039` + // Estimated: `6190` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(37_000_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn register_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1220` + // Estimated: `3481` + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(15_000_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn remove_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(20_000_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + fn add_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 17_000_000 picoseconds. + Weight::from_parts(18_000_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + fn remove_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1295` + // Estimated: `3481` + // Minimum execution time: 17_000_000 picoseconds. + Weight::from_parts(18_000_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } } \ No newline at end of file From 3e42d6f6bcb4b90a3a35b63e594cda7a3e841e23 Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 19 Feb 2026 11:07:48 +0100 Subject: [PATCH 02/43] remove comment --- pallets/ema-oracle/src/tests/external_oracle.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 22e5ff2c63..42cd465c4a 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -366,7 +366,6 @@ fn multiple_sources_in_same_block() { }); } -// --- soft limit --- #[test] fn amm_trades_are_limited_to_max_unique_entries() { From 77fe7414860f0ffae7c9812f8f281b96bec290ed Mon Sep 17 00:00:00 2001 From: Daniel Moka Date: Thu, 19 Feb 2026 15:57:31 +0000 Subject: [PATCH 03/43] chore: bump versions, cargo fmt --- Cargo.lock | 10 +++++----- integration-tests/Cargo.toml | 2 +- pallets/dca/Cargo.toml | 2 +- pallets/ema-oracle/Cargo.toml | 2 +- pallets/ema-oracle/src/tests/external_oracle.rs | 1 - .../ema-oracle/src/tests/update_bifrost_oracle.rs | 12 +++++------- pallets/omnipool-liquidity-mining/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 9 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52108d4596..41d96896d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6161,7 +6161,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "395.0.0" +version = "396.0.0" dependencies = [ "alloy-primitives 0.7.7", "alloy-sol-types 0.7.7", @@ -9856,7 +9856,7 @@ dependencies = [ [[package]] name = "pallet-dca" -version = "1.15.0" +version = "1.15.1" dependencies = [ "cumulus-pallet-parachain-system", "cumulus-primitives-core", @@ -10152,7 +10152,7 @@ dependencies = [ [[package]] name = "pallet-ema-oracle" -version = "1.10.0" +version = "1.11.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -11034,7 +11034,7 @@ dependencies = [ [[package]] name = "pallet-omnipool-liquidity-mining" -version = "3.2.0" +version = "3.2.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -15289,7 +15289,7 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "runtime-integration-tests" -version = "1.69.0" +version = "1.70.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 62fedc86a7..b3b595666e 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.69.0" +version = "1.70.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/dca/Cargo.toml b/pallets/dca/Cargo.toml index d27a08ac7c..3cc427f6b7 100644 --- a/pallets/dca/Cargo.toml +++ b/pallets/dca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = 'pallet-dca' -version = "1.15.0" +version = "1.15.1" description = 'A pallet to manage DCA scheduling' authors = ['GalacticCouncil'] edition = '2021' diff --git a/pallets/ema-oracle/Cargo.toml b/pallets/ema-oracle/Cargo.toml index c5eb2cd456..3c0b3077bd 100644 --- a/pallets/ema-oracle/Cargo.toml +++ b/pallets/ema-oracle/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-ema-oracle" -version = "1.10.0" +version = "1.11.0" description = "Exponential moving average oracle for AMM pools" authors = ["GalacticCouncil"] edition = "2021" diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 42cd465c4a..c65ff1e533 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -366,7 +366,6 @@ fn multiple_sources_in_same_block() { }); } - #[test] fn amm_trades_are_limited_to_max_unique_entries() { new_test_ext().execute_with(|| { diff --git a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs index f1c6eec258..2602a65ddc 100644 --- a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs +++ b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs @@ -31,7 +31,10 @@ use crate::tests::mock::ALICE; use polkadot_xcm::v5::prelude::*; fn setup_bifrost_auth() { - assert_ok!(EmaOracle::register_external_source(RuntimeOrigin::root(), BIFROST_SOURCE)); + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE + )); assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), BIFROST_SOURCE, @@ -177,12 +180,7 @@ fn should_fail_when_price_is_zero() { ); assert_noop!( - EmaOracle::update_bifrost_oracle( - RuntimeOrigin::signed(ALICE), - Box::new(hdx), - Box::new(dot), - (100, 0) - ), + EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(ALICE), Box::new(hdx), Box::new(dot), (100, 0)), Error::::PriceIsZero ); }); diff --git a/pallets/omnipool-liquidity-mining/Cargo.toml b/pallets/omnipool-liquidity-mining/Cargo.toml index f2e2dd5610..f4a86b8ab0 100644 --- a/pallets/omnipool-liquidity-mining/Cargo.toml +++ b/pallets/omnipool-liquidity-mining/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool-liquidity-mining" -version = "3.2.0" +version = "3.2.1" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 8c7447cd4a..d698d45240 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "395.0.0" +version = "396.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 7b80bde6ba..ef40e16b4e 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 395, + spec_version: 396, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From a96055b65ec73a16c4b928f8605816dcad3b131d Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 6 Mar 2026 10:36:06 +0100 Subject: [PATCH 04/43] remove unsed bifrost account declaration as it is not needed anymore, we have these in storae via migration --- runtime/hydradx/src/assets.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 09a58f76d7..194070dfde 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -657,10 +657,6 @@ where } } -// sibling:2030 = 7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq -pub fn bifrost_account() -> AccountId { - hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into() -} impl pallet_ema_oracle::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AuthorityOrigin = EitherOf, GeneralAdmin>; From 5d1165bba9758ae4f836a861310a9088516c907b Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 6 Mar 2026 13:09:47 +0100 Subject: [PATCH 05/43] fix weights calculation going for worst case --- pallets/ema-oracle/src/lib.rs | 7 +- pallets/ema-oracle/src/weights.rs | 4 +- .../hydradx/src/benchmarking/ema_oracle.rs | 12 ++- .../hydradx/src/weights/pallet_ema_oracle.rs | 74 ++++++++++--------- 4 files changed, 60 insertions(+), 37 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index c2b2fbf025..673e263b06 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -102,6 +102,11 @@ pub const BIFROST_SOURCE: [u8; 8] = *b"bifrosto"; /// Rounded up to 300 for safety margin. pub const MAX_EXTERNAL_ENTRIES_PER_BLOCK: u32 = 300; +/// Upper bound on the number of authorized accounts per external oracle source. +/// Used for worst-case weight estimation when removing a source, as `clear_prefix` +/// must delete all associated authorized accounts. +pub const MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE: u32 = 20; + const LOG_TARGET: &str = "runtime::ema-oracle"; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -381,7 +386,7 @@ pub mod pallet { } #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::remove_external_source())] + #[pallet::weight(::WeightInfo::remove_external_source(MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE))] pub fn remove_external_source(origin: OriginFor, source: Source) -> DispatchResult { T::AuthorityOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); diff --git a/pallets/ema-oracle/src/weights.rs b/pallets/ema-oracle/src/weights.rs index 0b354c666c..2fd692eb58 100644 --- a/pallets/ema-oracle/src/weights.rs +++ b/pallets/ema-oracle/src/weights.rs @@ -21,7 +21,7 @@ pub trait WeightInfo { fn get_entry() -> Weight; fn set_external_oracle() -> Weight; fn register_external_source() -> Weight; - fn remove_external_source() -> Weight; + fn remove_external_source(n: u32) -> Weight; fn add_authorized_account() -> Weight; fn remove_authorized_account() -> Weight; } @@ -67,7 +67,7 @@ impl WeightInfo for () { Weight::zero() } - fn remove_external_source() -> Weight { + fn remove_external_source(_n: u32) -> Weight { Weight::zero() } diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index 55870289d0..7b03467a38 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -18,7 +18,7 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use crate::bifrost_account; +use hex_literal::hex; use hydradx_traits::oracle::OraclePeriod; use hydradx_traits::AggregatedEntry; use pallet_ema_oracle::ordered_pair; @@ -44,6 +44,11 @@ use sp_core::{ConstU32, Get}; /// Default oracle source. const SOURCE: Source = *b"dummysrc"; +// sibling:2030 = 7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq +pub fn bifrost_account() -> AccountId { + hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into() +} + fn fill_whitelist_storage(n: u32) { for i in 0..n { assert_ok!(EmaOracle::add_oracle(RawOrigin::Root.into(), SOURCE, (HDX, i))); @@ -480,8 +485,13 @@ runtime_benchmarks! { } remove_external_source { + let n in 0 .. pallet_ema_oracle::MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE; let source: Source = *b"newsrcxx"; EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); + for i in 0..n { + let account: AccountId = frame_benchmarking::account("authorized", i, 0); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, account).expect("error when adding authorized account"); + } }: _(RawOrigin::Root, source) verify { assert!(!pallet_ema_oracle::ExternalSources::::contains_key(source)); diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 6dedf5f137..7a875cb86e 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for `pallet_ema_oracle` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 48.0.0 -//! DATE: 2026-02-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `Mac.chello.hu`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` @@ -28,10 +28,9 @@ // ./target/release/hydradx // benchmark // pallet -// --wasm-execution=compiled -// --pallet +// -p // pallet_ema_oracle -// --extrinsic +// -e // * // --heap-pages // 4096 @@ -43,7 +42,8 @@ // scripts/pallet-weight-template.hbs // --output // runtime/hydradx/src/weights/pallet_ema_oracle.rs -// --quiet +// --wasm-execution +// compiled #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -67,7 +67,7 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Measured: `1872` // Estimated: `2126` // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(19_000_000, 2126) + Weight::from_parts(20_000_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -81,8 +81,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `3373` - // Minimum execution time: 31_000_000 picoseconds. - Weight::from_parts(33_000_000, 3373) + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(31_000_000, 3373) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -103,12 +103,12 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// The range of component `b` is `[1, 300]`. fn on_finalize_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1218 + b * (813 ±0)` - // Estimated: `2700 + b * (8007 ±0)` + // Measured: `1079 + b * (893 ±0)` + // Estimated: `2598 + b * (8007 ±0)` // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(46_000_000, 2700) - // Standard Error: 16_044 - .saturating_add(Weight::from_parts(30_078_986, 0).saturating_mul(b.into())) + Weight::from_parts(114_162_714, 2598) + // Standard Error: 59_641 + .saturating_add(Weight::from_parts(28_826_085, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -126,10 +126,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1809 + b * (179 ±0)` // Estimated: `6190 + b * (179 ±0)` - // Minimum execution time: 23_000_000 picoseconds. - Weight::from_parts(23_750_768, 6190) - // Standard Error: 4_879 - .saturating_add(Weight::from_parts(501_702, 0).saturating_mul(b.into())) + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_713_804, 6190) + // Standard Error: 3_900 + .saturating_add(Weight::from_parts(420_911, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) @@ -145,10 +145,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1809 + b * (179 ±0)` // Estimated: `6190 + b * (179 ±0)` - // Minimum execution time: 23_000_000 picoseconds. - Weight::from_parts(23_443_865, 6190) - // Standard Error: 4_850 - .saturating_add(Weight::from_parts(506_256, 0).saturating_mul(b.into())) + // Minimum execution time: 22_000_000 picoseconds. + Weight::from_parts(22_858_498, 6190) + // Standard Error: 3_770 + .saturating_add(Weight::from_parts(418_212, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) @@ -159,8 +159,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 6328) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) @@ -180,7 +180,7 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Measured: `2039` // Estimated: `6190` // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(38_000_000, 6190) + Weight::from_parts(36_000_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -200,8 +200,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2039` // Estimated: `6190` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(37_000_000, 6190) + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(36_000_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -218,14 +218,22 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - fn remove_external_source() -> Weight { + /// Storage: `EmaOracle::AuthorizedAccounts` (r:20 w:20) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 20]`. + fn remove_external_source(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `3481` + // Measured: `1293 + n * (46 ±0)` + // Estimated: `3481 + n * (2531 ±0)` // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 3481) + Weight::from_parts(19_366_046, 3481) + // Standard Error: 3_089 + .saturating_add(Weight::from_parts(922_264, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2531).saturating_mul(n.into())) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) @@ -235,8 +243,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(18_000_000, 3481) + // Minimum execution time: 16_000_000 picoseconds. + Weight::from_parts(17_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -249,7 +257,7 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Measured: `1295` // Estimated: `3481` // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(18_000_000, 3481) + Weight::from_parts(17_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From 59a78201132fa127085f754046df59ca0b52e45d Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 6 Mar 2026 13:18:49 +0100 Subject: [PATCH 06/43] simplify udpate logic --- pallets/ema-oracle/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 673e263b06..0fa1ba6ee5 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -468,7 +468,8 @@ impl Pallet { assets: (AssetId, AssetId), oracle_entry: OracleEntry>, ) -> Result<(), ()> { - if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && !ExternalSources::::contains_key(src) { + let is_external_source = ExternalSources::::contains_key(src); + if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && !is_external_source { // if we don't track oracle for given asset pair, don't throw error return Ok(()); } @@ -478,7 +479,7 @@ impl Pallet { entry.accumulate_volume_and_update_from(&oracle_entry); Ok(()) } else { - if !ExternalSources::::contains_key(src) && accumulator.len() >= T::MaxUniqueEntries::get() as usize + if !is_external_source && accumulator.len() >= T::MaxUniqueEntries::get() as usize { //We have soft limit up to MaxUniqueEntries, only for AMM internal oracle updates return Err(()); From 2bc8982b61fc31e2c9df46f8d17af5d14cc820fb Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 6 Mar 2026 13:29:04 +0100 Subject: [PATCH 07/43] merge tests --- .../ema-oracle/src/tests/external_oracle.rs | 62 +++---------------- 1 file changed, 7 insertions(+), 55 deletions(-) diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index c65ff1e533..0a002e9515 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -408,61 +408,11 @@ fn amm_trades_are_limited_to_max_unique_entries() { } #[test] -fn external_sources_can_add_entries_beyond_max_unique_entries() { +fn soft_limit_only_applies_to_non_external_sources() { new_test_ext().execute_with(|| { - //Arrange - fill accumulator to max with AMM trades let max_entries = <::MaxUniqueEntries as Get>::get(); - for i in 0..max_entries { - assert_ok!(OnActivityHandler::::on_trade( - SOURCE, - i, - i + 1, - 1_000, - 1_000, - 2_000, - 2_000, - Price::new(2_000, 2_000), - Some(1_000_u128), - )); - } - - assert_ok!(EmaOracle::register_external_source( - RuntimeOrigin::root(), - EXTERNAL_SOURCE - )); - assert_ok!(EmaOracle::add_authorized_account( - RuntimeOrigin::root(), - EXTERNAL_SOURCE, - ALICE - )); - - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - - //Act - external source adds entry beyond the soft limit - assert_ok!(EmaOracle::set_external_oracle( - RuntimeOrigin::signed(ALICE), - EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), - (100, 99), - )); - - //Assert - accumulator has more entries than MaxUniqueEntries - assert_eq!(Accumulator::::get().len(), (max_entries + 1) as usize); - }); -} -#[test] -fn soft_limit_only_for_non_external_sources() { - new_test_ext().execute_with(|| { - let max_entries = <::MaxUniqueEntries as Get>::get(); - - // Fill the accumulator with non-external entries + // Fill the accumulator to max with AMM trades for i in 0..max_entries { assert_ok!(OnActivityHandler::::on_trade( SOURCE, @@ -477,7 +427,7 @@ fn soft_limit_only_for_non_external_sources() { )); } - // Non-external source should fail + // Non-external source should fail when accumulator is full assert_noop!( OnActivityHandler::::on_trade( SOURCE, @@ -494,7 +444,7 @@ fn soft_limit_only_for_non_external_sources() { Error::::TooManyUniqueEntries ); - // But external sources should still be able to insert + // But external sources should still be able to insert beyond the limit assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), EXTERNAL_SOURCE @@ -512,7 +462,6 @@ fn soft_limit_only_for_non_external_sources() { .into_versioned(); let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - // External source can still insert past the soft limit assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, @@ -520,5 +469,8 @@ fn soft_limit_only_for_non_external_sources() { Box::new(dot), (100, 99), )); + + // Accumulator has more entries than MaxUniqueEntries + assert_eq!(Accumulator::::get().len(), (max_entries + 1) as usize); }); } From 2c790fb3ac90cb44a939900150eb71edcb52777d Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 10 Mar 2026 15:57:14 +0100 Subject: [PATCH 08/43] use compile safe deprecated annotation --- integration-tests/src/driver/mod.rs | 1 + integration-tests/src/oracle.rs | 4 ++++ integration-tests/src/stableswap.rs | 1 + pallets/ema-oracle/src/lib.rs | 4 ++-- pallets/ema-oracle/src/tests/update_bifrost_oracle.rs | 2 ++ runtime/hydradx/src/benchmarking/ema_oracle.rs | 1 + 6 files changed, 11 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/driver/mod.rs b/integration-tests/src/driver/mod.rs index c3b776e9fc..cb191eb058 100644 --- a/integration-tests/src/driver/mod.rs +++ b/integration-tests/src/driver/mod.rs @@ -125,6 +125,7 @@ impl HydrationTestDriver { self } + #[allow(deprecated)] pub fn update_bifrost_oracle( &self, asset_a: Box, diff --git a/integration-tests/src/oracle.rs b/integration-tests/src/oracle.rs index f925dd80e8..d790599f39 100644 --- a/integration-tests/src/oracle.rs +++ b/integration-tests/src/oracle.rs @@ -440,6 +440,7 @@ fn arrange_bifrost_assets() -> ( } #[test] +#[allow(deprecated)] fn bifrost_oracle_should_be_updated() { TestNet::reset(); @@ -487,6 +488,7 @@ fn bifrost_oracle_should_be_updated() { } #[test] +#[allow(deprecated)] fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { TestNet::reset(); @@ -528,6 +530,7 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { } #[test] +#[allow(deprecated)] fn bifrost_oracle_update_should_return_fee() { // arrange TestNet::reset(); @@ -591,6 +594,7 @@ fn bifrost_oracle_update_should_return_fee() { } #[test] +#[allow(deprecated)] fn bifrost_oracle_update_fail_should_charge_fee() { // arrange TestNet::reset(); diff --git a/integration-tests/src/stableswap.rs b/integration-tests/src/stableswap.rs index 9dfaa0de75..05a7e8c517 100644 --- a/integration-tests/src/stableswap.rs +++ b/integration-tests/src/stableswap.rs @@ -254,6 +254,7 @@ mod circuit_breaker { } #[test] +#[allow(deprecated)] fn pool_with_pegs_should_update_pegs_only_once_per_block() { let dot_location: polkadot_xcm::v5::Location = polkadot_xcm::v5::Location::new( 1, diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 0fa1ba6ee5..db6cf19992 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -342,8 +342,8 @@ pub mod pallet { Ok(()) } - /// DEPRECATED - Use `set_external_oracle` instead. - /// This is kept for backward compatibility with bifrost and will be removed in the future. + #[deprecated(note = "Use `set_external_oracle` instead. Kept only for backward compatibility with bifrost and will be removed in the future")] + #[allow(deprecated)] #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::update_bifrost_oracle() .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] diff --git a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs index 2602a65ddc..d0feeb4805 100644 --- a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs +++ b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![allow(deprecated)] + use super::*; pub use mock::{EmaOracle, RuntimeOrigin, Test}; diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index 7b03467a38..a14f7409a2 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -16,6 +16,7 @@ // limitations under the License. #![cfg(feature = "runtime-benchmarks")] +#![allow(deprecated)] use super::*; use hex_literal::hex; From bad9c68acd002d1ad869cc13d6e6f141199cb411 Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 07:26:08 +0100 Subject: [PATCH 09/43] formatting --- pallets/ema-oracle/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index db6cf19992..a92485210b 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -342,7 +342,9 @@ pub mod pallet { Ok(()) } - #[deprecated(note = "Use `set_external_oracle` instead. Kept only for backward compatibility with bifrost and will be removed in the future")] + #[deprecated( + note = "Use `set_external_oracle` instead. Kept only for backward compatibility with bifrost and will be removed in the future" + )] #[allow(deprecated)] #[pallet::call_index(2)] #[pallet::weight(::WeightInfo::update_bifrost_oracle() @@ -479,8 +481,7 @@ impl Pallet { entry.accumulate_volume_and_update_from(&oracle_entry); Ok(()) } else { - if !is_external_source && accumulator.len() >= T::MaxUniqueEntries::get() as usize - { + if !is_external_source && accumulator.len() >= T::MaxUniqueEntries::get() as usize { //We have soft limit up to MaxUniqueEntries, only for AMM internal oracle updates return Err(()); } From e2c5c321903a9a6214d8b9264e1ac3b7c44902d6 Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 07:27:09 +0100 Subject: [PATCH 10/43] bump version --- integration-tests/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 18fc4a6208..78d6ec08cb 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.71.0" +version = "1.72.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" From 41677c2b72e2c1d7784092cedcc8f724b6b2f75f Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 07:28:58 +0100 Subject: [PATCH 11/43] bump versions --- Cargo.lock | 6 +++--- pallets/omnipool/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1038819ee5..7b2faf8235 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6161,7 +6161,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "399.0.0" +version = "400.0.0" dependencies = [ "alloy-primitives 0.7.7", "alloy-sol-types 0.7.7", @@ -11005,7 +11005,7 @@ dependencies = [ [[package]] name = "pallet-omnipool" -version = "7.1.0" +version = "7.1.1" dependencies = [ "bitflags 1.3.2", "frame-benchmarking", @@ -15289,7 +15289,7 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "runtime-integration-tests" -version = "1.71.0" +version = "1.72.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/pallets/omnipool/Cargo.toml b/pallets/omnipool/Cargo.toml index fd76106916..bc80490e7f 100644 --- a/pallets/omnipool/Cargo.toml +++ b/pallets/omnipool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-omnipool" -version = "7.1.0" +version = "7.1.1" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 79724ae891..b25171388d 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "399.0.0" +version = "400.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 50a083f0f0..a4efba3256 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 399, + spec_version: 400, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From f16236caa6344305f04fe41f883e7e242ec1c762 Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 07:36:44 +0100 Subject: [PATCH 12/43] remove unused imports --- pallets/ema-oracle/src/tests/external_oracle.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 0a002e9515..68f9aea73c 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -15,12 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::mock::{self, EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; +use super::mock::{EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; use super::SOURCE; use crate::pallet::{AuthorizedAccounts, ExternalSources}; use crate::*; -use frame_support::pallet_prelude::*; use frame_support::{assert_noop, assert_ok}; use pretty_assertions::assert_eq; From 26a752c1f4d1ca5202089c170813c79f526ae33e Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 08:45:52 +0100 Subject: [PATCH 13/43] centralize bifrost oracle declaration --- integration-tests/src/driver/mod.rs | 1 - integration-tests/src/oracle.rs | 1 - integration-tests/src/polkadot_test_net.rs | 4 ++++ runtime/hydradx/src/assets.rs | 3 ++- runtime/hydradx/src/benchmarking/ema_oracle.rs | 7 +++---- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/integration-tests/src/driver/mod.rs b/integration-tests/src/driver/mod.rs index cb191eb058..cee26f383d 100644 --- a/integration-tests/src/driver/mod.rs +++ b/integration-tests/src/driver/mod.rs @@ -4,7 +4,6 @@ use crate::polkadot_test_net::*; use frame_support::assert_ok; use frame_support::traits::fungible::Mutate; use frame_support::BoundedVec; -use hydradx_runtime::bifrost_account; use hydradx_runtime::AssetLocation; use hydradx_runtime::*; use hydradx_traits::stableswap::AssetAmount; diff --git a/integration-tests/src/oracle.rs b/integration-tests/src/oracle.rs index d790599f39..35fd217a63 100644 --- a/integration-tests/src/oracle.rs +++ b/integration-tests/src/oracle.rs @@ -13,7 +13,6 @@ use frame_support::{ traits::tokens::fungibles::Mutate, }; use hydra_dx_math::ema::smoothing_from_period; -use hydradx_runtime::bifrost_account; use hydradx_runtime::AssetLocation; use hydradx_runtime::AssetRegistry; use hydradx_runtime::{EmaOracle, RuntimeOrigin}; diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index f24e912ff3..04df502908 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -121,6 +121,10 @@ pub const BOB_INITIAL_DAI_BALANCE: Balance = 1_000_000_000 * UNITS; pub const CHARLIE_INITIAL_NATIVE_BALANCE: Balance = 1_000 * UNITS; pub const CHARLIE_INITIAL_LRNA_BALANCE: Balance = 1_000 * UNITS; +pub fn bifrost_account() -> AccountId { + hydradx_runtime::BifrostAccount::get() +} + pub fn parachain_reserve_account() -> AccountId { polkadot_parachain::primitives::Sibling::from(ACALA_PARA_ID).into_account_truncating() } diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 194070dfde..1e8def21eb 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -642,7 +642,8 @@ impl pallet_circuit_breaker::Config for Runtime { parameter_types! { pub SupportedPeriods: BoundedVec> = BoundedVec::truncate_from(vec![ OraclePeriod::LastBlock, OraclePeriod::Short, OraclePeriod::TenMinutes]); - + // sibling:2030 = 7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq + pub BifrostAccount: AccountId = hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into(); } pub struct OracleWhitelist(PhantomData); diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index a14f7409a2..e841a6d8a4 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -19,7 +19,7 @@ #![allow(deprecated)] use super::*; -use hex_literal::hex; +use crate::assets::BifrostAccount; use hydradx_traits::oracle::OraclePeriod; use hydradx_traits::AggregatedEntry; use pallet_ema_oracle::ordered_pair; @@ -45,9 +45,8 @@ use sp_core::{ConstU32, Get}; /// Default oracle source. const SOURCE: Source = *b"dummysrc"; -// sibling:2030 = 7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq -pub fn bifrost_account() -> AccountId { - hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into() +fn bifrost_account() -> AccountId { + BifrostAccount::get() } fn fill_whitelist_storage(n: u32) { From 48a7c0311c4a53a4702e8693ba465556b6bb1aa8 Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 11:25:39 +0100 Subject: [PATCH 14/43] add script to test bifrost oracle update --- .../chopsticks/fakeSignatureBifrost.js | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 scripts/init-testnet/chopsticks/fakeSignatureBifrost.js diff --git a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js new file mode 100644 index 0000000000..71f584945f --- /dev/null +++ b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js @@ -0,0 +1,101 @@ +import { signFakeWithApi } from '@acala-network/chopsticks-utils'; +import { ApiPromise, WsProvider } from "@polkadot/api"; + +// Bifrost sovereign account: sibling:2030 +// Hex: 7369626cee070000000000000000000000000000000000000000000000000000 +const BIFROST_SOVEREIGN = "7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq"; + +// "bifrosto" as hex +const BIFROST_SOURCE = "0x626966726f73746f"; + +// Raw storage keys (precomputed from twox128/twox64Concat hashes) +// ExternalSources: twox128("EmaOracle") ++ twox128("ExternalSources") ++ twox64Concat("bifrosto") +const EXTERNAL_SOURCES_KEY = "0x5258a12472693b34a3ed25509781e55f62dd2a081ec5b2cc6c15dfa73f370fe7c7e860d98b04912c626966726f73746f"; +// AuthorizedAccounts: twox128("EmaOracle") ++ twox128("AuthorizedAccounts") ++ twox64Concat("bifrosto") ++ twox64Concat(bifrost_sovereign_hex) +const AUTHORIZED_ACCOUNTS_KEY = "0x5258a12472693b34a3ed25509781e55fe1be1943a3ea7bb89bd8ed79a8155ea9c7e860d98b04912c626966726f73746fc31effe0f5c540f27369626cee070000000000000000000000000000000000000000000000000000"; + +const main = async () => { + const uri = "ws://127.0.0.1:8000"; + const provider = new WsProvider(uri); + + // Register custom signed extensions so extrinsics encode correctly + const api = await ApiPromise.create({ + provider, + signedExtensions: { + ValidateClaim: { extrinsic: {}, payload: {} }, + CheckMetadataHash: { extrinsic: { mode: "u8" }, payload: {} }, + StorageWeightReclaim: { extrinsic: {}, payload: {} }, + }, + }); + + // Step 1: Inject ExternalSources and AuthorizedAccounts via raw storage keys. + // These storage items are not in the on-chain metadata yet, so we use raw keys. + console.log("Injecting ExternalSources and AuthorizedAccounts storage..."); + await provider.send("dev_setStorage", [[ + [EXTERNAL_SOURCES_KEY, "0x00"], + [AUTHORIZED_ACCOUNTS_KEY, "0x00"], + ]]); + await provider.send("dev_newBlock", [{}]); + + // Step 2: Set Instant block build mode so the extrinsic gets included + // in a new block automatically upon submission. + await provider.send("dev_setBlockBuildMode", ["Instant"]); + + // Step 3: Query asset registry for asset 15 XCM location + const asset15Location = await api.query.assetRegistry.assetLocations(15); + if (asset15Location.isNone) { + console.error("Asset 15 has no XCM location registered"); + process.exit(1); + } + const asset15Loc = asset15Location.unwrap(); + console.log("Asset 15 location:", JSON.stringify(asset15Loc.toJSON(), null, 2)); + + // DOT location: Location::parent() = { parents: 1, interior: "Here" } + const dotLocation = { V4: { parents: 1, interior: "Here" } }; + const asset15VersionedLocation = { V4: asset15Loc.toJSON() }; + + // Price: (numerator, denominator) — e.g. 1 DOT = 50 units of asset 15 + const price = [50_000_000_000_000, 1_000_000_000_000]; + + // Step 4: Build and fake-sign the extrinsic as Bifrost sovereign + const tx = api.tx.emaOracle.updateBifrostOracle( + dotLocation, + asset15VersionedLocation, + price, + ); + + console.log("Fake-signing as Bifrost sovereign:", BIFROST_SOVEREIGN); + await signFakeWithApi(api, tx, BIFROST_SOVEREIGN); + + // Step 5: Submit and wait for inclusion + console.log("Submitting oracle update extrinsic..."); + await new Promise((resolve, reject) => { + tx.send((result) => { + console.log("Status:", result.status.type); + if (result.status.isInBlock) { + console.log("Included in block:", result.status.asInBlock.toHex()); + resolve(result); + } + }).catch(reject); + }); + + // Step 6: Verify oracle was updated + console.log("\nVerification — bifrost oracle entries:"); + for (const period of ["LastBlock", "Short", "TenMinutes"]) { + const entry = await api.query.emaOracle.oracles(BIFROST_SOURCE, [5, 15], period); + const data = entry.toJSON(); + if (data && data[0]) { + console.log(` ${period}: price n=${data[0].price.n}, d=${data[0].price.d}`); + } else { + console.log(` ${period}: NOT FOUND`); + } + } + + console.log("\nDONE"); + await api.disconnect(); +}; + +main().catch((e) => { + console.error(e); + process.exit(1); +}); From c2510afb0499ddcf338cda609864a33be7b5400b Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 11:28:34 +0100 Subject: [PATCH 15/43] remove verification --- .../chopsticks/fakeSignatureBifrost.js | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js index 71f584945f..27d87e6350 100644 --- a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js +++ b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js @@ -1,5 +1,5 @@ -import { signFakeWithApi } from '@acala-network/chopsticks-utils'; -import { ApiPromise, WsProvider } from "@polkadot/api"; +import {signFakeWithApi} from '@acala-network/chopsticks-utils'; +import {ApiPromise, WsProvider} from "@polkadot/api"; // Bifrost sovereign account: sibling:2030 // Hex: 7369626cee070000000000000000000000000000000000000000000000000000 @@ -22,9 +22,9 @@ const main = async () => { const api = await ApiPromise.create({ provider, signedExtensions: { - ValidateClaim: { extrinsic: {}, payload: {} }, - CheckMetadataHash: { extrinsic: { mode: "u8" }, payload: {} }, - StorageWeightReclaim: { extrinsic: {}, payload: {} }, + ValidateClaim: {extrinsic: {}, payload: {}}, + CheckMetadataHash: {extrinsic: {mode: "u8"}, payload: {}}, + StorageWeightReclaim: {extrinsic: {}, payload: {}}, }, }); @@ -51,8 +51,8 @@ const main = async () => { console.log("Asset 15 location:", JSON.stringify(asset15Loc.toJSON(), null, 2)); // DOT location: Location::parent() = { parents: 1, interior: "Here" } - const dotLocation = { V4: { parents: 1, interior: "Here" } }; - const asset15VersionedLocation = { V4: asset15Loc.toJSON() }; + const dotLocation = {V4: {parents: 1, interior: "Here"}}; + const asset15VersionedLocation = {V4: asset15Loc.toJSON()}; // Price: (numerator, denominator) — e.g. 1 DOT = 50 units of asset 15 const price = [50_000_000_000_000, 1_000_000_000_000]; @@ -79,18 +79,6 @@ const main = async () => { }).catch(reject); }); - // Step 6: Verify oracle was updated - console.log("\nVerification — bifrost oracle entries:"); - for (const period of ["LastBlock", "Short", "TenMinutes"]) { - const entry = await api.query.emaOracle.oracles(BIFROST_SOURCE, [5, 15], period); - const data = entry.toJSON(); - if (data && data[0]) { - console.log(` ${period}: price n=${data[0].price.n}, d=${data[0].price.d}`); - } else { - console.log(` ${period}: NOT FOUND`); - } - } - console.log("\nDONE"); await api.disconnect(); }; From dcddf41e690f2dcd0b07177ebb06756e49853c1a Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 12 Mar 2026 13:45:17 +0100 Subject: [PATCH 16/43] add bifrost migration for external sources, to the runtime impls --- runtime/hydradx/src/migrations/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/hydradx/src/migrations/mod.rs b/runtime/hydradx/src/migrations/mod.rs index bce66f6851..cf4edd684b 100644 --- a/runtime/hydradx/src/migrations/mod.rs +++ b/runtime/hydradx/src/migrations/mod.rs @@ -16,7 +16,8 @@ use crate::Runtime; // New migrations which need to be cleaned up after every Runtime upgrade -pub type UnreleasedSingleBlockMigrations = (); +pub type UnreleasedSingleBlockMigrations = + pallet_ema_oracle::migrations::v2::MigrateV1ToV2; // These migrations can run on every runtime upgrade pub type PermanentSingleBlockMigrations = pallet_xcm::migration::MigrateToLatestXcmVersion; From 3a3b19147ecc50ed596f973d5fe5a5b2fda6eaa0 Mon Sep 17 00:00:00 2001 From: dmoka Date: Wed, 8 Apr 2026 13:30:16 +0200 Subject: [PATCH 17/43] make the external oracle call callable by TC too for emergency in case of an eternal source hacked so they bloat our chain wih free set oracle extrinsics --- pallets/dca/src/tests/mock.rs | 1 + pallets/ema-oracle/src/lib.rs | 12 ++++++++---- pallets/ema-oracle/src/tests/mock.rs | 1 + pallets/omnipool-liquidity-mining/src/tests/mock.rs | 1 + runtime/hydradx/src/assets.rs | 2 ++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index 5fd4c34c63..781a1ba87c 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -143,6 +143,7 @@ parameter_types! { impl pallet_ema_oracle::Config for Test { type AuthorityOrigin = EnsureRoot; + type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 572ff44756..7fdb0ae981 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -144,6 +144,10 @@ pub mod pallet { /// Origin that can enable oracle for assets that would be rejected by `OracleWhitelist` otherwise. type AuthorityOrigin: EnsureOrigin; + /// Origin that can manage external oracle sources and authorized accounts. + /// Should include the Technical Committee for fast emergency response. + type ExternalOracleOrigin: EnsureOrigin; + /// Provider for the current block number. type BlockNumberProvider: BlockNumberProvider>; @@ -376,7 +380,7 @@ pub mod pallet { #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::register_external_source())] pub fn register_external_source(origin: OriginFor, source: Source) -> DispatchResult { - T::AuthorityOrigin::ensure_origin(origin)?; + T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!( !ExternalSources::::contains_key(source), Error::::SourceAlreadyRegistered @@ -389,7 +393,7 @@ pub mod pallet { #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::remove_external_source(MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE))] pub fn remove_external_source(origin: OriginFor, source: Source) -> DispatchResult { - T::AuthorityOrigin::ensure_origin(origin)?; + T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); ExternalSources::::remove(source); let _ = AuthorizedAccounts::::clear_prefix(source, u32::MAX, None); @@ -400,7 +404,7 @@ pub mod pallet { #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::add_authorized_account())] pub fn add_authorized_account(origin: OriginFor, source: Source, account: T::AccountId) -> DispatchResult { - T::AuthorityOrigin::ensure_origin(origin)?; + T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); AuthorizedAccounts::::insert(source, &account, ()); Self::deposit_event(Event::AuthorizedAccountAdded { source, account }); @@ -414,7 +418,7 @@ pub mod pallet { source: Source, account: T::AccountId, ) -> DispatchResult { - T::AuthorityOrigin::ensure_origin(origin)?; + T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); AuthorizedAccounts::::remove(source, &account); Self::deposit_event(Event::AuthorizedAccountRemoved { source, account }); diff --git a/pallets/ema-oracle/src/tests/mock.rs b/pallets/ema-oracle/src/tests/mock.rs index 13e9613f9e..d5cc4e7cb1 100644 --- a/pallets/ema-oracle/src/tests/mock.rs +++ b/pallets/ema-oracle/src/tests/mock.rs @@ -137,6 +137,7 @@ impl Contains<(Source, AssetId, AssetId)> for OracleWhitelist { impl Config for Test { type AuthorityOrigin = EnsureRoot; + type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = System; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = OracleWhitelist; diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index 9b7e448c25..2ce3a00396 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -298,6 +298,7 @@ parameter_types! { impl pallet_ema_oracle::Config for Test { type AuthorityOrigin = EnsureRoot; + type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 513019c32f..bb6f5ea02a 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -652,6 +652,8 @@ where impl pallet_ema_oracle::Config for Runtime { type AuthorityOrigin = EitherOf, GeneralAdmin>; + type ExternalOracleOrigin = + EitherOf, EitherOf>; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. From beaaf52e6e67ede202261bf75af68aa6eebdb488 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 10 Apr 2026 08:49:12 +0200 Subject: [PATCH 18/43] add missing ddos test --- .../ema-oracle/src/tests/external_oracle.rs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 68f9aea73c..0ec71c0bf3 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -473,3 +473,101 @@ fn soft_limit_only_applies_to_non_external_sources() { assert_eq!(Accumulator::::get().len(), (max_entries + 1) as usize); }); } + +// Verifies the accepted design: external oracles have priority and can consume +// accumulator slots, causing AMM trades for *new* pairs to revert with +// TooManyUniqueEntries when the accumulator is full. This locks in the current +// behavior so future refactors don't silently change it. +// +// Important subtlety: the accumulator is keyed by (Source, AssetPair). So an external +// entry under (EXTERNAL_SOURCE, pair) is a DIFFERENT key from an AMM entry under +// (SOURCE, pair) — they don't alias. Only an AMM pair that was *already tracked by +// SOURCE* earlier in the same block keeps working via get_mut. +#[test] +fn external_entries_blocking_amm_new_pairs_reverts_amm_trade() { + new_test_ext().execute_with(|| { + let max_entries = <::MaxUniqueEntries as Get>::get(); + + // 1. Place one AMM entry first — (SOURCE, (100, 101)) — so we can later verify + // that updates to this specific key still work even when the accumulator is full. + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + 100, + 101, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + assert_eq!(Accumulator::::get().len(), 1); + + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + for i in 0..(max_entries - 1) { + assert_ok!(OnActivityHandler::::on_trade( + EXTERNAL_SOURCE, + i, + i + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + } + assert_eq!(Accumulator::::get().len(), max_entries as usize); + + // Actively traded pairs keep working. + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + 100, + 101, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); + + assert_noop!( + OnActivityHandler::::on_trade( + SOURCE, + 0, + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + ) + .map_err(|(_w, e)| e), + Error::::TooManyUniqueEntries + ); + + assert_noop!( + OnActivityHandler::::on_trade( + SOURCE, + 2 * max_entries, + 2 * max_entries + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + ) + .map_err(|(_w, e)| e), + Error::::TooManyUniqueEntries + ); + + // Accumulator size unchanged — failed AMM insertions did not grow it. + assert_eq!(Accumulator::::get().len(), max_entries as usize); + }); +} From 2130eab464073297b15be152637db4d16cc28006 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 10 Apr 2026 08:49:50 +0200 Subject: [PATCH 19/43] update test script --- .../chopsticks/fakeSignatureBifrost.js | 161 ++++++++++++++---- 1 file changed, 125 insertions(+), 36 deletions(-) diff --git a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js index 27d87e6350..47a3535929 100644 --- a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js +++ b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js @@ -1,24 +1,32 @@ +// Exercises the PRE-PR `emaOracle.updateBifrostOracle` path against a chopsticks fork. +// +// The old runtime gates this call with `BifrostOrigin = EnsureSignedBy`, so the +// caller must literally be the Bifrost sibling sovereign account. We use chopsticks' +// `signFakeWithApi` to bypass signature verification. No storage injection is needed because +// the `ExternalSources` / `AuthorizedAccounts` maps don't exist yet in the old runtime. +// +// The old code also has a 10% `is_within_range` guard that kicks in if a TenMinutes oracle +// entry already exists for (BIFROST_SOURCE, DOT/asset_15). We query the current entry and +// reuse its exact price to avoid tripping it. + import {signFakeWithApi} from '@acala-network/chopsticks-utils'; import {ApiPromise, WsProvider} from "@polkadot/api"; -// Bifrost sovereign account: sibling:2030 -// Hex: 7369626cee070000000000000000000000000000000000000000000000000000 +// Bifrost sovereign (sibling:2030) const BIFROST_SOVEREIGN = "7LCt6dFs6sraSg31uKfbRH7soQ66GRb3LAkGZJ1ie3369crq"; -// "bifrosto" as hex +// "bifrosto" as [u8; 8] const BIFROST_SOURCE = "0x626966726f73746f"; -// Raw storage keys (precomputed from twox128/twox64Concat hashes) -// ExternalSources: twox128("EmaOracle") ++ twox128("ExternalSources") ++ twox64Concat("bifrosto") -const EXTERNAL_SOURCES_KEY = "0x5258a12472693b34a3ed25509781e55f62dd2a081ec5b2cc6c15dfa73f370fe7c7e860d98b04912c626966726f73746f"; -// AuthorizedAccounts: twox128("EmaOracle") ++ twox128("AuthorizedAccounts") ++ twox64Concat("bifrosto") ++ twox64Concat(bifrost_sovereign_hex) -const AUTHORIZED_ACCOUNTS_KEY = "0x5258a12472693b34a3ed25509781e55fe1be1943a3ea7bb89bd8ed79a8155ea9c7e860d98b04912c626966726f73746fc31effe0f5c540f27369626cee070000000000000000000000000000000000000000000000000000"; +// Asset whose price Bifrost feeds in production. 15 = vDOT on Hydration. +const QUOTE_ASSET_ID = 15; const main = async () => { const uri = "ws://127.0.0.1:8000"; const provider = new WsProvider(uri); - // Register custom signed extensions so extrinsics encode correctly + // Same custom signed extensions as the PR script — needed so extrinsics encode + // against this runtime's metadata. const api = await ApiPromise.create({ provider, signedExtensions: { @@ -28,57 +36,138 @@ const main = async () => { }, }); - // Step 1: Inject ExternalSources and AuthorizedAccounts via raw storage keys. - // These storage items are not in the on-chain metadata yet, so we use raw keys. - console.log("Injecting ExternalSources and AuthorizedAccounts storage..."); - await provider.send("dev_setStorage", [[ - [EXTERNAL_SOURCES_KEY, "0x00"], - [AUTHORIZED_ACCOUNTS_KEY, "0x00"], - ]]); - await provider.send("dev_newBlock", [{}]); - - // Step 2: Set Instant block build mode so the extrinsic gets included - // in a new block automatically upon submission. + // Instant block build so our tx lands in a block as soon as it's submitted. await provider.send("dev_setBlockBuildMode", ["Instant"]); - // Step 3: Query asset registry for asset 15 XCM location - const asset15Location = await api.query.assetRegistry.assetLocations(15); - if (asset15Location.isNone) { - console.error("Asset 15 has no XCM location registered"); + // Resolve the quote asset's XCM location from the asset registry. + const quoteLocationOpt = await api.query.assetRegistry.assetLocations(QUOTE_ASSET_ID); + if (quoteLocationOpt.isNone) { + console.error(`Asset ${QUOTE_ASSET_ID} has no XCM location registered`); process.exit(1); } - const asset15Loc = asset15Location.unwrap(); - console.log("Asset 15 location:", JSON.stringify(asset15Loc.toJSON(), null, 2)); + const quoteVersionedLocation = {V4: quoteLocationOpt.unwrap().toJSON()}; + // DOT = relay chain, Location::parent() + const dotVersionedLocation = {V4: {parents: 1, interior: "Here"}}; + + console.log("Asset location:", JSON.stringify(quoteVersionedLocation)); - // DOT location: Location::parent() = { parents: 1, interior: "Here" } - const dotLocation = {V4: {parents: 1, interior: "Here"}}; - const asset15VersionedLocation = {V4: asset15Loc.toJSON()}; + // Determine a price that passes the 10% range check: reuse the current TenMinutes + // entry's price if one already exists for this pair under BIFROST_SOURCE. + // + // Storage key uses the ORDERED pair (smaller asset id first). DOT (asset 5 on Hydration) + // < vDOT (15), so the ordered pair is (5, 15). + const DOT_ASSET_ID = 5; + const orderedPair = DOT_ASSET_ID < QUOTE_ASSET_ID + ? [DOT_ASSET_ID, QUOTE_ASSET_ID] + : [QUOTE_ASSET_ID, DOT_ASSET_ID]; - // Price: (numerator, denominator) — e.g. 1 DOT = 50 units of asset 15 - const price = [50_000_000_000_000, 1_000_000_000_000]; + const existing = await api.query.emaOracle.oracles( + BIFROST_SOURCE, + orderedPair, + 'TenMinutes', + ); + + let price; + let prevLastBlockUpdatedAt = null; + if (existing.isSome) { + const [entry] = existing.unwrap(); + const p = entry.price; + // price is EmaPrice { n, d } + price = [p.n.toString(), p.d.toString()]; + console.log("Reusing current TenMinutes price:", price); + } else { + price = ["1000000000000", "1000000000000"]; // 1.0 — no range check will apply + console.log("No existing oracle, using default price:", price); + } + + // Snapshot pre-tx LastBlock entry so we can detect the accumulator flush. + const prevLastBlock = await api.query.emaOracle.oracles( + BIFROST_SOURCE, + orderedPair, + 'LastBlock', + ); + if (prevLastBlock.isSome) { + const [entry] = prevLastBlock.unwrap(); + prevLastBlockUpdatedAt = entry.updatedAt.toNumber(); + console.log("Pre-tx LastBlock.updated_at:", prevLastBlockUpdatedAt); + } - // Step 4: Build and fake-sign the extrinsic as Bifrost sovereign const tx = api.tx.emaOracle.updateBifrostOracle( - dotLocation, - asset15VersionedLocation, + dotVersionedLocation, + quoteVersionedLocation, price, ); console.log("Fake-signing as Bifrost sovereign:", BIFROST_SOVEREIGN); await signFakeWithApi(api, tx, BIFROST_SOVEREIGN); - // Step 5: Submit and wait for inclusion - console.log("Submitting oracle update extrinsic..."); + console.log("Submitting..."); await new Promise((resolve, reject) => { tx.send((result) => { console.log("Status:", result.status.type); + if (result.dispatchError) { + const err = result.dispatchError; + if (err.isModule) { + const decoded = api.registry.findMetaError(err.asModule); + console.error(`DispatchError: ${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`); + } else { + console.error("DispatchError:", err.toString()); + } + reject(new Error("dispatch error")); + return; + } if (result.status.isInBlock) { console.log("Included in block:", result.status.asInBlock.toHex()); + for (const {event} of result.events) { + console.log(` event: ${event.section}.${event.method}`); + } resolve(result); } }).catch(reject); }); + // === Post-tx verification ================================================== + // Prove the PR's v1->v2 migration actually seeded the new storages and the + // deprecated extrinsic's write-path landed. + + console.log("\n--- Post-tx storage verification ---"); + + // 1. ExternalSources[BIFROST_SOURCE] must exist (seeded by MigrateV1ToV2). + const extSrc = await api.query.emaOracle.externalSources(BIFROST_SOURCE); + console.log("ExternalSources[bifrosto]:", extSrc.isSome ? "PRESENT" : "MISSING"); + + // 2. AuthorizedAccounts[BIFROST_SOURCE][bifrost_sovereign] must exist. + const auth = await api.query.emaOracle.authorizedAccounts(BIFROST_SOURCE, BIFROST_SOVEREIGN); + console.log(`AuthorizedAccounts[bifrosto][${BIFROST_SOVEREIGN}]:`, auth.isSome ? "PRESENT" : "MISSING"); + + // 3. The extrinsic should have pushed an entry into the accumulator, and + // `on_finalize` should have flushed it into LastBlock with the current + // block's `updated_at`. If `prevLastBlockUpdatedAt` was set, the new one + // must be strictly greater. + const newLastBlock = await api.query.emaOracle.oracles( + BIFROST_SOURCE, + orderedPair, + 'LastBlock', + ); + if (newLastBlock.isSome) { + const [entry] = newLastBlock.unwrap(); + const nowUpdatedAt = entry.updatedAt.toNumber(); + console.log("Post-tx LastBlock.updated_at:", nowUpdatedAt); + if (prevLastBlockUpdatedAt !== null) { + const advanced = nowUpdatedAt > prevLastBlockUpdatedAt; + console.log( + `LastBlock entry advanced: ${advanced ? "YES" : "NO"}`, + `(${prevLastBlockUpdatedAt} -> ${nowUpdatedAt})`, + ); + } + } else { + console.log("Post-tx LastBlock entry: MISSING"); + } + + const ok = extSrc.isSome && auth.isSome; + console.log("\nRESULT:", ok ? "PASS" : "FAIL"); + if (!ok) process.exitCode = 1; + console.log("\nDONE"); await api.disconnect(); }; From 0fcac790524f90cce7362ebd2a6ebe0175de9690 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 10 Apr 2026 10:33:03 +0200 Subject: [PATCH 20/43] formatting --- runtime/hydradx/src/assets.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index bb6f5ea02a..41cc88098c 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -652,8 +652,7 @@ where impl pallet_ema_oracle::Config for Runtime { type AuthorityOrigin = EitherOf, GeneralAdmin>; - type ExternalOracleOrigin = - EitherOf, EitherOf>; + type ExternalOracleOrigin = EitherOf, EitherOf>; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. From a34a01d7fb380b2c62831a3c537ebcc79a565710 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 10 Apr 2026 10:39:13 +0200 Subject: [PATCH 21/43] bump versions --- Cargo.lock | 2 +- pallets/ema-oracle/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cab9d85e7e..3240389e9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10431,7 +10431,7 @@ dependencies = [ [[package]] name = "pallet-ema-oracle" -version = "1.11.0" +version = "1.12.0" dependencies = [ "frame-benchmarking", "frame-support", diff --git a/pallets/ema-oracle/Cargo.toml b/pallets/ema-oracle/Cargo.toml index 3c0b3077bd..10d44afe5a 100644 --- a/pallets/ema-oracle/Cargo.toml +++ b/pallets/ema-oracle/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-ema-oracle" -version = "1.11.0" +version = "1.12.0" description = "Exponential moving average oracle for AMM pools" authors = ["GalacticCouncil"] edition = "2021" From 2b99d40b79bd640b740547910b8a7e68df88a3c5 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 10 Apr 2026 11:11:25 +0200 Subject: [PATCH 22/43] add missing tests demonstrating someone can call external oracle update for multiple pairs in given source --- .../ema-oracle/src/tests/external_oracle.rs | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 0ec71c0bf3..068a199363 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -571,3 +571,85 @@ fn external_entries_blocking_amm_new_pairs_reverts_amm_trade() { assert_eq!(Accumulator::::get().len(), max_entries as usize); }); } + +// Demonstrates that a SINGLE authorized external oracle account can submit +// multiple `set_external_oracle` calls for DIFFERENT pairs in the SAME block. +// There is no per-block rate limit per account, per source, or per pair count. +#[test] +fn single_authorized_account_can_update_many_pairs_in_one_block() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + ALICE + )); + + System::set_block_number(3); + + // Build several distinct locations that the mock converter resolves to distinct asset IDs. + let loc_0 = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned(); + let loc_1 = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(1)].into()), + ) + .into_versioned(); + let loc_2 = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(2)].into()), + ) + .into_versioned(); + let loc_4 = polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(3)].into()), + ) + .into_versioned(); + let loc_5 = polkadot_xcm::v5::Location::parent().into_versioned(); + + // ALICE submits 4 DISTINCT pair updates in the SAME block. + // No rate limit, no "one pair per source per block" constraint — all succeed. + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(loc_0.clone()), + Box::new(loc_1.clone()), + (100, 99), + )); + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(loc_0.clone()), + Box::new(loc_2.clone()), + (200, 99), + )); + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(loc_1.clone()), + Box::new(loc_4.clone()), + (300, 99), + )); + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(loc_2), + Box::new(loc_5), + (400, 99), + )); + + // All 4 distinct pairs landed in the accumulator from a single account. + let acc = Accumulator::::get(); + assert_eq!(acc.len(), 4); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 1)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 2)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(1, 4)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(2, 5)))); + }); +} From 624f3fa4afac325b60617bf566c6deb8dcec0f41 Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 08:52:25 +0200 Subject: [PATCH 23/43] allow external oracle updates only for specific pairs --- integration-tests/src/driver/mod.rs | 15 +- integration-tests/src/oracle.rs | 176 +++++- pallets/ema-oracle/src/lib.rs | 164 ++++- pallets/ema-oracle/src/migrations/v2.rs | 21 +- .../ema-oracle/src/tests/external_oracle.rs | 571 +++++++++++++----- .../src/tests/update_bifrost_oracle.rs | 30 + runtime/hydradx/src/assets.rs | 2 +- .../hydradx/src/benchmarking/ema_oracle.rs | 98 ++- 8 files changed, 864 insertions(+), 213 deletions(-) diff --git a/integration-tests/src/driver/mod.rs b/integration-tests/src/driver/mod.rs index cee26f383d..aaba010ad6 100644 --- a/integration-tests/src/driver/mod.rs +++ b/integration-tests/src/driver/mod.rs @@ -12,6 +12,7 @@ use pallet_asset_registry::AssetType; use pallet_stableswap::MAX_ASSETS_IN_POOL; use primitives::constants::chain::{OMNIPOOL_SOURCE, STABLESWAP_SOURCE}; use primitives::{AccountId, AssetId}; +use sp_runtime::traits::Convert; use sp_runtime::{FixedU128, Permill}; use sp_std::cell::RefCell; use xcm_emulator::TestExt; @@ -132,12 +133,22 @@ impl HydrationTestDriver { price: (Balance, Balance), ) -> &Self { self.execute(|| { - // Ensure BIFROST_SOURCE is registered and bifrost_account is authorized - // We need this because we added support for multiple oracle sources which required migration + let asset_a_id = + ::LocationToAssetIdConversion::convert( + (*asset_a).clone(), + ) + .expect("driver: could not resolve asset_a location to asset id"); + let asset_b_id = + ::LocationToAssetIdConversion::convert( + (*asset_b).clone(), + ) + .expect("driver: could not resolve asset_b location to asset id"); + let _ = EmaOracle::register_external_source(RuntimeOrigin::root(), pallet_ema_oracle::BIFROST_SOURCE); let _ = EmaOracle::add_authorized_account( RuntimeOrigin::root(), pallet_ema_oracle::BIFROST_SOURCE, + (asset_a_id, asset_b_id), bifrost_account(), ); assert_ok!(EmaOracle::update_bifrost_oracle( diff --git a/integration-tests/src/oracle.rs b/integration-tests/src/oracle.rs index 679dabc42f..81c248b5e6 100644 --- a/integration-tests/src/oracle.rs +++ b/integration-tests/src/oracle.rs @@ -5,6 +5,7 @@ use crate::polkadot_test_net::*; use frame_support::assert_noop; use frame_support::dispatch::GetDispatchInfo; use frame_support::storage::with_transaction; +use frame_support::traits::Get; use frame_support::traits::OnFinalize; use frame_support::traits::OnInitialize; use frame_support::{ @@ -446,7 +447,6 @@ fn bifrost_oracle_should_be_updated() { let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); Hydra::execute_with(|| { - // Register BIFROST_SOURCE as external source and authorize bifrost account assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), BIFROST_SOURCE, @@ -454,6 +454,7 @@ fn bifrost_oracle_should_be_updated() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), BIFROST_SOURCE, + (asset_a_id, asset_b_id), bifrost_account(), )); @@ -494,7 +495,7 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); Hydra::execute_with(|| { - // Register BIFROST_SOURCE as external source and authorize bifrost account + // Arrange assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), BIFROST_SOURCE, @@ -502,6 +503,7 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), BIFROST_SOURCE, + (asset_a_id, asset_b_id), bifrost_account(), )); @@ -512,7 +514,7 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { asset_b, (50, 100) )); - // will store the data received in the sell as oracle values + hydradx_run_to_next_block(); // assert @@ -533,10 +535,10 @@ fn bifrost_oracle_should_be_added_when_pair_not_whitelisted() { fn bifrost_oracle_update_should_return_fee() { // arrange TestNet::reset(); - let (_asset_a_id, _asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); + let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); let balance = 10 * UNITS; Hydra::execute_with(|| { - // Register BIFROST_SOURCE as external source and authorize bifrost account + // Arrange assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), BIFROST_SOURCE, @@ -544,6 +546,7 @@ fn bifrost_oracle_update_should_return_fee() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), BIFROST_SOURCE, + (asset_a_id, asset_b_id), bifrost_account(), )); @@ -646,3 +649,166 @@ fn bifrost_oracle_update_fail_should_charge_fee() { ); }); } + +#[test] +fn many_same_pair_external_updates_do_not_block_router_sell_through_omnipool() { + TestNet::reset(); + + let (asset_a_id, asset_b_id, asset_a, asset_b) = arrange_bifrost_assets(); + + Hydra::execute_with(|| { + hydradx_run_to_next_block(); + init_omnipool(); + // Drain accumulator entries produced by init_omnipool's add_token hooks. + hydradx_run_to_next_block(); + + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + (asset_a_id, asset_b_id), + bifrost_account(), + )); + + // 50 > MaxUniqueEntries (40) — if same-pair updates didn't merge, Router::sell below + // would be rejected with TooManyUniqueEntries. + let spam_count: u32 = 50; + for i in 0..spam_count { + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(bifrost_account()), + BIFROST_SOURCE, + asset_a.clone(), + asset_b.clone(), + (100 + i as u128, 99), + )); + } + + let acc = pallet_ema_oracle::Accumulator::::get(); + let bifrost_slots: usize = acc.keys().filter(|(src, _)| *src == BIFROST_SOURCE).count(); + assert_eq!(bifrost_slots, 1); + + let amount_in = 10 * UNITS; + let bob_dai_before = hydradx_runtime::Currencies::free_balance(DAI, &AccountId::from(BOB)); + assert_ok!(hydradx_runtime::Router::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + amount_in, + 0, + vec![].try_into().unwrap() + )); + let bob_dai_after = hydradx_runtime::Currencies::free_balance(DAI, &AccountId::from(BOB)); + assert!(bob_dai_after > bob_dai_before); + + let acc = pallet_ema_oracle::Accumulator::::get(); + let bifrost_slots: usize = acc.keys().filter(|(src, _)| *src == BIFROST_SOURCE).count(); + let omnipool_slots: usize = acc.keys().filter(|(src, _)| *src == OMNIPOOL_SOURCE).count(); + assert_eq!(bifrost_slots, 1); + assert!(omnipool_slots >= 1); + + hydradx_run_to_next_block(); + + assert!(EmaOracle::get_price(asset_a_id, asset_b_id, LastBlock, BIFROST_SOURCE).is_ok()); + assert!(EmaOracle::get_price(HDX, LRNA, LastBlock, OMNIPOOL_SOURCE).is_ok()); + }); +} + +#[test] +fn router_sell_must_succeed_even_when_external_source_fills_accumulator() { + TestNet::reset(); + + // Register `pair_count + 1` assets, each with a unique XCM location, so the pallet's + // location→asset converter resolves them. Pairs are built as (base, base + i). + let base: AssetId = 200; + let pair_count: u32 = 45; // > MaxUniqueEntries (40) with margin + let para: u32 = 3000; + let ext_location = |asset_id: AssetId| -> polkadot_xcm::v5::Location { + polkadot_xcm::v5::Location::new( + 1, + [ + polkadot_xcm::v5::Junction::Parachain(para), + polkadot_xcm::v5::Junction::GeneralIndex(asset_id as u128), + ], + ) + }; + let ext_boxed = |asset_id: AssetId| -> Box { + Box::new(ext_location(asset_id).into_versioned()) + }; + + Hydra::execute_with(|| { + assert_ok!(with_transaction(|| { + hydradx_run_to_next_block(); + for i in 0..=pair_count { + let asset_id = base + i; + let loc = ext_location(asset_id); + // 3-char symbol derived from i so each registration is unique. + let sym: Vec = format!("E{i:02}").into_bytes(); + assert_ok!(AssetRegistry::register_sufficient_asset( + Some(asset_id), + Some(sym.try_into().unwrap()), + AssetKind::Token, + 1_000_000, + None, + None, + Some(AssetLocation::try_from(loc).unwrap()), + None, + )); + } + TransactionOutcome::Commit(DispatchResult::Ok(())) + })); + }); + + Hydra::execute_with(|| { + hydradx_run_to_next_block(); + init_omnipool(); + // Drain accumulator entries produced by init_omnipool's add_token hooks. + hydradx_run_to_next_block(); + + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE, + )); + for i in 1..=pair_count { + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + (base, base + i), + bifrost_account(), + )); + } + + // Fill the accumulator with `pair_count` DISTINCT external pair entries. + for i in 1..=pair_count { + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(bifrost_account()), + BIFROST_SOURCE, + ext_boxed(base), + ext_boxed(base + i), + (100, 99), + )); + } + + let acc = pallet_ema_oracle::Accumulator::::get(); + let external_slots: usize = acc.keys().filter(|(src, _)| *src == BIFROST_SOURCE).count(); + let max_entries: u32 = ::MaxUniqueEntries::get(); + assert!( + external_slots >= max_entries as usize, + "expected >= {max_entries} external accumulator slots to trigger the cap, got {external_slots}" + ); + + let bob_dai_before = hydradx_runtime::Currencies::free_balance(DAI, &AccountId::from(BOB)); + assert_ok!(hydradx_runtime::Router::sell( + RuntimeOrigin::signed(BOB.into()), + HDX, + DAI, + 10 * UNITS, + 0, + vec![].try_into().unwrap(), + )); + let bob_dai_after = hydradx_runtime::Currencies::free_balance(DAI, &AccountId::from(BOB)); + assert!(bob_dai_after > bob_dai_before); + }); +} diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 7fdb0ae981..ea4f9137ee 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -103,10 +103,10 @@ pub const BIFROST_SOURCE: [u8; 8] = *b"bifrosto"; /// Rounded up to 300 for safety margin. pub const MAX_EXTERNAL_ENTRIES_PER_BLOCK: u32 = 300; -/// Upper bound on the number of authorized accounts per external oracle source. +/// Upper bound on the number of authorized (pair, account) entries per external oracle source. /// Used for worst-case weight estimation when removing a source, as `clear_prefix` -/// must delete all associated authorized accounts. -pub const MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE: u32 = 20; +/// must delete all associated authorization entries. +pub const MAX_AUTHORIZED_ENTRIES_PER_SOURCE: u32 = 40; const LOG_TARGET: &str = "runtime::ema-oracle"; @@ -145,7 +145,6 @@ pub mod pallet { type AuthorityOrigin: EnsureOrigin; /// Origin that can manage external oracle sources and authorized accounts. - /// Should include the Technical Committee for fast emergency response. type ExternalOracleOrigin: EnsureOrigin; /// Provider for the current block number. @@ -179,7 +178,7 @@ pub mod pallet { SourceAlreadyRegistered, /// The external source was not found. SourceNotFound, - /// The caller is not authorized for the given source. + /// The caller is not authorized for the given (source, pair). NotAuthorized, /// Price must not be zero. PriceIsZero, @@ -202,10 +201,18 @@ pub mod pallet { ExternalSourceRegistered { source: Source }, /// An external oracle source was removed. ExternalSourceRemoved { source: Source }, - /// An authorized account was added for an external source. - AuthorizedAccountAdded { source: Source, account: T::AccountId }, - /// An authorized account was removed for an external source. - AuthorizedAccountRemoved { source: Source, account: T::AccountId }, + /// An account was authorized to update the given (source, pair). + AuthorizedAccountAdded { + source: Source, + pair: (AssetId, AssetId), + account: T::AccountId, + }, + /// An authorization was removed for the given (source, pair, account). + AuthorizedAccountRemoved { + source: Source, + pair: (AssetId, AssetId), + account: T::AccountId, + }, } /// Accumulator for oracle data in current block that will be recorded at the end of the block. @@ -241,10 +248,22 @@ pub mod pallet { #[pallet::storage] pub type ExternalSources = StorageMap<_, Twox64Concat, Source, (), OptionQuery>; - /// Authorized accounts per external oracle source. + /// Authorized accounts per (external oracle source, asset pair). + /// + /// Authorization is scoped per-pair so that a compromised external oracle account can + /// only update the specific pairs it was authorized for, limiting DDoS blast radius. + /// The asset pair is stored in `ordered_pair` form. #[pallet::storage] - pub type AuthorizedAccounts = - StorageDoubleMap<_, Twox64Concat, Source, Twox64Concat, T::AccountId, (), OptionQuery>; + pub type AuthorizedAccounts = StorageNMap< + _, + ( + NMapKey, + NMapKey, + NMapKey, + ), + (), + OptionQuery, + >; #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] @@ -301,6 +320,14 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Add an oracle to the whitelist so it is tracked by the pallet. + /// + /// Parameters: + /// - `origin`: `AuthorityOrigin` + /// - `source`: data source identifier + /// - `assets`: the asset pair to track + /// + /// Emits `AddedToWhitelist` event when successful. #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::add_oracle())] pub fn add_oracle(origin: OriginFor, source: Source, assets: (AssetId, AssetId)) -> DispatchResult { @@ -318,6 +345,14 @@ pub mod pallet { Ok(()) } + /// Remove an oracle from the whitelist and delete all its stored entries. + /// + /// Parameters: + /// - `origin`: `AuthorityOrigin` + /// - `source`: data source identifier + /// - `assets`: the asset pair to stop tracking + /// + /// Emits `RemovedFromWhitelist` event when successful. #[pallet::call_index(1)] #[pallet::weight(::WeightInfo::remove_oracle())] pub fn remove_oracle(origin: OriginFor, source: Source, assets: (AssetId, AssetId)) -> DispatchResult { @@ -345,6 +380,15 @@ pub mod pallet { Ok(()) } + /// Update an oracle entry for BIFROST_SOURCE. Thin wrapper around `set_external_oracle`. + /// + /// Parameters: + /// - `origin`: signed origin — must be authorized for the specific `(BIFROST_SOURCE, pair)` + /// - `asset_a`: XCM location of the first asset + /// - `asset_b`: XCM location of the second asset + /// - `price`: price as `(numerator, denominator)` + /// + /// Emits `OracleUpdated` event on the next `on_finalize`. #[deprecated( note = "Use `set_external_oracle` instead. Kept only for backward compatibility with bifrost and will be removed in the future" )] @@ -363,6 +407,19 @@ pub mod pallet { Self::do_set_oracle(who, BIFROST_SOURCE, asset_a, asset_b, price) } + /// Submit an oracle price update for an external source. + /// + /// The call is feeless on success (`Pays::No`). + /// + /// Parameters: + /// - `origin`: signed origin — must be authorized for the specific `(source, pair)` via + /// `add_authorized_account` + /// - `source`: external source identifier (must be registered via `register_external_source`) + /// - `asset_a`: XCM location of the first asset + /// - `asset_b`: XCM location of the second asset + /// - `price`: price as `(numerator, denominator)` — both must be non-zero + /// + /// Emits `OracleUpdated` event on the next `on_finalize`. #[pallet::call_index(6)] #[pallet::weight(::WeightInfo::set_external_oracle() .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] @@ -377,6 +434,13 @@ pub mod pallet { Self::do_set_oracle(who, source, asset_a, asset_b, price) } + /// Register a new external oracle source. + /// + /// Parameters: + /// - `origin`: `ExternalOracleOrigin` + /// - `source`: 8-byte source identifier to register + /// + /// Emits `ExternalSourceRegistered` event when successful. #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::register_external_source())] pub fn register_external_source(origin: OriginFor, source: Source) -> DispatchResult { @@ -390,38 +454,74 @@ pub mod pallet { Ok(()) } + /// Remove an external oracle source and all its per-pair authorizations. + /// + /// Parameters: + /// - `origin`: `ExternalOracleOrigin` + /// - `source`: source identifier to remove + /// + /// Emits `ExternalSourceRemoved` event when successful. #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::remove_external_source(MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE))] + #[pallet::weight(::WeightInfo::remove_external_source(MAX_AUTHORIZED_ENTRIES_PER_SOURCE))] pub fn remove_external_source(origin: OriginFor, source: Source) -> DispatchResult { T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); ExternalSources::::remove(source); - let _ = AuthorizedAccounts::::clear_prefix(source, u32::MAX, None); + let _ = AuthorizedAccounts::::clear_prefix((source,), u32::MAX, None); Self::deposit_event(Event::ExternalSourceRemoved { source }); Ok(()) } + /// Authorize `account` to submit oracle updates for a specific `(source, pair)`. + /// + /// Authorization is scoped per-pair so a compromised account can only update the + /// pairs it was explicitly granted, limiting DDoS blast radius. + /// + /// Parameters: + /// - `origin`: `ExternalOracleOrigin` + /// - `source`: external source identifier (must already be registered) + /// - `assets`: the asset pair to authorize — stored in ordered form + /// - `account`: the account to authorize + /// + /// Emits `AuthorizedAccountAdded` event when successful. #[pallet::call_index(4)] #[pallet::weight(::WeightInfo::add_authorized_account())] - pub fn add_authorized_account(origin: OriginFor, source: Source, account: T::AccountId) -> DispatchResult { + pub fn add_authorized_account( + origin: OriginFor, + source: Source, + assets: (AssetId, AssetId), + account: T::AccountId, + ) -> DispatchResult { T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); - AuthorizedAccounts::::insert(source, &account, ()); - Self::deposit_event(Event::AuthorizedAccountAdded { source, account }); + let pair = ordered_pair(assets.0, assets.1); + AuthorizedAccounts::::insert((source, pair, &account), ()); + Self::deposit_event(Event::AuthorizedAccountAdded { source, pair, account }); Ok(()) } + /// Revoke oracle-update authorization for `account` on a specific `(source, pair)`. + /// + /// Parameters: + /// - `origin`: `ExternalOracleOrigin` + /// - `source`: external source identifier (must already be registered) + /// - `assets`: the asset pair to revoke — matched in ordered form + /// - `account`: the account to revoke + /// + /// Emits `AuthorizedAccountRemoved` event when successful. #[pallet::call_index(7)] #[pallet::weight(::WeightInfo::remove_authorized_account())] pub fn remove_authorized_account( origin: OriginFor, source: Source, + assets: (AssetId, AssetId), account: T::AccountId, ) -> DispatchResult { T::ExternalOracleOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); - AuthorizedAccounts::::remove(source, &account); - Self::deposit_event(Event::AuthorizedAccountRemoved { source, account }); + let pair = ordered_pair(assets.0, assets.1); + AuthorizedAccounts::::remove((source, pair, &account)); + Self::deposit_event(Event::AuthorizedAccountRemoved { source, pair, account }); Ok(()) } } @@ -436,16 +536,17 @@ impl Pallet { price: (Balance, Balance), ) -> DispatchResultWithPostInfo { ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); - ensure!( - AuthorizedAccounts::::contains_key(source, &who), - Error::::NotAuthorized - ); ensure!(price.0 != 0 && price.1 != 0, Error::::PriceIsZero); let asset_a = T::LocationToAssetIdConversion::convert(*asset_a).ok_or(Error::::AssetNotFound)?; let asset_b = T::LocationToAssetIdConversion::convert(*asset_b).ok_or(Error::::AssetNotFound)?; let ordered = ordered_pair(asset_a, asset_b); + + ensure!( + AuthorizedAccounts::::contains_key((source, ordered, &who)), + Error::::NotAuthorized + ); let entry: OracleEntry> = { let e = OracleEntry::new( EmaPrice::new(price.0, price.1), @@ -484,9 +585,18 @@ impl Pallet { entry.accumulate_volume_and_update_from(&oracle_entry); Ok(()) } else { - if !is_external_source && accumulator.len() >= T::MaxUniqueEntries::get() as usize { - //We have soft limit up to MaxUniqueEntries, only for AMM internal oracle updates - return Err(()); + // The `MaxUniqueEntries` soft cap applies ONLY to non-external (AMM) + // entries. An authorized external caller must not be able to push + // legitimate AMM new-pair trades out of the accumulator by filling it + // with distinct external pairs. + if !is_external_source { + let non_external_len = accumulator + .keys() + .filter(|(s, _)| !ExternalSources::::contains_key(s)) + .count(); + if non_external_len >= T::MaxUniqueEntries::get() as usize { + return Err(()); + } } accumulator.insert((src, assets), oracle_entry); Ok(()) @@ -710,7 +820,6 @@ impl OnTradeHandler for OnActivityHandler fn on_trade_weight() -> Weight { let max_entries = T::MaxUniqueEntries::get(); - // on_trade + on_finalize / max_entries T::WeightInfo::on_trade_multiple_tokens(max_entries) .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)) } @@ -751,7 +860,6 @@ impl OnLiquidityChangedHandler for OnActivit fn on_liquidity_changed_weight() -> Weight { let max_entries = T::MaxUniqueEntries::get(); - // on_liquidity + on_finalize / max_entries T::WeightInfo::on_liquidity_changed_multiple_tokens(max_entries) .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)) } diff --git a/pallets/ema-oracle/src/migrations/v2.rs b/pallets/ema-oracle/src/migrations/v2.rs index 523459a3d1..517dcce87a 100644 --- a/pallets/ema-oracle/src/migrations/v2.rs +++ b/pallets/ema-oracle/src/migrations/v2.rs @@ -17,29 +17,26 @@ impl> UncheckedOnRuntimeUpgr fn on_runtime_upgrade() -> Weight { log::info!(target: "runtime::ema-oracle", "v1->v2 migration started"); - // Register BIFROST_SOURCE as an external source ExternalSources::::insert(BIFROST_SOURCE, ()); - // Add the bifrost sovereign account as an authorized account for BIFROST_SOURCE let bifrost_account = BifrostAccount::get(); - AuthorizedAccounts::::insert(BIFROST_SOURCE, &bifrost_account, ()); + let dot_vdot = ordered_pair(5, 15); + AuthorizedAccounts::::insert((BIFROST_SOURCE, dot_vdot, &bifrost_account), ()); - log::info!(target: "runtime::ema-oracle", "v1->v2 migration finished: registered BIFROST_SOURCE and authorized bifrost account"); + log::info!(target: "runtime::ema-oracle", "v1->v2 migration finished"); T::DbWeight::get().reads_writes(0, 2) } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), sp_runtime::DispatchError> { - assert!( - ExternalSources::::contains_key(BIFROST_SOURCE), - "BIFROST_SOURCE should be registered as external source" - ); let bifrost_account = BifrostAccount::get(); - assert!( - AuthorizedAccounts::::contains_key(BIFROST_SOURCE, &bifrost_account), - "Bifrost account should be authorized for BIFROST_SOURCE" - ); + assert!(ExternalSources::::contains_key(BIFROST_SOURCE)); + assert!(AuthorizedAccounts::::contains_key(( + BIFROST_SOURCE, + ordered_pair(5, 15), + &bifrost_account + ))); Ok(()) } } diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 068a199363..e724a1737b 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::mock::{EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; +use super::mock::{expect_events, EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; use super::SOURCE; use crate::pallet::{AuthorizedAccounts, ExternalSources}; use crate::*; @@ -26,10 +26,24 @@ use pretty_assertions::assert_eq; const EXTERNAL_SOURCE: Source = *b"external"; const ANOTHER_SOURCE: Source = *b"another_"; +const HDX_DOT_PAIR: (AssetId, AssetId) = (0, 5); + pub fn new_test_ext() -> sp_io::TestExternalities { ExtBuilder::default().build() } +fn hdx_location() -> polkadot_xcm::VersionedLocation { + polkadot_xcm::v5::Location::new( + 0, + polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), + ) + .into_versioned() +} + +fn dot_location() -> polkadot_xcm::VersionedLocation { + polkadot_xcm::v5::Location::parent().into_versioned() +} + #[test] fn register_external_source_works() { new_test_ext().execute_with(|| { @@ -66,23 +80,55 @@ fn register_external_source_requires_authority() { } #[test] -fn remove_external_source_works() { +fn remove_external_source_clears_all_pair_authorizations() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), EXTERNAL_SOURCE )); + // Authorize ALICE for two different pairs under the same source. assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + (0, 5), ALICE )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (1, 2), + ALICE + )); + // And BOB for another pair. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (0, 1), + BOB + )); + assert_ok!(EmaOracle::remove_external_source( RuntimeOrigin::root(), EXTERNAL_SOURCE )); + assert!(!ExternalSources::::contains_key(EXTERNAL_SOURCE)); - assert!(!AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + // All pair authorizations under the removed source must be gone. + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (0, 5), + ALICE + ))); + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (1, 2), + ALICE + ))); + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (0, 1), + BOB + ))); }); } @@ -106,9 +152,41 @@ fn add_authorized_account_works() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); - assert!(AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + assert!(AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + ))); + }); +} + +#[test] +fn add_authorized_account_stores_in_ordered_pair_form() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + // Pass the pair in reverse order; storage must be keyed by the ordered form. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (5, 0), + ALICE + )); + assert!(AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + ordered_pair(0, 5), + ALICE + ))); + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (5, 0), + ALICE + ))); }); } @@ -116,7 +194,7 @@ fn add_authorized_account_works() { fn add_account_for_nonexistent_source_fails() { new_test_ext().execute_with(|| { assert_noop!( - EmaOracle::add_authorized_account(RuntimeOrigin::root(), EXTERNAL_SOURCE, ALICE), + EmaOracle::add_authorized_account(RuntimeOrigin::root(), EXTERNAL_SOURCE, HDX_DOT_PAIR, ALICE), Error::::SourceNotFound ); }); @@ -132,44 +210,85 @@ fn remove_authorized_account_works() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); assert_ok!(EmaOracle::remove_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); - assert!(!AuthorizedAccounts::::contains_key(EXTERNAL_SOURCE, ALICE)); + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + ))); }); } #[test] -fn set_external_oracle_happy_path() { +fn remove_authorized_account_only_affects_the_given_pair() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), EXTERNAL_SOURCE )); + // ALICE is authorized for two pairs under the same source. assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + (0, 5), + ALICE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (1, 2), ALICE )); - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + // Revoking one pair must leave the other intact. + assert_ok!(EmaOracle::remove_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (0, 5), + ALICE + )); + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (0, 5), + ALICE + ))); + assert!(AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + (1, 2), + ALICE + ))); + }); +} + +#[test] +fn set_external_oracle_happy_path() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); System::set_block_number(3); let res = EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), ); assert_eq!(res, Ok(Pays::No.into())); @@ -188,19 +307,46 @@ fn set_external_oracle_unauthorized_rejected() { EXTERNAL_SOURCE )); - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx_location()), + Box::new(dot_location()), + (100, 99), + ), + Error::::NotAuthorized + ); + }); +} + +// Core DDoS protection invariant: an account authorized for pair A must NOT be able to push +// updates for pair B under the same source. This is the test that prevents the regression +// the refactor was introduced to fix. +#[test] +fn authorized_account_cannot_update_unauthorized_pair() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + // ALICE is authorized ONLY for (0, 1), not for (0, 5). + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (0, 1), + ALICE + )); + + System::set_block_number(3); + // Attempting to update (hdx, dot) = (0, 5) must fail. assert_noop!( EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), ), Error::::NotAuthorized @@ -218,22 +364,16 @@ fn set_external_oracle_zero_price_rejected() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - assert_noop!( EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx.clone()), - Box::new(dot.clone()), + Box::new(hdx_location()), + Box::new(dot_location()), (0, 100), ), Error::::PriceIsZero @@ -243,8 +383,8 @@ fn set_external_oracle_zero_price_rejected() { EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 0), ), Error::::PriceIsZero @@ -255,19 +395,12 @@ fn set_external_oracle_zero_price_rejected() { #[test] fn set_external_oracle_unregistered_source_rejected() { new_test_ext().execute_with(|| { - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - assert_noop!( EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), ), Error::::SourceNotFound @@ -285,24 +418,18 @@ fn external_sources_bypass_whitelist() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); // Use INSUFFICIENT_ASSET which is normally excluded by the whitelist - let asset_a_loc = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let asset_b_loc = polkadot_xcm::v5::Location::parent().into_versioned(); - System::set_block_number(3); assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(asset_a_loc), - Box::new(asset_b_loc), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), )); @@ -326,36 +453,31 @@ fn multiple_sources_in_same_block() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), ANOTHER_SOURCE, + HDX_DOT_PAIR, BOB )); - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - System::set_block_number(3); assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx.clone()), - Box::new(dot.clone()), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), )); assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(BOB), ANOTHER_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (200, 99), )); @@ -451,21 +573,15 @@ fn soft_limit_only_applies_to_non_external_sources() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), EXTERNAL_SOURCE, + HDX_DOT_PAIR, ALICE )); - let hdx = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), - ) - .into_versioned(); - let dot = polkadot_xcm::v5::Location::parent().into_versioned(); - assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(hdx), - Box::new(dot), + Box::new(hdx_location()), + Box::new(dot_location()), (100, 99), )); @@ -474,22 +590,11 @@ fn soft_limit_only_applies_to_non_external_sources() { }); } -// Verifies the accepted design: external oracles have priority and can consume -// accumulator slots, causing AMM trades for *new* pairs to revert with -// TooManyUniqueEntries when the accumulator is full. This locks in the current -// behavior so future refactors don't silently change it. -// -// Important subtlety: the accumulator is keyed by (Source, AssetPair). So an external -// entry under (EXTERNAL_SOURCE, pair) is a DIFFERENT key from an AMM entry under -// (SOURCE, pair) — they don't alias. Only an AMM pair that was *already tracked by -// SOURCE* earlier in the same block keeps working via get_mut. #[test] -fn external_entries_blocking_amm_new_pairs_reverts_amm_trade() { +fn external_entries_do_not_block_amm_new_pair_trades() { new_test_ext().execute_with(|| { let max_entries = <::MaxUniqueEntries as Get>::get(); - // 1. Place one AMM entry first — (SOURCE, (100, 101)) — so we can later verify - // that updates to this specific key still work even when the accumulator is full. assert_ok!(OnActivityHandler::::on_trade( SOURCE, 100, @@ -522,7 +627,6 @@ fn external_entries_blocking_amm_new_pairs_reverts_amm_trade() { } assert_eq!(Accumulator::::get().len(), max_entries as usize); - // Actively traded pairs keep working. assert_ok!(OnActivityHandler::::on_trade( SOURCE, 100, @@ -535,86 +639,74 @@ fn external_entries_blocking_amm_new_pairs_reverts_amm_trade() { Some(1_000_u128), )); - assert_noop!( - OnActivityHandler::::on_trade( - SOURCE, - 0, - 1, - 1_000, - 1_000, - 2_000, - 2_000, - Price::new(2_000, 2_000), - Some(1_000_u128), - ) - .map_err(|(_w, e)| e), - Error::::TooManyUniqueEntries - ); + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + 0, + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); - assert_noop!( - OnActivityHandler::::on_trade( - SOURCE, - 2 * max_entries, - 2 * max_entries + 1, - 1_000, - 1_000, - 2_000, - 2_000, - Price::new(2_000, 2_000), - Some(1_000_u128), - ) - .map_err(|(_w, e)| e), - Error::::TooManyUniqueEntries - ); + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + 2 * max_entries, + 2 * max_entries + 1, + 1_000, + 1_000, + 2_000, + 2_000, + Price::new(2_000, 2_000), + Some(1_000_u128), + )); - // Accumulator size unchanged — failed AMM insertions did not grow it. - assert_eq!(Accumulator::::get().len(), max_entries as usize); + assert_eq!(Accumulator::::get().len(), (max_entries + 2) as usize); }); } -// Demonstrates that a SINGLE authorized external oracle account can submit -// multiple `set_external_oracle` calls for DIFFERENT pairs in the SAME block. -// There is no per-block rate limit per account, per source, or per pair count. #[test] -fn single_authorized_account_can_update_many_pairs_in_one_block() { +fn account_can_update_only_explicitly_authorized_pairs_in_one_block() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), EXTERNAL_SOURCE )); - assert_ok!(EmaOracle::add_authorized_account( - RuntimeOrigin::root(), - EXTERNAL_SOURCE, - ALICE - )); - - System::set_block_number(3); // Build several distinct locations that the mock converter resolves to distinct asset IDs. let loc_0 = polkadot_xcm::v5::Location::new( 0, polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(0)].into()), ) - .into_versioned(); + .into_versioned(); // → asset 0 let loc_1 = polkadot_xcm::v5::Location::new( 0, polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(1)].into()), ) - .into_versioned(); + .into_versioned(); // → asset 1 let loc_2 = polkadot_xcm::v5::Location::new( 0, polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(2)].into()), ) - .into_versioned(); - let loc_4 = polkadot_xcm::v5::Location::new( - 0, - polkadot_xcm::v5::Junctions::X1([polkadot_xcm::v5::Junction::GeneralIndex(3)].into()), - ) - .into_versioned(); - let loc_5 = polkadot_xcm::v5::Location::parent().into_versioned(); + .into_versioned(); // → asset 2 + let loc_dot = polkadot_xcm::v5::Location::parent().into_versioned(); // → asset 5 + + // ALICE is authorized for exactly three pairs: (0, 1), (0, 2), (2, 5). + // She is NOT authorized for (0, 5), so that update must fail. + for pair in &[(0_u32, 1_u32), (0, 2), (2, 5)] { + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + *pair, + ALICE + )); + } + + System::set_block_number(3); - // ALICE submits 4 DISTINCT pair updates in the SAME block. - // No rate limit, no "one pair per source per block" constraint — all succeed. + // Three authorized pairs land in the accumulator in the same block. assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, @@ -632,24 +724,203 @@ fn single_authorized_account_can_update_many_pairs_in_one_block() { assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(loc_1.clone()), - Box::new(loc_4.clone()), + Box::new(loc_2.clone()), + Box::new(loc_dot.clone()), (300, 99), )); + + // The unauthorized pair (0, 5) is rejected — this is the DDoS mitigation. + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(loc_0), + Box::new(loc_dot), + (400, 99), + ), + Error::::NotAuthorized + ); + + let acc = Accumulator::::get(); + assert_eq!(acc.len(), 3); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 1)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 2)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(2, 5)))); + // The rejected pair did NOT land in the accumulator. + assert!(!acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 5)))); + }); +} + +#[test] +fn set_external_oracle_accepts_reversed_location_order() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, // canonical (0, 5) + ALICE + )); + + System::set_block_number(3); + + // Call with (dot, hdx) instead of (hdx, dot). assert_ok!(EmaOracle::set_external_oracle( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, - Box::new(loc_2), - Box::new(loc_5), - (400, 99), + Box::new(dot_location()), + Box::new(hdx_location()), + (100, 99), )); - // All 4 distinct pairs landed in the accumulator from a single account. + // Accumulator stores in ordered_pair form regardless of call-site order. let acc = Accumulator::::get(); - assert_eq!(acc.len(), 4); - assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 1)))); - assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 2)))); - assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(1, 4)))); - assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(2, 5)))); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(0, 5)))); + }); +} + +#[test] +fn add_authorized_account_requires_external_oracle_origin() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_noop!( + EmaOracle::add_authorized_account(RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR, BOB), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn remove_authorized_account_requires_external_oracle_origin() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + assert_noop!( + EmaOracle::remove_authorized_account(RuntimeOrigin::signed(BOB), EXTERNAL_SOURCE, HDX_DOT_PAIR, ALICE), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn remove_external_source_requires_external_oracle_origin() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_noop!( + EmaOracle::remove_external_source(RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE), + sp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn remove_account_for_nonexistent_source_fails() { + new_test_ext().execute_with(|| { + assert_noop!( + EmaOracle::remove_authorized_account(RuntimeOrigin::root(), EXTERNAL_SOURCE, HDX_DOT_PAIR, ALICE), + Error::::SourceNotFound + ); + }); +} + +#[test] +fn authorized_account_events_carry_pair_in_ordered_form() { + new_test_ext().execute_with(|| { + System::set_block_number(1); // events are only recorded on blocks >= 1 + + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + // Intentionally pass the pair reversed so we prove ordering normalization happens + // before the event is emitted. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (5, 0), + ALICE + )); + expect_events(vec![crate::Event::AuthorizedAccountAdded { + source: EXTERNAL_SOURCE, + pair: ordered_pair(0, 5), + account: ALICE, + } + .into()]); + + assert_ok!(EmaOracle::remove_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (5, 0), + ALICE + )); + expect_events(vec![crate::Event::AuthorizedAccountRemoved { + source: EXTERNAL_SOURCE, + pair: ordered_pair(0, 5), + account: ALICE, + } + .into()]); + }); +} + +#[test] +fn set_external_oracle_rejected_after_source_removed() { + new_test_ext().execute_with(|| { + // Arrange: source + authorization, and a baseline successful update. + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + System::set_block_number(3); + + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx_location()), + Box::new(dot_location()), + (100, 99), + )); + + // Act: governance removes the entire source. + assert_ok!(EmaOracle::remove_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + // Assert: the same caller, pair, and price now hits the source gate first. + assert_noop!( + EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx_location()), + Box::new(dot_location()), + (100, 99), + ), + Error::::SourceNotFound + ); }); } diff --git a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs index d0feeb4805..a6294ca74e 100644 --- a/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs +++ b/pallets/ema-oracle/src/tests/update_bifrost_oracle.rs @@ -32,6 +32,8 @@ pub fn new_test_ext() -> sp_io::TestExternalities { use crate::tests::mock::ALICE; use polkadot_xcm::v5::prelude::*; +const BIFROST_HDX_DOT_PAIR: (crate::AssetId, crate::AssetId) = (0, 5); + fn setup_bifrost_auth() { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -40,6 +42,7 @@ fn setup_bifrost_auth() { assert_ok!(EmaOracle::add_authorized_account( RuntimeOrigin::root(), BIFROST_SOURCE, + BIFROST_HDX_DOT_PAIR, ALICE )); } @@ -188,6 +191,33 @@ fn should_fail_when_price_is_zero() { }); } +#[test] +fn bifrost_oracle_rejects_unauthorized_pair() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + BIFROST_SOURCE + )); + // Alice is authorized only for (0, 1), not for (0, 5). + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + BIFROST_SOURCE, + (0, 1), + ALICE + )); + + let hdx = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]).into_versioned(); + let dot = polkadot_xcm::v5::Location::parent().into_versioned(); + + System::set_block_number(3); + + assert_noop!( + EmaOracle::update_bifrost_oracle(RuntimeOrigin::signed(ALICE), Box::new(hdx), Box::new(dot), (100, 99)), + Error::::NotAuthorized + ); + }); +} + pub fn update_aggregated_oracles() { EmaOracle::on_finalize(6); System::set_block_number(7); diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 41cc88098c..57e264fb42 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -652,7 +652,7 @@ where impl pallet_ema_oracle::Config for Runtime { type AuthorityOrigin = EitherOf, GeneralAdmin>; - type ExternalOracleOrigin = EitherOf, EitherOf>; + type ExternalOracleOrigin = EitherOf, EitherOf>; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index e841a6d8a4..0c80f3a796 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -231,8 +231,13 @@ runtime_benchmarks! { let b in 1 .. (<::MaxUniqueEntries as Get>::get() - 1); let max_entries = <::MaxUniqueEntries as Get>::get(); + let max_external = pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; fill_whitelist_storage(max_entries); + let ext_source: Source = *b"benchex1"; + EmaOracle::register_external_source(RawOrigin::Root.into(), ext_source) + .expect("error when registering external source"); + let initial_data_block: BlockNumberFor = 5u32; let block_num = initial_data_block.saturating_add(1_000_000u32); @@ -243,6 +248,9 @@ runtime_benchmarks! { let (amount_in, amount_out) = (1_000_000_000_000, 2_000_000_000_000); let (liquidity_asset_in, liquidity_asset_out) = (1_000_000_000_000_000, 2_000_000_000_000_000); let shares_issuance = 1_000_000_000_000; + + // Pre-seed AMM pairs in the previous block so their `Oracles` rows exist and + // the measured call exercises the "already-tracked" path in update_oracle. for i in 0 .. b { let asset_a = (i + 1) * 1_000; let asset_b = asset_a + 500; @@ -254,6 +262,19 @@ runtime_benchmarks! { SOURCE, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); } + // Pre-seed the worst-case number of external pairs. Asset-id range is disjoint + // from the AMM one above (+100_000 offset) so no key collision in the registry. + for i in 0 .. max_external { + let asset_a = 100_000 + (i + 1) * 1_000; + let asset_b = asset_a + 500; + + register_asset_with_id([b"EX1", asset_a.to_string().as_bytes()].concat(), asset_a).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + register_asset_with_id([b"EX2", asset_b.to_string().as_bytes()].concat(), asset_b).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + + assert_ok!(OnActivityHandler::::on_trade( + ext_source, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, + EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); + } as frame_support::traits::OnFinalize>>::on_finalize(initial_data_block); frame_system::Pallet::::set_block_number(block_num); @@ -265,6 +286,7 @@ runtime_benchmarks! { updated_at: block_num, shares_issuance: Some(shares_issuance), }; + // Refill the current block's accumulator with `b` AMM entries. for i in 0 .. b { let asset_a = (i + 1) * 1_000; let asset_b = asset_a + 500; @@ -273,6 +295,15 @@ runtime_benchmarks! { EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); entries.push(((SOURCE, ordered_pair(asset_a, asset_b)), entry.clone())); } + // Refill the worst-case external state. + for i in 0 .. max_external { + let asset_a = 100_000 + (i + 1) * 1_000; + let asset_b = asset_a + 500; + assert_ok!(OnActivityHandler::::on_trade( + ext_source, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, + EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance))); + entries.push(((ext_source, ordered_pair(asset_a, asset_b)), entry.clone())); + } let asset_a = (b + 1) * 1_000; let asset_b = asset_a + 500; register_asset_with_id([b"AS1", asset_a.to_string().as_bytes()].concat(), asset_a).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; @@ -284,7 +315,7 @@ runtime_benchmarks! { OnActivityHandler::::on_trade( SOURCE, asset_a, asset_b, amount_in, amount_out, liquidity_asset_in, liquidity_asset_out, EmaPrice::new(liquidity_asset_in, liquidity_asset_out), Some(shares_issuance)) - .map_err(|(_w, e)| e) + .map_err(|(_w, err)| err) ); } verify { @@ -294,11 +325,18 @@ runtime_benchmarks! { assert_eq!(pallet_ema_oracle::Pallet::::accumulator(), entries.into_iter().collect()); } + // See `on_trade_multiple_tokens` for the worst-case external accumulator state. on_liquidity_changed_multiple_tokens { let b in 1 .. (<::MaxUniqueEntries as Get>::get() - 1); + let max_entries = <::MaxUniqueEntries as Get>::get(); + let max_external = pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; fill_whitelist_storage(max_entries); + let ext_source: Source = *b"benchex2"; + EmaOracle::register_external_source(RawOrigin::Root.into(), ext_source) + .expect("error when registering external source"); + let initial_data_block: BlockNumberFor = 5u32; let block_num = initial_data_block.saturating_add(1_000_000u32); @@ -320,6 +358,17 @@ runtime_benchmarks! { SOURCE, asset_a, asset_b, amount_a, amount_b, liquidity_asset_a, liquidity_asset_b, EmaPrice::new(liquidity_asset_a, liquidity_asset_b), Some(shares_issuance))); } + for i in 0 .. max_external { + let asset_a = 100_000 + (i + 1) * 1_000; + let asset_b = asset_a + 500; + + register_asset_with_id([b"EX1", asset_a.to_string().as_bytes()].concat(), asset_a).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + register_asset_with_id([b"EX2", asset_b.to_string().as_bytes()].concat(), asset_b).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + + assert_ok!(OnActivityHandler::::on_trade( + ext_source, asset_a, asset_b, amount_a, amount_b, liquidity_asset_a, liquidity_asset_b, + EmaPrice::new(liquidity_asset_a, liquidity_asset_b), Some(shares_issuance))); + } as frame_support::traits::OnFinalize>>::on_finalize(initial_data_block); frame_system::Pallet::::set_block_number(block_num); @@ -339,6 +388,14 @@ runtime_benchmarks! { EmaPrice::new(liquidity_asset_a, liquidity_asset_b), Some(shares_issuance))); entries.push(((SOURCE, ordered_pair(asset_a, asset_b)), entry.clone())); } + for i in 0 .. max_external { + let asset_a = 100_000 + (i + 1) * 1_000; + let asset_b = asset_a + 500; + assert_ok!(OnActivityHandler::::on_trade( + ext_source, asset_a, asset_b, amount_a, amount_b, liquidity_asset_a, liquidity_asset_b, + EmaPrice::new(liquidity_asset_a, liquidity_asset_b), Some(shares_issuance))); + entries.push(((ext_source, ordered_pair(asset_a, asset_b)), entry.clone())); + } let asset_a = (b + 1) * 1_000; let asset_b = asset_a + 500; register_asset_with_id([b"AS1", asset_a.to_string().as_bytes()].concat(), asset_a).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; @@ -350,7 +407,7 @@ runtime_benchmarks! { OnActivityHandler::::on_liquidity_changed( SOURCE, asset_a, asset_b, amount_a, amount_b, liquidity_asset_a, liquidity_asset_b, EmaPrice::new(liquidity_asset_a, liquidity_asset_b), Some(shares_issuance)) - .map_err(|(_w, e)| e) + .map_err(|(_w, err)| err) ); } verify { @@ -418,9 +475,12 @@ runtime_benchmarks! { let max_entries = <::MaxUniqueEntries as Get>::get(); fill_whitelist_storage(max_entries - 1); - // Register BIFROST_SOURCE as external source and authorize bifrost account + let asset_a_id: AssetId = 0; + let asset_b_id: AssetId = 3; + + // Register BIFROST_SOURCE as external source and authorize bifrost account for this pair EmaOracle::register_external_source(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE).expect("error when registering external source"); - EmaOracle::add_authorized_account(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE, bifrost_account()).expect("error when adding authorized account"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), pallet_ema_oracle::BIFROST_SOURCE, (asset_a_id, asset_b_id), bifrost_account()).expect("error when adding authorized account"); let initial_data_block: BlockNumberFor = 5u32; let oracle_age: BlockNumberFor = 7u32; @@ -428,15 +488,13 @@ runtime_benchmarks! { frame_system::Pallet::::set_block_number(initial_data_block); as frame_support::traits::OnInitialize>>::on_initialize(initial_data_block); - let asset_a = 0; - let asset_b = 3; let hdx_loc = polkadot_xcm::v5::Location::new(0, [polkadot_xcm::v5::Junction::GeneralIndex(0)]); let dot_loc = polkadot_xcm::v5::Location::new(1, [polkadot_xcm::v5::Junction::Parachain(1000), polkadot_xcm::v5::Junction::GeneralIndex(0)]); let dot_asset_loc = AssetLocation::try_from(dot_loc.clone()).unwrap(); - register_asset_with_id_and_loc(b"AS2".to_vec(), asset_b, dot_asset_loc).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; + register_asset_with_id_and_loc(b"AS2".to_vec(), asset_b_id, dot_asset_loc).map_err(|_| BenchmarkError::Stop("Failed to register asset"))?; let asset_a = Box::new(hdx_loc.into_versioned()); let asset_b = Box::new(dot_loc.into_versioned()); @@ -456,8 +514,10 @@ runtime_benchmarks! { fill_whitelist_storage(max_entries - 1); let external_source: Source = *b"external"; + // Pair expected after LocationToAssetIdConversion for (hdx_loc, dot_loc) used below. + let auth_pair: (AssetId, AssetId) = (0, 3); EmaOracle::register_external_source(RawOrigin::Root.into(), external_source).expect("error when registering external source"); - EmaOracle::add_authorized_account(RawOrigin::Root.into(), external_source, bifrost_account()).expect("error when adding authorized account"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), external_source, auth_pair, bifrost_account()).expect("error when adding authorized account"); let initial_data_block: BlockNumberFor = 5u32; frame_system::Pallet::::set_block_number(initial_data_block); @@ -484,13 +544,17 @@ runtime_benchmarks! { assert!(pallet_ema_oracle::ExternalSources::::contains_key(source)); } + // Worst-case: remove a source that has `n` (pair, account) authorization entries — all must + // be cleared by `clear_prefix`. remove_external_source { - let n in 0 .. pallet_ema_oracle::MAX_AUTHORIZED_ACCOUNTS_PER_SOURCE; + let n in 0 .. pallet_ema_oracle::MAX_AUTHORIZED_ENTRIES_PER_SOURCE; let source: Source = *b"newsrcxx"; EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); for i in 0..n { let account: AccountId = frame_benchmarking::account("authorized", i, 0); - EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, account).expect("error when adding authorized account"); + // Spread entries across distinct pairs so clear_prefix must actually delete n entries. + let pair = (i, i + 1); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, pair, account).expect("error when adding authorized account"); } }: _(RawOrigin::Root, source) verify { @@ -499,19 +563,23 @@ runtime_benchmarks! { add_authorized_account { let source: Source = *b"newsrcxx"; + let pair: (AssetId, AssetId) = (0, 3); EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); - }: _(RawOrigin::Root, source, bifrost_account()) + }: _(RawOrigin::Root, source, pair, bifrost_account()) verify { - assert!(pallet_ema_oracle::AuthorizedAccounts::::contains_key(source, bifrost_account())); + let ordered = ordered_pair(pair.0, pair.1); + assert!(pallet_ema_oracle::AuthorizedAccounts::::contains_key((source, ordered, bifrost_account()))); } remove_authorized_account { let source: Source = *b"newsrcxx"; + let pair: (AssetId, AssetId) = (0, 3); EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); - EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, bifrost_account()).expect("error when adding authorized account"); - }: _(RawOrigin::Root, source, bifrost_account()) + EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, pair, bifrost_account()).expect("error when adding authorized account"); + }: _(RawOrigin::Root, source, pair, bifrost_account()) verify { - assert!(!pallet_ema_oracle::AuthorizedAccounts::::contains_key(source, bifrost_account())); + let ordered = ordered_pair(pair.0, pair.1); + assert!(!pallet_ema_oracle::AuthorizedAccounts::::contains_key((source, ordered, bifrost_account()))); } } From e1af7ecc4bd52a01335643f06212d127cdb3de69 Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 08:57:24 +0200 Subject: [PATCH 24/43] bump versions --- Cargo.lock | 4 ++-- integration-tests/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a007c9c11..36d6d11b95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "409.0.0" +version = "410.0.0" dependencies = [ "alloy-primitives 0.7.7", "alloy-sol-types 0.7.7", @@ -15646,7 +15646,7 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "runtime-integration-tests" -version = "1.77.0" +version = "1.78.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 0789ea310c..3a144e9d7b 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.77.0" +version = "1.78.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 454f4f4885..00a0ad6f9b 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "409.0.0" +version = "410.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 6045cd4408..cfa293f7f7 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 409, + spec_version: 410, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 0536e3bfeac426ee0fd19c1be76c63308ed67ee3 Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 12:34:07 +0200 Subject: [PATCH 25/43] bump versions --- Cargo.lock | 4 ++-- integration-tests/Cargo.toml | 2 +- runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/lib.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f2e1330675..c778bb98f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "410.0.0" +version = "411.0.0" dependencies = [ "alloy-primitives 0.7.7", "alloy-sol-types 0.7.7", @@ -15646,7 +15646,7 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "runtime-integration-tests" -version = "1.78.0" +version = "1.79.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 3a144e9d7b..f1846d05a2 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.78.0" +version = "1.79.0" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 00a0ad6f9b..221ce37d1e 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "410.0.0" +version = "411.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 cfa293f7f7..ff49a52b2c 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 410, + spec_version: 411, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 1519b2ece9c8de7034ee937350013b2e1506206f Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 12:17:40 +0000 Subject: [PATCH 26/43] Update pallets weights [ignore benchmarks] --- .../hydradx/src/weights/pallet-ema-oracle.rs | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs new file mode 100644 index 0000000000..e4b30a3e92 --- /dev/null +++ b/runtime/hydradx/src/weights/pallet-ema-oracle.rs @@ -0,0 +1,264 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_ema_oracle` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// ./bin/hydradx +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet +// pallet-ema-oracle +// --extrinsic +// * +// --heap-pages +// 4096 +// --steps +// 50 +// --repeat +// 20 +// --template +// scripts/pallet-weight-template.hbs +// --output +// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// --quiet + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; +use crate::*; + +/// Weights for `pallet_ema_oracle`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_ema_oracle::WeightInfo for HydraWeight { + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn add_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1872` + // Estimated: `2126` + // Minimum execution time: 29_440_000 picoseconds. + Weight::from_parts(29_948_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::Oracles` (r:0 w:3) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn remove_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1888` + // Estimated: `3373` + // Minimum execution time: 46_611_000 picoseconds. + Weight::from_parts(47_388_000, 3373) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `EmaOracle::Accumulator` (r:1 w:0) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn on_finalize_no_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `2227` + // Minimum execution time: 4_389_000 picoseconds. + Weight::from_parts(4_506_000, 2227) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::Oracles` (r:900 w:900) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 300]`. + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1079 + b * (893 ±0)` + // Estimated: `2598 + b * (8007 ±0)` + // Minimum execution time: 73_000_000 picoseconds. + Weight::from_parts(18_789_916, 2598) + // Standard Error: 29_985 + .saturating_add(Weight::from_parts(43_497_480, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `b` is `[1, 39]`. + fn on_trade_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `52670 + b * (164 ±0)` + // Estimated: `54173 + b * (163 ±0)` + // Minimum execution time: 587_941_000 picoseconds. + Weight::from_parts(595_614_059, 54173) + // Standard Error: 11_167 + .saturating_add(Weight::from_parts(1_637_196, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `b` is `[1, 39]`. + fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `52670 + b * (164 ±0)` + // Estimated: `54173 + b * (163 ±0)` + // Minimum execution time: 587_377_000 picoseconds. + Weight::from_parts(590_901_224, 54173) + // Standard Error: 11_785 + .saturating_add(Weight::from_parts(1_798_645, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + } + /// Storage: `EmaOracle::Oracles` (r:2 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn get_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `1546` + // Estimated: `6328` + // Minimum execution time: 32_827_000 picoseconds. + Weight::from_parts(33_351_000, 6328) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_bifrost_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 62_558_000 picoseconds. + Weight::from_parts(63_041_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_external_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 62_352_000 picoseconds. + Weight::from_parts(63_509_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn register_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1220` + // Estimated: `3481` + // Minimum execution time: 26_262_000 picoseconds. + Weight::from_parts(26_793_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 40]`. + fn remove_external_source(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1296 + n * (61 ±0)` + // Estimated: `3481 + n * (2547 ±0)` + // Minimum execution time: 32_616_000 picoseconds. + Weight::from_parts(35_836_583, 3481) + // Standard Error: 3_665 + .saturating_add(Weight::from_parts(1_229_943, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn add_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 30_402_000 picoseconds. + Weight::from_parts(30_934_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn remove_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1295` + // Estimated: `3481` + // Minimum execution time: 30_537_000 picoseconds. + Weight::from_parts(30_903_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file From 170e7acf0761b58bf181e28c44dbc20572ad4ac9 Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 14:40:49 +0200 Subject: [PATCH 27/43] update weights --- .../hydradx/src/weights/pallet-ema-oracle.rs | 264 ------------------ .../hydradx/src/weights/pallet_ema_oracle.rs | 140 +++++----- 2 files changed, 70 insertions(+), 334 deletions(-) delete mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs deleted file mode 100644 index e4b30a3e92..0000000000 --- a/runtime/hydradx/src/weights/pallet-ema-oracle.rs +++ /dev/null @@ -1,264 +0,0 @@ -// This file is part of HydraDX. - -// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -//! Autogenerated weights for `pallet_ema_oracle` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// ./bin/hydradx -// benchmark -// pallet -// --wasm-execution=compiled -// --pallet -// pallet-ema-oracle -// --extrinsic -// * -// --heap-pages -// 4096 -// --steps -// 50 -// --repeat -// 20 -// --template -// scripts/pallet-weight-template.hbs -// --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs -// --quiet - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; -use crate::*; - -/// Weights for `pallet_ema_oracle`. -pub struct WeightInfo(PhantomData); - -/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. -pub struct HydraWeight(PhantomData); -impl pallet_ema_oracle::WeightInfo for HydraWeight { - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn add_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1872` - // Estimated: `2126` - // Minimum execution time: 29_440_000 picoseconds. - Weight::from_parts(29_948_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::Oracles` (r:0 w:3) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn remove_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1888` - // Estimated: `3373` - // Minimum execution time: 46_611_000 picoseconds. - Weight::from_parts(47_388_000, 3373) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `EmaOracle::Accumulator` (r:1 w:0) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn on_finalize_no_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `2227` - // Minimum execution time: 4_389_000 picoseconds. - Weight::from_parts(4_506_000, 2227) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::Oracles` (r:900 w:900) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 300]`. - fn on_finalize_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1079 + b * (893 ±0)` - // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 73_000_000 picoseconds. - Weight::from_parts(18_789_916, 2598) - // Standard Error: 29_985 - .saturating_add(Weight::from_parts(43_497_480, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `b` is `[1, 39]`. - fn on_trade_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `52670 + b * (164 ±0)` - // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 587_941_000 picoseconds. - Weight::from_parts(595_614_059, 54173) - // Standard Error: 11_167 - .saturating_add(Weight::from_parts(1_637_196, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `b` is `[1, 39]`. - fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `52670 + b * (164 ±0)` - // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 587_377_000 picoseconds. - Weight::from_parts(590_901_224, 54173) - // Standard Error: 11_785 - .saturating_add(Weight::from_parts(1_798_645, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) - } - /// Storage: `EmaOracle::Oracles` (r:2 w:0) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn get_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `1546` - // Estimated: `6328` - // Minimum execution time: 32_827_000 picoseconds. - Weight::from_parts(33_351_000, 6328) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn update_bifrost_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 62_558_000 picoseconds. - Weight::from_parts(63_041_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn set_external_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 62_352_000 picoseconds. - Weight::from_parts(63_509_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - fn register_external_source() -> Weight { - // Proof Size summary in bytes: - // Measured: `1220` - // Estimated: `3481` - // Minimum execution time: 26_262_000 picoseconds. - Weight::from_parts(26_793_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 40]`. - fn remove_external_source(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1296 + n * (61 ±0)` - // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_616_000 picoseconds. - Weight::from_parts(35_836_583, 3481) - // Standard Error: 3_665 - .saturating_add(Weight::from_parts(1_229_943, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn add_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `3481` - // Minimum execution time: 30_402_000 picoseconds. - Weight::from_parts(30_934_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn remove_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1295` - // Estimated: `3481` - // Minimum execution time: 30_537_000 picoseconds. - Weight::from_parts(30_903_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 7a875cb86e..e4b30a3e92 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -18,19 +18,20 @@ //! Autogenerated weights for `pallet_ema_oracle` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 48.0.0 -//! DATE: 2026-03-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `Mac.chello.hu`, CPU: `` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./target/release/hydradx +// ./bin/hydradx // benchmark // pallet -// -p -// pallet_ema_oracle -// -e +// --wasm-execution=compiled +// --pallet +// pallet-ema-oracle +// --extrinsic // * // --heap-pages // 4096 @@ -41,9 +42,8 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet_ema_oracle.rs -// --wasm-execution -// compiled +// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// --quiet #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -66,8 +66,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_000_000, 2126) + // Minimum execution time: 29_440_000 picoseconds. + Weight::from_parts(29_948_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -81,8 +81,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `3373` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 3373) + // Minimum execution time: 46_611_000 picoseconds. + Weight::from_parts(47_388_000, 3373) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -92,8 +92,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `742` // Estimated: `2227` - // Minimum execution time: 3_000_000 picoseconds. - Weight::from_parts(3_000_000, 2227) + // Minimum execution time: 4_389_000 picoseconds. + Weight::from_parts(4_506_000, 2227) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `EmaOracle::Accumulator` (r:1 w:1) @@ -105,53 +105,53 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1079 + b * (893 ±0)` // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 46_000_000 picoseconds. - Weight::from_parts(114_162_714, 2598) - // Standard Error: 59_641 - .saturating_add(Weight::from_parts(28_826_085, 0).saturating_mul(b.into())) + // Minimum execution time: 73_000_000 picoseconds. + Weight::from_parts(18_789_916, 2598) + // Standard Error: 29_985 + .saturating_add(Weight::from_parts(43_497_480, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) } + /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 39]`. fn on_trade_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1809 + b * (179 ±0)` - // Estimated: `6190 + b * (179 ±0)` - // Minimum execution time: 21_000_000 picoseconds. - Weight::from_parts(22_713_804, 6190) - // Standard Error: 3_900 - .saturating_add(Weight::from_parts(420_911, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `52670 + b * (164 ±0)` + // Estimated: `54173 + b * (163 ±0)` + // Minimum execution time: 587_941_000 picoseconds. + Weight::from_parts(595_614_059, 54173) + // Standard Error: 11_167 + .saturating_add(Weight::from_parts(1_637_196, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } + /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// The range of component `b` is `[1, 39]`. fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1809 + b * (179 ±0)` - // Estimated: `6190 + b * (179 ±0)` - // Minimum execution time: 22_000_000 picoseconds. - Weight::from_parts(22_858_498, 6190) - // Standard Error: 3_770 - .saturating_add(Weight::from_parts(418_212, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `52670 + b * (164 ±0)` + // Estimated: `54173 + b * (163 ±0)` + // Minimum execution time: 587_377_000 picoseconds. + Weight::from_parts(590_901_224, 54173) + // Standard Error: 11_785 + .saturating_add(Weight::from_parts(1_798_645, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 179).saturating_mul(b.into())) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } /// Storage: `EmaOracle::Oracles` (r:2 w:0) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) @@ -159,49 +159,49 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 6328) + // Minimum execution time: 32_827_000 picoseconds. + Weight::from_parts(33_351_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn update_bifrost_oracle() -> Weight { // Proof Size summary in bytes: - // Measured: `2039` + // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(36_000_000, 6190) + // Minimum execution time: 62_558_000 picoseconds. + Weight::from_parts(63_041_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_external_oracle() -> Weight { // Proof Size summary in bytes: - // Measured: `2039` + // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(36_000_000, 6190) + // Minimum execution time: 62_352_000 picoseconds. + Weight::from_parts(63_509_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -211,53 +211,53 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(15_000_000, 3481) + // Minimum execution time: 26_262_000 picoseconds. + Weight::from_parts(26_793_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:20 w:20) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 20]`. + /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 40]`. fn remove_external_source(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1293 + n * (46 ±0)` - // Estimated: `3481 + n * (2531 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(19_366_046, 3481) - // Standard Error: 3_089 - .saturating_add(Weight::from_parts(922_264, 0).saturating_mul(n.into())) + // Measured: `1296 + n * (61 ±0)` + // Estimated: `3481 + n * (2547 ±0)` + // Minimum execution time: 32_616_000 picoseconds. + Weight::from_parts(35_836_583, 3481) + // Standard Error: 3_665 + .saturating_add(Weight::from_parts(1_229_943, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2531).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) fn add_authorized_account() -> Weight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 16_000_000 picoseconds. - Weight::from_parts(17_000_000, 3481) + // Minimum execution time: 30_402_000 picoseconds. + Weight::from_parts(30_934_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) fn remove_authorized_account() -> Weight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 17_000_000 picoseconds. - Weight::from_parts(17_000_000, 3481) + // Minimum execution time: 30_537_000 picoseconds. + Weight::from_parts(30_903_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From da05bab701d6c0bc17ce51f055f4ff305a0a5b4f Mon Sep 17 00:00:00 2001 From: dmoka Date: Mon, 13 Apr 2026 20:20:57 +0200 Subject: [PATCH 28/43] invert check so we make calls cheaper --- pallets/dca/src/tests/mock.rs | 1 + pallets/ema-oracle/src/lib.rs | 13 +++++++++---- pallets/ema-oracle/src/tests/mock.rs | 9 +++++++++ pallets/omnipool-liquidity-mining/src/tests/mock.rs | 1 + runtime/hydradx/src/assets.rs | 11 ++++++++++- 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index 781a1ba87c..c5585bdb7a 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -147,6 +147,7 @@ impl pallet_ema_oracle::Config for Test { type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; + type InternalSources = Everything; type MaxUniqueEntries = ConstU32<20>; type LocationToAssetIdConversion = (); #[cfg(feature = "runtime-benchmarks")] diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index ea4f9137ee..7db82efa3b 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -156,6 +156,11 @@ pub mod pallet { /// Whitelist determining what oracles are tracked by the pallet. type OracleWhitelist: Contains<(Source, AssetId, AssetId)>; + /// Identifies internal (AMM) oracle sources. + /// Implemented with hardcoded source constants in the runtime (0 storage reads). + /// Used to count non-external entries in the accumulator without costly storage lookups. + type InternalSources: Contains; + /// Location to Asset Id converter type LocationToAssetIdConversion: sp_runtime::traits::Convert>; @@ -574,8 +579,8 @@ impl Pallet { assets: (AssetId, AssetId), oracle_entry: OracleEntry>, ) -> Result<(), ()> { - let is_external_source = ExternalSources::::contains_key(src); - if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && !is_external_source { + let is_internal_source = T::InternalSources::contains(&src); + if !T::OracleWhitelist::contains(&(src, assets.0, assets.1)) && is_internal_source { // if we don't track oracle for given asset pair, don't throw error return Ok(()); } @@ -589,10 +594,10 @@ impl Pallet { // entries. An authorized external caller must not be able to push // legitimate AMM new-pair trades out of the accumulator by filling it // with distinct external pairs. - if !is_external_source { + if is_internal_source { let non_external_len = accumulator .keys() - .filter(|(s, _)| !ExternalSources::::contains_key(s)) + .filter(|(s, _)| T::InternalSources::contains(s)) .count(); if non_external_len >= T::MaxUniqueEntries::get() as usize { return Err(()); diff --git a/pallets/ema-oracle/src/tests/mock.rs b/pallets/ema-oracle/src/tests/mock.rs index d5cc4e7cb1..3191d1957a 100644 --- a/pallets/ema-oracle/src/tests/mock.rs +++ b/pallets/ema-oracle/src/tests/mock.rs @@ -135,12 +135,21 @@ impl Contains<(Source, AssetId, AssetId)> for OracleWhitelist { } } +/// Identifies internal (AMM) sources by checking they are not registered as external. +pub struct InternalSources; +impl Contains for InternalSources { + fn contains(s: &Source) -> bool { + !ema_oracle::pallet::ExternalSources::::contains_key(s) + } +} + impl Config for Test { type AuthorityOrigin = EnsureRoot; type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = System; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = OracleWhitelist; + type InternalSources = InternalSources; type MaxUniqueEntries = ConstU32<45>; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index 2ce3a00396..d0459133d4 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -302,6 +302,7 @@ impl pallet_ema_oracle::Config for Test { type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; + type InternalSources = Everything; type MaxUniqueEntries = ConstU32<20>; type LocationToAssetIdConversion = (); #[cfg(feature = "runtime-benchmarks")] diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index 57e264fb42..e2c58ef818 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -77,7 +77,7 @@ use pallet_staking::{ use pallet_transaction_multi_payment::{AddTxAssetOnAccount, AssetIdOf, RemoveTxAssetOnKilled}; use pallet_xyk::weights::WeightInfo as XykWeights; use primitives::constants::{ - chain::{CORE_ASSET_ID, OMNIPOOL_SOURCE, XYK_SOURCE}, + chain::{CORE_ASSET_ID, OMNIPOOL_SOURCE, STABLESWAP_SOURCE, XYK_SOURCE}, currency::{NATIVE_EXISTENTIAL_DEPOSIT, UNITS}, time::DAYS, }; @@ -638,6 +638,14 @@ parameter_types! { pub BifrostAccount: AccountId = hex!["7369626cee070000000000000000000000000000000000000000000000000000"].into(); } +/// Identifies internal (AMM) oracle sources via hardcoded constants. Zero storage reads. +pub struct InternalOracleSources; +impl Contains for InternalOracleSources { + fn contains(s: &Source) -> bool { + matches!(s, &OMNIPOOL_SOURCE | &STABLESWAP_SOURCE | &XYK_SOURCE) + } +} + pub struct OracleWhitelist(PhantomData); impl Contains<(Source, AssetId, AssetId)> for OracleWhitelist where @@ -659,6 +667,7 @@ impl pallet_ema_oracle::Config for Runtime { type BlockNumberProvider = System; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = OracleWhitelist; + type InternalSources = InternalOracleSources; /// With every asset trading against LRNA we will only have as many pairs as there will be assets, so /// 40 seems a decent upper bound for the foreseeable future. type MaxUniqueEntries = ConstU32<40>; From 4c6c6d31f566eaa3ea58eed6231e649a09090dfe Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 07:17:30 +0200 Subject: [PATCH 29/43] upate weights --- .../hydradx/src/weights/pallet_ema_oracle.rs | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index e4b30a3e92..3756969fb4 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -21,11 +21,11 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 //! DATE: 2026-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `Mac.chello.hu`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./bin/hydradx +// ./target/release/hydradx // benchmark // pallet // --wasm-execution=compiled @@ -42,7 +42,7 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// runtime/hydradx/src/weights/pallet_ema_oracle.rs // --quiet #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,8 +66,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 29_440_000 picoseconds. - Weight::from_parts(29_948_000, 2126) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -81,8 +81,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `3373` - // Minimum execution time: 46_611_000 picoseconds. - Weight::from_parts(47_388_000, 3373) + // Minimum execution time: 31_000_000 picoseconds. + Weight::from_parts(34_000_000, 3373) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -92,8 +92,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `742` // Estimated: `2227` - // Minimum execution time: 4_389_000 picoseconds. - Weight::from_parts(4_506_000, 2227) + // Minimum execution time: 3_000_000 picoseconds. + Weight::from_parts(3_000_000, 2227) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `EmaOracle::Accumulator` (r:1 w:1) @@ -105,17 +105,17 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1079 + b * (893 ±0)` // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 73_000_000 picoseconds. - Weight::from_parts(18_789_916, 2598) - // Standard Error: 29_985 - .saturating_add(Weight::from_parts(43_497_480, 0).saturating_mul(b.into())) + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(66_828_196, 2598) + // Standard Error: 41_259 + .saturating_add(Weight::from_parts(28_518_249, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) } - /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) @@ -126,15 +126,15 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `52670 + b * (164 ±0)` // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 587_941_000 picoseconds. - Weight::from_parts(595_614_059, 54173) - // Standard Error: 11_167 - .saturating_add(Weight::from_parts(1_637_196, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 112_000_000 picoseconds. + Weight::from_parts(139_936_960, 54173) + // Standard Error: 55_511 + .saturating_add(Weight::from_parts(547_491, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } - /// Storage: `EmaOracle::ExternalSources` (r:2 w:0) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) @@ -145,11 +145,11 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `52670 + b * (164 ±0)` // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 587_377_000 picoseconds. - Weight::from_parts(590_901_224, 54173) - // Standard Error: 11_785 - .saturating_add(Weight::from_parts(1_798_645, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + // Minimum execution time: 113_000_000 picoseconds. + Weight::from_parts(155_496_025, 54173) + // Standard Error: 53_970 + .saturating_add(Weight::from_parts(42_929, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } @@ -159,8 +159,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 32_827_000 picoseconds. - Weight::from_parts(33_351_000, 6328) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) @@ -179,8 +179,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 62_558_000 picoseconds. - Weight::from_parts(63_041_000, 6190) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(38_000_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -200,8 +200,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 62_352_000 picoseconds. - Weight::from_parts(63_509_000, 6190) + // Minimum execution time: 37_000_000 picoseconds. + Weight::from_parts(38_000_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -211,8 +211,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 26_262_000 picoseconds. - Weight::from_parts(26_793_000, 3481) + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -225,10 +225,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1296 + n * (61 ±0)` // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_616_000 picoseconds. - Weight::from_parts(35_836_583, 3481) - // Standard Error: 3_665 - .saturating_add(Weight::from_parts(1_229_943, 0).saturating_mul(n.into())) + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(20_563_210, 3481) + // Standard Error: 7_951 + .saturating_add(Weight::from_parts(917_253, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -243,8 +243,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 30_402_000 picoseconds. - Weight::from_parts(30_934_000, 3481) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(18_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -256,8 +256,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 30_537_000 picoseconds. - Weight::from_parts(30_903_000, 3481) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From 095b5b36f5dc9ff9599af87619308fd9df85c87e Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 07:43:19 +0000 Subject: [PATCH 30/43] Update pallets weights [ignore benchmarks] --- .../hydradx/src/weights/pallet-ema-oracle.rs | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs new file mode 100644 index 0000000000..0ce55dd43c --- /dev/null +++ b/runtime/hydradx/src/weights/pallet-ema-oracle.rs @@ -0,0 +1,260 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_ema_oracle` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// ./bin/hydradx +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet +// pallet-ema-oracle +// --extrinsic +// * +// --heap-pages +// 4096 +// --steps +// 50 +// --repeat +// 20 +// --template +// scripts/pallet-weight-template.hbs +// --output +// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// --quiet + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; +use crate::*; + +/// Weights for `pallet_ema_oracle`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_ema_oracle::WeightInfo for HydraWeight { + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn add_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1872` + // Estimated: `2126` + // Minimum execution time: 29_394_000 picoseconds. + Weight::from_parts(29_917_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::Oracles` (r:0 w:3) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn remove_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1888` + // Estimated: `3373` + // Minimum execution time: 46_998_000 picoseconds. + Weight::from_parts(47_575_000, 3373) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + /// Storage: `EmaOracle::Accumulator` (r:1 w:0) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn on_finalize_no_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `2227` + // Minimum execution time: 4_568_000 picoseconds. + Weight::from_parts(4_774_000, 2227) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `EmaOracle::Oracles` (r:900 w:900) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 300]`. + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1079 + b * (893 ±0)` + // Estimated: `2598 + b * (8007 ±0)` + // Minimum execution time: 73_612_000 picoseconds. + Weight::from_parts(7_222_649, 2598) + // Standard Error: 26_847 + .saturating_add(Weight::from_parts(43_787_766, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `b` is `[1, 39]`. + fn on_trade_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `52666 + b * (164 ±0)` + // Estimated: `54169 + b * (163 ±0)` + // Minimum execution time: 223_713_000 picoseconds. + Weight::from_parts(228_872_802, 54169) + // Standard Error: 7_230 + .saturating_add(Weight::from_parts(560_607, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `b` is `[1, 39]`. + fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `52666 + b * (164 ±0)` + // Estimated: `54169 + b * (163 ±0)` + // Minimum execution time: 225_093_000 picoseconds. + Weight::from_parts(229_430_359, 54169) + // Standard Error: 5_972 + .saturating_add(Weight::from_parts(527_151, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + } + /// Storage: `EmaOracle::Oracles` (r:2 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn get_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `1546` + // Estimated: `6328` + // Minimum execution time: 32_847_000 picoseconds. + Weight::from_parts(33_488_000, 6328) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn update_bifrost_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_780_000 picoseconds. + Weight::from_parts(61_381_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Accumulator` (r:1 w:1) + /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn set_external_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_690_000 picoseconds. + Weight::from_parts(61_333_000, 6190) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn register_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1220` + // Estimated: `3481` + // Minimum execution time: 26_090_000 picoseconds. + Weight::from_parts(26_561_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 40]`. + fn remove_external_source(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1296 + n * (61 ±0)` + // Estimated: `3481 + n * (2547 ±0)` + // Minimum execution time: 32_153_000 picoseconds. + Weight::from_parts(36_216_196, 3481) + // Standard Error: 3_822 + .saturating_add(Weight::from_parts(1_268_132, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn add_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 30_068_000 picoseconds. + Weight::from_parts(30_735_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn remove_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1295` + // Estimated: `3481` + // Minimum execution time: 30_641_000 picoseconds. + Weight::from_parts(31_108_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file From 3519a34d8432c8504e620abaf3ed7ed39d9fb600 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 10:02:15 +0200 Subject: [PATCH 31/43] upate weights --- .../hydradx/src/weights/pallet-ema-oracle.rs | 260 ------------------ .../hydradx/src/weights/pallet_ema_oracle.rs | 92 +++---- 2 files changed, 44 insertions(+), 308 deletions(-) delete mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs deleted file mode 100644 index 0ce55dd43c..0000000000 --- a/runtime/hydradx/src/weights/pallet-ema-oracle.rs +++ /dev/null @@ -1,260 +0,0 @@ -// This file is part of HydraDX. - -// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -//! Autogenerated weights for `pallet_ema_oracle` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// ./bin/hydradx -// benchmark -// pallet -// --wasm-execution=compiled -// --pallet -// pallet-ema-oracle -// --extrinsic -// * -// --heap-pages -// 4096 -// --steps -// 50 -// --repeat -// 20 -// --template -// scripts/pallet-weight-template.hbs -// --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs -// --quiet - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; -use crate::*; - -/// Weights for `pallet_ema_oracle`. -pub struct WeightInfo(PhantomData); - -/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. -pub struct HydraWeight(PhantomData); -impl pallet_ema_oracle::WeightInfo for HydraWeight { - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn add_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1872` - // Estimated: `2126` - // Minimum execution time: 29_394_000 picoseconds. - Weight::from_parts(29_917_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::Oracles` (r:0 w:3) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn remove_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1888` - // Estimated: `3373` - // Minimum execution time: 46_998_000 picoseconds. - Weight::from_parts(47_575_000, 3373) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `EmaOracle::Accumulator` (r:1 w:0) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn on_finalize_no_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `2227` - // Minimum execution time: 4_568_000 picoseconds. - Weight::from_parts(4_774_000, 2227) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::Oracles` (r:900 w:900) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 300]`. - fn on_finalize_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1079 + b * (893 ±0)` - // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 73_612_000 picoseconds. - Weight::from_parts(7_222_649, 2598) - // Standard Error: 26_847 - .saturating_add(Weight::from_parts(43_787_766, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `b` is `[1, 39]`. - fn on_trade_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `52666 + b * (164 ±0)` - // Estimated: `54169 + b * (163 ±0)` - // Minimum execution time: 223_713_000 picoseconds. - Weight::from_parts(228_872_802, 54169) - // Standard Error: 7_230 - .saturating_add(Weight::from_parts(560_607, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// The range of component `b` is `[1, 39]`. - fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `52666 + b * (164 ±0)` - // Estimated: `54169 + b * (163 ±0)` - // Minimum execution time: 225_093_000 picoseconds. - Weight::from_parts(229_430_359, 54169) - // Standard Error: 5_972 - .saturating_add(Weight::from_parts(527_151, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) - } - /// Storage: `EmaOracle::Oracles` (r:2 w:0) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn get_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `1546` - // Estimated: `6328` - // Minimum execution time: 32_847_000 picoseconds. - Weight::from_parts(33_488_000, 6328) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn update_bifrost_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_780_000 picoseconds. - Weight::from_parts(61_381_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - fn set_external_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_690_000 picoseconds. - Weight::from_parts(61_333_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - fn register_external_source() -> Weight { - // Proof Size summary in bytes: - // Measured: `1220` - // Estimated: `3481` - // Minimum execution time: 26_090_000 picoseconds. - Weight::from_parts(26_561_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 40]`. - fn remove_external_source(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1296 + n * (61 ±0)` - // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_153_000 picoseconds. - Weight::from_parts(36_216_196, 3481) - // Standard Error: 3_822 - .saturating_add(Weight::from_parts(1_268_132, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn add_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `3481` - // Minimum execution time: 30_068_000 picoseconds. - Weight::from_parts(30_735_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn remove_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1295` - // Estimated: `3481` - // Minimum execution time: 30_641_000 picoseconds. - Weight::from_parts(31_108_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 3756969fb4..0ce55dd43c 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -19,13 +19,13 @@ //! Autogenerated weights for `pallet_ema_oracle` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `Mac.chello.hu`, CPU: `` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./target/release/hydradx +// ./bin/hydradx // benchmark // pallet // --wasm-execution=compiled @@ -42,7 +42,7 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet_ema_oracle.rs +// runtime/hydradx/src/weights/pallet-ema-oracle.rs // --quiet #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,8 +66,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 2126) + // Minimum execution time: 29_394_000 picoseconds. + Weight::from_parts(29_917_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -81,8 +81,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `3373` - // Minimum execution time: 31_000_000 picoseconds. - Weight::from_parts(34_000_000, 3373) + // Minimum execution time: 46_998_000 picoseconds. + Weight::from_parts(47_575_000, 3373) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -92,8 +92,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `742` // Estimated: `2227` - // Minimum execution time: 3_000_000 picoseconds. - Weight::from_parts(3_000_000, 2227) + // Minimum execution time: 4_568_000 picoseconds. + Weight::from_parts(4_774_000, 2227) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `EmaOracle::Accumulator` (r:1 w:1) @@ -105,18 +105,16 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1079 + b * (893 ±0)` // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 45_000_000 picoseconds. - Weight::from_parts(66_828_196, 2598) - // Standard Error: 41_259 - .saturating_add(Weight::from_parts(28_518_249, 0).saturating_mul(b.into())) + // Minimum execution time: 73_612_000 picoseconds. + Weight::from_parts(7_222_649, 2598) + // Standard Error: 26_847 + .saturating_add(Weight::from_parts(43_787_766, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) @@ -124,18 +122,16 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// The range of component `b` is `[1, 39]`. fn on_trade_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `52670 + b * (164 ±0)` - // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 112_000_000 picoseconds. - Weight::from_parts(139_936_960, 54173) - // Standard Error: 55_511 - .saturating_add(Weight::from_parts(547_491, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `52666 + b * (164 ±0)` + // Estimated: `54169 + b * (163 ±0)` + // Minimum execution time: 223_713_000 picoseconds. + Weight::from_parts(228_872_802, 54169) + // Standard Error: 7_230 + .saturating_add(Weight::from_parts(560_607, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::Accumulator` (r:1 w:1) @@ -143,13 +139,13 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// The range of component `b` is `[1, 39]`. fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `52670 + b * (164 ±0)` - // Estimated: `54173 + b * (163 ±0)` - // Minimum execution time: 113_000_000 picoseconds. - Weight::from_parts(155_496_025, 54173) - // Standard Error: 53_970 - .saturating_add(Weight::from_parts(42_929, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `52666 + b * (164 ±0)` + // Estimated: `54169 + b * (163 ±0)` + // Minimum execution time: 225_093_000 picoseconds. + Weight::from_parts(229_430_359, 54169) + // Standard Error: 5_972 + .saturating_add(Weight::from_parts(527_151, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) } @@ -159,8 +155,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 6328) + // Minimum execution time: 32_847_000 picoseconds. + Weight::from_parts(33_488_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) @@ -179,8 +175,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(38_000_000, 6190) + // Minimum execution time: 60_780_000 picoseconds. + Weight::from_parts(61_381_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -200,8 +196,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 37_000_000 picoseconds. - Weight::from_parts(38_000_000, 6190) + // Minimum execution time: 60_690_000 picoseconds. + Weight::from_parts(61_333_000, 6190) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -211,8 +207,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(16_000_000, 3481) + // Minimum execution time: 26_090_000 picoseconds. + Weight::from_parts(26_561_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -225,10 +221,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1296 + n * (61 ±0)` // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(20_563_210, 3481) - // Standard Error: 7_951 - .saturating_add(Weight::from_parts(917_253, 0).saturating_mul(n.into())) + // Minimum execution time: 32_153_000 picoseconds. + Weight::from_parts(36_216_196, 3481) + // Standard Error: 3_822 + .saturating_add(Weight::from_parts(1_268_132, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -243,8 +239,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3481) + // Minimum execution time: 30_068_000 picoseconds. + Weight::from_parts(30_735_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -256,8 +252,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 3481) + // Minimum execution time: 30_641_000 picoseconds. + Weight::from_parts(31_108_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From e0f96108d501cf570343e50a18755d34bb5fe605 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 12:08:32 +0200 Subject: [PATCH 32/43] worst case and weignt optimizations --- pallets/ema-oracle/src/lib.rs | 11 +- .../hydradx/src/benchmarking/ema_oracle.rs | 12 +- .../hydradx/src/weights/pallet_ema_oracle.rs | 129 +++++++----------- 3 files changed, 69 insertions(+), 83 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 7db82efa3b..c7e86d4f64 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -98,10 +98,12 @@ pub const MAX_PERIODS: u32 = OraclePeriod::all_periods().len() as u32; pub const BIFROST_SOURCE: [u8; 8] = *b"bifrosto"; -/// Max external oracle entries per block based on proof_size budget: -/// 75% of MAX_POV_SIZE (5MB) / ~14,197 bytes per set_external_oracle call ≈ 275. -/// Rounded up to 300 for safety margin. -pub const MAX_EXTERNAL_ENTRIES_PER_BLOCK: u32 = 300; +/// Denominator used by `fractional_on_finalize_weight` to split the worst-case +/// `on_finalize` cost across contributing calls. Not a hard cap — the `on_finalize` +/// weight function is linear in entry count, so aggregate accounting stays correct +/// even if more than this actually land in a block. Benchmarks measure a smaller +/// range (see benchmarking file) and the linear formula extrapolates up to this value. +pub const MAX_EXTERNAL_ENTRIES_PER_BLOCK: u32 = 100; /// Upper bound on the number of authorized (pair, account) entries per external oracle source. /// Used for worst-case weight estimation when removing a source, as `clear_prefix` @@ -224,6 +226,7 @@ pub mod pallet { #[pallet::storage] #[pallet::unbounded] #[pallet::getter(fn accumulator)] + #[pallet::whitelist_storage] pub type Accumulator = StorageValue<_, BTreeMap<(Source, (AssetId, AssetId)), OracleEntry>>, ValueQuery>; diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index 0c80f3a796..cfa1670ed3 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -45,6 +45,12 @@ use sp_core::{ConstU32, Get}; /// Default oracle source. const SOURCE: Source = *b"dummysrc"; +/// Benchmark range / worst-case pre-fill for external-oracle entries. +/// Decoupled from `MAX_EXTERNAL_ENTRIES_PER_BLOCK`: the `on_finalize` weight formula +/// is linear in entry count, so fitting it from a small range is both faster and +/// more stable, and the formula extrapolates up to the runtime accounting value. +const BENCH_EXTERNAL_ENTRIES: u32 = 10; + fn bifrost_account() -> AccountId { BifrostAccount::get() } @@ -175,7 +181,7 @@ runtime_benchmarks! { } on_finalize_multiple_tokens { - let b in 1 .. pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; + let b in 1 .. BENCH_EXTERNAL_ENTRIES; // Register an external source so on_trade bypasses the whitelist and soft limit let external_source: Source = *b"benchext"; @@ -231,7 +237,7 @@ runtime_benchmarks! { let b in 1 .. (<::MaxUniqueEntries as Get>::get() - 1); let max_entries = <::MaxUniqueEntries as Get>::get(); - let max_external = pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; + let max_external = BENCH_EXTERNAL_ENTRIES; fill_whitelist_storage(max_entries); let ext_source: Source = *b"benchex1"; @@ -330,7 +336,7 @@ runtime_benchmarks! { let b in 1 .. (<::MaxUniqueEntries as Get>::get() - 1); let max_entries = <::MaxUniqueEntries as Get>::get(); - let max_external = pallet_ema_oracle::MAX_EXTERNAL_ENTRIES_PER_BLOCK; + let max_external = BENCH_EXTERNAL_ENTRIES; fill_whitelist_storage(max_entries); let ext_source: Source = *b"benchex2"; diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 0ce55dd43c..fa3dac66b1 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -21,11 +21,11 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 //! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! HOSTNAME: `Mac.chello.hu`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./bin/hydradx +// ./target/release/hydradx // benchmark // pallet // --wasm-execution=compiled @@ -42,7 +42,7 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// runtime/hydradx/src/weights/pallet_ema_oracle.rs // --quiet #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,88 +66,71 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 29_394_000 picoseconds. - Weight::from_parts(29_917_000, 2126) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `EmaOracle::Oracles` (r:0 w:3) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) fn remove_oracle() -> Weight { // Proof Size summary in bytes: // Measured: `1888` - // Estimated: `3373` - // Minimum execution time: 46_998_000 picoseconds. - Weight::from_parts(47_575_000, 3373) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) + // Estimated: `2126` + // Minimum execution time: 30_000_000 picoseconds. + Weight::from_parts(31_000_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - /// Storage: `EmaOracle::Accumulator` (r:1 w:0) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn on_finalize_no_entry() -> Weight { // Proof Size summary in bytes: // Measured: `742` - // Estimated: `2227` - // Minimum execution time: 4_568_000 picoseconds. - Weight::from_parts(4_774_000, 2227) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Estimated: `0` + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(3_000_000, 0) } - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `EmaOracle::Oracles` (r:900 w:900) + /// Storage: `EmaOracle::Oracles` (r:30 w:30) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 300]`. + /// The range of component `b` is `[1, 10]`. fn on_finalize_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1079 + b * (893 ±0)` - // Estimated: `2598 + b * (8007 ±0)` - // Minimum execution time: 73_612_000 picoseconds. - Weight::from_parts(7_222_649, 2598) - // Standard Error: 26_847 - .saturating_add(Weight::from_parts(43_787_766, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) + // Measured: `1206 + b * (890 ±0)` + // Estimated: `990 + b * (8007 ±0)` + // Minimum execution time: 43_000_000 picoseconds. + Weight::from_parts(12_716_946, 990) + // Standard Error: 151_691 + .saturating_add(Weight::from_parts(30_142_042, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) } /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 39]`. fn on_trade_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `52666 + b * (164 ±0)` - // Estimated: `54169 + b * (163 ±0)` - // Minimum execution time: 223_713_000 picoseconds. - Weight::from_parts(228_872_802, 54169) - // Standard Error: 7_230 - .saturating_add(Weight::from_parts(560_607, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 26_000_000 picoseconds. + Weight::from_parts(33_575_563, 6190) + // Standard Error: 18_923 + .saturating_add(Weight::from_parts(303_915, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// The range of component `b` is `[1, 39]`. fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `52666 + b * (164 ±0)` - // Estimated: `54169 + b * (163 ±0)` - // Minimum execution time: 225_093_000 picoseconds. - Weight::from_parts(229_430_359, 54169) - // Standard Error: 5_972 - .saturating_add(Weight::from_parts(527_151, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 163).saturating_mul(b.into())) + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 25_000_000 picoseconds. + Weight::from_parts(27_108_439, 6190) + // Standard Error: 5_768 + .saturating_add(Weight::from_parts(342_190, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::Oracles` (r:2 w:0) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) @@ -155,8 +138,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 32_847_000 picoseconds. - Weight::from_parts(33_488_000, 6328) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) @@ -169,16 +152,13 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn update_bifrost_oracle() -> Weight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 60_780_000 picoseconds. - Weight::from_parts(61_381_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(36_000_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) @@ -190,16 +170,13 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Accumulator` (r:1 w:1) - /// Proof: `EmaOracle::Accumulator` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) fn set_external_oracle() -> Weight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 60_690_000 picoseconds. - Weight::from_parts(61_333_000, 6190) - .saturating_add(T::DbWeight::get().reads(7_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(36_000_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) @@ -207,8 +184,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 26_090_000 picoseconds. - Weight::from_parts(26_561_000, 3481) + // Minimum execution time: 15_000_000 picoseconds. + Weight::from_parts(16_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -221,10 +198,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1296 + n * (61 ±0)` // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_153_000 picoseconds. - Weight::from_parts(36_216_196, 3481) - // Standard Error: 3_822 - .saturating_add(Weight::from_parts(1_268_132, 0).saturating_mul(n.into())) + // Minimum execution time: 19_000_000 picoseconds. + Weight::from_parts(19_039_044, 3481) + // Standard Error: 4_032 + .saturating_add(Weight::from_parts(992_979, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -239,8 +216,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 30_068_000 picoseconds. - Weight::from_parts(30_735_000, 3481) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(18_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -252,8 +229,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 30_641_000 picoseconds. - Weight::from_parts(31_108_000, 3481) + // Minimum execution time: 18_000_000 picoseconds. + Weight::from_parts(19_000_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From fee2df74fe515632f3edb02b5db3b9f9ef9270a0 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 12:41:16 +0200 Subject: [PATCH 33/43] fix failing test due to different weight --- integration-tests/src/exchange_asset.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/src/exchange_asset.rs b/integration-tests/src/exchange_asset.rs index 8c9df88e72..3d043d8a48 100644 --- a/integration-tests/src/exchange_asset.rs +++ b/integration-tests/src/exchange_asset.rs @@ -1206,7 +1206,7 @@ mod circuit_breaker { Hydra::execute_with(|| { let trapped_event = &last_hydra_events(10)[3].clone(); - assert_trapped_acala_token(trapped_event, 90054588142157u128); + assert_trapped_acala_token(trapped_event, 90350628868136u128); let fee = hydradx_runtime::Tokens::free_balance(ACA, &hydradx_runtime::Treasury::account_id()); assert!(fee > 0, "treasury should have received fees"); From 759bf662295a5edff86c2030d6e04d67879a4a9d Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 13:16:03 +0200 Subject: [PATCH 34/43] add set oracle by ids --- pallets/ema-oracle/src/lib.rs | 48 ++++- .../ema-oracle/src/tests/external_oracle.rs | 165 ++++++++++++++++++ pallets/ema-oracle/src/weights.rs | 5 + .../hydradx/src/benchmarking/ema_oracle.rs | 20 +++ .../hydradx/src/weights/pallet_ema_oracle.rs | 9 + 5 files changed, 244 insertions(+), 3 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index c7e86d4f64..f5bebf7bf8 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -442,6 +442,38 @@ pub mod pallet { Self::do_set_oracle(who, source, asset_a, asset_b, price) } + /// Update an external oracle entry using local `AssetId`s directly. + /// + /// Cheaper variant of `set_external_oracle` for callers that already know the local + /// AssetIds — skips the `VersionedLocation` → `AssetId` conversion and the + /// `AssetRegistry::LocationAssets` storage read. Authorization shares the same + /// `AuthorizedAccounts` storage as the location variant. + /// + /// Parameters: + /// - `origin`: signed origin — must be authorized for the specific `(source, pair)` via + /// `add_authorized_account` + /// - `source`: external source identifier (must be registered via `register_external_source`) + /// - `asset_a`: local AssetId of the first asset + /// - `asset_b`: local AssetId of the second asset + /// - `price`: price as `(numerator, denominator)` — both must be non-zero + /// + /// The call is feeless on success (`Pays::No`). + /// + /// Emits `OracleUpdated` event on the next `on_finalize`. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::set_oracle_by_ids() + .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] + pub fn set_oracle_by_ids( + origin: OriginFor, + source: Source, + asset_a: AssetId, + asset_b: AssetId, + price: (Balance, Balance), + ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin)?; + Self::do_set_oracle_inner(who, source, asset_a, asset_b, price) + } + /// Register a new external oracle source. /// /// Parameters: @@ -543,12 +575,22 @@ impl Pallet { asset_b: Box, price: (Balance, Balance), ) -> DispatchResultWithPostInfo { - ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); - ensure!(price.0 != 0 && price.1 != 0, Error::::PriceIsZero); - let asset_a = T::LocationToAssetIdConversion::convert(*asset_a).ok_or(Error::::AssetNotFound)?; let asset_b = T::LocationToAssetIdConversion::convert(*asset_b).ok_or(Error::::AssetNotFound)?; + Self::do_set_oracle_inner(who, source, asset_a, asset_b, price) + } + + fn do_set_oracle_inner( + who: T::AccountId, + source: Source, + asset_a: AssetId, + asset_b: AssetId, + price: (Balance, Balance), + ) -> DispatchResultWithPostInfo { + ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); + ensure!(price.0 != 0 && price.1 != 0, Error::::PriceIsZero); + let ordered = ordered_pair(asset_a, asset_b); ensure!( diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index e724a1737b..80708f07bf 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -924,3 +924,168 @@ fn set_external_oracle_rejected_after_source_removed() { ); }); } + +#[test] +fn set_oracle_by_ids_happy_path() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + System::set_block_number(3); + + let res = EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + HDX_DOT_PAIR.1, + (100, 99), + ); + assert_eq!(res, Ok(Pays::No.into())); + + let acc = Accumulator::::get(); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(HDX_DOT_PAIR.0, HDX_DOT_PAIR.1)))); + }); +} + +#[test] +fn set_oracle_by_ids_unauthorized_rejected() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + assert_noop!( + EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + HDX_DOT_PAIR.1, + (100, 99), + ), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn set_oracle_by_ids_unknown_asset_id_returns_not_authorized() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + // Authorize ALICE only for the real pair. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + const BOGUS_ASSET_ID: AssetId = 99_999; + assert_noop!( + EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + BOGUS_ASSET_ID, + (100, 99), + ), + Error::::NotAuthorized + ); + }); +} + +#[test] +fn set_oracle_by_ids_zero_price_rejected() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + assert_noop!( + EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + HDX_DOT_PAIR.1, + (0, 100), + ), + Error::::PriceIsZero + ); + + assert_noop!( + EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + HDX_DOT_PAIR.1, + (100, 0), + ), + Error::::PriceIsZero + ); + }); +} + +#[test] +fn set_oracle_by_ids_unregistered_source_rejected() { + new_test_ext().execute_with(|| { + assert_noop!( + EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.0, + HDX_DOT_PAIR.1, + (100, 99), + ), + Error::::SourceNotFound + ); + }); +} + +#[test] +fn set_oracle_by_ids_accepts_reversed_id_order() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + // Authorize the ordered pair (HDX_DOT_PAIR.0, HDX_DOT_PAIR.1) where .0 < .1. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + System::set_block_number(3); + + // Caller passes the pair in reversed order; ordered_pair() normalizes it so auth still matches. + assert_ok!(EmaOracle::set_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX_DOT_PAIR.1, + HDX_DOT_PAIR.0, + (100, 99), + )); + + let acc = Accumulator::::get(); + assert!(acc.contains_key(&(EXTERNAL_SOURCE, ordered_pair(HDX_DOT_PAIR.0, HDX_DOT_PAIR.1)))); + }); +} diff --git a/pallets/ema-oracle/src/weights.rs b/pallets/ema-oracle/src/weights.rs index 2fd692eb58..ae83a31f4a 100644 --- a/pallets/ema-oracle/src/weights.rs +++ b/pallets/ema-oracle/src/weights.rs @@ -20,6 +20,7 @@ pub trait WeightInfo { fn on_liquidity_changed_multiple_tokens(b: u32) -> Weight; fn get_entry() -> Weight; fn set_external_oracle() -> Weight; + fn set_oracle_by_ids() -> Weight; fn register_external_source() -> Weight; fn remove_external_source(n: u32) -> Weight; fn add_authorized_account() -> Weight; @@ -63,6 +64,10 @@ impl WeightInfo for () { Weight::zero() } + fn set_oracle_by_ids() -> Weight { + Weight::zero() + } + fn register_external_source() -> Weight { Weight::zero() } diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index cfa1670ed3..2e2c674a71 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -543,6 +543,26 @@ runtime_benchmarks! { assert!(!Accumulator::::get().is_empty()); } + set_oracle_by_ids { + let max_entries = <::MaxUniqueEntries as Get>::get(); + fill_whitelist_storage(max_entries - 1); + + let external_source: Source = *b"extbyids"; + let asset_a_id: AssetId = HDX; + let asset_b_id: AssetId = DOT; + let auth_pair = pallet_ema_oracle::ordered_pair(asset_a_id, asset_b_id); + EmaOracle::register_external_source(RawOrigin::Root.into(), external_source).expect("error when registering external source"); + EmaOracle::add_authorized_account(RawOrigin::Root.into(), external_source, auth_pair, bifrost_account()).expect("error when adding authorized account"); + + let initial_data_block: BlockNumberFor = 5u32; + frame_system::Pallet::::set_block_number(initial_data_block); + as frame_support::traits::OnInitialize>>::on_initialize(initial_data_block); + + }: _(RawOrigin::Signed(bifrost_account()), external_source, asset_a_id, asset_b_id, (100,99)) + verify { + assert!(!Accumulator::::get().is_empty()); + } + register_external_source { let source: Source = *b"newsrcxx"; }: _(RawOrigin::Root, source) diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index fa3dac66b1..23f0037903 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -178,6 +178,15 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { Weight::from_parts(36_000_000, 6190) .saturating_add(T::DbWeight::get().reads(6_u64)) } + + fn set_oracle_by_ids() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 36_000_000 picoseconds. + Weight::from_parts(36_000_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) fn register_external_source() -> Weight { From 09ea86d562b941d6aceca7dedd8b0945961fd61b Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 13:19:00 +0200 Subject: [PATCH 35/43] renaming --- pallets/ema-oracle/src/lib.rs | 4 +-- .../ema-oracle/src/tests/external_oracle.rs | 26 +++++++++---------- pallets/ema-oracle/src/weights.rs | 4 +-- .../hydradx/src/benchmarking/ema_oracle.rs | 2 +- .../hydradx/src/weights/pallet_ema_oracle.rs | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index f5bebf7bf8..0a5ffac236 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -461,9 +461,9 @@ pub mod pallet { /// /// Emits `OracleUpdated` event on the next `on_finalize`. #[pallet::call_index(8)] - #[pallet::weight(::WeightInfo::set_oracle_by_ids() + #[pallet::weight(::WeightInfo::set_external_oracle_by_ids() .saturating_add(fractional_on_finalize_weight::(MAX_EXTERNAL_ENTRIES_PER_BLOCK)))] - pub fn set_oracle_by_ids( + pub fn set_external_oracle_by_ids( origin: OriginFor, source: Source, asset_a: AssetId, diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 80708f07bf..6c84e83945 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -926,7 +926,7 @@ fn set_external_oracle_rejected_after_source_removed() { } #[test] -fn set_oracle_by_ids_happy_path() { +fn set_external_oracle_by_ids_happy_path() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -941,7 +941,7 @@ fn set_oracle_by_ids_happy_path() { System::set_block_number(3); - let res = EmaOracle::set_oracle_by_ids( + let res = EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -956,7 +956,7 @@ fn set_oracle_by_ids_happy_path() { } #[test] -fn set_oracle_by_ids_unauthorized_rejected() { +fn set_external_oracle_by_ids_unauthorized_rejected() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -964,7 +964,7 @@ fn set_oracle_by_ids_unauthorized_rejected() { )); assert_noop!( - EmaOracle::set_oracle_by_ids( + EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -977,7 +977,7 @@ fn set_oracle_by_ids_unauthorized_rejected() { } #[test] -fn set_oracle_by_ids_unknown_asset_id_returns_not_authorized() { +fn set_external_oracle_by_ids_unknown_asset_id_returns_not_authorized() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -993,7 +993,7 @@ fn set_oracle_by_ids_unknown_asset_id_returns_not_authorized() { const BOGUS_ASSET_ID: AssetId = 99_999; assert_noop!( - EmaOracle::set_oracle_by_ids( + EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -1006,7 +1006,7 @@ fn set_oracle_by_ids_unknown_asset_id_returns_not_authorized() { } #[test] -fn set_oracle_by_ids_zero_price_rejected() { +fn set_external_oracle_by_ids_zero_price_rejected() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -1020,7 +1020,7 @@ fn set_oracle_by_ids_zero_price_rejected() { )); assert_noop!( - EmaOracle::set_oracle_by_ids( + EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -1031,7 +1031,7 @@ fn set_oracle_by_ids_zero_price_rejected() { ); assert_noop!( - EmaOracle::set_oracle_by_ids( + EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -1044,10 +1044,10 @@ fn set_oracle_by_ids_zero_price_rejected() { } #[test] -fn set_oracle_by_ids_unregistered_source_rejected() { +fn set_external_oracle_by_ids_unregistered_source_rejected() { new_test_ext().execute_with(|| { assert_noop!( - EmaOracle::set_oracle_by_ids( + EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.0, @@ -1060,7 +1060,7 @@ fn set_oracle_by_ids_unregistered_source_rejected() { } #[test] -fn set_oracle_by_ids_accepts_reversed_id_order() { +fn set_external_oracle_by_ids_accepts_reversed_id_order() { new_test_ext().execute_with(|| { assert_ok!(EmaOracle::register_external_source( RuntimeOrigin::root(), @@ -1077,7 +1077,7 @@ fn set_oracle_by_ids_accepts_reversed_id_order() { System::set_block_number(3); // Caller passes the pair in reversed order; ordered_pair() normalizes it so auth still matches. - assert_ok!(EmaOracle::set_oracle_by_ids( + assert_ok!(EmaOracle::set_external_oracle_by_ids( RuntimeOrigin::signed(ALICE), EXTERNAL_SOURCE, HDX_DOT_PAIR.1, diff --git a/pallets/ema-oracle/src/weights.rs b/pallets/ema-oracle/src/weights.rs index ae83a31f4a..8742c2e6c1 100644 --- a/pallets/ema-oracle/src/weights.rs +++ b/pallets/ema-oracle/src/weights.rs @@ -20,7 +20,7 @@ pub trait WeightInfo { fn on_liquidity_changed_multiple_tokens(b: u32) -> Weight; fn get_entry() -> Weight; fn set_external_oracle() -> Weight; - fn set_oracle_by_ids() -> Weight; + fn set_external_oracle_by_ids() -> Weight; fn register_external_source() -> Weight; fn remove_external_source(n: u32) -> Weight; fn add_authorized_account() -> Weight; @@ -64,7 +64,7 @@ impl WeightInfo for () { Weight::zero() } - fn set_oracle_by_ids() -> Weight { + fn set_external_oracle_by_ids() -> Weight { Weight::zero() } diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index 2e2c674a71..fdbb9d055b 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -543,7 +543,7 @@ runtime_benchmarks! { assert!(!Accumulator::::get().is_empty()); } - set_oracle_by_ids { + set_external_oracle_by_ids { let max_entries = <::MaxUniqueEntries as Get>::get(); fill_whitelist_storage(max_entries - 1); diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 23f0037903..ac165d6fad 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -179,7 +179,7 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { .saturating_add(T::DbWeight::get().reads(6_u64)) } - fn set_oracle_by_ids() -> Weight { + fn set_external_oracle_by_ids() -> Weight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` From 2b7e58e70347f078e4cc794bdb2ce8b7f35122d4 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 12:51:53 +0000 Subject: [PATCH 36/43] Update pallets weights [ignore benchmarks] --- .../hydradx/src/weights/pallet-ema-oracle.rs | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs new file mode 100644 index 0000000000..8e011bc61a --- /dev/null +++ b/runtime/hydradx/src/weights/pallet-ema-oracle.rs @@ -0,0 +1,253 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_ema_oracle` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// ./bin/hydradx +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet +// pallet-ema-oracle +// --extrinsic +// * +// --heap-pages +// 4096 +// --steps +// 50 +// --repeat +// 20 +// --template +// scripts/pallet-weight-template.hbs +// --output +// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// --quiet + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; +use crate::*; + +/// Weights for `pallet_ema_oracle`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_ema_oracle::WeightInfo for HydraWeight { + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn add_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1872` + // Estimated: `2126` + // Minimum execution time: 29_313_000 picoseconds. + Weight::from_parts(29_812_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:0 w:3) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn remove_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1888` + // Estimated: `2126` + // Minimum execution time: 46_644_000 picoseconds. + Weight::from_parts(47_208_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + fn on_finalize_no_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `0` + // Minimum execution time: 4_453_000 picoseconds. + Weight::from_parts(4_587_000, 0) + } + /// Storage: `EmaOracle::Oracles` (r:30 w:30) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 10]`. + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1206 + b * (890 ±0)` + // Estimated: `990 + b * (8007 ±0)` + // Minimum execution time: 72_103_000 picoseconds. + Weight::from_parts(32_369_574, 990) + // Standard Error: 19_046 + .saturating_add(Weight::from_parts(42_073_587, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 39]`. + fn on_trade_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 42_270_000 picoseconds. + Weight::from_parts(43_041_778, 6190) + // Standard Error: 4_124 + .saturating_add(Weight::from_parts(473_361, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 39]`. + fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 41_998_000 picoseconds. + Weight::from_parts(43_012_625, 6190) + // Standard Error: 4_043 + .saturating_add(Weight::from_parts(465_619, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `EmaOracle::Oracles` (r:2 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn get_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `1546` + // Estimated: `6328` + // Minimum execution time: 33_028_000 picoseconds. + Weight::from_parts(33_473_000, 6328) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + fn update_bifrost_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_085_000 picoseconds. + Weight::from_parts(60_600_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + fn set_external_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_077_000 picoseconds. + Weight::from_parts(60_857_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:0) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn set_external_oracle_by_ids() -> Weight { + // Proof Size summary in bytes: + // Measured: `2351` + // Estimated: `3590` + // Minimum execution time: 43_037_000 picoseconds. + Weight::from_parts(43_696_000, 3590) + .saturating_add(T::DbWeight::get().reads(4_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn register_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1220` + // Estimated: `3481` + // Minimum execution time: 26_219_000 picoseconds. + Weight::from_parts(27_009_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 40]`. + fn remove_external_source(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1296 + n * (61 ±0)` + // Estimated: `3481 + n * (2547 ±0)` + // Minimum execution time: 32_413_000 picoseconds. + Weight::from_parts(35_262_223, 3481) + // Standard Error: 8_755 + .saturating_add(Weight::from_parts(1_340_680, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn add_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 31_119_000 picoseconds. + Weight::from_parts(31_711_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn remove_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1295` + // Estimated: `3481` + // Minimum execution time: 31_525_000 picoseconds. + Weight::from_parts(32_028_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file From bd078381fbecd144944f4a9c112b63c5d4f652b5 Mon Sep 17 00:00:00 2001 From: dmoka Date: Tue, 14 Apr 2026 15:21:46 +0200 Subject: [PATCH 37/43] update weights --- .../hydradx/src/weights/pallet-ema-oracle.rs | 253 ------------------ .../hydradx/src/weights/pallet_ema_oracle.rs | 101 +++---- 2 files changed, 54 insertions(+), 300 deletions(-) delete mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs deleted file mode 100644 index 8e011bc61a..0000000000 --- a/runtime/hydradx/src/weights/pallet-ema-oracle.rs +++ /dev/null @@ -1,253 +0,0 @@ -// This file is part of HydraDX. - -// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -//! Autogenerated weights for `pallet_ema_oracle` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// ./bin/hydradx -// benchmark -// pallet -// --wasm-execution=compiled -// --pallet -// pallet-ema-oracle -// --extrinsic -// * -// --heap-pages -// 4096 -// --steps -// 50 -// --repeat -// 20 -// --template -// scripts/pallet-weight-template.hbs -// --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs -// --quiet - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; -use crate::*; - -/// Weights for `pallet_ema_oracle`. -pub struct WeightInfo(PhantomData); - -/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. -pub struct HydraWeight(PhantomData); -impl pallet_ema_oracle::WeightInfo for HydraWeight { - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn add_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1872` - // Estimated: `2126` - // Minimum execution time: 29_313_000 picoseconds. - Weight::from_parts(29_812_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Oracles` (r:0 w:3) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn remove_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1888` - // Estimated: `2126` - // Minimum execution time: 46_644_000 picoseconds. - Weight::from_parts(47_208_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - fn on_finalize_no_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `0` - // Minimum execution time: 4_453_000 picoseconds. - Weight::from_parts(4_587_000, 0) - } - /// Storage: `EmaOracle::Oracles` (r:30 w:30) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 10]`. - fn on_finalize_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1206 + b * (890 ±0)` - // Estimated: `990 + b * (8007 ±0)` - // Minimum execution time: 72_103_000 picoseconds. - Weight::from_parts(32_369_574, 990) - // Standard Error: 19_046 - .saturating_add(Weight::from_parts(42_073_587, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 39]`. - fn on_trade_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3719 + b * (172 ±0)` - // Estimated: `6190` - // Minimum execution time: 42_270_000 picoseconds. - Weight::from_parts(43_041_778, 6190) - // Standard Error: 4_124 - .saturating_add(Weight::from_parts(473_361, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 39]`. - fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3719 + b * (172 ±0)` - // Estimated: `6190` - // Minimum execution time: 41_998_000 picoseconds. - Weight::from_parts(43_012_625, 6190) - // Standard Error: 4_043 - .saturating_add(Weight::from_parts(465_619, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `EmaOracle::Oracles` (r:2 w:0) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn get_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `1546` - // Estimated: `6328` - // Minimum execution time: 33_028_000 picoseconds. - Weight::from_parts(33_473_000, 6328) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - fn update_bifrost_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_085_000 picoseconds. - Weight::from_parts(60_600_000, 6190) - .saturating_add(T::DbWeight::get().reads(6_u64)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - fn set_external_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_077_000 picoseconds. - Weight::from_parts(60_857_000, 6190) - .saturating_add(T::DbWeight::get().reads(6_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:1 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:0) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn set_external_oracle_by_ids() -> Weight { - // Proof Size summary in bytes: - // Measured: `2351` - // Estimated: `3590` - // Minimum execution time: 43_037_000 picoseconds. - Weight::from_parts(43_696_000, 3590) - .saturating_add(T::DbWeight::get().reads(4_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - fn register_external_source() -> Weight { - // Proof Size summary in bytes: - // Measured: `1220` - // Estimated: `3481` - // Minimum execution time: 26_219_000 picoseconds. - Weight::from_parts(27_009_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 40]`. - fn remove_external_source(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1296 + n * (61 ±0)` - // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_413_000 picoseconds. - Weight::from_parts(35_262_223, 3481) - // Standard Error: 8_755 - .saturating_add(Weight::from_parts(1_340_680, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn add_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `3481` - // Minimum execution time: 31_119_000 picoseconds. - Weight::from_parts(31_711_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn remove_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1295` - // Estimated: `3481` - // Minimum execution time: 31_525_000 picoseconds. - Weight::from_parts(32_028_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index ac165d6fad..8e011bc61a 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -21,11 +21,11 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 //! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `Mac.chello.hu`, CPU: `` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// ./target/release/hydradx +// ./bin/hydradx // benchmark // pallet // --wasm-execution=compiled @@ -42,7 +42,7 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet_ema_oracle.rs +// runtime/hydradx/src/weights/pallet-ema-oracle.rs // --quiet #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,8 +66,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 2126) + // Minimum execution time: 29_313_000 picoseconds. + Weight::from_parts(29_812_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,8 +79,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `2126` - // Minimum execution time: 30_000_000 picoseconds. - Weight::from_parts(31_000_000, 2126) + // Minimum execution time: 46_644_000 picoseconds. + Weight::from_parts(47_208_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -88,8 +88,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `742` // Estimated: `0` - // Minimum execution time: 2_000_000 picoseconds. - Weight::from_parts(3_000_000, 0) + // Minimum execution time: 4_453_000 picoseconds. + Weight::from_parts(4_587_000, 0) } /// Storage: `EmaOracle::Oracles` (r:30 w:30) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) @@ -98,10 +98,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1206 + b * (890 ±0)` // Estimated: `990 + b * (8007 ±0)` - // Minimum execution time: 43_000_000 picoseconds. - Weight::from_parts(12_716_946, 990) - // Standard Error: 151_691 - .saturating_add(Weight::from_parts(30_142_042, 0).saturating_mul(b.into())) + // Minimum execution time: 72_103_000 picoseconds. + Weight::from_parts(32_369_574, 990) + // Standard Error: 19_046 + .saturating_add(Weight::from_parts(42_073_587, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) @@ -113,10 +113,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `3719 + b * (172 ±0)` // Estimated: `6190` - // Minimum execution time: 26_000_000 picoseconds. - Weight::from_parts(33_575_563, 6190) - // Standard Error: 18_923 - .saturating_add(Weight::from_parts(303_915, 0).saturating_mul(b.into())) + // Minimum execution time: 42_270_000 picoseconds. + Weight::from_parts(43_041_778, 6190) + // Standard Error: 4_124 + .saturating_add(Weight::from_parts(473_361, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `AssetRegistry::Assets` (r:2 w:0) @@ -126,10 +126,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `3719 + b * (172 ±0)` // Estimated: `6190` - // Minimum execution time: 25_000_000 picoseconds. - Weight::from_parts(27_108_439, 6190) - // Standard Error: 5_768 - .saturating_add(Weight::from_parts(342_190, 0).saturating_mul(b.into())) + // Minimum execution time: 41_998_000 picoseconds. + Weight::from_parts(43_012_625, 6190) + // Standard Error: 4_043 + .saturating_add(Weight::from_parts(465_619, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::Oracles` (r:2 w:0) @@ -138,16 +138,16 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 6328) + // Minimum execution time: 33_028_000 picoseconds. + Weight::from_parts(33_473_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) @@ -156,16 +156,16 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(36_000_000, 6190) + // Minimum execution time: 60_085_000 picoseconds. + Weight::from_parts(60_600_000, 6190) .saturating_add(T::DbWeight::get().reads(6_u64)) } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) /// Storage: `AssetRegistry::Assets` (r:2 w:0) @@ -174,18 +174,25 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(36_000_000, 6190) + // Minimum execution time: 60_077_000 picoseconds. + Weight::from_parts(60_857_000, 6190) .saturating_add(T::DbWeight::get().reads(6_u64)) } - + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:0) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) fn set_external_oracle_by_ids() -> Weight { // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 36_000_000 picoseconds. - Weight::from_parts(36_000_000, 6190) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `2351` + // Estimated: `3590` + // Minimum execution time: 43_037_000 picoseconds. + Weight::from_parts(43_696_000, 3590) + .saturating_add(T::DbWeight::get().reads(4_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) @@ -193,8 +200,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 15_000_000 picoseconds. - Weight::from_parts(16_000_000, 3481) + // Minimum execution time: 26_219_000 picoseconds. + Weight::from_parts(27_009_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -207,10 +214,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1296 + n * (61 ±0)` // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 19_000_000 picoseconds. - Weight::from_parts(19_039_044, 3481) - // Standard Error: 4_032 - .saturating_add(Weight::from_parts(992_979, 0).saturating_mul(n.into())) + // Minimum execution time: 32_413_000 picoseconds. + Weight::from_parts(35_262_223, 3481) + // Standard Error: 8_755 + .saturating_add(Weight::from_parts(1_340_680, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) @@ -225,8 +232,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(18_000_000, 3481) + // Minimum execution time: 31_119_000 picoseconds. + Weight::from_parts(31_711_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -238,8 +245,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 18_000_000 picoseconds. - Weight::from_parts(19_000_000, 3481) + // Minimum execution time: 31_525_000 picoseconds. + Weight::from_parts(32_028_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } From afa87bce3c0e444e65df2badb60676d898e1d578 Mon Sep 17 00:00:00 2001 From: dmoka Date: Wed, 15 Apr 2026 12:01:14 +0200 Subject: [PATCH 38/43] add missing oracle removements when we remove resource --- pallets/ema-oracle/src/lib.rs | 5 +- .../ema-oracle/src/tests/external_oracle.rs | 354 +++++++++++++++++- .../hydradx/src/benchmarking/ema_oracle.rs | 28 +- 3 files changed, 380 insertions(+), 7 deletions(-) diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 0a5ffac236..9736623e2f 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -494,7 +494,8 @@ pub mod pallet { Ok(()) } - /// Remove an external oracle source and all its per-pair authorizations. + /// Remove an external oracle source, its per-pair authorizations, and ALL oracle data it + /// ever wrote (both committed `Oracles` rows and any in-flight `Accumulator` entries). /// /// Parameters: /// - `origin`: `ExternalOracleOrigin` @@ -508,6 +509,8 @@ pub mod pallet { ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); ExternalSources::::remove(source); let _ = AuthorizedAccounts::::clear_prefix((source,), u32::MAX, None); + let _ = Oracles::::clear_prefix((source,), u32::MAX, None); + Accumulator::::mutate(|acc| acc.retain(|(s, _), _| *s != source)); Self::deposit_event(Event::ExternalSourceRemoved { source }); Ok(()) } diff --git a/pallets/ema-oracle/src/tests/external_oracle.rs b/pallets/ema-oracle/src/tests/external_oracle.rs index 6c84e83945..33c04bbb4a 100644 --- a/pallets/ema-oracle/src/tests/external_oracle.rs +++ b/pallets/ema-oracle/src/tests/external_oracle.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::mock::{expect_events, EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ALICE, BOB}; +use super::mock::{expect_events, EmaOracle, ExtBuilder, RuntimeOrigin, System, Test, ACA, ALICE, BOB, DOT, HDX}; use super::SOURCE; -use crate::pallet::{AuthorizedAccounts, ExternalSources}; +use crate::pallet::{AuthorizedAccounts, ExternalSources, Oracles}; use crate::*; use frame_support::{assert_noop, assert_ok}; @@ -132,6 +132,356 @@ fn remove_external_source_clears_all_pair_authorizations() { }); } +#[test] +fn remove_external_source_clears_oracle_data_and_accumulator() { + new_test_ext().execute_with(|| { + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + HDX_DOT_PAIR, + ALICE + )); + + System::set_block_number(3); + assert_ok!(EmaOracle::set_external_oracle( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + Box::new(hdx_location()), + Box::new(dot_location()), + (100, 99), + )); + + // In-flight accumulator entry exists pre-finalize. + let ordered = ordered_pair(HDX_DOT_PAIR.0, HDX_DOT_PAIR.1); + assert!(Accumulator::::get().contains_key(&(EXTERNAL_SOURCE, ordered))); + + // Flush to persistent Oracles via on_finalize. + EmaOracle::on_finalize(3); + System::set_block_number(4); + let stored_before: Vec<_> = Oracles::::iter_prefix((EXTERNAL_SOURCE,)).collect(); + assert!(!stored_before.is_empty(), "Oracles row must exist pre-removal"); + + // Also seed an in-flight accumulator entry for a different pair in the new block so we + // exercise the Accumulator::retain path too. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (1, 2), + ALICE + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + 1, + 2, + (123, 456), + )); + assert!(Accumulator::::get().contains_key(&(EXTERNAL_SOURCE, ordered_pair(1, 2)))); + + assert_ok!(EmaOracle::remove_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + // 1. Source registration gone. + assert!(!ExternalSources::::contains_key(EXTERNAL_SOURCE)); + // 2. Authorizations gone (existing coverage, re-asserted here for integration). + assert!(!AuthorizedAccounts::::contains_key(( + EXTERNAL_SOURCE, + ordered, + ALICE + ))); + // 3. Committed Oracles rows gone — this is the new guarantee. + assert_eq!( + Oracles::::iter_prefix((EXTERNAL_SOURCE,)).count(), + 0, + "committed Oracles rows must be cleared across every supported period" + ); + // 4. In-flight accumulator entries for this source also dropped. + let acc = Accumulator::::get(); + assert!( + !acc.keys().any(|(s, _)| *s == EXTERNAL_SOURCE), + "no accumulator entries for the removed source may survive" + ); + }); +} + +#[test] +fn remove_external_source_only_touches_the_targeted_source() { + new_test_ext().execute_with(|| { + const THIRD_SOURCE: Source = *b"third___"; + const NEIGHBOR_SOURCE: Source = *b"externaL"; // byte-adjacent to EXTERNAL_SOURCE + + let pair_hd = ordered_pair(HDX, DOT); + let pair_ha = ordered_pair(HDX, ACA); + let pair_da = ordered_pair(DOT, ACA); + let pair_auth_only = ordered_pair(4, 5); + + // ─── AMM SOURCE: 2 whitelisted pairs ─── + assert_ok!(EmaOracle::add_oracle(RuntimeOrigin::root(), SOURCE, (HDX, DOT))); + assert_ok!(EmaOracle::add_oracle(RuntimeOrigin::root(), SOURCE, (HDX, ACA))); + + // ─── EXTERNAL_SOURCE: 3 pairs with data + 1 pair auth-only ─── + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (HDX, DOT), + ALICE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (HDX, ACA), + ALICE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (DOT, ACA), + BOB + )); + // Auth-only — never submitted to; verifies clearing works for pairs without Oracles rows. + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + EXTERNAL_SOURCE, + (4, 5), + ALICE + )); + + // ─── ANOTHER_SOURCE: 2 pairs overlapping with EXTERNAL_SOURCE pairs ─── + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + ANOTHER_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + ANOTHER_SOURCE, + (HDX, DOT), + BOB + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + ANOTHER_SOURCE, + (DOT, ACA), + BOB + )); + + // ─── THIRD_SOURCE: empty (registered but no auths, no data) ─── + assert_ok!(EmaOracle::register_external_source(RuntimeOrigin::root(), THIRD_SOURCE)); + + // ─── NEIGHBOR_SOURCE: byte-adjacent to EXTERNAL_SOURCE, on shared pair ─── + assert_ok!(EmaOracle::register_external_source( + RuntimeOrigin::root(), + NEIGHBOR_SOURCE + )); + assert_ok!(EmaOracle::add_authorized_account( + RuntimeOrigin::root(), + NEIGHBOR_SOURCE, + (HDX, DOT), + ALICE + )); + + // ─── Block 3: write across every (source, pair) with data ─── + System::set_block_number(3); + + // AMM writes (two pairs). + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + HDX, + DOT, + 1_000, + 500, + 2_000, + 1_000, + Price::new(2_000, 1_000), + Some(2_000_u128), + )); + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + HDX, + ACA, + 800, + 400, + 2_000, + 1_000, + Price::new(2_000, 1_000), + Some(2_000_u128), + )); + // EXTERNAL_SOURCE writes on 3 pairs (auth-only pair deliberately not written). + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX, + DOT, + (3_000, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX, + ACA, + (3_500, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(BOB), + EXTERNAL_SOURCE, + DOT, + ACA, + (4_000, 1_000), + )); + // ANOTHER_SOURCE writes on 2 pairs. + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(BOB), + ANOTHER_SOURCE, + HDX, + DOT, + (5_000, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(BOB), + ANOTHER_SOURCE, + DOT, + ACA, + (5_500, 1_000), + )); + // NEIGHBOR_SOURCE write on shared pair. + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + NEIGHBOR_SOURCE, + HDX, + DOT, + (7_000, 1_000), + )); + + // Commit to Oracles for every supported period. + EmaOracle::on_finalize(3); + System::set_block_number(4); + + // Snapshot the per-source row counts so post-removal assertions can compare exactly. + let rows = |s: Source| Oracles::::iter_prefix((s,)).count(); + let source_rows_pre = rows(SOURCE); + let another_rows_pre = rows(ANOTHER_SOURCE); + let neighbor_rows_pre = rows(NEIGHBOR_SOURCE); + assert!(source_rows_pre >= 2, "AMM: expect ≥2 pairs × N periods rows"); + assert!(rows(EXTERNAL_SOURCE) >= 3, "EXTERNAL_SOURCE: 3 data pairs"); + assert!(another_rows_pre >= 2, "ANOTHER_SOURCE: 2 pairs"); + assert!(neighbor_rows_pre >= 1, "NEIGHBOR_SOURCE: 1 pair"); + + // ─── Seed in-flight Accumulator entries across all sources in the new block ─── + assert_ok!(OnActivityHandler::::on_trade( + SOURCE, + HDX, + DOT, + 2_000, + 1_000, + 4_000, + 2_000, + Price::new(2_000, 1_000), + Some(2_000_u128), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + EXTERNAL_SOURCE, + HDX, + DOT, + (9_000, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(BOB), + EXTERNAL_SOURCE, + DOT, + ACA, + (9_100, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(BOB), + ANOTHER_SOURCE, + HDX, + DOT, + (11_000, 1_000), + )); + assert_ok!(EmaOracle::set_external_oracle_by_ids( + RuntimeOrigin::signed(ALICE), + NEIGHBOR_SOURCE, + HDX, + DOT, + (13_000, 1_000), + )); + let acc_before = Accumulator::::get(); + assert!(acc_before.contains_key(&(SOURCE, pair_hd))); + assert!(acc_before.contains_key(&(EXTERNAL_SOURCE, pair_hd))); + assert!(acc_before.contains_key(&(EXTERNAL_SOURCE, pair_da))); + assert!(acc_before.contains_key(&(ANOTHER_SOURCE, pair_hd))); + assert!(acc_before.contains_key(&(NEIGHBOR_SOURCE, pair_hd))); + + // ─── ACT ─── + assert_ok!(EmaOracle::remove_external_source( + RuntimeOrigin::root(), + EXTERNAL_SOURCE + )); + + // ─── ASSERT: EXTERNAL_SOURCE fully wiped across every pair ─── + assert!(!ExternalSources::::contains_key(EXTERNAL_SOURCE)); + assert_eq!( + AuthorizedAccounts::::iter_prefix((EXTERNAL_SOURCE,)).count(), + 0, + "all 4 authorizations (incl. the data-less (4,5)) must be cleared" + ); + assert_eq!( + Oracles::::iter_prefix((EXTERNAL_SOURCE,)).count(), + 0, + "all 3 data pairs' Oracles rows must be cleared across every period" + ); + let acc_after = Accumulator::::get(); + assert!(!acc_after.contains_key(&(EXTERNAL_SOURCE, pair_hd))); + assert!(!acc_after.contains_key(&(EXTERNAL_SOURCE, pair_ha))); + assert!(!acc_after.contains_key(&(EXTERNAL_SOURCE, pair_da))); + assert!(!acc_after.contains_key(&(EXTERNAL_SOURCE, pair_auth_only))); + + // ─── ASSERT: AMM SOURCE untouched (both pairs) ─── + assert!(WhitelistedAssets::::get().contains(&(SOURCE, (HDX, DOT)))); + assert!(WhitelistedAssets::::get().contains(&(SOURCE, (HDX, ACA)))); + assert_eq!(rows(SOURCE), source_rows_pre, "AMM Oracles row count must be unchanged"); + assert!(acc_after.contains_key(&(SOURCE, pair_hd))); + + // ─── ASSERT: ANOTHER_SOURCE untouched (both overlapping pairs) ─── + assert!(ExternalSources::::contains_key(ANOTHER_SOURCE)); + assert!(AuthorizedAccounts::::contains_key((ANOTHER_SOURCE, pair_hd, BOB))); + assert!(AuthorizedAccounts::::contains_key((ANOTHER_SOURCE, pair_da, BOB))); + assert_eq!( + rows(ANOTHER_SOURCE), + another_rows_pre, + "ANOTHER_SOURCE Oracles row count must be unchanged" + ); + assert!(acc_after.contains_key(&(ANOTHER_SOURCE, pair_hd))); + + // ─── ASSERT: NEIGHBOR_SOURCE (byte-adjacent) untouched ─── + assert!(ExternalSources::::contains_key(NEIGHBOR_SOURCE)); + assert!(AuthorizedAccounts::::contains_key(( + NEIGHBOR_SOURCE, + pair_hd, + ALICE + ))); + assert_eq!( + rows(NEIGHBOR_SOURCE), + neighbor_rows_pre, + "byte-adjacent NEIGHBOR_SOURCE must be untouched" + ); + assert!(acc_after.contains_key(&(NEIGHBOR_SOURCE, pair_hd))); + + // ─── ASSERT: THIRD_SOURCE (empty registration) untouched ─── + assert!(ExternalSources::::contains_key(THIRD_SOURCE)); + }); +} + #[test] fn remove_nonexistent_source_fails() { new_test_ext().execute_with(|| { diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index fdbb9d055b..f40975bccf 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -570,21 +570,41 @@ runtime_benchmarks! { assert!(pallet_ema_oracle::ExternalSources::::contains_key(source)); } - // Worst-case: remove a source that has `n` (pair, account) authorization entries — all must - // be cleared by `clear_prefix`. + // Worst-case: remove a source that has `n` (pair, account) authorization entries — each must + // be dropped by `clear_prefix` on both `AuthorizedAccounts` and `Oracles`. For every + // authorized pair we also pre-commit one full set of `Oracles` rows (one per supported + // period), so the benchmark measures the combined cost of the two clear_prefix calls plus + // the accumulator retain. remove_external_source { let n in 0 .. pallet_ema_oracle::MAX_AUTHORIZED_ENTRIES_PER_SOURCE; let source: Source = *b"newsrcxx"; EmaOracle::register_external_source(RawOrigin::Root.into(), source).expect("error when registering external source"); + let periods = ::SupportedPeriods::get(); + let seed_entry: OracleEntry> = OracleEntry::new( + EmaPrice::new(1u128, 1u128), + Volume::default(), + Liquidity::default(), + None, + 0u32.into(), + ); for i in 0..n { let account: AccountId = frame_benchmarking::account("authorized", i, 0); - // Spread entries across distinct pairs so clear_prefix must actually delete n entries. - let pair = (i, i + 1); + // Spread entries across distinct pairs so clear_prefix must actually delete n entries + // in each storage. Using (i, i+n+1) keeps pairs disjoint and non-degenerate. + let pair = (i, i + n + 1); + let ordered = ordered_pair(pair.0, pair.1); EmaOracle::add_authorized_account(RawOrigin::Root.into(), source, pair, account).expect("error when adding authorized account"); + // Pre-populate `Oracles` rows for every supported period so the NEW cleanup path has + // the worst-case number of rows to clear. + for period in periods.iter().copied() { + pallet_ema_oracle::Oracles::::insert((source, ordered, period), (seed_entry.clone(), 0u32)); + } } }: _(RawOrigin::Root, source) verify { assert!(!pallet_ema_oracle::ExternalSources::::contains_key(source)); + // All per-source Oracles rows must be gone. + assert_eq!(pallet_ema_oracle::Oracles::::iter_prefix((source,)).count(), 0); } add_authorized_account { From 423ebaded9eb13a1b619ac3a90f06378774b907a Mon Sep 17 00:00:00 2001 From: dmoka Date: Thu, 16 Apr 2026 08:04:44 +0200 Subject: [PATCH 39/43] fix script --- scripts/init-testnet/chopsticks/fakeSignatureBifrost.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js index 47a3535929..c4289da131 100644 --- a/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js +++ b/scripts/init-testnet/chopsticks/fakeSignatureBifrost.js @@ -136,9 +136,10 @@ const main = async () => { const extSrc = await api.query.emaOracle.externalSources(BIFROST_SOURCE); console.log("ExternalSources[bifrosto]:", extSrc.isSome ? "PRESENT" : "MISSING"); - // 2. AuthorizedAccounts[BIFROST_SOURCE][bifrost_sovereign] must exist. - const auth = await api.query.emaOracle.authorizedAccounts(BIFROST_SOURCE, BIFROST_SOVEREIGN); - console.log(`AuthorizedAccounts[bifrosto][${BIFROST_SOVEREIGN}]:`, auth.isSome ? "PRESENT" : "MISSING"); + // 2. AuthorizedAccounts[(BIFROST_SOURCE, orderedPair, bifrost_sovereign)] must exist. + // NMap with 3 keys: (Source, (AssetId, AssetId), AccountId). + const auth = await api.query.emaOracle.authorizedAccounts(BIFROST_SOURCE, orderedPair, BIFROST_SOVEREIGN); + console.log(`AuthorizedAccounts[bifrosto, (${orderedPair.join(',')}), ${BIFROST_SOVEREIGN}]:`, auth.isSome ? "PRESENT" : "MISSING"); // 3. The extrinsic should have pushed an entry into the accumulator, and // `on_finalize` should have flushed it into LastBlock with the current From 7c87d6d3e393f2195f5da863e5f68aa0037ace79 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 17 Apr 2026 07:50:08 +0200 Subject: [PATCH 40/43] make clippy happy --- runtime/hydradx/src/benchmarking/ema_oracle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/hydradx/src/benchmarking/ema_oracle.rs b/runtime/hydradx/src/benchmarking/ema_oracle.rs index f40975bccf..64586e0d7b 100644 --- a/runtime/hydradx/src/benchmarking/ema_oracle.rs +++ b/runtime/hydradx/src/benchmarking/ema_oracle.rs @@ -585,7 +585,7 @@ runtime_benchmarks! { Volume::default(), Liquidity::default(), None, - 0u32.into(), + 0u32, ); for i in 0..n { let account: AccountId = frame_benchmarking::account("authorized", i, 0); From 5a7ae929089800cc53f6ca0ca50da09cd29d49a8 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 17 Apr 2026 08:57:13 +0000 Subject: [PATCH 41/43] Update pallets weights [ignore benchmarks] --- .../hydradx/src/weights/pallet-ema-oracle.rs | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs new file mode 100644 index 0000000000..204bbcd0e1 --- /dev/null +++ b/runtime/hydradx/src/weights/pallet-ema-oracle.rs @@ -0,0 +1,255 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//! Autogenerated weights for `pallet_ema_oracle` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 +//! DATE: 2026-04-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` + +// Executed Command: +// ./bin/hydradx +// benchmark +// pallet +// --wasm-execution=compiled +// --pallet +// pallet-ema-oracle +// --extrinsic +// * +// --heap-pages +// 4096 +// --steps +// 50 +// --repeat +// 20 +// --template +// scripts/pallet-weight-template.hbs +// --output +// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// --quiet + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; +use crate::*; + +/// Weights for `pallet_ema_oracle`. +pub struct WeightInfo(PhantomData); + +/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl pallet_ema_oracle::WeightInfo for HydraWeight { + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn add_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1872` + // Estimated: `2126` + // Minimum execution time: 29_147_000 picoseconds. + Weight::from_parts(29_556_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:0 w:3) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn remove_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `1888` + // Estimated: `2126` + // Minimum execution time: 46_471_000 picoseconds. + Weight::from_parts(47_285_000, 2126) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + fn on_finalize_no_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `742` + // Estimated: `0` + // Minimum execution time: 4_325_000 picoseconds. + Weight::from_parts(4_499_000, 0) + } + /// Storage: `EmaOracle::Oracles` (r:30 w:30) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 10]`. + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1206 + b * (890 ±0)` + // Estimated: `990 + b * (8007 ±0)` + // Minimum execution time: 72_570_000 picoseconds. + Weight::from_parts(32_479_579, 990) + // Standard Error: 19_402 + .saturating_add(Weight::from_parts(42_186_697, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 39]`. + fn on_trade_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 42_568_000 picoseconds. + Weight::from_parts(43_759_780, 6190) + // Standard Error: 4_417 + .saturating_add(Weight::from_parts(556_769, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// The range of component `b` is `[1, 39]`. + fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3719 + b * (172 ±0)` + // Estimated: `6190` + // Minimum execution time: 42_786_000 picoseconds. + Weight::from_parts(43_613_978, 6190) + // Standard Error: 4_034 + .saturating_add(Weight::from_parts(558_459, 0).saturating_mul(b.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `EmaOracle::Oracles` (r:2 w:0) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + fn get_entry() -> Weight { + // Proof Size summary in bytes: + // Measured: `1546` + // Estimated: `6328` + // Minimum execution time: 32_984_000 picoseconds. + Weight::from_parts(33_460_000, 6328) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + fn update_bifrost_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_048_000 picoseconds. + Weight::from_parts(60_606_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } + /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) + /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) + /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:2 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + fn set_external_oracle() -> Weight { + // Proof Size summary in bytes: + // Measured: `2055` + // Estimated: `6190` + // Minimum execution time: 60_034_000 picoseconds. + Weight::from_parts(60_678_000, 6190) + .saturating_add(T::DbWeight::get().reads(6_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `AssetRegistry::Assets` (r:1 w:0) + /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:0) + /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) + fn set_external_oracle_by_ids() -> Weight { + // Proof Size summary in bytes: + // Measured: `2351` + // Estimated: `3590` + // Minimum execution time: 43_137_000 picoseconds. + Weight::from_parts(43_840_000, 3590) + .saturating_add(T::DbWeight::get().reads(4_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + fn register_external_source() -> Weight { + // Proof Size summary in bytes: + // Measured: `1220` + // Estimated: `3481` + // Minimum execution time: 26_002_000 picoseconds. + Weight::from_parts(26_706_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:120 w:120) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) + /// The range of component `n` is `[0, 40]`. + fn remove_external_source(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1331 + n * (216 ±0)` + // Estimated: `3481 + n * (8007 ±0)` + // Minimum execution time: 38_431_000 picoseconds. + Weight::from_parts(45_145_462, 3481) + // Standard Error: 8_749 + .saturating_add(Weight::from_parts(4_815_890, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().writes(1_u64)) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(n.into())) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn add_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `3481` + // Minimum execution time: 30_632_000 picoseconds. + Weight::from_parts(31_148_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) + /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) + /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + fn remove_authorized_account() -> Weight { + // Proof Size summary in bytes: + // Measured: `1295` + // Estimated: `3481` + // Minimum execution time: 30_637_000 picoseconds. + Weight::from_parts(30_912_000, 3481) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} \ No newline at end of file From 206c3f83871bd7fff30435db72ea9712897b6144 Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 17 Apr 2026 11:22:09 +0200 Subject: [PATCH 42/43] supliy authorized origin for calling admin functions --- pallets/dca/src/tests/mock.rs | 1 - pallets/ema-oracle/src/lib.rs | 22 +++++++++---------- pallets/ema-oracle/src/tests/mock.rs | 1 - .../src/tests/mock.rs | 1 - runtime/hydradx/src/assets.rs | 3 +-- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/pallets/dca/src/tests/mock.rs b/pallets/dca/src/tests/mock.rs index c5585bdb7a..c9ad9dbafd 100644 --- a/pallets/dca/src/tests/mock.rs +++ b/pallets/dca/src/tests/mock.rs @@ -143,7 +143,6 @@ parameter_types! { impl pallet_ema_oracle::Config for Test { type AuthorityOrigin = EnsureRoot; - type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; diff --git a/pallets/ema-oracle/src/lib.rs b/pallets/ema-oracle/src/lib.rs index 9736623e2f..08a5abe05f 100644 --- a/pallets/ema-oracle/src/lib.rs +++ b/pallets/ema-oracle/src/lib.rs @@ -143,12 +143,10 @@ 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. + /// Origin that can enable oracle for assets that would be rejected by `OracleWhitelist` otherwise + /// and manage external oracle sources and authorized accounts. type AuthorityOrigin: EnsureOrigin; - /// Origin that can manage external oracle sources and authorized accounts. - type ExternalOracleOrigin: EnsureOrigin; - /// Provider for the current block number. type BlockNumberProvider: BlockNumberProvider>; @@ -477,14 +475,14 @@ pub mod pallet { /// Register a new external oracle source. /// /// Parameters: - /// - `origin`: `ExternalOracleOrigin` + /// - `origin`: `AuthorityOrigin` /// - `source`: 8-byte source identifier to register /// /// Emits `ExternalSourceRegistered` event when successful. #[pallet::call_index(3)] #[pallet::weight(::WeightInfo::register_external_source())] pub fn register_external_source(origin: OriginFor, source: Source) -> DispatchResult { - T::ExternalOracleOrigin::ensure_origin(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; ensure!( !ExternalSources::::contains_key(source), Error::::SourceAlreadyRegistered @@ -498,14 +496,14 @@ pub mod pallet { /// ever wrote (both committed `Oracles` rows and any in-flight `Accumulator` entries). /// /// Parameters: - /// - `origin`: `ExternalOracleOrigin` + /// - `origin`: `AuthorityOrigin` /// - `source`: source identifier to remove /// /// Emits `ExternalSourceRemoved` event when successful. #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::remove_external_source(MAX_AUTHORIZED_ENTRIES_PER_SOURCE))] pub fn remove_external_source(origin: OriginFor, source: Source) -> DispatchResult { - T::ExternalOracleOrigin::ensure_origin(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); ExternalSources::::remove(source); let _ = AuthorizedAccounts::::clear_prefix((source,), u32::MAX, None); @@ -521,7 +519,7 @@ pub mod pallet { /// pairs it was explicitly granted, limiting DDoS blast radius. /// /// Parameters: - /// - `origin`: `ExternalOracleOrigin` + /// - `origin`: `AuthorityOrigin` /// - `source`: external source identifier (must already be registered) /// - `assets`: the asset pair to authorize — stored in ordered form /// - `account`: the account to authorize @@ -535,7 +533,7 @@ pub mod pallet { assets: (AssetId, AssetId), account: T::AccountId, ) -> DispatchResult { - T::ExternalOracleOrigin::ensure_origin(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); let pair = ordered_pair(assets.0, assets.1); AuthorizedAccounts::::insert((source, pair, &account), ()); @@ -546,7 +544,7 @@ pub mod pallet { /// Revoke oracle-update authorization for `account` on a specific `(source, pair)`. /// /// Parameters: - /// - `origin`: `ExternalOracleOrigin` + /// - `origin`: `AuthorityOrigin` /// - `source`: external source identifier (must already be registered) /// - `assets`: the asset pair to revoke — matched in ordered form /// - `account`: the account to revoke @@ -560,7 +558,7 @@ pub mod pallet { assets: (AssetId, AssetId), account: T::AccountId, ) -> DispatchResult { - T::ExternalOracleOrigin::ensure_origin(origin)?; + T::AuthorityOrigin::ensure_origin(origin)?; ensure!(ExternalSources::::contains_key(source), Error::::SourceNotFound); let pair = ordered_pair(assets.0, assets.1); AuthorizedAccounts::::remove((source, pair, &account)); diff --git a/pallets/ema-oracle/src/tests/mock.rs b/pallets/ema-oracle/src/tests/mock.rs index 3191d1957a..175b77bf2a 100644 --- a/pallets/ema-oracle/src/tests/mock.rs +++ b/pallets/ema-oracle/src/tests/mock.rs @@ -145,7 +145,6 @@ impl Contains for InternalSources { impl Config for Test { type AuthorityOrigin = EnsureRoot; - type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = System; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = OracleWhitelist; diff --git a/pallets/omnipool-liquidity-mining/src/tests/mock.rs b/pallets/omnipool-liquidity-mining/src/tests/mock.rs index d0459133d4..8c6f695d40 100644 --- a/pallets/omnipool-liquidity-mining/src/tests/mock.rs +++ b/pallets/omnipool-liquidity-mining/src/tests/mock.rs @@ -298,7 +298,6 @@ parameter_types! { impl pallet_ema_oracle::Config for Test { type AuthorityOrigin = EnsureRoot; - type ExternalOracleOrigin = EnsureRoot; type BlockNumberProvider = MockBlockNumberProvider; type SupportedPeriods = SupportedPeriods; type OracleWhitelist = Everything; diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index e2c58ef818..295f7d676f 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -659,8 +659,7 @@ where } impl pallet_ema_oracle::Config for Runtime { - type AuthorityOrigin = EitherOf, GeneralAdmin>; - type ExternalOracleOrigin = EitherOf, EitherOf>; + type AuthorityOrigin = EitherOf, EconomicParameters>; /// The definition of the oracle time periods currently assumes a 6 second block time. /// We use the parachain blocks anyway, because we want certain guarantees over how many blocks correspond /// to which smoothing factor. From 6a2c92ad2a45e027c3cfff3078a5d28bdee3430b Mon Sep 17 00:00:00 2001 From: dmoka Date: Fri, 17 Apr 2026 11:24:43 +0200 Subject: [PATCH 43/43] rebench weights --- .../hydradx/src/weights/pallet-ema-oracle.rs | 255 ------------------ .../hydradx/src/weights/pallet_ema_oracle.rs | 88 +++--- 2 files changed, 45 insertions(+), 298 deletions(-) delete mode 100644 runtime/hydradx/src/weights/pallet-ema-oracle.rs diff --git a/runtime/hydradx/src/weights/pallet-ema-oracle.rs b/runtime/hydradx/src/weights/pallet-ema-oracle.rs deleted file mode 100644 index 204bbcd0e1..0000000000 --- a/runtime/hydradx/src/weights/pallet-ema-oracle.rs +++ /dev/null @@ -1,255 +0,0 @@ -// This file is part of HydraDX. - -// Copyright (C) 2020-2024 Intergalactic, Limited (GIB). -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -//! Autogenerated weights for `pallet_ema_oracle` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// ./bin/hydradx -// benchmark -// pallet -// --wasm-execution=compiled -// --pallet -// pallet-ema-oracle -// --extrinsic -// * -// --heap-pages -// 4096 -// --steps -// 50 -// --repeat -// 20 -// --template -// scripts/pallet-weight-template.hbs -// --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs -// --quiet - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use core::marker::PhantomData; -use crate::*; - -/// Weights for `pallet_ema_oracle`. -pub struct WeightInfo(PhantomData); - -/// Weights for `pallet_ema_oracle` using the HydraDX node and recommended hardware. -pub struct HydraWeight(PhantomData); -impl pallet_ema_oracle::WeightInfo for HydraWeight { - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn add_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1872` - // Estimated: `2126` - // Minimum execution time: 29_147_000 picoseconds. - Weight::from_parts(29_556_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:1) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Oracles` (r:0 w:3) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn remove_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `1888` - // Estimated: `2126` - // Minimum execution time: 46_471_000 picoseconds. - Weight::from_parts(47_285_000, 2126) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) - } - fn on_finalize_no_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `742` - // Estimated: `0` - // Minimum execution time: 4_325_000 picoseconds. - Weight::from_parts(4_499_000, 0) - } - /// Storage: `EmaOracle::Oracles` (r:30 w:30) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 10]`. - fn on_finalize_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1206 + b * (890 ±0)` - // Estimated: `990 + b * (8007 ±0)` - // Minimum execution time: 72_570_000 picoseconds. - Weight::from_parts(32_479_579, 990) - // Standard Error: 19_402 - .saturating_add(Weight::from_parts(42_186_697, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) - .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 39]`. - fn on_trade_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3719 + b * (172 ±0)` - // Estimated: `6190` - // Minimum execution time: 42_568_000 picoseconds. - Weight::from_parts(43_759_780, 6190) - // Standard Error: 4_417 - .saturating_add(Weight::from_parts(556_769, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// The range of component `b` is `[1, 39]`. - fn on_liquidity_changed_multiple_tokens(b: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `3719 + b * (172 ±0)` - // Estimated: `6190` - // Minimum execution time: 42_786_000 picoseconds. - Weight::from_parts(43_613_978, 6190) - // Standard Error: 4_034 - .saturating_add(Weight::from_parts(558_459, 0).saturating_mul(b.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `EmaOracle::Oracles` (r:2 w:0) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - fn get_entry() -> Weight { - // Proof Size summary in bytes: - // Measured: `1546` - // Estimated: `6328` - // Minimum execution time: 32_984_000 picoseconds. - Weight::from_parts(33_460_000, 6328) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - fn update_bifrost_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_048_000 picoseconds. - Weight::from_parts(60_606_000, 6190) - .saturating_add(T::DbWeight::get().reads(6_u64)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::LocationAssets` (r:1 w:0) - /// Proof: `AssetRegistry::LocationAssets` (`max_values`: None, `max_size`: Some(622), added: 3097, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:2 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - fn set_external_oracle() -> Weight { - // Proof Size summary in bytes: - // Measured: `2055` - // Estimated: `6190` - // Minimum execution time: 60_034_000 picoseconds. - Weight::from_parts(60_678_000, 6190) - .saturating_add(T::DbWeight::get().reads(6_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:1 w:0) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `AssetRegistry::Assets` (r:1 w:0) - /// Proof: `AssetRegistry::Assets` (`max_values`: None, `max_size`: Some(125), added: 2600, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::WhitelistedAssets` (r:1 w:0) - /// Proof: `EmaOracle::WhitelistedAssets` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`) - fn set_external_oracle_by_ids() -> Weight { - // Proof Size summary in bytes: - // Measured: `2351` - // Estimated: `3590` - // Minimum execution time: 43_137_000 picoseconds. - Weight::from_parts(43_840_000, 3590) - .saturating_add(T::DbWeight::get().reads(4_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - fn register_external_source() -> Weight { - // Proof Size summary in bytes: - // Measured: `1220` - // Estimated: `3481` - // Minimum execution time: 26_002_000 picoseconds. - Weight::from_parts(26_706_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::Oracles` (r:120 w:120) - /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) - /// The range of component `n` is `[0, 40]`. - fn remove_external_source(n: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `1331 + n * (216 ±0)` - // Estimated: `3481 + n * (8007 ±0)` - // Minimum execution time: 38_431_000 picoseconds. - Weight::from_parts(45_145_462, 3481) - // Standard Error: 8_749 - .saturating_add(Weight::from_parts(4_815_890, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 8007).saturating_mul(n.into())) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn add_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1261` - // Estimated: `3481` - // Minimum execution time: 30_632_000 picoseconds. - Weight::from_parts(31_148_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) - /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) - /// Storage: `EmaOracle::AuthorizedAccounts` (r:0 w:1) - /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) - fn remove_authorized_account() -> Weight { - // Proof Size summary in bytes: - // Measured: `1295` - // Estimated: `3481` - // Minimum execution time: 30_637_000 picoseconds. - Weight::from_parts(30_912_000, 3481) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} \ No newline at end of file diff --git a/runtime/hydradx/src/weights/pallet_ema_oracle.rs b/runtime/hydradx/src/weights/pallet_ema_oracle.rs index 8e011bc61a..fef13c79a4 100644 --- a/runtime/hydradx/src/weights/pallet_ema_oracle.rs +++ b/runtime/hydradx/src/weights/pallet_ema_oracle.rs @@ -19,7 +19,7 @@ //! Autogenerated weights for `pallet_ema_oracle` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 49.1.0 -//! DATE: 2026-04-14, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2026-04-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` @@ -42,7 +42,7 @@ // --template // scripts/pallet-weight-template.hbs // --output -// runtime/hydradx/src/weights/pallet-ema-oracle.rs +// runtime/hydradx/src/weights/pallet_ema_oracle.rs // --quiet #![cfg_attr(rustfmt, rustfmt_skip)] @@ -66,8 +66,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1872` // Estimated: `2126` - // Minimum execution time: 29_313_000 picoseconds. - Weight::from_parts(29_812_000, 2126) + // Minimum execution time: 29_147_000 picoseconds. + Weight::from_parts(29_556_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -79,8 +79,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1888` // Estimated: `2126` - // Minimum execution time: 46_644_000 picoseconds. - Weight::from_parts(47_208_000, 2126) + // Minimum execution time: 46_471_000 picoseconds. + Weight::from_parts(47_285_000, 2126) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -88,8 +88,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `742` // Estimated: `0` - // Minimum execution time: 4_453_000 picoseconds. - Weight::from_parts(4_587_000, 0) + // Minimum execution time: 4_325_000 picoseconds. + Weight::from_parts(4_499_000, 0) } /// Storage: `EmaOracle::Oracles` (r:30 w:30) /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) @@ -98,10 +98,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1206 + b * (890 ±0)` // Estimated: `990 + b * (8007 ±0)` - // Minimum execution time: 72_103_000 picoseconds. - Weight::from_parts(32_369_574, 990) - // Standard Error: 19_046 - .saturating_add(Weight::from_parts(42_073_587, 0).saturating_mul(b.into())) + // Minimum execution time: 72_570_000 picoseconds. + Weight::from_parts(32_479_579, 990) + // Standard Error: 19_402 + .saturating_add(Weight::from_parts(42_186_697, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into()))) .saturating_add(Weight::from_parts(0, 8007).saturating_mul(b.into())) @@ -113,10 +113,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `3719 + b * (172 ±0)` // Estimated: `6190` - // Minimum execution time: 42_270_000 picoseconds. - Weight::from_parts(43_041_778, 6190) - // Standard Error: 4_124 - .saturating_add(Weight::from_parts(473_361, 0).saturating_mul(b.into())) + // Minimum execution time: 42_568_000 picoseconds. + Weight::from_parts(43_759_780, 6190) + // Standard Error: 4_417 + .saturating_add(Weight::from_parts(556_769, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `AssetRegistry::Assets` (r:2 w:0) @@ -126,10 +126,10 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `3719 + b * (172 ±0)` // Estimated: `6190` - // Minimum execution time: 41_998_000 picoseconds. - Weight::from_parts(43_012_625, 6190) - // Standard Error: 4_043 - .saturating_add(Weight::from_parts(465_619, 0).saturating_mul(b.into())) + // Minimum execution time: 42_786_000 picoseconds. + Weight::from_parts(43_613_978, 6190) + // Standard Error: 4_034 + .saturating_add(Weight::from_parts(558_459, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `EmaOracle::Oracles` (r:2 w:0) @@ -138,8 +138,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1546` // Estimated: `6328` - // Minimum execution time: 33_028_000 picoseconds. - Weight::from_parts(33_473_000, 6328) + // Minimum execution time: 32_984_000 picoseconds. + Weight::from_parts(33_460_000, 6328) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) @@ -156,8 +156,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 60_085_000 picoseconds. - Weight::from_parts(60_600_000, 6190) + // Minimum execution time: 60_048_000 picoseconds. + Weight::from_parts(60_606_000, 6190) .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) @@ -174,8 +174,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2055` // Estimated: `6190` - // Minimum execution time: 60_077_000 picoseconds. - Weight::from_parts(60_857_000, 6190) + // Minimum execution time: 60_034_000 picoseconds. + Weight::from_parts(60_678_000, 6190) .saturating_add(T::DbWeight::get().reads(6_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) @@ -190,8 +190,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `2351` // Estimated: `3590` - // Minimum execution time: 43_037_000 picoseconds. - Weight::from_parts(43_696_000, 3590) + // Minimum execution time: 43_137_000 picoseconds. + Weight::from_parts(43_840_000, 3590) .saturating_add(T::DbWeight::get().reads(4_u64)) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:1) @@ -200,8 +200,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1220` // Estimated: `3481` - // Minimum execution time: 26_219_000 picoseconds. - Weight::from_parts(27_009_000, 3481) + // Minimum execution time: 26_002_000 picoseconds. + Weight::from_parts(26_706_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -209,20 +209,22 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) /// Storage: `EmaOracle::AuthorizedAccounts` (r:40 w:40) /// Proof: `EmaOracle::AuthorizedAccounts` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`) + /// Storage: `EmaOracle::Oracles` (r:120 w:120) + /// Proof: `EmaOracle::Oracles` (`max_values`: None, `max_size`: Some(194), added: 2669, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 40]`. fn remove_external_source(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1296 + n * (61 ±0)` - // Estimated: `3481 + n * (2547 ±0)` - // Minimum execution time: 32_413_000 picoseconds. - Weight::from_parts(35_262_223, 3481) - // Standard Error: 8_755 - .saturating_add(Weight::from_parts(1_340_680, 0).saturating_mul(n.into())) + // Measured: `1331 + n * (216 ±0)` + // Estimated: `3481 + n * (8007 ±0)` + // Minimum execution time: 38_431_000 picoseconds. + Weight::from_parts(45_145_462, 3481) + // Standard Error: 8_749 + .saturating_add(Weight::from_parts(4_815_890, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) + .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2547).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(n.into()))) + .saturating_add(Weight::from_parts(0, 8007).saturating_mul(n.into())) } /// Storage: `EmaOracle::ExternalSources` (r:1 w:0) /// Proof: `EmaOracle::ExternalSources` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`) @@ -232,8 +234,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1261` // Estimated: `3481` - // Minimum execution time: 31_119_000 picoseconds. - Weight::from_parts(31_711_000, 3481) + // Minimum execution time: 30_632_000 picoseconds. + Weight::from_parts(31_148_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -245,8 +247,8 @@ impl pallet_ema_oracle::WeightInfo for HydraWeight { // Proof Size summary in bytes: // Measured: `1295` // Estimated: `3481` - // Minimum execution time: 31_525_000 picoseconds. - Weight::from_parts(32_028_000, 3481) + // Minimum execution time: 30_637_000 picoseconds. + Weight::from_parts(30_912_000, 3481) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) }