From 32ab7c9e39044d1f5f7b4ee9555bc9375a16c01d Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Fri, 16 Jul 2021 00:37:43 +0100 Subject: [PATCH 01/28] pallet_price_oracle initial commit --- Cargo.lock | 13 +++++++++++++ runtime/Cargo.toml | 1 + runtime/src/lib.rs | 13 +++++++++++++ 3 files changed, 27 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 31bb2227b64..657fe19495f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,6 +552,7 @@ dependencies = [ "pallet-lbp", "pallet-multi-payment-benchmarking", "pallet-nft", + "pallet-price-oracle", "pallet-randomness-collective-flip", "pallet-scheduler", "pallet-session", @@ -5590,6 +5591,18 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-price-oracle" +version = "1.0.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec 2.1.3", + "primitives", + "sp-io", + "substrate-wasm-builder 3.0.0", +] + [[package]] name = "pallet-proxy" version = "3.0.0" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 45df429503d..208b98dc7ac 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -34,6 +34,7 @@ pallet-xyk = { path = "../pallets/xyk",default-features=false} pallet-xyk-rpc-runtime-api = {path = "../pallets/xyk/rpc/runtime-api",default-features=false} pallet-nft = { path = '../pallets/nft', default-features = false } pallet-lbp = { path = '../pallets/lbp', default-features = false } +pallet-price-oracle = { path = '../pallets/price-oracle', default-features = false } primitives = { default-features = false, path = "../primitives" } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 17d80082b5c..29fbbfaf773 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -212,6 +212,7 @@ impl Filter for BaseFilter { | Call::Utility(_) | Call::Vesting(_) | Call::NFT(_) + | Call::PriceOracle(_) | Call::Sudo(_) => true, Call::XYK(_) => false, @@ -441,6 +442,17 @@ impl pallet_lbp::Config for Runtime { type WeightInfo = pallet_lbp::weights::HydraWeight; } +parameter_types! { + pub BucketLength: u32 = 10u32; + pub BucketDepth: u32 = 4u32; +} + +impl pallet_price_oracle::Config for Runtime { + type Event = Event; + type BucketLength = BucketLength; + type BucketDepth = BucketDepth; +} + /// Parachain Config impl cumulus_pallet_parachain_system::Config for Runtime { @@ -851,6 +863,7 @@ construct_runtime!( LBP: pallet_lbp::{Pallet, Call, Storage, Event}, MultiTransactionPayment: pallet_transaction_multi_payment::{Pallet, Call, Config, Storage, Event}, NFT: pallet_nft::{Pallet, Call, Event, Storage}, + PriceOracle: pallet_price_oracle::{Pallet, Call, Storage, Event}, } ); From 98cd5ba6ee85e1f85c43f0b3844de40cea69aa3e Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Thu, 22 Jul 2021 18:10:03 +0100 Subject: [PATCH 02/28] price-oracle initial implementation --- Cargo.lock | 4 ++++ runtime/src/lib.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 657fe19495f..7ebacb1be98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5599,7 +5599,11 @@ dependencies = [ "frame-system", "parity-scale-codec 2.1.3", "primitives", + "serde", + "sp-core", "sp-io", + "sp-runtime", + "sp-std", "substrate-wasm-builder 3.0.0", ] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 29fbbfaf773..eb2ce2ca9be 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -445,12 +445,14 @@ impl pallet_lbp::Config for Runtime { parameter_types! { pub BucketLength: u32 = 10u32; pub BucketDepth: u32 = 4u32; + pub MaxAssetCount: u32 = u32::MAX; } impl pallet_price_oracle::Config for Runtime { type Event = Event; type BucketLength = BucketLength; type BucketDepth = BucketDepth; + type MaxAssetCount = MaxAssetCount; } /// Parachain Config From 7125c2cc0401a8ffcc719bd19b59ca0a31ef9190 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Thu, 22 Jul 2021 18:21:52 +0100 Subject: [PATCH 03/28] price-oracle initial implementation --- pallets/price-oracle/Cargo.toml | 54 +++++ pallets/price-oracle/src/lib.rs | 355 ++++++++++++++++++++++++++++++ pallets/price-oracle/src/mock.rs | 92 ++++++++ pallets/price-oracle/src/tests.rs | 181 +++++++++++++++ 4 files changed, 682 insertions(+) create mode 100644 pallets/price-oracle/Cargo.toml create mode 100644 pallets/price-oracle/src/lib.rs create mode 100644 pallets/price-oracle/src/mock.rs create mode 100644 pallets/price-oracle/src/tests.rs diff --git a/pallets/price-oracle/Cargo.toml b/pallets/price-oracle/Cargo.toml new file mode 100644 index 00000000000..e4dc46a7dd0 --- /dev/null +++ b/pallets/price-oracle/Cargo.toml @@ -0,0 +1,54 @@ +[package] +authors = ['GalacticCouncil'] +description = 'Price Oracle Pallet' +edition = '2018' +homepage = 'https://github.com/galacticcouncil/basilisk-node' +license = 'Apache 2.0' +name = 'pallet-price-oracle' +repository = 'https://github.com/galacticcouncil/basilisk-node' +version = '1.0.0' + +[package.metadata.docs.rs] +targets = ['x86_64-unknown-linux-gnu'] + +[build-dependencies] +substrate-wasm-builder = {package = 'substrate-wasm-builder', version = '3.0.0'} + +# alias "parity-scale-code" to "codec" +[dependencies.codec] +default-features = false +features = ['derive'] +package = 'parity-scale-codec' +version = '2.0.0' + +[dependencies] +#primitive-types = {default-features = false, version = '0.8.0'} +serde = {features = ['derive'], optional = true, version = '1.0.101'} + +# Local dependencies +primitives = {path = '../../primitives', default-features = false} + +# Substrate dependencies +#frame-benchmarking = {default-features = false, optional = true, version = '3.0.0'} +frame-support = {default-features = false, version = '3.0.0'} +frame-system = {default-features = false, version = '3.0.0'} +#frame-system-benchmarking = {default-features = false, optional = true, version = '3.0.0'} +sp-core = {default-features = false, version = '3.0.0'} +sp-runtime = {default-features = false, version = '3.0.0'} +sp-std = {default-features = false, version = '3.0.0'} + +[dev-dependencies] +sp-io = {default-features = false, version = '3.0.0'} + +[features] +default = ['std'] +std = [ + 'serde', + 'codec/std', + 'frame-support/std', + 'frame-system/std', + 'sp-runtime/std', + 'sp-core/std', + 'sp-std/std', +# 'primitives/std', +] diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs new file mode 100644 index 00000000000..a0a2921dfca --- /dev/null +++ b/pallets/price-oracle/src/lib.rs @@ -0,0 +1,355 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2021 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. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use sp_std::prelude::*; +use sp_std::ops::{Add, Mul, Index, IndexMut}; +use sp_std::iter::Sum; +use frame_support::traits::{Get, Len}; +use frame_support::sp_runtime::RuntimeDebug; +use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; +use frame_support::dispatch::DispatchResult; +use primitives::{Balance, Price}; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +// mod benchmarking; + +// pub mod weights; + +// use weights::WeightInfo; + +// Re-export pallet items so that they can be accessed from the crate namespace. +pub use pallet::*; + +pub const BUCKET_SIZE: u32 = 10; + +pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; + +pub trait PriceInfoCalculation { + fn calculate_price_info(entries: &[PriceEntry]) -> Option; +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] +pub struct PriceInfo { + avg_price: Price, + volume: Balance, +} + +impl Default for PriceInfo { + fn default() -> Self { + Self { + avg_price: Price::zero(), + volume: Zero::zero(), + } + } +} + +impl Add for &PriceInfo { + type Output = PriceInfo; + fn add(self, other: Self) -> Self::Output { + PriceInfo { + avg_price: self.avg_price.add(other.avg_price), + volume: self.volume.add(other.volume), + } + } +} + +impl<'a> Sum<&'a Self> for PriceInfo { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold( PriceInfo{ avg_price: Price::zero(), volume: Balance::zero() }, |a, b| {&a + b}) + } +} + +impl PriceInfoCalculation for PriceInfo { + fn calculate_price_info(entries: &[PriceEntry]) -> Option { + let intermediate_result: Vec = entries.iter().map(|x| PriceEntry{price: x.price.mul(Price::from(x.liq_amount)), amount: x.amount, liq_amount: x.liq_amount}).collect(); + + let sum = intermediate_result.iter().sum::(); + let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liq_amount as u128))?; + Some(PriceInfo { + avg_price: weighted_avg_price, + volume: sum.amount, + }) +} +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] +pub struct BucketQueue { + bucket: Bucket, + last: u32, +} + +impl BucketQueue { + const BUCKET_SIZE: u32 = BUCKET_SIZE; +} + +impl Default for BucketQueue { + fn default() -> Self { + Self { + bucket: Bucket::default(), + last: Self::BUCKET_SIZE - 1, + } + } +} + +pub trait BucketQueueT { + fn update_last(&mut self, price_info: PriceInfo); + fn get_last(&self) -> PriceInfo; + fn calculate_average(&self) -> Option; +} + +impl BucketQueueT for BucketQueue { + fn update_last(&mut self, price_info: PriceInfo) { + self.last = (self.last + 1) % Self::BUCKET_SIZE; + self.bucket[self.last as usize] = price_info; + } + + fn get_last(&self) -> PriceInfo { + self.bucket[self.last as usize] + } + + fn calculate_average(&self) -> Option { + let sum = self.bucket.iter().sum::(); + Some(PriceInfo { + avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128))?, + volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128)?, + }) + } +} + +impl Index for BucketQueue { + type Output = PriceInfo; + fn index(&self, index: usize) -> &Self::Output { + &self.bucket[index] + } +} + +impl IndexMut for BucketQueue { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.bucket[index] + } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] +pub struct PriceEntry { + price: Price, + amount: Balance, + liq_amount: Balance, +} + +impl Add for PriceEntry { + type Output = Self; + fn add(self, other: Self) -> Self { + Self { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl Zero for PriceEntry { + fn zero() -> Self { + Self { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + } + } + + fn is_zero(&self) -> bool { + if self == &PriceEntry::zero() { + true + } else { + false + } + } +} + +impl Add for &PriceEntry { + type Output = PriceEntry; + fn add(self, other: Self) -> Self::Output { + PriceEntry { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl<'a> Sum<&'a Self> for PriceEntry { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold( PriceEntry{ price: Price::zero(), amount: Balance::zero(), liq_amount: Balance::zero() }, |a, b| {&a + b}) + } +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config { + type Event: From> + IsType<::Event>; + + #[pallet::constant] + type BucketLength: Get; + + #[pallet::constant] + type BucketDepth: Get; + + #[pallet::constant] + type MaxAssetCount: Get; + } + + #[pallet::error] + pub enum Error { + PriceDataNotFound, + AssetCountOverflow, + PriceComputationError, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + Placeholder(), + } + + #[pallet::storage] + #[pallet::getter(fn asset_count)] + pub type NumOfTrackedAssets = StorageValue<_, u32, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn price_buffer)] + pub type PriceBuffer = + StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn price_data_ten)] + pub type PriceDataTen = + StorageValue<_, Vec<(T::AccountId, BucketQueue)>, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn price_data_hundred)] + pub type PriceDataHundred = + StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + + #[pallet::storage] + #[pallet::getter(fn price_data_thousand)] + pub type PriceDataThousand = + StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + + #[pallet::hooks] + impl Hooks for Pallet { + fn on_initialize(_n: T::BlockNumber) -> Weight { + Self::update_data(); + Weight::zero() + } + } + + #[pallet::call] + impl Pallet { + } +} + +impl Pallet { + fn new_entry(asset_pair: T::AccountId) -> DispatchResult { + PriceDataTen::::append((asset_pair, BucketQueue::default())); + + let incremented_asset_count = Self::asset_count().checked_add(1).ok_or(Error::::AssetCountOverflow)?; + >::put(incremented_asset_count); + + Ok(()) + } + + fn on_trade(asset_pair: T::AccountId, entry: PriceEntry) -> DispatchResult { + if PriceBuffer::::contains_key(&asset_pair) { + PriceBuffer::::append(asset_pair, entry); + } else { + PriceBuffer::::insert(asset_pair, vec![entry]); + } + + Ok(()) + } + + fn update_data() -> DispatchResult { + PriceDataTen::::try_mutate(|data_ten| -> DispatchResult { + + for (asset_pair, data) in data_ten.iter_mut() { + + let maybe_price = >::try_get(asset_pair); + let result = if let Ok(prices) = maybe_price { + PriceInfo::calculate_price_info(prices.as_slice()) + .ok_or(Error::::PriceComputationError)? + } else { + PriceInfo::default() + }; + + data.update_last(result); + } + + PriceBuffer::::remove_all(); + + Ok(()) + })?; + + let now = >::block_number(); + if now.is_zero() { return Ok(()) } // TODO ??? + + if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { + for element_from_ten in PriceDataTen::::get().iter() { + PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| -> DispatchResult { + data.update_last(element_from_ten.1.calculate_average().ok_or(Error::::PriceComputationError)?); + Ok(()) + })?; + } + } + + if (now % T::BlockNumber::from(BUCKET_SIZE.pow(2))) == T::BlockNumber::from(BUCKET_SIZE.pow(2) - 1) { + for element_from_hundred in PriceDataHundred::::iter() { + PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| -> DispatchResult { + data.update_last(element_from_hundred.1.calculate_average().ok_or(Error::::PriceComputationError)?); + Ok(()) + })?; + } + } + + Ok(()) + } +} diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs new file mode 100644 index 00000000000..b8e35248c39 --- /dev/null +++ b/pallets/price-oracle/src/mock.rs @@ -0,0 +1,92 @@ +use crate as price_oracle; +use crate::Config; +use frame_support::parameter_types; +use frame_support::traits::OnInitialize; +use frame_system; +use primitives::{AssetId, Balance, Price}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup, Zero}, +}; +use price_oracle::PriceEntry; + +pub type AccountId = u64; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +pub const ASSET_PAIR_A: AccountId = 1_000; +pub const ASSET_PAIR_B: AccountId = 2_000; + +pub const PRICE_ENTRY_1: PriceEntry = PriceEntry {price: Price::from_inner(2000000000000000000), amount: 1_000, liq_amount: 2_000}; +pub const PRICE_ENTRY_2: PriceEntry = PriceEntry {price: Price::from_inner(5000000000000000000), amount: 3_000, liq_amount: 4_000}; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + PriceOracle: price_oracle::{Pallet, Call, Storage, Event}, + } + +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 63; +} + +impl frame_system::Config for Test { + type BaseCallFilter = (); + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); +} + +parameter_types! { + pub const BucketLength: u32 = 10; + pub const BucketDepth: u32 = 4; + pub const MaxAssetCount: u32 = 5; +} + +impl Config for Test { + type Event = Event; + type BucketLength = BucketLength; + type BucketDepth = BucketDepth; + type MaxAssetCount = MaxAssetCount; +} + +pub struct ExtBuilder; + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let storage = frame_system::GenesisConfig::default().build_storage::().unwrap(); + sp_io::TestExternalities::from(storage) + } +} + +fn next_block() { + System::set_block_number(System::block_number() + 1); + PriceOracle::on_initialize(System::block_number()); +} diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs new file mode 100644 index 00000000000..cfbd37c42b0 --- /dev/null +++ b/pallets/price-oracle/src/tests.rs @@ -0,0 +1,181 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2021 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::*; +pub use crate::mock::{ + PriceOracle, Event as TestEvent, ExtBuilder, Origin, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, PRICE_ENTRY_2, +}; +use frame_support::{assert_noop, assert_ok, + traits::OnInitialize}; + +pub fn new_test_ext() -> sp_io::TestExternalities { + let ext = ExtBuilder.build(); + ext +} + +fn last_events(n: usize) -> Vec { + frame_system::Pallet::::events() + .into_iter() + .rev() + .take(n) + .rev() + .map(|e| e.event) + .collect() +} + +fn expect_events(e: Vec) { + assert_eq!(last_events(e.len()), e); +} + +#[test] +fn add_new_asset_pair_should_work() { + new_test_ext().execute_with(|| { + assert_eq!(PriceOracle::asset_count(), 0); + assert_eq!(>::get().contains( &(ASSET_PAIR_A, BucketQueue::default())), false); + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + assert_eq!(>::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), true); + assert_eq!(PriceOracle::asset_count(), 1); + }); +} + +#[test] +fn on_trade_should_work() { + new_test_ext().execute_with(|| { + assert_eq!(>::try_get(ASSET_PAIR_A), Err(())); + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); + let mut vec = Vec::new(); + vec.push(PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_2); + assert_eq!(>::try_get(ASSET_PAIR_A), Ok(vec)); + }); +} + +#[test] +fn update_data_should_work() { + new_test_ext().execute_with(|| { + System::set_block_number(3); + + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_B, PRICE_ENTRY_1)); + + assert_ok!(PriceOracle::update_data()); + + let data_ten_a = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; + let data_ten_b = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_B).unwrap().1; + + assert_eq!(data_ten_a.get_last(), PriceInfo {avg_price: 4.into(), volume: 4_000}); + assert_eq!(data_ten_b.get_last(), PriceInfo {avg_price: 2.into(), volume: 1_000}); + }); +} + +#[test] +fn update_empty_data_should_work() { + new_test_ext().execute_with(|| { + System::set_block_number(3); + + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + + assert_ok!(PriceOracle::update_data()); + + let data_ten = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; + + assert_eq!(data_ten.get_last(), PriceInfo {avg_price: Zero::zero(), volume: Zero::zero()}); + }); +} +#[test] +fn bucket_queue_should_work() { + let mut queue = BucketQueue::default(); + for i in 0..BucketQueue::BUCKET_SIZE { + assert_eq!(queue[i as usize], PriceInfo::default()); + } + assert_eq!(queue.get_last(), PriceInfo::default()); + + for i in 0..BucketQueue::BUCKET_SIZE { + let new_price = Price::from(i as u128); + queue.update_last(PriceInfo { avg_price: new_price, volume: 0 }); + assert_eq!(queue.get_last(), PriceInfo { avg_price: new_price, volume: 0 }); + // for k in 0..BucketQueue::BUCKET_SIZE { + // print!(" {}", queue.bucket[k as usize].avg_price.to_float()); + // } + // println!(); + + for j in 0..BucketQueue::BUCKET_SIZE { + if i < j { + assert_eq!(queue[j as usize], PriceInfo::default()); + } else { + assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from(j as u128), volume: 0 }); + } + } + } + + for i in BucketQueue::BUCKET_SIZE..BucketQueue::BUCKET_SIZE * 3 { + let new_price = Price::from(i as u128); + queue.update_last(PriceInfo { avg_price: new_price, volume: 0 }); + // for k in 0..BucketQueue::BUCKET_SIZE { + // print!(" {}", queue.bucket[k as usize].avg_price.to_float()); + // } + // println!(); + + for j in 0..BucketQueue::BUCKET_SIZE { + if (i % BucketQueue::BUCKET_SIZE) < j { + assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from((10 * (i / BucketQueue::BUCKET_SIZE).saturating_sub(1) + j) as u128), volume: 0 }); + } else { + assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from((j as u128) + 10u128 * (i / BucketQueue::BUCKET_SIZE) as u128), volume: 0 }); + } + } + } +} + +#[test] +fn continuous_trades_should_work() { + ExtBuilder.build().execute_with(|| { + assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + + for i in 0..210{ + System::set_block_number(i); + PriceOracle::on_initialize(System::block_number()); + + assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PriceEntry { price: Price::from((i + 1) as u128), amount: (i * 1_000).into(), liq_amount: 1u128 })); + + // let ten = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; + // let hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A); + // let thousand = PriceOracle::price_data_thousand(ASSET_PAIR_A); + // + // for i in 0..BUCKET_SIZE { + // print!(" {}", ten[i as usize].avg_price.to_float()); + // } + // println!(); + // + // for i in 0..BUCKET_SIZE { + // print!(" {}", hundred[i as usize].avg_price.to_float()); + // } + // println!(); + // + // for i in 0..BUCKET_SIZE { + // print!(" {}", thousand[i as usize].avg_price.to_float()); + // } + // println!("\n"); + + } + }) +} \ No newline at end of file From 0c3d9c5968a19006fd7427290a75b902602c9581 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Thu, 22 Jul 2021 18:26:54 +0100 Subject: [PATCH 04/28] clippy and formatting --- pallets/price-oracle/src/lib.rs | 110 +++++++++++++++---------- pallets/price-oracle/src/tests.rs | 130 +++++++++++++++++++++++------- 2 files changed, 167 insertions(+), 73 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index a0a2921dfca..921aab7129a 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -18,14 +18,14 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode}; -use sp_std::prelude::*; -use sp_std::ops::{Add, Mul, Index, IndexMut}; -use sp_std::iter::Sum; -use frame_support::traits::{Get, Len}; -use frame_support::sp_runtime::RuntimeDebug; -use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; use frame_support::dispatch::DispatchResult; +use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; +use frame_support::sp_runtime::RuntimeDebug; +use frame_support::traits::Get; use primitives::{Balance, Price}; +use sp_std::iter::Sum; +use sp_std::ops::{Add, Index, IndexMut, Mul}; +use sp_std::prelude::*; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -84,13 +84,26 @@ impl<'a> Sum<&'a Self> for PriceInfo { where I: Iterator, { - iter.fold( PriceInfo{ avg_price: Price::zero(), volume: Balance::zero() }, |a, b| {&a + b}) + iter.fold( + PriceInfo { + avg_price: Price::zero(), + volume: Balance::zero(), + }, + |a, b| &a + b, + ) } } impl PriceInfoCalculation for PriceInfo { fn calculate_price_info(entries: &[PriceEntry]) -> Option { - let intermediate_result: Vec = entries.iter().map(|x| PriceEntry{price: x.price.mul(Price::from(x.liq_amount)), amount: x.amount, liq_amount: x.liq_amount}).collect(); + let intermediate_result: Vec = entries + .iter() + .map(|x| PriceEntry { + price: x.price.mul(Price::from(x.liq_amount)), + amount: x.amount, + liq_amount: x.liq_amount, + }) + .collect(); let sum = intermediate_result.iter().sum::(); let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liq_amount as u128))?; @@ -98,7 +111,7 @@ impl PriceInfoCalculation for PriceInfo { avg_price: weighted_avg_price, volume: sum.amount, }) -} + } } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -138,7 +151,7 @@ impl BucketQueueT for BucketQueue { } fn calculate_average(&self) -> Option { - let sum = self.bucket.iter().sum::(); + let sum = self.bucket.iter().sum::(); Some(PriceInfo { avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128))?, volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128)?, @@ -188,11 +201,7 @@ impl Zero for PriceEntry { } fn is_zero(&self) -> bool { - if self == &PriceEntry::zero() { - true - } else { - false - } + self == &PriceEntry::zero() } } @@ -212,7 +221,14 @@ impl<'a> Sum<&'a Self> for PriceEntry { where I: Iterator, { - iter.fold( PriceEntry{ price: Price::zero(), amount: Balance::zero(), liq_amount: Balance::zero() }, |a, b| {&a + b}) + iter.fold( + PriceEntry { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + }, + |a, b| &a + b, + ) } } @@ -257,23 +273,19 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn price_buffer)] - pub type PriceBuffer = - StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + pub type PriceBuffer = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_ten)] - pub type PriceDataTen = - StorageValue<_, Vec<(T::AccountId, BucketQueue)>, ValueQuery>; + pub type PriceDataTen = StorageValue<_, Vec<(T::AccountId, BucketQueue)>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_hundred)] - pub type PriceDataHundred = - StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + pub type PriceDataHundred = StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_thousand)] - pub type PriceDataThousand = - StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + pub type PriceDataThousand = StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; #[pallet::hooks] impl Hooks for Pallet { @@ -284,22 +296,23 @@ pub mod pallet { } #[pallet::call] - impl Pallet { - } + impl Pallet {} } impl Pallet { fn new_entry(asset_pair: T::AccountId) -> DispatchResult { - PriceDataTen::::append((asset_pair, BucketQueue::default())); + PriceDataTen::::append((asset_pair, BucketQueue::default())); - let incremented_asset_count = Self::asset_count().checked_add(1).ok_or(Error::::AssetCountOverflow)?; + let incremented_asset_count = Self::asset_count() + .checked_add(1) + .ok_or(Error::::AssetCountOverflow)?; >::put(incremented_asset_count); Ok(()) } fn on_trade(asset_pair: T::AccountId, entry: PriceEntry) -> DispatchResult { - if PriceBuffer::::contains_key(&asset_pair) { + if PriceBuffer::::contains_key(&asset_pair) { PriceBuffer::::append(asset_pair, entry); } else { PriceBuffer::::insert(asset_pair, vec![entry]); @@ -309,19 +322,16 @@ impl Pallet { } fn update_data() -> DispatchResult { - PriceDataTen::::try_mutate(|data_ten| -> DispatchResult { - - for (asset_pair, data) in data_ten.iter_mut() { - + PriceDataTen::::try_mutate(|data_ten| -> DispatchResult { + for (asset_pair, data) in data_ten.iter_mut() { let maybe_price = >::try_get(asset_pair); let result = if let Ok(prices) = maybe_price { - PriceInfo::calculate_price_info(prices.as_slice()) - .ok_or(Error::::PriceComputationError)? + PriceInfo::calculate_price_info(prices.as_slice()).ok_or(Error::::PriceComputationError)? } else { PriceInfo::default() }; - data.update_last(result); + data.update_last(result); } PriceBuffer::::remove_all(); @@ -330,26 +340,38 @@ impl Pallet { })?; let now = >::block_number(); - if now.is_zero() { return Ok(()) } // TODO ??? + if now.is_zero() { + return Ok(()); + } // TODO ??? if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { - for element_from_ten in PriceDataTen::::get().iter() { + for element_from_ten in PriceDataTen::::get().iter() { PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| -> DispatchResult { - data.update_last(element_from_ten.1.calculate_average().ok_or(Error::::PriceComputationError)?); - Ok(()) + data.update_last( + element_from_ten + .1 + .calculate_average() + .ok_or(Error::::PriceComputationError)?, + ); + Ok(()) })?; } } if (now % T::BlockNumber::from(BUCKET_SIZE.pow(2))) == T::BlockNumber::from(BUCKET_SIZE.pow(2) - 1) { - for element_from_hundred in PriceDataHundred::::iter() { + for element_from_hundred in PriceDataHundred::::iter() { PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| -> DispatchResult { - data.update_last(element_from_hundred.1.calculate_average().ok_or(Error::::PriceComputationError)?); - Ok(()) + data.update_last( + element_from_hundred + .1 + .calculate_average() + .ok_or(Error::::PriceComputationError)?, + ); + Ok(()) })?; } } - Ok(()) + Ok(()) } } diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index cfbd37c42b0..d1f46a8c340 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -17,10 +17,10 @@ use super::*; pub use crate::mock::{ - PriceOracle, Event as TestEvent, ExtBuilder, Origin, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, PRICE_ENTRY_2, + Event as TestEvent, ExtBuilder, Origin, PriceOracle, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, + PRICE_ENTRY_2, }; -use frame_support::{assert_noop, assert_ok, - traits::OnInitialize}; +use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; pub fn new_test_ext() -> sp_io::TestExternalities { let ext = ExtBuilder.build(); @@ -45,9 +45,15 @@ fn expect_events(e: Vec) { fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { assert_eq!(PriceOracle::asset_count(), 0); - assert_eq!(>::get().contains( &(ASSET_PAIR_A, BucketQueue::default())), false); + assert_eq!( + >::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), + false + ); assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); - assert_eq!(>::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), true); + assert_eq!( + >::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), + true + ); assert_eq!(PriceOracle::asset_count(), 1); }); } @@ -58,7 +64,7 @@ fn on_trade_should_work() { assert_eq!(>::try_get(ASSET_PAIR_A), Err(())); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); - let mut vec = Vec::new(); + let mut vec = Vec::new(); vec.push(PRICE_ENTRY_1); vec.push(PRICE_ENTRY_2); assert_eq!(>::try_get(ASSET_PAIR_A), Ok(vec)); @@ -68,7 +74,7 @@ fn on_trade_should_work() { #[test] fn update_data_should_work() { new_test_ext().execute_with(|| { - System::set_block_number(3); + System::set_block_number(3); assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); @@ -79,27 +85,57 @@ fn update_data_should_work() { assert_ok!(PriceOracle::update_data()); - let data_ten_a = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; - let data_ten_b = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_B).unwrap().1; - - assert_eq!(data_ten_a.get_last(), PriceInfo {avg_price: 4.into(), volume: 4_000}); - assert_eq!(data_ten_b.get_last(), PriceInfo {avg_price: 2.into(), volume: 1_000}); + let data_ten_a = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_A) + .unwrap() + .1; + let data_ten_b = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_B) + .unwrap() + .1; + + assert_eq!( + data_ten_a.get_last(), + PriceInfo { + avg_price: 4.into(), + volume: 4_000 + } + ); + assert_eq!( + data_ten_b.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); }); } #[test] fn update_empty_data_should_work() { new_test_ext().execute_with(|| { - System::set_block_number(3); + System::set_block_number(3); assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); assert_ok!(PriceOracle::update_data()); - let data_ten = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; - - assert_eq!(data_ten.get_last(), PriceInfo {avg_price: Zero::zero(), volume: Zero::zero()}); + let data_ten = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_A) + .unwrap() + .1; + + assert_eq!( + data_ten.get_last(), + PriceInfo { + avg_price: Zero::zero(), + volume: Zero::zero() + } + ); }); } #[test] @@ -108,14 +144,23 @@ fn bucket_queue_should_work() { for i in 0..BucketQueue::BUCKET_SIZE { assert_eq!(queue[i as usize], PriceInfo::default()); } - assert_eq!(queue.get_last(), PriceInfo::default()); + assert_eq!(queue.get_last(), PriceInfo::default()); for i in 0..BucketQueue::BUCKET_SIZE { let new_price = Price::from(i as u128); - queue.update_last(PriceInfo { avg_price: new_price, volume: 0 }); - assert_eq!(queue.get_last(), PriceInfo { avg_price: new_price, volume: 0 }); + queue.update_last(PriceInfo { + avg_price: new_price, + volume: 0, + }); + assert_eq!( + queue.get_last(), + PriceInfo { + avg_price: new_price, + volume: 0 + } + ); // for k in 0..BucketQueue::BUCKET_SIZE { - // print!(" {}", queue.bucket[k as usize].avg_price.to_float()); + // print!(" {}", queue.bucket[k as usize].avg_price.to_float()); // } // println!(); @@ -123,24 +168,45 @@ fn bucket_queue_should_work() { if i < j { assert_eq!(queue[j as usize], PriceInfo::default()); } else { - assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from(j as u128), volume: 0 }); + assert_eq!( + queue[j as usize], + PriceInfo { + avg_price: Price::from(j as u128), + volume: 0 + } + ); } } } for i in BucketQueue::BUCKET_SIZE..BucketQueue::BUCKET_SIZE * 3 { let new_price = Price::from(i as u128); - queue.update_last(PriceInfo { avg_price: new_price, volume: 0 }); + queue.update_last(PriceInfo { + avg_price: new_price, + volume: 0, + }); // for k in 0..BucketQueue::BUCKET_SIZE { // print!(" {}", queue.bucket[k as usize].avg_price.to_float()); // } - // println!(); + // println!(); for j in 0..BucketQueue::BUCKET_SIZE { if (i % BucketQueue::BUCKET_SIZE) < j { - assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from((10 * (i / BucketQueue::BUCKET_SIZE).saturating_sub(1) + j) as u128), volume: 0 }); + assert_eq!( + queue[j as usize], + PriceInfo { + avg_price: Price::from((10 * (i / BucketQueue::BUCKET_SIZE).saturating_sub(1) + j) as u128), + volume: 0 + } + ); } else { - assert_eq!(queue[j as usize], PriceInfo { avg_price: Price::from((j as u128) + 10u128 * (i / BucketQueue::BUCKET_SIZE) as u128), volume: 0 }); + assert_eq!( + queue[j as usize], + PriceInfo { + avg_price: Price::from((j as u128) + 10u128 * (i / BucketQueue::BUCKET_SIZE) as u128), + volume: 0 + } + ); } } } @@ -151,11 +217,18 @@ fn continuous_trades_should_work() { ExtBuilder.build().execute_with(|| { assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); - for i in 0..210{ + for i in 0..210 { System::set_block_number(i); PriceOracle::on_initialize(System::block_number()); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PriceEntry { price: Price::from((i + 1) as u128), amount: (i * 1_000).into(), liq_amount: 1u128 })); + assert_ok!(PriceOracle::on_trade( + ASSET_PAIR_A, + PriceEntry { + price: Price::from((i + 1) as u128), + amount: (i * 1_000).into(), + liq_amount: 1u128 + } + )); // let ten = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; // let hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A); @@ -175,7 +248,6 @@ fn continuous_trades_should_work() { // print!(" {}", thousand[i as usize].avg_price.to_float()); // } // println!("\n"); - } }) -} \ No newline at end of file +} From 37ecf49ca1d7e9f8d82001f86908bb0662475502 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 3 Aug 2021 00:56:38 +0100 Subject: [PATCH 05/28] various adjustments --- Cargo.lock | 5 + pallets/exchange/benchmarking/src/mock.rs | 4 +- pallets/exchange/src/mock.rs | 4 +- pallets/lbp/src/lib.rs | 10 +- pallets/lbp/src/mock.rs | 6 +- pallets/price-oracle/Cargo.toml | 4 + pallets/price-oracle/src/lib.rs | 188 ++++++++++-------- pallets/price-oracle/src/mock.rs | 65 +++++- pallets/price-oracle/src/tests.rs | 26 +-- .../benchmarking/src/mock.rs | 4 +- pallets/transaction-multi-payment/src/mock.rs | 4 +- pallets/xyk/Cargo.toml | 1 + pallets/xyk/src/lib.rs | 19 +- pallets/xyk/src/mock.rs | 5 +- primitives/src/traits.rs | 39 ++++ runtime/src/lib.rs | 5 +- 16 files changed, 259 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ebacb1be98..f18f4f58c98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5597,6 +5597,10 @@ version = "1.0.0" dependencies = [ "frame-support", "frame-system", + "orml-tokens", + "orml-traits", + "pallet-asset-registry", + "pallet-xyk", "parity-scale-codec 2.1.3", "primitives", "serde", @@ -5947,6 +5951,7 @@ dependencies = [ "orml-traits", "orml-utilities", "pallet-asset-registry", + "pallet-price-oracle", "parity-scale-codec 2.1.3", "primitive-types 0.8.0", "primitives", diff --git a/pallets/exchange/benchmarking/src/mock.rs b/pallets/exchange/benchmarking/src/mock.rs index ac99ec1688b..3506fc4229d 100644 --- a/pallets/exchange/benchmarking/src/mock.rs +++ b/pallets/exchange/benchmarking/src/mock.rs @@ -28,8 +28,7 @@ use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup, Zero}, }; -use pallet_xyk::AssetPairAccountIdFor; -use primitives::{fee, AssetId, Balance}; +use primitives::{fee, AssetId, Balance, traits::AssetPairAccountIdFor}; pub type Amount = i128; pub type AccountId = u64; @@ -138,6 +137,7 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HDXAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = (); } impl pallet_exchange::Config for Test { diff --git a/pallets/exchange/src/mock.rs b/pallets/exchange/src/mock.rs index e93f50ff397..ed0484118a1 100644 --- a/pallets/exchange/src/mock.rs +++ b/pallets/exchange/src/mock.rs @@ -30,8 +30,7 @@ use sp_runtime::{ use pallet_xyk as xyk; use frame_support::traits::GenesisBuild; -use pallet_xyk::AssetPairAccountIdFor; -use primitives::{fee, AssetId, Balance}; +use primitives::{fee, AssetId, Balance, traits::AssetPairAccountIdFor}; pub type Amount = i128; pub type AccountId = u64; @@ -144,6 +143,7 @@ impl xyk::Config for Test { type NativeAssetId = HDXAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = (); } impl Config for Test { diff --git a/pallets/lbp/src/lib.rs b/pallets/lbp/src/lib.rs index 0e84493ebae..ca9d1a900ac 100644 --- a/pallets/lbp/src/lib.rs +++ b/pallets/lbp/src/lib.rs @@ -19,7 +19,7 @@ use frame_support::{ use frame_system::ensure_signed; use hydra_dx_math::lbp::Weight as LBPWeight; use orml_traits::{MultiCurrency, MultiCurrencyExtended, MultiReservableCurrency}; -use primitives::traits::{AMMTransfer, AMM}; +use primitives::traits::{AMMTransfer, AMM, AssetPairAccountIdFor}; use primitives::{ asset::AssetPair, fee::{Fee, WithFee}, @@ -172,7 +172,7 @@ pub mod pallet { type LBPWeightFunction: LBPWeightCalculation; /// Mapping of asset pairs to unique pool identities - type AssetPairPoolId: AssetPairPoolIdFor>; + type AssetPairPoolId: AssetPairAccountIdFor>; /// Weight information for the extrinsics type WeightInfo: WeightInfo; @@ -926,13 +926,9 @@ impl Pallet { } } -pub trait AssetPairPoolIdFor { - fn from_assets(asset_a: AssetId, asset_b: AssetId) -> PoolId; -} - pub struct AssetPairPoolId(PhantomData); -impl AssetPairPoolIdFor> for AssetPairPoolId +impl AssetPairAccountIdFor> for AssetPairPoolId where PoolId: UncheckedFrom + AsRef<[u8]>, { diff --git a/pallets/lbp/src/mock.rs b/pallets/lbp/src/mock.rs index 6c3c1668066..49bfaadf56d 100644 --- a/pallets/lbp/src/mock.rs +++ b/pallets/lbp/src/mock.rs @@ -1,10 +1,10 @@ use crate as lbp; -use crate::{AssetPairPoolIdFor, Config}; +use crate::Config; use frame_support::parameter_types; use frame_support::traits::GenesisBuild; use frame_system; use orml_traits::parameter_type_with_key; -use primitives::{AssetId, Balance, CORE_ASSET_ID}; +use primitives::{AssetId, Balance, CORE_ASSET_ID, traits::AssetPairAccountIdFor}; use sp_core::H256; use sp_runtime::{ testing::Header, @@ -93,7 +93,7 @@ impl orml_tokens::Config for Test { pub struct AssetPairPoolIdTest(); -impl AssetPairPoolIdFor for AssetPairPoolIdTest { +impl AssetPairAccountIdFor for AssetPairPoolIdTest { fn from_assets(asset_a: AssetId, asset_b: AssetId) -> u64 { let mut a = asset_a as u128; let mut b = asset_b as u128; diff --git a/pallets/price-oracle/Cargo.toml b/pallets/price-oracle/Cargo.toml index e4dc46a7dd0..1c54113567c 100644 --- a/pallets/price-oracle/Cargo.toml +++ b/pallets/price-oracle/Cargo.toml @@ -39,6 +39,10 @@ sp-std = {default-features = false, version = '3.0.0'} [dev-dependencies] sp-io = {default-features = false, version = '3.0.0'} +pallet-xyk = {path = '../xyk', default-features = false} +orml-tokens = {default-features = false, version = "0.4.1-dev"} +orml-traits = {default-features = false, version = "0.4.1-dev"} +pallet-asset-registry = {path = '../asset-registry', default-features = false} [features] default = ['std'] diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 921aab7129a..6d834c73a48 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -22,10 +22,12 @@ use frame_support::dispatch::DispatchResult; use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; use frame_support::sp_runtime::RuntimeDebug; use frame_support::traits::Get; -use primitives::{Balance, Price}; +use primitives::{AssetId, Balance, Price, asset::AssetPair, traits::{AMMTransfer, AMMHandlers}}; use sp_std::iter::Sum; +use sp_std::vec; use sp_std::ops::{Add, Index, IndexMut, Mul}; use sp_std::prelude::*; +use sp_std::marker::PhantomData; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -38,13 +40,69 @@ mod tests; // mod benchmarking; -// pub mod weights; - -// use weights::WeightInfo; - // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] +pub struct PriceEntry { + pub price: Price, + pub amount: Balance, + pub liq_amount: Balance, +} + +impl Add for PriceEntry { + type Output = Self; + fn add(self, other: Self) -> Self { + Self { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl Zero for PriceEntry { + fn zero() -> Self { + Self { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + } + } + + fn is_zero(&self) -> bool { + self == &PriceEntry::zero() + } +} + +impl Add for &PriceEntry { + type Output = PriceEntry; + fn add(self, other: Self) -> Self::Output { + PriceEntry { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl<'a> Sum<&'a Self> for PriceEntry { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold( + PriceEntry { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + }, + |a, b| &a + b, + ) + } +} + pub const BUCKET_SIZE: u32 = 10; pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; @@ -172,65 +230,7 @@ impl IndexMut for BucketQueue { } } -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] -pub struct PriceEntry { - price: Price, - amount: Balance, - liq_amount: Balance, -} - -impl Add for PriceEntry { - type Output = Self; - fn add(self, other: Self) -> Self { - Self { - price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), - } - } -} - -impl Zero for PriceEntry { - fn zero() -> Self { - Self { - price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), - } - } - - fn is_zero(&self) -> bool { - self == &PriceEntry::zero() - } -} - -impl Add for &PriceEntry { - type Output = PriceEntry; - fn add(self, other: Self) -> Self::Output { - PriceEntry { - price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), - } - } -} - -impl<'a> Sum<&'a Self> for PriceEntry { - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold( - PriceEntry { - price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), - }, - |a, b| &a + b, - ) - } -} +pub type AssetPairId = Vec; #[frame_support::pallet] pub mod pallet { @@ -262,10 +262,7 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event { - Placeholder(), - } + pub enum Event {} #[pallet::storage] #[pallet::getter(fn asset_count)] @@ -273,25 +270,28 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn price_buffer)] - pub type PriceBuffer = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; + pub type PriceBuffer = StorageMap<_, Blake2_128Concat, AssetPairId, Vec, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_ten)] - pub type PriceDataTen = StorageValue<_, Vec<(T::AccountId, BucketQueue)>, ValueQuery>; + pub type PriceDataTen = StorageValue<_, Vec<(AssetPairId, BucketQueue)>, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_hundred)] - pub type PriceDataHundred = StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + pub type PriceDataHundred = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_thousand)] - pub type PriceDataThousand = StorageMap<_, Blake2_128Concat, T::AccountId, BucketQueue, ValueQuery>; + pub type PriceDataThousand = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { Self::update_data(); - Weight::zero() + + PriceBuffer::::remove_all(); + + Weight::zero() // TODO } } @@ -300,8 +300,8 @@ pub mod pallet { } impl Pallet { - fn new_entry(asset_pair: T::AccountId) -> DispatchResult { - PriceDataTen::::append((asset_pair, BucketQueue::default())); + pub fn on_create_pool(asset_pair: AssetPair) -> DispatchResult { + PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); let incremented_asset_count = Self::asset_count() .checked_add(1) @@ -311,20 +311,23 @@ impl Pallet { Ok(()) } - fn on_trade(asset_pair: T::AccountId, entry: PriceEntry) -> DispatchResult { - if PriceBuffer::::contains_key(&asset_pair) { - PriceBuffer::::append(asset_pair, entry); + pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) -> DispatchResult { + let asset_pair_id = asset_pair.name(); + if PriceBuffer::::contains_key(&asset_pair_id) { + PriceBuffer::::append(asset_pair_id, price_entry); } else { - PriceBuffer::::insert(asset_pair, vec![entry]); + PriceBuffer::::insert(asset_pair_id, vec![price_entry]); } Ok(()) } fn update_data() -> DispatchResult { - PriceDataTen::::try_mutate(|data_ten| -> DispatchResult { - for (asset_pair, data) in data_ten.iter_mut() { - let maybe_price = >::try_get(asset_pair); + // TODO: maybe create a separate storage for liquidity_data and process it separately + + PriceDataTen::::mutate(|data_ten| -> DispatchResult { + for (asset_pair_id, data) in data_ten.iter_mut() { + let maybe_price = >::try_get(asset_pair_id); let result = if let Ok(prices) = maybe_price { PriceInfo::calculate_price_info(prices.as_slice()).ok_or(Error::::PriceComputationError)? } else { @@ -334,8 +337,6 @@ impl Pallet { data.update_last(result); } - PriceBuffer::::remove_all(); - Ok(()) })?; @@ -375,3 +376,22 @@ impl Pallet { Ok(()) } } + +pub struct PriceOracleHandler(PhantomData); +impl AMMHandlers for PriceOracleHandler { + fn on_create_pool(asset_pair: AssetPair) { + Pallet::::on_create_pool(asset_pair); + } + + fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance) { + let (price, amount) = amm_transfer.normalize_price().unwrap_or((Zero::zero(), Zero::zero())); + + let price_entry = PriceEntry { + price, + amount, + liq_amount, + }; + + Pallet::::on_trade(amm_transfer.assets, price_entry); + } +} diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index b8e35248c39..d76b187ec06 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -1,22 +1,25 @@ use crate as price_oracle; use crate::Config; use frame_support::parameter_types; +use orml_traits::parameter_type_with_key; use frame_support::traits::OnInitialize; use frame_system; -use primitives::{AssetId, Balance, Price}; +use primitives::{fee, AssetId, Balance, Price, traits::AssetPairAccountIdFor}; +use primitives::asset::AssetPair; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup, Zero}, }; -use price_oracle::PriceEntry; +use price_oracle::{PriceEntry, PriceOracleHandler}; +pub type Amount = i128; pub type AccountId = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; -pub const ASSET_PAIR_A: AccountId = 1_000; -pub const ASSET_PAIR_B: AccountId = 2_000; +pub const ASSET_PAIR_A: AssetPair = AssetPair{ asset_in: 1_000, asset_out: 2_000 }; +pub const ASSET_PAIR_B: AssetPair = AssetPair{ asset_in: 1_000, asset_out: 3_000 }; pub const PRICE_ENTRY_1: PriceEntry = PriceEntry {price: Price::from_inner(2000000000000000000), amount: 1_000, liq_amount: 2_000}; pub const PRICE_ENTRY_2: PriceEntry = PriceEntry {price: Price::from_inner(5000000000000000000), amount: 3_000, liq_amount: 4_000}; @@ -29,6 +32,9 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, PriceOracle: price_oracle::{Pallet, Call, Storage, Event}, + XYK: pallet_xyk::{Pallet, Call, Storage, Event}, + Currency: orml_tokens::{Pallet, Event}, + AssetRegistry: pallet_asset_registry::{Pallet, Storage}, } ); @@ -64,6 +70,57 @@ impl frame_system::Config for Test { type OnSetCode = (); } +pub struct AssetPairAccountIdTest(); + +impl AssetPairAccountIdFor for AssetPairAccountIdTest { + fn from_assets(asset_a: AssetId, asset_b: AssetId) -> u64 { + let mut a = asset_a as u128; + let mut b = asset_b as u128; + if a > b { + let tmp = a; + a = b; + b = tmp; + } + return (a * 1000 + b) as u64; + } +} + +parameter_types! { + pub const HdxAssetId: u32 = 0; + pub ExchangeFeeRate: fee::Fee = fee::Fee::default(); +} + +impl pallet_xyk::Config for Test { + type Event = Event; + type AssetPairAccountId = AssetPairAccountIdTest; + type Currency = Currency; + type NativeAssetId = HdxAssetId; + type WeightInfo = (); + type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = PriceOracleHandler; +} + +impl pallet_asset_registry::Config for Test { + type AssetId = AssetId; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: AssetId| -> Balance { + Zero::zero() + }; +} + +impl orml_tokens::Config for Test { + type Event = Event; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = AssetId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type OnDust = (); + type MaxLocks = (); +} + parameter_types! { pub const BucketLength: u32 = 10; pub const BucketDepth: u32 = 4; diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index d1f46a8c340..cbad21d8e7a 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -46,12 +46,12 @@ fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { assert_eq!(PriceOracle::asset_count(), 0); assert_eq!( - >::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), + >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); assert_eq!( - >::get().contains(&(ASSET_PAIR_A, BucketQueue::default())), + >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), true ); assert_eq!(PriceOracle::asset_count(), 1); @@ -61,13 +61,13 @@ fn add_new_asset_pair_should_work() { #[test] fn on_trade_should_work() { new_test_ext().execute_with(|| { - assert_eq!(>::try_get(ASSET_PAIR_A), Err(())); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); let mut vec = Vec::new(); vec.push(PRICE_ENTRY_1); vec.push(PRICE_ENTRY_2); - assert_eq!(>::try_get(ASSET_PAIR_A), Ok(vec)); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); }); } @@ -76,8 +76,8 @@ fn update_data_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_B)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); @@ -87,12 +87,12 @@ fn update_data_should_work() { let data_ten_a = PriceOracle::price_data_ten() .iter() - .find(|&x| x.0 == ASSET_PAIR_A) + .find(|&x| x.0 == ASSET_PAIR_A.name()) .unwrap() .1; let data_ten_b = PriceOracle::price_data_ten() .iter() - .find(|&x| x.0 == ASSET_PAIR_B) + .find(|&x| x.0 == ASSET_PAIR_B.name()) .unwrap() .1; @@ -118,14 +118,14 @@ fn update_empty_data_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_B)); - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_B)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); assert_ok!(PriceOracle::update_data()); let data_ten = PriceOracle::price_data_ten() .iter() - .find(|&x| x.0 == ASSET_PAIR_A) + .find(|&x| x.0 == ASSET_PAIR_A.name()) .unwrap() .1; @@ -215,7 +215,7 @@ fn bucket_queue_should_work() { #[test] fn continuous_trades_should_work() { ExtBuilder.build().execute_with(|| { - assert_ok!(PriceOracle::new_entry(ASSET_PAIR_A)); + assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); for i in 0..210 { System::set_block_number(i); diff --git a/pallets/transaction-multi-payment/benchmarking/src/mock.rs b/pallets/transaction-multi-payment/benchmarking/src/mock.rs index 869e2aa9f9f..eaf0e202565 100644 --- a/pallets/transaction-multi-payment/benchmarking/src/mock.rs +++ b/pallets/transaction-multi-payment/benchmarking/src/mock.rs @@ -31,10 +31,9 @@ use sp_runtime::{ use frame_support::weights::IdentityFee; use orml_currencies::BasicCurrencyAdapter; use pallet_transaction_multi_payment::MultiCurrencyAdapter; -use primitives::{Amount, AssetId, Balance}; +use primitives::{Amount, AssetId, Balance, traits::AssetPairAccountIdFor}; use frame_support::traits::Get; -use pallet_xyk::AssetPairAccountIdFor; use std::cell::RefCell; use frame_benchmarking::frame_support::weights::Pays; @@ -168,6 +167,7 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HdxAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = (); } parameter_type_with_key! { diff --git a/pallets/transaction-multi-payment/src/mock.rs b/pallets/transaction-multi-payment/src/mock.rs index 63d4bad87dc..ff9269fb50d 100644 --- a/pallets/transaction-multi-payment/src/mock.rs +++ b/pallets/transaction-multi-payment/src/mock.rs @@ -31,9 +31,8 @@ use sp_runtime::{ use frame_support::weights::IdentityFee; use frame_support::weights::Weight; use orml_currencies::BasicCurrencyAdapter; -use primitives::{Amount, AssetId, Balance, Price}; +use primitives::{Amount, AssetId, Balance, Price, traits::AssetPairAccountIdFor}; -use pallet_xyk::AssetPairAccountIdFor; use std::cell::RefCell; use frame_support::traits::{GenesisBuild, Get}; @@ -198,6 +197,7 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HdxAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = (); } parameter_type_with_key! { diff --git a/pallets/xyk/Cargo.toml b/pallets/xyk/Cargo.toml index 04a9f220fd2..b182bde0372 100644 --- a/pallets/xyk/Cargo.toml +++ b/pallets/xyk/Cargo.toml @@ -47,6 +47,7 @@ sp-std = {default-features = false, version = '3.0.0'} [dev-dependencies] sp-io = {default-features = false, version = '3.0.0'} +pallet-price-oracle = {path = '../price-oracle', default-features = false} [features] default = ['std'] diff --git a/pallets/xyk/src/lib.rs b/pallets/xyk/src/lib.rs index b62ec115397..a5b4aeff257 100644 --- a/pallets/xyk/src/lib.rs +++ b/pallets/xyk/src/lib.rs @@ -36,7 +36,7 @@ use frame_support::{dispatch::DispatchResult, ensure, traits::Get, transactional use frame_system::ensure_signed; use primitives::{ asset::AssetPair, fee, traits::AMM, AssetId, Balance, Price, MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, - MIN_TRADING_LIMIT, + MIN_TRADING_LIMIT, traits::AMMHandlers, }; use sp_std::{marker::PhantomData, vec, vec::Vec}; @@ -44,7 +44,7 @@ use frame_support::sp_runtime::app_crypto::sp_core::crypto::UncheckedFrom; use frame_support::sp_runtime::FixedPointNumber; use orml_traits::{MultiCurrency, MultiCurrencyExtended}; use primitives::fee::WithFee; -use primitives::traits::AMMTransfer; +use primitives::traits::{AMMTransfer, AssetPairAccountIdFor}; use primitives::Amount; #[cfg(test)] @@ -94,6 +94,9 @@ pub mod pallet { /// Trading fee rate #[pallet::constant] type GetExchangeFee: Get; + + /// AMM handlers + type AMMHandler: AMMHandlers; } #[pallet::error] @@ -260,6 +263,8 @@ pub mod pallet { let share_token = >::get_or_create_asset(token_name)?.into(); + T::AMMHandler::on_create_pool(asset_pair); + >::insert(&pair_account, &share_token); >::insert(&pair_account, (asset_a, asset_b)); @@ -503,10 +508,6 @@ pub mod pallet { } } -pub trait AssetPairAccountIdFor { - fn from_assets(asset_a: AssetId, asset_b: AssetId) -> AccountId; -} - pub struct AssetPairAccountId(PhantomData); impl AssetPairAccountIdFor for AssetPairAccountId @@ -695,6 +696,9 @@ impl AMM for Pallet { fn execute_sell(transfer: &AMMTransfer) -> DispatchResult { let pair_account = Self::get_pair_id(transfer.assets); + let total_liquidity = Self::total_liquidity(&pair_account); + T::AMMHandler::on_trade(transfer, total_liquidity); + if transfer.discount && transfer.discount_amount > 0u128 { let native_asset = T::NativeAssetId::get(); T::Currency::withdraw(native_asset, &transfer.origin, transfer.discount_amount)?; @@ -828,6 +832,9 @@ impl AMM for Pallet { fn execute_buy(transfer: &AMMTransfer) -> DispatchResult { let pair_account = Self::get_pair_id(transfer.assets); + let total_liquidity = Self::total_liquidity(&pair_account); + T::AMMHandler::on_trade(transfer, total_liquidity); + if transfer.discount && transfer.discount_amount > 0 { let native_asset = T::NativeAssetId::get(); T::Currency::withdraw(native_asset, &transfer.origin, transfer.discount_amount)?; diff --git a/pallets/xyk/src/mock.rs b/pallets/xyk/src/mock.rs index ceba0ca3e0a..7250d89bae7 100644 --- a/pallets/xyk/src/mock.rs +++ b/pallets/xyk/src/mock.rs @@ -16,7 +16,7 @@ // limitations under the License. use crate as xyk; -use crate::{AssetPairAccountIdFor, Config}; +use crate::Config; use frame_support::parameter_types; use frame_system as system; use orml_traits::parameter_type_with_key; @@ -27,7 +27,7 @@ use sp_runtime::{ }; use frame_support::traits::{GenesisBuild, Get}; -use primitives::{fee, AssetId, Balance}; +use primitives::{fee, AssetId, Balance, traits::AssetPairAccountIdFor}; use std::cell::RefCell; @@ -145,6 +145,7 @@ impl Config for Test { type NativeAssetId = NativeAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; + type AMMHandler = (); } pub struct ExtBuilder { diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index cbecd93e409..fec054fbb78 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -18,7 +18,10 @@ #![allow(clippy::upper_case_acronyms)] use frame_support::dispatch; +use frame_support::sp_runtime::traits::CheckedDiv; use sp_std::vec::Vec; +use crate::{Balance, Price}; +use crate::asset::AssetPair; /// Hold information to perform amm transfer /// Contains also exact amount which will be sold/bought @@ -32,6 +35,25 @@ pub struct AMMTransfer { pub fee: (AssetId, Balance), } +impl AMMTransfer +where + Balance: Copy +{ + pub fn normalize_price(&self) -> Option<(Price, Balance)> { + let ordered_asset_pair = self.assets.ordered_pair(); + let (balance_a, balance_b) = if ordered_asset_pair.0 == self.assets.asset_in { + (self.amount, self.amount_out) + } else { + (self.amount_out, self.amount) + }; + + let price_a = Price::from(balance_a); + let price_b = Price::from(balance_b); + let price = price_a.checked_div(&price_b); + price.map(|p| (p, balance_a)) + } +} + /// Traits for handling AMM Pool trades. pub trait AMM { /// Check if both assets exist in a pool. @@ -106,3 +128,20 @@ pub trait Resolver { /// Intention ```intention``` must be validated prior to call this function. fn resolve_matched_intentions(pair_account: &AccountId, intention: &Intention, matched: &[Intention]); } + +pub trait AMMHandlers +{ + fn on_create_pool(asset_pair: AssetPair); + // fn on_add_liquidity(asset_pair: AssetPair, amount: Balance); + // fn on_remove_liquidity(asset_pair: AssetPair, amount: Balance); + fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance); +} + +impl AMMHandlers for () { + fn on_create_pool(_asset_pair: AssetPair) {} + fn on_trade(_amm_transfer: &AMMTransfer, _liq_amount: Balance) {} +} + +pub trait AssetPairAccountIdFor { + fn from_assets(asset_a: AssetId, asset_b: AssetId) -> AccountId; +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index eb2ce2ca9be..907f86016a3 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -51,8 +51,6 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::BuildStorage; pub use sp_runtime::{FixedPointNumber, Perbill, Permill, Perquintill, Percent}; -use primitives::fee; - mod currency; mod weights; @@ -62,7 +60,7 @@ use pallet_xyk_rpc_runtime_api as xyk_rpc; use orml_currencies::BasicCurrencyAdapter; use orml_traits::parameter_type_with_key; -pub use primitives::{Amount, AssetId, Balance, Moment, CORE_ASSET_ID}; +pub use primitives::{fee, Amount, AssetId, Balance, Moment, CORE_ASSET_ID}; pub use pallet_asset_registry; @@ -418,6 +416,7 @@ impl pallet_xyk::Config for Runtime { type NativeAssetId = NativeAssetId; type WeightInfo = weights::xyk::HydraWeight; type GetExchangeFee = ExchangeFee; + type AMMHandler = pallet_price_oracle::PriceOracleHandler; } impl pallet_exchange::Config for Runtime { From 8bb43c1fd982181dce266f793a263f5ab3b1bcb9 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 4 Aug 2021 13:03:44 +0100 Subject: [PATCH 06/28] ignore zero prices and move impl types to own file --- Cargo.lock | 407 +++++++++++++++--------------- pallets/price-oracle/src/lib.rs | 255 +++---------------- pallets/price-oracle/src/mock.rs | 28 +- pallets/price-oracle/src/tests.rs | 168 ++++++++++-- pallets/price-oracle/src/types.rs | 214 ++++++++++++++++ primitives/src/traits.rs | 7 +- 6 files changed, 623 insertions(+), 456 deletions(-) create mode 100644 pallets/price-oracle/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index f18f4f58c98..3053fafc3a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -465,7 +465,7 @@ dependencies = [ "pallet-sudo", "pallet-transaction-payment-rpc", "pallet-xyk-rpc", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-cli", "polkadot-parachain", "polkadot-primitives", @@ -567,7 +567,7 @@ dependencies = [ "pallet-xyk", "pallet-xyk-rpc-runtime-api", "parachain-info", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "primitives", "serde", @@ -611,7 +611,7 @@ dependencies = [ "futures 0.3.15", "hex", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sc-client-api", "sc-keystore", @@ -643,7 +643,7 @@ dependencies = [ "jsonrpc-derive", "jsonrpc-pubsub", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-rpc", "serde", "serde_json", @@ -656,7 +656,7 @@ name = "beefy-primitives" version = "0.1.0" source = "git+https://github.com/paritytech/grandpa-bridge-gadget?branch=polkadot-v0.9.7#299dd5fd3fabd99c3c919f1312001283ddc5b365" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-application-crypto", "sp-core", @@ -845,7 +845,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "finality-grandpa", "frame-support", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-core", "sp-finality-grandpa", @@ -861,7 +861,7 @@ dependencies = [ "bp-runtime", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-std", ] @@ -874,7 +874,7 @@ dependencies = [ "bp-runtime", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-core", "sp-runtime", @@ -891,7 +891,7 @@ dependencies = [ "bp-messages", "bp-polkadot-core", "bp-runtime", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-runtime", "sp-std", @@ -906,7 +906,7 @@ dependencies = [ "frame-support", "hash-db", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -923,7 +923,7 @@ dependencies = [ "bp-header-chain", "ed25519-dalek", "finality-grandpa", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-finality-grandpa", "sp-runtime", @@ -939,7 +939,7 @@ dependencies = [ "bp-messages", "bp-polkadot-core", "bp-runtime", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-runtime", "sp-std", @@ -1578,7 +1578,7 @@ dependencies = [ "cumulus-client-network", "cumulus-primitives-core", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -1602,7 +1602,7 @@ dependencies = [ "cumulus-client-consensus-common", "cumulus-primitives-core", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "polkadot-client", "sc-client-api", @@ -1631,7 +1631,7 @@ dependencies = [ "async-trait", "dyn-clone", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "polkadot-runtime", "sc-client-api", @@ -1656,7 +1656,7 @@ dependencies = [ "cumulus-client-consensus-common", "cumulus-primitives-core", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "polkadot-client", "sc-client-api", @@ -1679,7 +1679,7 @@ dependencies = [ "derive_more", "futures 0.3.15", "futures-timer 3.0.2", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "polkadot-client", "polkadot-node-primitives", @@ -1703,7 +1703,7 @@ dependencies = [ "cumulus-primitives-core", "futures 0.3.15", "futures-timer 3.0.2", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-overseer", @@ -1727,7 +1727,7 @@ dependencies = [ "cumulus-client-consensus-common", "cumulus-client-pov-recovery", "cumulus-primitives-core", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "polkadot-overseer", "polkadot-primitives", @@ -1755,7 +1755,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-aura", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-application-crypto", "sp-consensus-aura", @@ -1776,7 +1776,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "serde", "sp-core", @@ -1809,7 +1809,7 @@ source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.7#c5c3a dependencies = [ "frame-support", "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-core-primitives", "polkadot-parachain", "polkadot-primitives", @@ -1827,7 +1827,7 @@ source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.7#c5c3a dependencies = [ "async-trait", "cumulus-primitives-core", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-client", "sc-client-api", "sp-api", @@ -2277,7 +2277,7 @@ dependencies = [ "futures-timer 3.0.2", "log", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", ] @@ -2335,7 +2335,7 @@ name = "fork-tree" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", ] [[package]] @@ -2357,7 +2357,7 @@ dependencies = [ "frame-system", "linregress", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "paste", "sp-api", "sp-io", @@ -2376,7 +2376,7 @@ dependencies = [ "chrono", "frame-benchmarking", "handlebars", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-cli", "sc-client-db", "sc-executor", @@ -2397,7 +2397,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-arithmetic", "sp-npos-elections", "sp-std", @@ -2410,7 +2410,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -2423,7 +2423,7 @@ name = "frame-metadata" version = "13.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-core", "sp-std", @@ -2441,7 +2441,7 @@ dependencies = [ "log", "max-encoded-len", "once_cell", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "paste", "serde", "smallvec 1.6.1", @@ -2498,7 +2498,7 @@ dependencies = [ "frame-support", "impl-trait-for-tuples", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-core", "sp-io", @@ -2515,7 +2515,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-runtime", "sp-std", @@ -2526,7 +2526,7 @@ name = "frame-system-rpc-runtime-api" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", ] @@ -2536,7 +2536,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "frame-support", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-runtime", "sp-std", @@ -3307,11 +3307,11 @@ dependencies = [ [[package]] name = "impl-codec" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df170efa359aebdd5cb7fe78edcc67107748e4737bdca8a8fb40d15ea7a877ed" +checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", ] [[package]] @@ -3711,7 +3711,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-parachains", @@ -4439,7 +4439,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "impl-trait-for-tuples", "max-encoded-len-derive", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.9.0", ] @@ -4933,7 +4933,7 @@ dependencies = [ "frame-system", "orml-traits", "orml-utilities", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-io", "sp-runtime", @@ -4949,7 +4949,7 @@ dependencies = [ "frame-support", "frame-system", "funty", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -4964,7 +4964,7 @@ dependencies = [ "frame-system", "max-encoded-len", "orml-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -4979,7 +4979,7 @@ dependencies = [ "impl-trait-for-tuples", "num-traits", "orml-utilities", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-io", "sp-runtime", @@ -4993,7 +4993,7 @@ version = "0.4.1-dev" source = "git+https://github.com/open-web3-stack/open-runtime-module-library?rev=443ee91bc2ca5f1fc155c0378eef6e89b67e2e97#443ee91bc2ca5f1fc155c0378eef6e89b67e2e97" dependencies = [ "frame-support", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-io", "sp-runtime", @@ -5008,7 +5008,7 @@ dependencies = [ "frame-support", "frame-system", "max-encoded-len", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-io", "sp-runtime", @@ -5030,7 +5030,7 @@ version = "3.1.0" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "primitives", "serde", @@ -5050,7 +5050,7 @@ dependencies = [ "frame-system", "pallet-session", "pallet-timestamp", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-consensus-aura", "sp-runtime", @@ -5065,7 +5065,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-session", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-authority-discovery", "sp-runtime", @@ -5080,7 +5080,7 @@ dependencies = [ "frame-support", "frame-system", "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-authorship", "sp-runtime", "sp-std", @@ -5098,7 +5098,7 @@ dependencies = [ "pallet-authorship", "pallet-session", "pallet-timestamp", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-consensus-babe", "sp-consensus-vrf", @@ -5119,7 +5119,7 @@ dependencies = [ "frame-system", "log", "max-encoded-len", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-runtime", "sp-std", ] @@ -5133,7 +5133,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-session", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -5148,7 +5148,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-treasury", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-runtime", "sp-std", ] @@ -5166,7 +5166,7 @@ dependencies = [ "frame-system", "log", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-finality-grandpa", "sp-runtime", @@ -5185,7 +5185,7 @@ dependencies = [ "log", "pallet-authorship", "pallet-session", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-staking", @@ -5201,7 +5201,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5216,7 +5216,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-io", "sp-runtime", @@ -5233,7 +5233,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "rand 0.7.3", "sp-arithmetic", "sp-core", @@ -5253,7 +5253,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-npos-elections", @@ -5271,7 +5271,7 @@ dependencies = [ "orml-traits", "pallet-asset-registry", "pallet-xyk", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "primitives", "serde", @@ -5295,7 +5295,7 @@ dependencies = [ "pallet-asset-registry", "pallet-exchange", "pallet-xyk", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitives", "serde", "sp-core", @@ -5313,7 +5313,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-arithmetic", "sp-runtime", "sp-std", @@ -5330,7 +5330,7 @@ dependencies = [ "log", "pallet-authorship", "pallet-session", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-core", "sp-finality-grandpa", @@ -5350,7 +5350,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5366,7 +5366,7 @@ dependencies = [ "frame-system", "log", "pallet-authorship", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-application-crypto", "sp-core", "sp-io", @@ -5383,7 +5383,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-keyring", @@ -5402,7 +5402,7 @@ dependencies = [ "hydra-dx-math", "orml-tokens", "orml-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "primitives", "serde", @@ -5422,7 +5422,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5438,7 +5438,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-mmr-primitives", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5453,7 +5453,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-core", @@ -5470,7 +5470,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "pallet-mmr-primitives", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-blockchain", @@ -5496,7 +5496,7 @@ dependencies = [ "pallet-transaction-multi-payment", "pallet-transaction-payment", "pallet-xyk", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitives", "serde", "sp-core", @@ -5514,7 +5514,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5531,7 +5531,7 @@ dependencies = [ "orml-nft", "orml-utilities", "pallet-balances", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitives", "serde", "sp-core", @@ -5547,7 +5547,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5562,7 +5562,7 @@ dependencies = [ "frame-system", "log", "pallet-balances", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-staking", @@ -5585,7 +5585,7 @@ dependencies = [ "pallet-offences", "pallet-session", "pallet-staking", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-runtime", "sp-staking", "sp-std", @@ -5601,7 +5601,7 @@ dependencies = [ "orml-traits", "pallet-asset-registry", "pallet-xyk", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitives", "serde", "sp-core", @@ -5620,7 +5620,7 @@ dependencies = [ "frame-support", "frame-system", "max-encoded-len", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5634,7 +5634,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "safe-mix", "sp-runtime", "sp-std", @@ -5648,7 +5648,7 @@ dependencies = [ "enumflags2", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5663,7 +5663,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5679,7 +5679,7 @@ dependencies = [ "impl-trait-for-tuples", "log", "pallet-timestamp", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5712,7 +5712,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "rand_chacha 0.2.2", "sp-runtime", "sp-std", @@ -5730,7 +5730,7 @@ dependencies = [ "log", "pallet-authorship", "pallet-session", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "paste", "rand_chacha 0.2.2", "serde", @@ -5769,7 +5769,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-io", "sp-runtime", "sp-std", @@ -5785,7 +5785,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-inherents", "sp-io", "sp-runtime", @@ -5802,7 +5802,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-treasury", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -5822,7 +5822,7 @@ dependencies = [ "pallet-balances", "pallet-transaction-payment", "pallet-xyk", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "primitives", "serde", @@ -5841,7 +5841,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "smallvec 1.6.1", "sp-core", @@ -5859,7 +5859,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "pallet-transaction-payment-rpc-runtime-api", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-blockchain", "sp-core", @@ -5873,7 +5873,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "pallet-transaction-payment", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-runtime", ] @@ -5888,7 +5888,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "pallet-balances", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -5902,7 +5902,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-io", "sp-runtime", @@ -5918,7 +5918,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-runtime", "sp-std", ] @@ -5930,7 +5930,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -5952,7 +5952,7 @@ dependencies = [ "orml-utilities", "pallet-asset-registry", "pallet-price-oracle", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "primitives", "serde", @@ -5971,7 +5971,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "pallet-xyk-rpc-runtime-api", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-blockchain", @@ -5984,7 +5984,7 @@ dependencies = [ name = "pallet-xyk-rpc-runtime-api" version = "3.0.0" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-runtime", @@ -6000,7 +6000,7 @@ dependencies = [ "cumulus-primitives-core", "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", ] @@ -6053,22 +6053,23 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b310f220c335f9df1b3d2e9fbe3890bbfeef5030dad771620f48c5c229877cd3" +checksum = "8975095a2a03bbbdc70a74ab11a4f76a6d0b84680d87c68d722531b0ac28e8a9" dependencies = [ "arrayvec 0.7.1", "bitvec 0.20.4", "byte-slice-cast 1.0.0", + "impl-trait-for-tuples", "parity-scale-codec-derive", "serde", ] [[package]] name = "parity-scale-codec-derive" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81038e13ca2c32587201d544ea2e6b6c47120f1e4eae04478f9f60b6bcb89145" +checksum = "40dbbfef7f0a1143c5b06e0d76a6278e25dac0bc1af4be51a0fbb73f07e7ad09" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", @@ -6450,7 +6451,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "futures 0.3.15", "lru", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-erasure-coding", "polkadot-node-core-runtime-api", "polkadot-node-network-protocol", @@ -6473,7 +6474,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "futures 0.3.15", "lru", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-erasure-coding", "polkadot-node-network-protocol", "polkadot-node-primitives", @@ -6559,7 +6560,7 @@ name = "polkadot-core-primitives" version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "sp-core", "sp-runtime", @@ -6571,7 +6572,7 @@ name = "polkadot-erasure-coding" version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-primitives", "polkadot-primitives", "reed-solomon-novelpoly", @@ -6602,7 +6603,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "async-trait", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "polkadot-node-network-protocol", "polkadot-node-subsystem", @@ -6621,7 +6622,7 @@ version = "0.1.0" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-erasure-coding", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -6644,7 +6645,7 @@ dependencies = [ "futures-timer 3.0.2", "kvdb", "merlin", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-jaeger", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -6671,7 +6672,7 @@ dependencies = [ "futures 0.3.15", "futures-timer 3.0.2", "kvdb", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-erasure-coding", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -6722,7 +6723,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "async-trait", "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-core-pvf", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -6791,7 +6792,7 @@ dependencies = [ "futures 0.3.15", "futures-timer 3.0.2", "libc", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "pin-project 1.0.7", "polkadot-core-primitives", "polkadot-parachain", @@ -6834,7 +6835,7 @@ dependencies = [ "lazy_static", "log", "mick-jaeger", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "polkadot-node-primitives", "polkadot-primitives", @@ -6849,7 +6850,7 @@ version = "0.1.0" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-jaeger", "polkadot-node-primitives", "polkadot-primitives", @@ -6864,7 +6865,7 @@ version = "0.1.0" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "polkadot-primitives", "polkadot-statement-table", @@ -6894,7 +6895,7 @@ dependencies = [ "lazy_static", "log", "mick-jaeger", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "pin-project 1.0.7", "polkadot-node-jaeger", @@ -6921,7 +6922,7 @@ dependencies = [ "futures-timer 3.0.2", "lru", "metered-channel", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "pin-project 1.0.7", "polkadot-node-jaeger", "polkadot-node-network-protocol", @@ -6964,7 +6965,7 @@ version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ "derive_more", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "polkadot-core-primitives", "serde", @@ -6981,7 +6982,7 @@ dependencies = [ "bitvec 0.20.4", "frame-system", "hex-literal", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "polkadot-core-primitives", "polkadot-parachain", @@ -7035,7 +7036,7 @@ dependencies = [ "jsonrpc-core", "pallet-mmr-rpc", "pallet-transaction-payment-rpc", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "sc-chain-spec", "sc-client-api", @@ -7109,7 +7110,7 @@ dependencies = [ "pallet-treasury", "pallet-utility", "pallet-vesting", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "polkadot-runtime-common", "rustc-hex", @@ -7160,7 +7161,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-treasury", "pallet-vesting", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "polkadot-runtime-parachains", "rustc-hex", @@ -7199,7 +7200,7 @@ dependencies = [ "pallet-staking", "pallet-timestamp", "pallet-vesting", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "rand 0.8.4", "rand_chacha 0.3.1", @@ -7317,7 +7318,7 @@ dependencies = [ "arrayvec 0.5.2", "futures 0.3.15", "indexmap", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", @@ -7335,7 +7336,7 @@ name = "polkadot-statement-table" version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-primitives", "sp-core", ] @@ -7345,7 +7346,7 @@ name = "polkadot-test-client" version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-node-subsystem", "polkadot-primitives", "polkadot-test-runtime", @@ -7396,7 +7397,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-vesting", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-common", @@ -7532,7 +7533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2415937401cb030a2a0a4d922483f945fa068f52a7dbb22ce0fe5f2b6f6adace" dependencies = [ "fixed-hash", - "impl-codec 0.5.0", + "impl-codec 0.5.1", "impl-rlp", "impl-serde", "uint", @@ -7544,7 +7545,7 @@ version = "4.2.0" dependencies = [ "frame-support", "frame-system", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.8.0", "serde", "sp-core", @@ -8044,7 +8045,7 @@ dependencies = [ "jsonrpsee-proc-macros", "jsonrpsee-ws-client", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "serde_json", "sp-core", @@ -8151,7 +8152,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-utility", "pallet-xcm", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-common", @@ -8333,7 +8334,7 @@ dependencies = [ "ip_network", "libp2p", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "prost", "prost-build", "rand 0.7.3", @@ -8357,7 +8358,7 @@ dependencies = [ "futures 0.3.15", "futures-timer 3.0.2", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-block-builder", "sc-client-api", "sc-proposer-metrics", @@ -8377,7 +8378,7 @@ name = "sc-block-builder" version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sp-api", "sp-block-builder", @@ -8394,7 +8395,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-chain-spec-derive", "sc-consensus-babe", "sc-consensus-epochs", @@ -8432,7 +8433,7 @@ dependencies = [ "libp2p", "log", "names", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "rand 0.7.3", "regex", "rpassword", @@ -8470,7 +8471,7 @@ dependencies = [ "kvdb", "lazy_static", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sc-executor", "sp-api", @@ -8505,7 +8506,7 @@ dependencies = [ "linked-hash-map", "log", "parity-db", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "parking_lot 0.11.1", "sc-client-api", @@ -8544,7 +8545,7 @@ dependencies = [ "futures 0.3.15", "futures-timer 3.0.2", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-block-builder", "sc-client-api", "sc-consensus-slots", @@ -8580,7 +8581,7 @@ dependencies = [ "num-bigint", "num-rational 0.2.4", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "pdqselect", "rand 0.7.3", @@ -8641,7 +8642,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "fork-tree", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sc-consensus", "sp-blockchain", @@ -8658,7 +8659,7 @@ dependencies = [ "futures-timer 3.0.2", "impl-trait-for-tuples", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sc-telemetry", "sp-api", @@ -8696,7 +8697,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-wasm 0.42.2", "parking_lot 0.11.1", "sc-executor-common", @@ -8722,7 +8723,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "derive_more", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "pwasm-utils", "sp-allocator", "sp-core", @@ -8739,7 +8740,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-executor-common", "sp-allocator", "sp-core", @@ -8754,7 +8755,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-wasm 0.42.2", "sc-executor-common", "scoped-tls", @@ -8779,7 +8780,7 @@ dependencies = [ "futures-timer 3.0.2", "linked-hash-map", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "pin-project 1.0.7", "rand 0.7.3", @@ -8819,7 +8820,7 @@ dependencies = [ "jsonrpc-derive", "jsonrpc-pubsub", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sc-finality-grandpa", "sc-rpc", @@ -8839,7 +8840,7 @@ dependencies = [ "futures 0.3.15", "log", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "prost", "sc-client-api", @@ -8896,7 +8897,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "hash-db", "lazy_static", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sc-client-api", "sc-executor", @@ -8935,7 +8936,7 @@ dependencies = [ "log", "lru", "nohash-hasher", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "pin-project 1.0.7", "prost", @@ -8992,7 +8993,7 @@ dependencies = [ "hyper-rustls", "log", "num_cpus", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "rand 0.7.3", "sc-client-api", @@ -9038,7 +9039,7 @@ dependencies = [ "jsonrpc-core", "jsonrpc-pubsub", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sc-block-builder", "sc-client-api", @@ -9075,7 +9076,7 @@ dependencies = [ "jsonrpc-derive", "jsonrpc-pubsub", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "serde", "serde_json", @@ -9122,7 +9123,7 @@ dependencies = [ "jsonrpc-pubsub", "lazy_static", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "parking_lot 0.11.1", "pin-project 1.0.7", @@ -9177,7 +9178,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "parity-util-mem-derive", "parking_lot 0.11.1", @@ -9304,7 +9305,7 @@ dependencies = [ "futures 0.3.15", "intervalier", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "parking_lot 0.11.1", "sc-client-api", @@ -9656,7 +9657,7 @@ version = "0.9.7" source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35bac7408a4cb12a578764217d06f3920b36aa" dependencies = [ "enumn", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "paste", "sp-runtime", "sp-std", @@ -9775,7 +9776,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "hash-db", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api-proc-macro", "sp-core", "sp-runtime", @@ -9803,7 +9804,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "max-encoded-len", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-core", "sp-io", @@ -9817,7 +9818,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "integer-sqrt", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-debug-derive", "sp-std", @@ -9829,7 +9830,7 @@ name = "sp-authority-discovery" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-application-crypto", "sp-runtime", @@ -9842,7 +9843,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "async-trait", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-inherents", "sp-runtime", "sp-std", @@ -9853,7 +9854,7 @@ name = "sp-block-builder" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-inherents", "sp-runtime", @@ -9868,7 +9869,7 @@ dependencies = [ "futures 0.3.15", "log", "lru", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sp-api", "sp-consensus", @@ -9897,7 +9898,7 @@ dependencies = [ "futures-timer 3.0.2", "libp2p", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "serde", "sp-api", @@ -9920,7 +9921,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "async-trait", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-application-crypto", "sp-consensus", @@ -9938,7 +9939,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "async-trait", "merlin", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-application-crypto", @@ -9958,7 +9959,7 @@ name = "sp-consensus-slots" version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-arithmetic", "sp-runtime", ] @@ -9968,7 +9969,7 @@ name = "sp-consensus-vrf" version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "schnorrkel", "sp-core", "sp-runtime", @@ -9996,7 +9997,7 @@ dependencies = [ "max-encoded-len", "merlin", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "parking_lot 0.11.1", "primitive-types 0.9.0", @@ -10045,7 +10046,7 @@ version = "0.9.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "environmental", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-std", "sp-storage", ] @@ -10057,7 +10058,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "finality-grandpa", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-application-crypto", @@ -10074,7 +10075,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "async-trait", "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-runtime", "sp-std", @@ -10090,7 +10091,7 @@ dependencies = [ "hash-db", "libsecp256k1", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "sp-core", "sp-externalities", @@ -10126,7 +10127,7 @@ dependencies = [ "derive_more", "futures 0.3.15", "merlin", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "schnorrkel", "serde", @@ -10148,7 +10149,7 @@ name = "sp-npos-elections" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-arithmetic", "sp-core", @@ -10206,7 +10207,7 @@ dependencies = [ "impl-trait-for-tuples", "log", "max-encoded-len", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "paste", "rand 0.7.3", @@ -10224,7 +10225,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "primitive-types 0.9.0", "sp-externalities", "sp-runtime-interface-proc-macro", @@ -10261,7 +10262,7 @@ name = "sp-session" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-core", "sp-runtime", @@ -10274,7 +10275,7 @@ name = "sp-staking" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-runtime", "sp-std", ] @@ -10287,7 +10288,7 @@ dependencies = [ "hash-db", "log", "num-traits", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.11.1", "rand 0.7.3", "smallvec 1.6.1", @@ -10313,7 +10314,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "impl-serde", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "ref-cast", "serde", "sp-debug-derive", @@ -10341,7 +10342,7 @@ dependencies = [ "async-trait", "futures-timer 3.0.2", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-api", "sp-inherents", "sp-runtime", @@ -10357,7 +10358,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "erased-serde", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parking_lot 0.10.2", "serde", "serde_json", @@ -10376,7 +10377,7 @@ dependencies = [ "derive_more", "futures 0.3.15", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-api", "sp-blockchain", @@ -10391,7 +10392,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "async-trait", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-inherents", "sp-runtime", @@ -10406,7 +10407,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "hash-db", "memory-db", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-core", "sp-std", "trie-db", @@ -10431,7 +10432,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "impl-serde", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "serde", "sp-runtime", "sp-std", @@ -10443,7 +10444,7 @@ name = "sp-version-proc-macro" version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "proc-macro-crate 1.0.0", "proc-macro2", "quote", @@ -10456,7 +10457,7 @@ version = "3.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-std", "wasmi", ] @@ -10640,7 +10641,7 @@ dependencies = [ "jsonrpc-core-client", "jsonrpc-derive", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sc-rpc-api", "serde", @@ -10676,7 +10677,7 @@ dependencies = [ "futures 0.3.15", "hash-db", "hex", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-client-api", "sc-client-db", "sc-consensus", @@ -10708,7 +10709,7 @@ dependencies = [ "memory-db", "pallet-babe", "pallet-timestamp", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "parity-util-mem", "sc-service", "serde", @@ -10742,7 +10743,7 @@ version = "2.0.0" source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c572625f6557dfdb19f47474369a0327d51dfbc" dependencies = [ "futures 0.3.15", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sc-block-builder", "sc-client-api", "sc-consensus", @@ -11449,7 +11450,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.7#9c5 dependencies = [ "frame-try-runtime", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "remote-externalities", "sc-chain-spec", "sc-cli", @@ -12137,7 +12138,7 @@ dependencies = [ "pallet-utility", "pallet-vesting", "pallet-xcm", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "polkadot-primitives", "polkadot-runtime-common", @@ -12270,7 +12271,7 @@ source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.7#5d35b dependencies = [ "derivative", "impl-trait-for-tuples", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", ] [[package]] @@ -12282,7 +12283,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "pallet-transaction-payment", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "polkadot-parachain", "sp-arithmetic", "sp-io", @@ -12300,7 +12301,7 @@ dependencies = [ "frame-support", "impl-trait-for-tuples", "log", - "parity-scale-codec 2.1.3", + "parity-scale-codec 2.2.0", "sp-arithmetic", "sp-core", "sp-io", diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 6d834c73a48..c06d3b51431 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -17,20 +17,16 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode}; use frame_support::dispatch::DispatchResult; -use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; -use frame_support::sp_runtime::RuntimeDebug; +use frame_support::sp_runtime::traits::Zero; use frame_support::traits::Get; -use primitives::{AssetId, Balance, Price, asset::AssetPair, traits::{AMMTransfer, AMMHandlers}}; -use sp_std::iter::Sum; -use sp_std::vec; -use sp_std::ops::{Add, Index, IndexMut, Mul}; -use sp_std::prelude::*; +use primitives::{ + asset::AssetPair, + traits::{AMMHandlers, AMMTransfer}, + AssetId, Balance, +}; use sp_std::marker::PhantomData; - -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; +use sp_std::prelude::*; #[cfg(test)] mod mock; @@ -38,198 +34,14 @@ mod mock; #[cfg(test)] mod tests; +mod types; +pub use types::*; + // mod benchmarking; // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] -pub struct PriceEntry { - pub price: Price, - pub amount: Balance, - pub liq_amount: Balance, -} - -impl Add for PriceEntry { - type Output = Self; - fn add(self, other: Self) -> Self { - Self { - price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), - } - } -} - -impl Zero for PriceEntry { - fn zero() -> Self { - Self { - price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), - } - } - - fn is_zero(&self) -> bool { - self == &PriceEntry::zero() - } -} - -impl Add for &PriceEntry { - type Output = PriceEntry; - fn add(self, other: Self) -> Self::Output { - PriceEntry { - price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), - } - } -} - -impl<'a> Sum<&'a Self> for PriceEntry { - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold( - PriceEntry { - price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), - }, - |a, b| &a + b, - ) - } -} - -pub const BUCKET_SIZE: u32 = 10; - -pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; - -pub trait PriceInfoCalculation { - fn calculate_price_info(entries: &[PriceEntry]) -> Option; -} - -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] -pub struct PriceInfo { - avg_price: Price, - volume: Balance, -} - -impl Default for PriceInfo { - fn default() -> Self { - Self { - avg_price: Price::zero(), - volume: Zero::zero(), - } - } -} - -impl Add for &PriceInfo { - type Output = PriceInfo; - fn add(self, other: Self) -> Self::Output { - PriceInfo { - avg_price: self.avg_price.add(other.avg_price), - volume: self.volume.add(other.volume), - } - } -} - -impl<'a> Sum<&'a Self> for PriceInfo { - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold( - PriceInfo { - avg_price: Price::zero(), - volume: Balance::zero(), - }, - |a, b| &a + b, - ) - } -} - -impl PriceInfoCalculation for PriceInfo { - fn calculate_price_info(entries: &[PriceEntry]) -> Option { - let intermediate_result: Vec = entries - .iter() - .map(|x| PriceEntry { - price: x.price.mul(Price::from(x.liq_amount)), - amount: x.amount, - liq_amount: x.liq_amount, - }) - .collect(); - - let sum = intermediate_result.iter().sum::(); - let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liq_amount as u128))?; - Some(PriceInfo { - avg_price: weighted_avg_price, - volume: sum.amount, - }) - } -} - -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] -pub struct BucketQueue { - bucket: Bucket, - last: u32, -} - -impl BucketQueue { - const BUCKET_SIZE: u32 = BUCKET_SIZE; -} - -impl Default for BucketQueue { - fn default() -> Self { - Self { - bucket: Bucket::default(), - last: Self::BUCKET_SIZE - 1, - } - } -} - -pub trait BucketQueueT { - fn update_last(&mut self, price_info: PriceInfo); - fn get_last(&self) -> PriceInfo; - fn calculate_average(&self) -> Option; -} - -impl BucketQueueT for BucketQueue { - fn update_last(&mut self, price_info: PriceInfo) { - self.last = (self.last + 1) % Self::BUCKET_SIZE; - self.bucket[self.last as usize] = price_info; - } - - fn get_last(&self) -> PriceInfo { - self.bucket[self.last as usize] - } - - fn calculate_average(&self) -> Option { - let sum = self.bucket.iter().sum::(); - Some(PriceInfo { - avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128))?, - volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128)?, - }) - } -} - -impl Index for BucketQueue { - type Output = PriceInfo; - fn index(&self, index: usize) -> &Self::Output { - &self.bucket[index] - } -} - -impl IndexMut for BucketQueue { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.bucket[index] - } -} - pub type AssetPairId = Vec; #[frame_support::pallet] @@ -257,17 +69,12 @@ pub mod pallet { #[pallet::error] pub enum Error { PriceDataNotFound, - AssetCountOverflow, PriceComputationError, } #[pallet::event] pub enum Event {} - #[pallet::storage] - #[pallet::getter(fn asset_count)] - pub type NumOfTrackedAssets = StorageValue<_, u32, ValueQuery>; - #[pallet::storage] #[pallet::getter(fn price_buffer)] pub type PriceBuffer = StorageMap<_, Blake2_128Concat, AssetPairId, Vec, ValueQuery>; @@ -291,7 +98,7 @@ pub mod pallet { PriceBuffer::::remove_all(); - Weight::zero() // TODO + Weight::zero() // TODO } } @@ -300,30 +107,19 @@ pub mod pallet { } impl Pallet { - pub fn on_create_pool(asset_pair: AssetPair) -> DispatchResult { - PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); - - let incremented_asset_count = Self::asset_count() - .checked_add(1) - .ok_or(Error::::AssetCountOverflow)?; - >::put(incremented_asset_count); - - Ok(()) - } - - pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) -> DispatchResult { - let asset_pair_id = asset_pair.name(); - if PriceBuffer::::contains_key(&asset_pair_id) { - PriceBuffer::::append(asset_pair_id, price_entry); - } else { - PriceBuffer::::insert(asset_pair_id, vec![price_entry]); + pub fn on_create_pool(asset_pair: AssetPair) { + let data = PriceDataTen::::get(); + if !data.iter().any(|bucket_tuple| bucket_tuple.0 == asset_pair.name()) { + PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); } + } - Ok(()) + pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) { + PriceBuffer::::append(asset_pair.name(), price_entry); } fn update_data() -> DispatchResult { - // TODO: maybe create a separate storage for liquidity_data and process it separately + // TODO: maybe create a separate storage for liquidity_data and process it separately PriceDataTen::::mutate(|data_ten| -> DispatchResult { for (asset_pair_id, data) in data_ten.iter_mut() { @@ -343,7 +139,7 @@ impl Pallet { let now = >::block_number(); if now.is_zero() { return Ok(()); - } // TODO ??? + } // TODO: delete me if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { for element_from_ten in PriceDataTen::::get().iter() { @@ -384,7 +180,16 @@ impl AMMHandlers for Price } fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance) { - let (price, amount) = amm_transfer.normalize_price().unwrap_or((Zero::zero(), Zero::zero())); + let (price, amount) = if let Some(price_tuple) = amm_transfer.normalize_price() { + price_tuple + } else { + return; + }; + + // zero prices are not added to the queue + if price.is_zero() { + return; + } let price_entry = PriceEntry { price, diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index d76b187ec06..f5732733f1e 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -1,28 +1,42 @@ use crate as price_oracle; use crate::Config; use frame_support::parameter_types; -use orml_traits::parameter_type_with_key; use frame_support::traits::OnInitialize; use frame_system; -use primitives::{fee, AssetId, Balance, Price, traits::AssetPairAccountIdFor}; +use orml_traits::parameter_type_with_key; +use price_oracle::{PriceEntry, PriceOracleHandler}; use primitives::asset::AssetPair; +use primitives::{fee, traits::AssetPairAccountIdFor, AssetId, Balance, Price}; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup, Zero}, }; -use price_oracle::{PriceEntry, PriceOracleHandler}; pub type Amount = i128; pub type AccountId = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; -pub const ASSET_PAIR_A: AssetPair = AssetPair{ asset_in: 1_000, asset_out: 2_000 }; -pub const ASSET_PAIR_B: AssetPair = AssetPair{ asset_in: 1_000, asset_out: 3_000 }; +pub const ASSET_PAIR_A: AssetPair = AssetPair { + asset_in: 1_000, + asset_out: 2_000, +}; +pub const ASSET_PAIR_B: AssetPair = AssetPair { + asset_in: 1_000, + asset_out: 3_000, +}; -pub const PRICE_ENTRY_1: PriceEntry = PriceEntry {price: Price::from_inner(2000000000000000000), amount: 1_000, liq_amount: 2_000}; -pub const PRICE_ENTRY_2: PriceEntry = PriceEntry {price: Price::from_inner(5000000000000000000), amount: 3_000, liq_amount: 4_000}; +pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { + price: Price::from_inner(2000000000000000000), + amount: 1_000, + liq_amount: 2_000, +}; +pub const PRICE_ENTRY_2: PriceEntry = PriceEntry { + price: Price::from_inner(5000000000000000000), + amount: 3_000, + liq_amount: 4_000, +}; frame_support::construct_runtime!( pub enum Test where diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index cbad21d8e7a..5a65544dca0 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -20,7 +20,7 @@ pub use crate::mock::{ Event as TestEvent, ExtBuilder, Origin, PriceOracle, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, PRICE_ENTRY_2, }; -use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; +use frame_support::{assert_noop, assert_storage_noop, assert_ok, traits::OnInitialize}; pub fn new_test_ext() -> sp_io::TestExternalities { let ext = ExtBuilder.build(); @@ -44,17 +44,27 @@ fn expect_events(e: Vec) { #[test] fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { - assert_eq!(PriceOracle::asset_count(), 0); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); + PriceOracle::on_create_pool(ASSET_PAIR_A); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), true ); - assert_eq!(PriceOracle::asset_count(), 1); + }); +} + +#[test] +fn add_existing_asset_pair_should_not_work() { + new_test_ext().execute_with(|| { + assert_eq!( + >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), + false + ); + PriceOracle::on_create_pool(ASSET_PAIR_A); + assert_storage_noop!(PriceOracle::on_create_pool(ASSET_PAIR_A)); }); } @@ -62,8 +72,8 @@ fn add_new_asset_pair_should_work() { fn on_trade_should_work() { new_test_ext().execute_with(|| { assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); let mut vec = Vec::new(); vec.push(PRICE_ENTRY_1); vec.push(PRICE_ENTRY_2); @@ -71,17 +81,139 @@ fn on_trade_should_work() { }); } +#[test] +fn on_trade_handler_should_work() { + new_test_ext().execute_with(|| { + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 1_000, + amount_out: 500, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + + PriceOracleHandler::::on_trade(&amm_transfer, 2_000); + let mut vec = Vec::new(); + vec.push(PRICE_ENTRY_1); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); + }); +} + +#[test] +fn price_normalization_should_work() { + new_test_ext().execute_with(|| { + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: Balance::MAX, + amount_out: 1, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + assert_storage_noop!(PriceOracleHandler::::on_trade(&amm_transfer, 2_000)); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 1, + amount_out: Balance::MAX, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + assert_storage_noop!(PriceOracleHandler::::on_trade(&amm_transfer, 2_000)); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: Balance::zero(), + amount_out: 1_000, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + assert_storage_noop!(PriceOracleHandler::::on_trade(&amm_transfer, 2_000)); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 1_000, + amount_out: Balance::zero(), + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + assert_storage_noop!(PriceOracleHandler::::on_trade(&amm_transfer, 2_000)); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 340282366920938463463, + amount_out: 1, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + PriceOracleHandler::::on_trade(&amm_transfer, 2_000); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 1, + amount_out: 340282366920938463463, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + assert_storage_noop!(PriceOracleHandler::::on_trade(&amm_transfer, 2_000)); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 2_000_000, + amount_out: 1_000, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + PriceOracleHandler::::on_trade(&amm_transfer, 2_000); + + let amm_transfer = AMMTransfer { + origin: 1, + assets: ASSET_PAIR_A, + amount: 1_000, + amount_out: 2_000_000, + discount: false, + discount_amount: 0, + fee: (1, 0), + }; + PriceOracleHandler::::on_trade(&amm_transfer, 2_000); + + let data = PriceBuffer::::get(ASSET_PAIR_A.name()); + assert_eq!(data[0].price, Price::from(340282366920938463463)); + assert_eq!(data[1].price, Price::from(2_000)); + assert_eq!(data[2].price, Price::from_float(0.0005)); + assert_eq!(data.len(), 3); + }); +} + #[test] fn update_data_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_B)); - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); + PriceOracle::on_create_pool(ASSET_PAIR_B); + PriceOracle::on_create_pool(ASSET_PAIR_A); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1)); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2)); - assert_ok!(PriceOracle::on_trade(ASSET_PAIR_B, PRICE_ENTRY_1)); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); + PriceOracle::on_trade(ASSET_PAIR_B, PRICE_ENTRY_1); assert_ok!(PriceOracle::update_data()); @@ -118,8 +250,8 @@ fn update_empty_data_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_B)); - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); + PriceOracle::on_create_pool(ASSET_PAIR_B); + PriceOracle::on_create_pool(ASSET_PAIR_A); assert_ok!(PriceOracle::update_data()); @@ -215,20 +347,20 @@ fn bucket_queue_should_work() { #[test] fn continuous_trades_should_work() { ExtBuilder.build().execute_with(|| { - assert_ok!(PriceOracle::on_create_pool(ASSET_PAIR_A)); + PriceOracle::on_create_pool(ASSET_PAIR_A); for i in 0..210 { System::set_block_number(i); PriceOracle::on_initialize(System::block_number()); - assert_ok!(PriceOracle::on_trade( + PriceOracle::on_trade( ASSET_PAIR_A, PriceEntry { price: Price::from((i + 1) as u128), amount: (i * 1_000).into(), - liq_amount: 1u128 - } - )); + liq_amount: 1u128, + }, + ); // let ten = PriceOracle::price_data_ten().iter().find(|&x| x.0 == ASSET_PAIR_A).unwrap().1; // let hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A); diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs new file mode 100644 index 00000000000..8a49f1e6e90 --- /dev/null +++ b/pallets/price-oracle/src/types.rs @@ -0,0 +1,214 @@ +// This file is part of HydraDX. + +// Copyright (C) 2020-2021 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 codec::{Decode, Encode}; +use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; +use frame_support::sp_runtime::RuntimeDebug; +pub use primitives::{Balance, Price}; +use sp_std::iter::Sum; +use sp_std::ops::{Add, Index, IndexMut, Mul}; +use sp_std::prelude::*; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] +pub struct PriceEntry { + pub price: Price, + pub amount: Balance, + pub liq_amount: Balance, +} + +impl Add for PriceEntry { + type Output = Self; + fn add(self, other: Self) -> Self { + Self { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl Zero for PriceEntry { + fn zero() -> Self { + Self { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + } + } + + fn is_zero(&self) -> bool { + self == &PriceEntry::zero() + } +} + +impl Add for &PriceEntry { + type Output = PriceEntry; + fn add(self, other: Self) -> Self::Output { + PriceEntry { + price: self.price.add(other.price), + amount: self.amount.add(other.amount), + liq_amount: self.liq_amount.add(other.liq_amount), + } + } +} + +impl<'a> Sum<&'a Self> for PriceEntry { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold( + PriceEntry { + price: Price::zero(), + amount: Balance::zero(), + liq_amount: Balance::zero(), + }, + |a, b| &a + b, + ) + } +} + +pub const BUCKET_SIZE: u32 = 10; + +pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; + +pub trait PriceInfoCalculation { + fn calculate_price_info(entries: &[PriceEntry]) -> Option; +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] +pub struct PriceInfo { + pub avg_price: Price, + pub volume: Balance, +} + +impl Default for PriceInfo { + fn default() -> Self { + Self { + avg_price: Price::zero(), + volume: Zero::zero(), + } + } +} + +impl Add for &PriceInfo { + type Output = PriceInfo; + fn add(self, other: Self) -> Self::Output { + PriceInfo { + avg_price: self.avg_price.add(other.avg_price), + volume: self.volume.add(other.volume), + } + } +} + +impl<'a> Sum<&'a Self> for PriceInfo { + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold( + PriceInfo { + avg_price: Price::zero(), + volume: Balance::zero(), + }, + |a, b| &a + b, + ) + } +} + +impl PriceInfoCalculation for PriceInfo { + fn calculate_price_info(entries: &[PriceEntry]) -> Option { + let intermediate_result: Vec = entries + .iter() + .map(|x| PriceEntry { + price: x.price.mul(Price::from(x.liq_amount)), + amount: x.amount, + liq_amount: x.liq_amount, + }) + .collect(); + + let sum = intermediate_result.iter().sum::(); + let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liq_amount as u128))?; + Some(PriceInfo { + avg_price: weighted_avg_price, + volume: sum.amount, + }) + } +} + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] +pub struct BucketQueue { + bucket: Bucket, + last: u32, +} + +impl BucketQueue { + pub const BUCKET_SIZE: u32 = BUCKET_SIZE; +} + +impl Default for BucketQueue { + fn default() -> Self { + Self { + bucket: Bucket::default(), + last: Self::BUCKET_SIZE - 1, + } + } +} + +pub trait BucketQueueT { + fn update_last(&mut self, price_info: PriceInfo); + fn get_last(&self) -> PriceInfo; + fn calculate_average(&self) -> Option; +} + +impl BucketQueueT for BucketQueue { + fn update_last(&mut self, price_info: PriceInfo) { + self.last = (self.last + 1) % Self::BUCKET_SIZE; + self.bucket[self.last as usize] = price_info; + } + + fn get_last(&self) -> PriceInfo { + self.bucket[self.last as usize] + } + + fn calculate_average(&self) -> Option { + let sum = self.bucket.iter().sum::(); + Some(PriceInfo { + avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128))?, + volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128)?, + }) + } +} + +impl Index for BucketQueue { + type Output = PriceInfo; + fn index(&self, index: usize) -> &Self::Output { + &self.bucket[index] + } +} + +impl IndexMut for BucketQueue { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.bucket[index] + } +} diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index fec054fbb78..c09204c9b41 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -18,7 +18,7 @@ #![allow(clippy::upper_case_acronyms)] use frame_support::dispatch; -use frame_support::sp_runtime::traits::CheckedDiv; +use frame_support::sp_runtime::{FixedPointNumber, traits::CheckedDiv}; use sp_std::vec::Vec; use crate::{Balance, Price}; use crate::asset::AssetPair; @@ -39,6 +39,7 @@ impl AMMTransfer where Balance: Copy { + /// Calculate price from ordered assets pub fn normalize_price(&self) -> Option<(Price, Balance)> { let ordered_asset_pair = self.assets.ordered_pair(); let (balance_a, balance_b) = if ordered_asset_pair.0 == self.assets.asset_in { @@ -47,8 +48,8 @@ where (self.amount_out, self.amount) }; - let price_a = Price::from(balance_a); - let price_b = Price::from(balance_b); + let price_a = Price::checked_from_integer(balance_a)?; + let price_b = Price::checked_from_integer(balance_b)?; let price = price_a.checked_div(&price_b); price.map(|p| (p, balance_a)) } From a5ca64e2d0b660c6257ea16f191c9d77bc797f2d Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 4 Aug 2021 21:46:18 +0100 Subject: [PATCH 07/28] remove unused config params --- pallets/price-oracle/src/lib.rs | 16 +++------------- pallets/price-oracle/src/mock.rs | 9 --------- runtime/src/lib.rs | 9 --------- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index c06d3b51431..4f7412f7947 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -19,7 +19,6 @@ use frame_support::dispatch::DispatchResult; use frame_support::sp_runtime::traits::Zero; -use frame_support::traits::Get; use primitives::{ asset::AssetPair, traits::{AMMHandlers, AMMTransfer}, @@ -55,15 +54,6 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; - - #[pallet::constant] - type BucketLength: Get; - - #[pallet::constant] - type BucketDepth: Get; - - #[pallet::constant] - type MaxAssetCount: Get; } #[pallet::error] @@ -96,7 +86,7 @@ pub mod pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { Self::update_data(); - PriceBuffer::::remove_all(); + PriceBuffer::::remove_all(None); Weight::zero() // TODO } @@ -139,7 +129,7 @@ impl Pallet { let now = >::block_number(); if now.is_zero() { return Ok(()); - } // TODO: delete me + } // TODO: delete me. It is here just to make testing easier. if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { for element_from_ten in PriceDataTen::::get().iter() { @@ -186,7 +176,7 @@ impl AMMHandlers for Price return; }; - // zero prices are not added to the queue + // zero prices are ignored and not added to the queue if price.is_zero() { return; } diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index f5732733f1e..8e7f73ef17e 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -135,17 +135,8 @@ impl orml_tokens::Config for Test { type MaxLocks = (); } -parameter_types! { - pub const BucketLength: u32 = 10; - pub const BucketDepth: u32 = 4; - pub const MaxAssetCount: u32 = 5; -} - impl Config for Test { type Event = Event; - type BucketLength = BucketLength; - type BucketDepth = BucketDepth; - type MaxAssetCount = MaxAssetCount; } pub struct ExtBuilder; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 950962cf7b4..44fdc03f182 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -445,17 +445,8 @@ impl pallet_lbp::Config for Runtime { type WeightInfo = pallet_lbp::weights::HydraWeight; } -parameter_types! { - pub BucketLength: u32 = 10u32; - pub BucketDepth: u32 = 4u32; - pub MaxAssetCount: u32 = u32::MAX; -} - impl pallet_price_oracle::Config for Runtime { type Event = Event; - type BucketLength = BucketLength; - type BucketDepth = BucketDepth; - type MaxAssetCount = MaxAssetCount; } parameter_types! { From c7af9ce15b0e33cfe268da172b520c6c07ccdea6 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Fri, 6 Aug 2021 15:11:17 +0100 Subject: [PATCH 08/28] initial price-oracle benchmarking --- Cargo.lock | 2 + pallets/lbp/src/benchmarking.rs | 17 +++ pallets/lbp/src/lib.rs | 17 +++ pallets/lbp/src/mock.rs | 17 +++ pallets/lbp/src/tests.rs | 17 +++ pallets/lbp/src/weights.rs | 2 +- pallets/price-oracle/Cargo.toml | 10 +- pallets/price-oracle/src/benchmarking.rs | 149 +++++++++++++++++++++++ pallets/price-oracle/src/lib.rs | 12 +- pallets/price-oracle/src/mock.rs | 17 +++ pallets/price-oracle/src/tests.rs | 48 +++++++- pallets/price-oracle/src/types.rs | 15 +-- runtime/Cargo.toml | 2 + runtime/src/lib.rs | 1 + 14 files changed, 303 insertions(+), 23 deletions(-) create mode 100644 pallets/price-oracle/src/benchmarking.rs diff --git a/Cargo.lock b/Cargo.lock index 6e067765c1e..ace6ad4532e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5786,8 +5786,10 @@ dependencies = [ name = "pallet-price-oracle" version = "1.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", + "frame-system-benchmarking", "orml-tokens", "orml-traits", "pallet-asset-registry", diff --git a/pallets/lbp/src/benchmarking.rs b/pallets/lbp/src/benchmarking.rs index 4c6b9f83f65..744cffc32ff 100644 --- a/pallets/lbp/src/benchmarking.rs +++ b/pallets/lbp/src/benchmarking.rs @@ -1,3 +1,20 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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. + #![cfg(feature = "runtime-benchmarks")] use super::*; diff --git a/pallets/lbp/src/lib.rs b/pallets/lbp/src/lib.rs index ca9d1a900ac..8e5426b92b6 100644 --- a/pallets/lbp/src/lib.rs +++ b/pallets/lbp/src/lib.rs @@ -1,3 +1,20 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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. + #![cfg_attr(not(feature = "std"), no_std)] #![allow(clippy::unused_unit)] #![allow(clippy::upper_case_acronyms)] diff --git a/pallets/lbp/src/mock.rs b/pallets/lbp/src/mock.rs index 49bfaadf56d..b1c3007dcfe 100644 --- a/pallets/lbp/src/mock.rs +++ b/pallets/lbp/src/mock.rs @@ -1,3 +1,20 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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 crate as lbp; use crate::Config; use frame_support::parameter_types; diff --git a/pallets/lbp/src/tests.rs b/pallets/lbp/src/tests.rs index 9d657070e41..8d744ebcb2d 100644 --- a/pallets/lbp/src/tests.rs +++ b/pallets/lbp/src/tests.rs @@ -1,3 +1,20 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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::*; pub use crate::mock::{ run_to_block, Currency, Event as TestEvent, ExtBuilder, LBPPallet, Origin, System, Test, ACA, ALICE, BOB, CHARLIE, diff --git a/pallets/lbp/src/weights.rs b/pallets/lbp/src/weights.rs index e2fd1f80c93..f5ef367579f 100644 --- a/pallets/lbp/src/weights.rs +++ b/pallets/lbp/src/weights.rs @@ -1,4 +1,4 @@ -// This file is part of HydraDX-node. +// This file is part of Basilisk-node. // Copyright (C) 2021 Intergalactic Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/pallets/price-oracle/Cargo.toml b/pallets/price-oracle/Cargo.toml index 1c54113567c..07e5d03aeb8 100644 --- a/pallets/price-oracle/Cargo.toml +++ b/pallets/price-oracle/Cargo.toml @@ -29,10 +29,10 @@ serde = {features = ['derive'], optional = true, version = '1.0.101'} primitives = {path = '../../primitives', default-features = false} # Substrate dependencies -#frame-benchmarking = {default-features = false, optional = true, version = '3.0.0'} +frame-benchmarking = {default-features = false, optional = true, version = '3.0.0'} frame-support = {default-features = false, version = '3.0.0'} frame-system = {default-features = false, version = '3.0.0'} -#frame-system-benchmarking = {default-features = false, optional = true, version = '3.0.0'} +frame-system-benchmarking = {default-features = false, optional = true, version = '3.0.0'} sp-core = {default-features = false, version = '3.0.0'} sp-runtime = {default-features = false, version = '3.0.0'} sp-std = {default-features = false, version = '3.0.0'} @@ -46,6 +46,11 @@ pallet-asset-registry = {path = '../asset-registry', default-features = false} [features] default = ['std'] +runtime-benchmarks = [ + "frame-benchmarking", + "frame-system/runtime-benchmarks", + "frame-support/runtime-benchmarks", +] std = [ 'serde', 'codec/std', @@ -54,5 +59,4 @@ std = [ 'sp-runtime/std', 'sp-core/std', 'sp-std/std', -# 'primitives/std', ] diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs new file mode 100644 index 00000000000..0d817e86a21 --- /dev/null +++ b/pallets/price-oracle/src/benchmarking.rs @@ -0,0 +1,149 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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. + +#![cfg(feature = "runtime-benchmarks")] + +use super::*; + +use frame_benchmarking::benchmarks; +use frame_support::traits::OnInitialize; + +use crate::Pallet as PriceOracle; + +pub const ASSET_PAIR_A: AssetPair = AssetPair { + asset_in: 1_000, + asset_out: 2_000, +}; + +pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { + price: Price::from_inner(2000000000000000000), + amount: 1_000, + liq_amount: 2_000, +}; + +pub const NUM_OF_ITERS: u32 = 100; + +benchmarks! { + on_initialize_no_entry { + let t: u32 = 5; + PriceOracle::::on_create_pool(ASSET_PAIR_A); + + assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(t.into()); } + verify { + assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + } + + on_initialize_one_entry { + let t: u32 = 5; + PriceOracle::::on_create_pool(ASSET_PAIR_A); + PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + + let mut vec = Vec::new(); + vec.push(PRICE_ENTRY_1); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(t.into()); } + verify { + assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + } + + on_initialize_multiple_entries_one_token { + let t in 1 .. NUM_OF_ITERS; + PriceOracle::::on_create_pool(ASSET_PAIR_A); + let mut vec = Vec::new(); + + for i in 0 .. t { + PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + // PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1);// ?????????????? + // vec.push(PRICE_ENTRY_1); + + assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(t.into()); } + verify { + assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: t as u128 * 1_000}); + } + + on_initialize_one_entry_multiple_tokens { + let t in 1 .. NUM_OF_ITERS; + let mut vec = Vec::new(); + let mut asset_pair = AssetPair::default(); + + for i in 0 .. t { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + PriceOracle::::on_create_pool(asset_pair); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + // assert_eq!(PriceBuffer::::try_get(asset_pair.name()), Ok(vec)); + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(t.into()); } + verify { + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::{new_test_ext, Test}; + use frame_support::assert_ok; + + #[test] + fn test_benchmarks() { + new_test_ext().execute_with(|| { + assert_ok!(test_benchmark_on_initialize_no_entry::()); + assert_ok!(test_benchmark_on_initialize_one_entry::()); + assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token::()); + assert_ok!(test_benchmark_on_initialize_one_entry_multiple_tokens::()); + }); + } +} diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 4f7412f7947..838ce7973d1 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -1,4 +1,4 @@ -// This file is part of HydraDX. +// This file is part of Basilisk-node. // Copyright (C) 2020-2021 Intergalactic, Limited (GIB). // SPDX-License-Identifier: Apache-2.0 @@ -36,7 +36,7 @@ mod tests; mod types; pub use types::*; -// mod benchmarking; +mod benchmarking; // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; @@ -58,7 +58,6 @@ pub mod pallet { #[pallet::error] pub enum Error { - PriceDataNotFound, PriceComputationError, } @@ -138,7 +137,6 @@ impl Pallet { element_from_ten .1 .calculate_average() - .ok_or(Error::::PriceComputationError)?, ); Ok(()) })?; @@ -152,7 +150,6 @@ impl Pallet { element_from_hundred .1 .calculate_average() - .ok_or(Error::::PriceComputationError)?, ); Ok(()) })?; @@ -176,8 +173,9 @@ impl AMMHandlers for Price return; }; - // zero prices are ignored and not added to the queue - if price.is_zero() { + // we assume that zero prices are not valid + // zero values are ignored and not added to the queue + if price.is_zero() || amount.is_zero() || liq_amount.is_zero() { return; } diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 8e7f73ef17e..8ccd23ce3ce 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -1,3 +1,20 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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 crate as price_oracle; use crate::Config; use frame_support::parameter_types; diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 5a65544dca0..6031935a613 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -1,4 +1,4 @@ -// This file is part of HydraDX. +// This file is part of Basilisk-node. // Copyright (C) 2020-2021 Intergalactic, Limited (GIB). // SPDX-License-Identifier: Apache-2.0 @@ -246,21 +246,40 @@ fn update_data_should_work() { } #[test] -fn update_empty_data_should_work() { +fn update_data_with_incorrect_input_should_not_work() { new_test_ext().execute_with(|| { System::set_block_number(3); - PriceOracle::on_create_pool(ASSET_PAIR_B); PriceOracle::on_create_pool(ASSET_PAIR_A); - assert_ok!(PriceOracle::update_data()); + PriceOracle::on_trade(ASSET_PAIR_A, PriceEntry { + price: Price::from(1), + amount: Zero::zero(), + liq_amount: Zero::zero(), + }); + + assert_noop!(PriceOracle::update_data(), + Error::::PriceComputationError, + ); + }); +} + +#[test] +fn update_empty_data_should_work() { + new_test_ext().execute_with(|| { + + PriceOracle::on_create_pool(ASSET_PAIR_A); + + for i in 0..1002 { + System::set_block_number(i); + assert_ok!(PriceOracle::update_data()); + } let data_ten = PriceOracle::price_data_ten() .iter() .find(|&x| x.0 == ASSET_PAIR_A.name()) .unwrap() .1; - assert_eq!( data_ten.get_last(), PriceInfo { @@ -268,8 +287,27 @@ fn update_empty_data_should_work() { volume: Zero::zero() } ); + + let data_hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A.name()); + assert_eq!( + data_hundred.get_last(), + PriceInfo { + avg_price: Zero::zero(), + volume: Zero::zero() + } + ); + + let data_thousand = PriceOracle::price_data_thousand(ASSET_PAIR_A.name()); + assert_eq!( + data_thousand.get_last(), + PriceInfo { + avg_price: Zero::zero(), + volume: Zero::zero() + } + ); }); } + #[test] fn bucket_queue_should_work() { let mut queue = BucketQueue::default(); diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs index 8a49f1e6e90..303d881c331 100644 --- a/pallets/price-oracle/src/types.rs +++ b/pallets/price-oracle/src/types.rs @@ -1,4 +1,4 @@ -// This file is part of HydraDX. +// This file is part of Basilisk-node. // Copyright (C) 2020-2021 Intergalactic, Limited (GIB). // SPDX-License-Identifier: Apache-2.0 @@ -163,6 +163,7 @@ pub struct BucketQueue { } impl BucketQueue { + // make sure that BUCKET_SIZE != 0 pub const BUCKET_SIZE: u32 = BUCKET_SIZE; } @@ -178,7 +179,7 @@ impl Default for BucketQueue { pub trait BucketQueueT { fn update_last(&mut self, price_info: PriceInfo); fn get_last(&self) -> PriceInfo; - fn calculate_average(&self) -> Option; + fn calculate_average(&self) -> PriceInfo; } impl BucketQueueT for BucketQueue { @@ -191,12 +192,12 @@ impl BucketQueueT for BucketQueue { self.bucket[self.last as usize] } - fn calculate_average(&self) -> Option { + fn calculate_average(&self) -> PriceInfo { let sum = self.bucket.iter().sum::(); - Some(PriceInfo { - avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128))?, - volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128)?, - }) + PriceInfo { + avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128)).expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), + volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128).expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), + } } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index f39e1c29680..cf9b14f4bc4 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -133,6 +133,7 @@ runtime-benchmarks = [ "pallet-xyk/runtime-benchmarks", "pallet-exchange-benchmarking", "pallet-lbp/runtime-benchmarks", + "pallet-price-oracle/runtime-benchmarks", 'sp-runtime/runtime-benchmarks', 'pallet-collator-selection/runtime-benchmarks', "pallet-xcm/runtime-benchmarks", @@ -181,6 +182,7 @@ std = [ "pallet-transaction-multi-payment/std", 'pallet-transaction-payment-rpc-runtime-api/std', 'pallet-lbp/std', + 'pallet-price-oracle/std', 'pallet-utility/std', 'sp-api/std', 'sp-block-builder/std', diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 44fdc03f182..8ff85dad26c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1075,6 +1075,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_nft, NFT); add_benchmark!(params, batches, pallet_timestamp, Timestamp); add_benchmark!(params, batches, transaction_multi_payment, MultiBench::); + add_benchmark!(params, batches, pallet_price_oracle, PriceOracle); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) From e9d4c6242b30987e7abec3bd4173a0208964fab0 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Sun, 8 Aug 2021 23:32:04 +0100 Subject: [PATCH 09/28] update price-oracle benchmarking --- pallets/price-oracle/src/benchmarking.rs | 46 +++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 0d817e86a21..597ef76838a 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -36,6 +36,7 @@ pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { }; pub const NUM_OF_ITERS: u32 = 100; +pub const NUM_OF_NESTED_ITERS: u32 = 100; benchmarks! { on_initialize_no_entry { @@ -87,9 +88,6 @@ benchmarks! { vec.push(PRICE_ENTRY_1); } - // PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1);// ?????????????? - // vec.push(PRICE_ENTRY_1); - assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); let price_data = PriceOracle::::price_data_ten(); @@ -107,7 +105,7 @@ benchmarks! { on_initialize_one_entry_multiple_tokens { let t in 1 .. NUM_OF_ITERS; let mut vec = Vec::new(); - let mut asset_pair = AssetPair::default(); + let asset_pair = AssetPair::default(); for i in 0 .. t { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; @@ -116,18 +114,50 @@ benchmarks! { vec.push(PRICE_ENTRY_1); } - // assert_eq!(PriceBuffer::::try_get(asset_pair.name()), Ok(vec)); - let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); }: { PriceOracle::::on_initialize(t.into()); } verify { - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + for i in 0 .. t { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + } + } + + on_initialize_multiple_entries_multiple_tokens { + let t in 1 .. NUM_OF_ITERS; + let u in 1 .. NUM_OF_NESTED_ITERS; + let mut vec = Vec::new(); + let asset_pair = AssetPair::default(); + + for i in 0 .. t { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + for j in 0 .. u { + PriceOracle::::on_create_pool(asset_pair); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + } + vec.push(PRICE_ENTRY_1); + } + let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(t.into()); } + verify { + for i in 0 .. t { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: u as u128 * 1_000}); + } + } } From e83579e3bea61a710a67f624fe7c3cdc16a6341a Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Mon, 9 Aug 2021 02:11:36 +0100 Subject: [PATCH 10/28] add new benchmarks and parameterize weight calculation of on_initialize --- pallets/price-oracle/src/benchmarking.rs | 114 +++++++++++++++--- pallets/price-oracle/src/lib.rs | 25 ++-- pallets/price-oracle/src/mock.rs | 1 + pallets/price-oracle/src/weights.rs | 143 +++++++++++++++++++++++ runtime/src/lib.rs | 1 + runtime/src/weights/mod.rs | 1 + runtime/src/weights/price_oracle.rs | 95 +++++++++++++++ 7 files changed, 354 insertions(+), 26 deletions(-) create mode 100644 pallets/price-oracle/src/weights.rs create mode 100644 runtime/src/weights/price_oracle.rs diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 597ef76838a..1e6a41a9fcb 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -40,7 +40,7 @@ pub const NUM_OF_NESTED_ITERS: u32 = 100; benchmarks! { on_initialize_no_entry { - let t: u32 = 5; + let block_num: u32 = 5; PriceOracle::::on_create_pool(ASSET_PAIR_A); assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); @@ -49,7 +49,7 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(t.into()); } + }: { PriceOracle::::on_initialize(block_num.into()); } verify { assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); @@ -58,7 +58,7 @@ benchmarks! { } on_initialize_one_entry { - let t: u32 = 5; + let block_num: u32 = 5; PriceOracle::::on_create_pool(ASSET_PAIR_A); PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); @@ -70,7 +70,7 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(t.into()); } + }: { PriceOracle::::on_initialize(block_num.into()); } verify { assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); @@ -79,11 +79,12 @@ benchmarks! { } on_initialize_multiple_entries_one_token { - let t in 1 .. NUM_OF_ITERS; + let block_num: u32 = 5; + let a in 1 .. NUM_OF_ITERS; // trade_num PriceOracle::::on_create_pool(ASSET_PAIR_A); let mut vec = Vec::new(); - for i in 0 .. t { + for i in 0 .. a { PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); vec.push(PRICE_ENTRY_1); } @@ -94,20 +95,21 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(t.into()); } + }: { PriceOracle::::on_initialize(block_num.into()); } verify { assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: t as u128 * 1_000}); + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); } on_initialize_one_entry_multiple_tokens { - let t in 1 .. NUM_OF_ITERS; + let block_num: u32 = 5; + let b in 1 .. NUM_OF_ITERS; // token num let mut vec = Vec::new(); let asset_pair = AssetPair::default(); - for i in 0 .. t { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_create_pool(asset_pair); PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); @@ -118,9 +120,9 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(t.into()); } + }: { PriceOracle::::on_initialize(block_num.into()); } verify { - for i in 0 .. t { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); @@ -130,14 +132,15 @@ benchmarks! { } on_initialize_multiple_entries_multiple_tokens { - let t in 1 .. NUM_OF_ITERS; - let u in 1 .. NUM_OF_NESTED_ITERS; + let block_num: u32 = 5; + let b in 1 .. NUM_OF_ITERS; + let a in 1 .. NUM_OF_NESTED_ITERS; let mut vec = Vec::new(); let asset_pair = AssetPair::default(); - for i in 0 .. t { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - for j in 0 .. u { + for j in 0 .. a { PriceOracle::::on_create_pool(asset_pair); PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); } @@ -148,14 +151,85 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(t.into()); } + }: { PriceOracle::::on_initialize(block_num.into()); } verify { - for i in 0 .. t { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: u as u128 * 1_000}); + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); + } + + } + + on_initialize_uniform_distribution { + let block_num: u32 = 5; + let mut vec = Vec::new(); + let asset_pair = AssetPair::default(); + + for i in 0 .. NUM_OF_ITERS { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + PriceOracle::::on_create_pool(asset_pair); + // 2 trades + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(block_num.into()); } + verify { + for i in 0 .. NUM_OF_ITERS { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 2_000}); + } + + } + + // we expect this test to have similar results as the previous test + on_initialize_nonuniform_distribution { + let block_num: u32 = 5; + let mut vec = Vec::new(); + + let asset_pair = AssetPair {asset_in: 100, asset_out: 200}; + for i in 0 .. NUM_OF_ITERS + 1{ + PriceOracle::::on_create_pool(asset_pair); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + for i in 1 .. NUM_OF_ITERS { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + PriceOracle::::on_create_pool(asset_pair); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + }: { PriceOracle::::on_initialize(block_num.into()); } + verify { + let asset_pair = AssetPair {asset_in: 100, asset_out: 200}; + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: (NUM_OF_ITERS + 1) as u128 * 1_000}); + + for i in 1 .. NUM_OF_ITERS { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); } } @@ -174,6 +248,8 @@ mod tests { assert_ok!(test_benchmark_on_initialize_one_entry::()); assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token::()); assert_ok!(test_benchmark_on_initialize_one_entry_multiple_tokens::()); + assert_ok!(test_benchmark_on_initialize_uniform_distribution::()); + assert_ok!(test_benchmark_on_initialize_nonuniform_distribution::()); }); } } diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 838ce7973d1..6d8c7384c25 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::dispatch::DispatchResult; +use frame_support::dispatch::{DispatchError, DispatchResult}; use frame_support::sp_runtime::traits::Zero; use primitives::{ asset::AssetPair, @@ -26,6 +26,7 @@ use primitives::{ }; use sp_std::marker::PhantomData; use sp_std::prelude::*; +use sp_std::convert::TryInto; #[cfg(test)] mod mock; @@ -36,6 +37,9 @@ mod tests; mod types; pub use types::*; +pub mod weights; +use weights::WeightInfo; + mod benchmarking; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -54,11 +58,15 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { type Event: From> + IsType<::Event>; + + /// Weight information for the extrinsics. + type WeightInfo: WeightInfo; } #[pallet::error] pub enum Error { PriceComputationError, + UpdateDataOverflow, } #[pallet::event] @@ -83,11 +91,11 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { - Self::update_data(); + let (num_of_processed_buckets, num_of_processed_trades) = Self::update_data().unwrap_or_else(|_| (0, 0)); PriceBuffer::::remove_all(None); - Weight::zero() // TODO + T::WeightInfo::on_initialize_multiple_entries_multiple_tokens(num_of_processed_buckets, num_of_processed_trades) // TODO: rebenchmark } } @@ -107,13 +115,16 @@ impl Pallet { PriceBuffer::::append(asset_pair.name(), price_entry); } - fn update_data() -> DispatchResult { - // TODO: maybe create a separate storage for liquidity_data and process it separately + fn update_data() -> Result<(u32, u32), DispatchError> { + let mut num_of_processed_buckets = 0u32; + let mut num_of_processed_trades = 0u32; PriceDataTen::::mutate(|data_ten| -> DispatchResult { for (asset_pair_id, data) in data_ten.iter_mut() { let maybe_price = >::try_get(asset_pair_id); let result = if let Ok(prices) = maybe_price { + num_of_processed_buckets.checked_add(1).ok_or(Error::::UpdateDataOverflow)?; + num_of_processed_trades.checked_add(prices.len().try_into().map_err(|_| Error::::UpdateDataOverflow)?).ok_or(Error::::UpdateDataOverflow)?; PriceInfo::calculate_price_info(prices.as_slice()).ok_or(Error::::PriceComputationError)? } else { PriceInfo::default() @@ -127,7 +138,7 @@ impl Pallet { let now = >::block_number(); if now.is_zero() { - return Ok(()); + return Ok((num_of_processed_buckets, num_of_processed_trades)); } // TODO: delete me. It is here just to make testing easier. if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { @@ -156,7 +167,7 @@ impl Pallet { } } - Ok(()) + Ok((num_of_processed_buckets, num_of_processed_trades)) } } diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 8ccd23ce3ce..3dfd9effaac 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -154,6 +154,7 @@ impl orml_tokens::Config for Test { impl Config for Test { type Event = Event; + type WeightInfo = (); } pub struct ExtBuilder; diff --git a/pallets/price-oracle/src/weights.rs b/pallets/price-oracle/src/weights.rs new file mode 100644 index 00000000000..09430531b51 --- /dev/null +++ b/pallets/price-oracle/src/weights.rs @@ -0,0 +1,143 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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_price_oracle +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-08-08, STEPS: [10, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// ./target/release/basilisk +// benchmark +// --chain +// dev +// --execution +// wasm +// --wasm-execution +// compiled +// --pallet +// pallet-price-oracle +// --steps=10 +// --repeat=20 +// --extrinsic +// * +// --heap-pages=4096 +// --template=./.maintain/pallet-weight-template.hbs +// --output=./pallets/price-oracle/src/weights.rs + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_price_oracle. +pub trait WeightInfo { + fn on_initialize_no_entry() -> Weight; + fn on_initialize_one_entry() -> Weight; + fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight; + fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight; + fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight; +} + +/// Weights for pallet_price_oracle using the hack.hydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl WeightInfo for HydraWeight { + fn on_initialize_no_entry() -> Weight { + (38_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn on_initialize_one_entry() -> Weight { + (43_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { + (43_629_000 as Weight) + // Standard Error: 6_000 + .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 203_000 + .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } + fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 655_000 + .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 655_000 + .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn on_initialize_no_entry() -> Weight { + (38_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + fn on_initialize_one_entry() -> Weight { + (43_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { + (43_629_000 as Weight) + // Standard Error: 6_000 + .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 203_000 + .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } + fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 655_000 + .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 655_000 + .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) + .saturating_add(RocksDbWeight::get().reads(1 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } +} \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8ff85dad26c..d1fc9a6dd83 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -447,6 +447,7 @@ impl pallet_lbp::Config for Runtime { impl pallet_price_oracle::Config for Runtime { type Event = Event; + type WeightInfo = weights::price_oracle::HydraWeight; } parameter_types! { diff --git a/runtime/src/weights/mod.rs b/runtime/src/weights/mod.rs index 2ea98543789..8d755fc9fd9 100644 --- a/runtime/src/weights/mod.rs +++ b/runtime/src/weights/mod.rs @@ -3,3 +3,4 @@ pub mod payment; pub mod system; pub mod timestamp; pub mod xyk; +pub mod price_oracle; diff --git a/runtime/src/weights/price_oracle.rs b/runtime/src/weights/price_oracle.rs new file mode 100644 index 00000000000..44b24711db6 --- /dev/null +++ b/runtime/src/weights/price_oracle.rs @@ -0,0 +1,95 @@ +// This file is part of Basilisk-node. + +// Copyright (C) 2020-2021 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_price_oracle +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 +//! DATE: 2021-08-08, STEPS: [10, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 + +// Executed Command: +// ./target/release/basilisk +// benchmark +// --chain +// dev +// --execution +// wasm +// --wasm-execution +// compiled +// --pallet +// pallet-price-oracle +// --steps=10 +// --repeat=20 +// --extrinsic +// * +// --heap-pages=4096 +// --template=./.maintain/pallet-weight-template.hbs +// --output=./pallets/price-oracle/src/weights.rs + +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(clippy::unnecessary_cast)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +use pallet_price_oracle::weights::WeightInfo; + +/// Weights for pallet_price_oracle using the hack.hydraDX node and recommended hardware. +pub struct HydraWeight(PhantomData); +impl WeightInfo for HydraWeight { + fn on_initialize_no_entry() -> Weight { + (38_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn on_initialize_one_entry() -> Weight { + (43_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { + (43_629_000 as Weight) + // Standard Error: 6_000 + .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 203_000 + .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } + fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { + (0 as Weight) + // Standard Error: 655_000 + .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) + // Standard Error: 655_000 + .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + } +} From 42f43e4861d20cca8a6593ee103f356fc36767c2 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 11 Aug 2021 10:17:35 +0200 Subject: [PATCH 11/28] don't decrease the price over time and add new test and benchmark --- pallets/price-oracle/src/benchmarking.rs | 50 +++++++++++++++ pallets/price-oracle/src/lib.rs | 29 +++++---- pallets/price-oracle/src/tests.rs | 80 ++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 15 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 1e6a41a9fcb..c2c4598edbd 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -103,6 +103,54 @@ benchmarks! { assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); } + on_initialize_multiple_entries_one_token_all_bucket_levels { + let block_num: u32 = BUCKET_SIZE.pow(3); + let a in 1 .. NUM_OF_ITERS; // trade_num + PriceOracle::::on_create_pool(ASSET_PAIR_A); + frame_system::Pallet::::set_block_number(Zero::zero()); + + let mut vec = Vec::new(); + for i in 0 .. a { + PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + + for round in 1.. block_num { + frame_system::Pallet::::set_block_number((round - 1) .into()); + PriceOracle::::on_initialize((round - 1).into()); + + let mut vec = Vec::new(); + for i in 0 .. a { + PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + vec.push(PRICE_ENTRY_1); + } + + assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + + let price_data = PriceOracle::::price_data_ten(); + let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); + } + + frame_system::Pallet::::set_block_number(block_num.into()); + + }: { PriceOracle::::on_initialize(block_num.into()); } + verify { + assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + let bucket_queue = PriceOracle::::price_data_hundred(ASSET_PAIR_A.name()); + for i in 0 .. BucketQueue::BUCKET_SIZE { + assert_eq!(bucket_queue[i as usize].volume, a as u128 * 1_000); + } + + let bucket_queue = PriceOracle::::price_data_thousand(ASSET_PAIR_A.name()); + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); + } + on_initialize_one_entry_multiple_tokens { let block_num: u32 = 5; let b in 1 .. NUM_OF_ITERS; // token num @@ -247,7 +295,9 @@ mod tests { assert_ok!(test_benchmark_on_initialize_no_entry::()); assert_ok!(test_benchmark_on_initialize_one_entry::()); assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token::()); + assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token_all_bucket_levels::()); assert_ok!(test_benchmark_on_initialize_one_entry_multiple_tokens::()); + assert_ok!(test_benchmark_on_initialize_multiple_entries_multiple_tokens::()); assert_ok!(test_benchmark_on_initialize_uniform_distribution::()); assert_ok!(test_benchmark_on_initialize_nonuniform_distribution::()); }); diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 6d8c7384c25..d0c1e45880d 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -24,9 +24,9 @@ use primitives::{ traits::{AMMHandlers, AMMTransfer}, AssetId, Balance, }; +use sp_std::convert::TryInto; use sp_std::marker::PhantomData; use sp_std::prelude::*; -use sp_std::convert::TryInto; #[cfg(test)] mod mock; @@ -95,7 +95,10 @@ pub mod pallet { PriceBuffer::::remove_all(None); - T::WeightInfo::on_initialize_multiple_entries_multiple_tokens(num_of_processed_buckets, num_of_processed_trades) // TODO: rebenchmark + T::WeightInfo::on_initialize_multiple_entries_multiple_tokens( + num_of_processed_buckets, + num_of_processed_trades, + ) // TODO: rebenchmark } } @@ -123,11 +126,15 @@ impl Pallet { for (asset_pair_id, data) in data_ten.iter_mut() { let maybe_price = >::try_get(asset_pair_id); let result = if let Ok(prices) = maybe_price { - num_of_processed_buckets.checked_add(1).ok_or(Error::::UpdateDataOverflow)?; - num_of_processed_trades.checked_add(prices.len().try_into().map_err(|_| Error::::UpdateDataOverflow)?).ok_or(Error::::UpdateDataOverflow)?; + num_of_processed_buckets + .checked_add(1) + .ok_or(Error::::UpdateDataOverflow)?; + num_of_processed_trades + .checked_add(prices.len().try_into().map_err(|_| Error::::UpdateDataOverflow)?) + .ok_or(Error::::UpdateDataOverflow)?; PriceInfo::calculate_price_info(prices.as_slice()).ok_or(Error::::PriceComputationError)? } else { - PriceInfo::default() + data.get_last() }; data.update_last(result); @@ -144,11 +151,7 @@ impl Pallet { if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { for element_from_ten in PriceDataTen::::get().iter() { PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| -> DispatchResult { - data.update_last( - element_from_ten - .1 - .calculate_average() - ); + data.update_last(element_from_ten.1.calculate_average()); Ok(()) })?; } @@ -157,11 +160,7 @@ impl Pallet { if (now % T::BlockNumber::from(BUCKET_SIZE.pow(2))) == T::BlockNumber::from(BUCKET_SIZE.pow(2) - 1) { for element_from_hundred in PriceDataHundred::::iter() { PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| -> DispatchResult { - data.update_last( - element_from_hundred - .1 - .calculate_average() - ); + data.update_last(element_from_hundred.1.calculate_average()); Ok(()) })?; } diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 6031935a613..67a181fb8e2 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -421,3 +421,83 @@ fn continuous_trades_should_work() { } }) } + + +#[test] +fn stable_price_should_work() { + new_test_ext().execute_with(|| { + let num_of_iters = BucketQueue::BUCKET_SIZE.pow(3); + PriceOracle::on_create_pool(ASSET_PAIR_A); + + for i in 0 .. num_of_iters { + System::set_block_number(i.into()); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); + PriceOracle::on_initialize(i.into()); + } + + let data_ten = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_A.name()) + .unwrap() + .1; + let data_hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A.name()); + let data_thousand = PriceOracle::price_data_thousand(ASSET_PAIR_A.name()); + + assert_eq!( + data_ten.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + assert_eq!( + data_hundred.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + assert_eq!( + data_thousand.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + + for i in num_of_iters .. 2 * num_of_iters { + System::set_block_number(i.into()); + PriceOracle::on_initialize(i.into()); + } + + let data_ten_a = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_A.name()) + .unwrap() + .1; + let data_hundred = PriceOracle::price_data_hundred(ASSET_PAIR_A.name()); + let data_thousand = PriceOracle::price_data_thousand(ASSET_PAIR_A.name()); + + assert_eq!( + data_ten.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + assert_eq!( + data_hundred.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + assert_eq!( + data_thousand.get_last(), + PriceInfo { + avg_price: 2.into(), + volume: 1_000 + } + ); + }); +} \ No newline at end of file From 1ef603927755fa2a2f6eaafa64a4bc17a3143352 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 11 Aug 2021 14:48:25 +0200 Subject: [PATCH 12/28] comment errors, rename PriceEntry field names and perform checked div in calculate_price_info --- pallets/price-oracle/src/benchmarking.rs | 4 +-- pallets/price-oracle/src/lib.rs | 7 ++-- pallets/price-oracle/src/mock.rs | 8 ++--- pallets/price-oracle/src/tests.rs | 8 ++--- pallets/price-oracle/src/types.rs | 41 +++++++++++++----------- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index c2c4598edbd..f3eb058decb 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -31,8 +31,8 @@ pub const ASSET_PAIR_A: AssetPair = AssetPair { pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { price: Price::from_inner(2000000000000000000), - amount: 1_000, - liq_amount: 2_000, + trade_amount: 1_000, + liquidity_amount: 2_000, }; pub const NUM_OF_ITERS: u32 = 100; diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index d0c1e45880d..328c07e922a 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -65,7 +65,10 @@ pub mod pallet { #[pallet::error] pub enum Error { + /// Calculation error occurred while calculating average price PriceComputationError, + + /// An unexpected overflow occurred UpdateDataOverflow, } @@ -191,8 +194,8 @@ impl AMMHandlers for Price let price_entry = PriceEntry { price, - amount, - liq_amount, + trade_amount: amount, + liquidity_amount: liq_amount, }; Pallet::::on_trade(amm_transfer.assets, price_entry); diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 3dfd9effaac..442a1e99134 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -46,13 +46,13 @@ pub const ASSET_PAIR_B: AssetPair = AssetPair { pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { price: Price::from_inner(2000000000000000000), - amount: 1_000, - liq_amount: 2_000, + trade_amount: 1_000, + liquidity_amount: 2_000, }; pub const PRICE_ENTRY_2: PriceEntry = PriceEntry { price: Price::from_inner(5000000000000000000), - amount: 3_000, - liq_amount: 4_000, + trade_amount: 3_000, + liquidity_amount: 4_000, }; frame_support::construct_runtime!( diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 67a181fb8e2..658a86e2817 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -254,8 +254,8 @@ fn update_data_with_incorrect_input_should_not_work() { PriceOracle::on_trade(ASSET_PAIR_A, PriceEntry { price: Price::from(1), - amount: Zero::zero(), - liq_amount: Zero::zero(), + trade_amount: Zero::zero(), + liquidity_amount: Zero::zero(), }); assert_noop!(PriceOracle::update_data(), @@ -395,8 +395,8 @@ fn continuous_trades_should_work() { ASSET_PAIR_A, PriceEntry { price: Price::from((i + 1) as u128), - amount: (i * 1_000).into(), - liq_amount: 1u128, + trade_amount: (i * 1_000).into(), + liquidity_amount: 1u128, }, ); diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs index 303d881c331..b7a038bcb31 100644 --- a/pallets/price-oracle/src/types.rs +++ b/pallets/price-oracle/src/types.rs @@ -16,11 +16,11 @@ // limitations under the License. use codec::{Decode, Encode}; -use frame_support::sp_runtime::traits::{CheckedDiv, Zero}; +use frame_support::sp_runtime::traits::{CheckedDiv, CheckedMul, Zero}; use frame_support::sp_runtime::RuntimeDebug; pub use primitives::{Balance, Price}; use sp_std::iter::Sum; -use sp_std::ops::{Add, Index, IndexMut, Mul}; +use sp_std::ops::{Add, Index, IndexMut}; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -30,8 +30,8 @@ use serde::{Deserialize, Serialize}; #[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] pub struct PriceEntry { pub price: Price, - pub amount: Balance, - pub liq_amount: Balance, + pub trade_amount: Balance, + pub liquidity_amount: Balance, } impl Add for PriceEntry { @@ -39,8 +39,8 @@ impl Add for PriceEntry { fn add(self, other: Self) -> Self { Self { price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), + trade_amount: self.trade_amount.add(other.trade_amount), + liquidity_amount: self.liquidity_amount.add(other.liquidity_amount), } } } @@ -49,8 +49,8 @@ impl Zero for PriceEntry { fn zero() -> Self { Self { price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), + trade_amount: Balance::zero(), + liquidity_amount: Balance::zero(), } } @@ -64,8 +64,8 @@ impl Add for &PriceEntry { fn add(self, other: Self) -> Self::Output { PriceEntry { price: self.price.add(other.price), - amount: self.amount.add(other.amount), - liq_amount: self.liq_amount.add(other.liq_amount), + trade_amount: self.trade_amount.add(other.trade_amount), + liquidity_amount: self.liquidity_amount.add(other.liquidity_amount), } } } @@ -78,8 +78,8 @@ impl<'a> Sum<&'a Self> for PriceEntry { iter.fold( PriceEntry { price: Price::zero(), - amount: Balance::zero(), - liq_amount: Balance::zero(), + trade_amount: Balance::zero(), + liquidity_amount: Balance::zero(), }, |a, b| &a + b, ) @@ -139,18 +139,21 @@ impl PriceInfoCalculation for PriceInfo { fn calculate_price_info(entries: &[PriceEntry]) -> Option { let intermediate_result: Vec = entries .iter() - .map(|x| PriceEntry { - price: x.price.mul(Price::from(x.liq_amount)), - amount: x.amount, - liq_amount: x.liq_amount, + .map(|x| -> Option { + let price = x.price.checked_mul(&Price::from(x.liquidity_amount))?; + Some(PriceEntry { + price, + trade_amount: x.trade_amount, + liquidity_amount: x.liquidity_amount, + }) }) - .collect(); + .collect::>>()?; let sum = intermediate_result.iter().sum::(); - let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liq_amount as u128))?; + let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liquidity_amount as u128))?; Some(PriceInfo { avg_price: weighted_avg_price, - volume: sum.amount, + volume: sum.trade_amount, }) } } From 9d6f15fdfb33f875b7aa7027580f2aaf3b2a9fc3 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Fri, 13 Aug 2021 09:53:42 +0200 Subject: [PATCH 13/28] rework benchmarking --- pallets/price-oracle/src/benchmarking.rs | 12 ------------ pallets/price-oracle/src/lib.rs | 17 ++++++++++------- pallets/xyk/src/lib.rs | 2 +- primitives/src/traits.rs | 7 ++++++- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index f3eb058decb..435c4229b61 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -41,20 +41,8 @@ pub const NUM_OF_NESTED_ITERS: u32 = 100; benchmarks! { on_initialize_no_entry { let block_num: u32 = 5; - PriceOracle::::on_create_pool(ASSET_PAIR_A); - - assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); - - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(block_num.into()); } verify { - assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); } on_initialize_one_entry { diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 328c07e922a..b2497851f67 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -19,6 +19,7 @@ use frame_support::dispatch::{DispatchError, DispatchResult}; use frame_support::sp_runtime::traits::Zero; +use frame_support::pallet_prelude::Weight; use primitives::{ asset::AssetPair, traits::{AMMHandlers, AMMTransfer}, @@ -40,7 +41,7 @@ pub use types::*; pub mod weights; use weights::WeightInfo; -mod benchmarking; +mod benchmarking; // TODO: rebenchmark // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; @@ -75,10 +76,13 @@ pub mod pallet { #[pallet::event] pub enum Event {} + /// Data generated by trades stored for processing. + /// In every block, all accumulated entries are processed and then removed from the storage. #[pallet::storage] #[pallet::getter(fn price_buffer)] pub type PriceBuffer = StorageMap<_, Blake2_128Concat, AssetPairId, Vec, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn price_data_ten)] pub type PriceDataTen = StorageValue<_, Vec<(AssetPairId, BucketQueue)>, ValueQuery>; @@ -95,13 +99,8 @@ pub mod pallet { impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { let (num_of_processed_buckets, num_of_processed_trades) = Self::update_data().unwrap_or_else(|_| (0, 0)); - PriceBuffer::::remove_all(None); - - T::WeightInfo::on_initialize_multiple_entries_multiple_tokens( - num_of_processed_buckets, - num_of_processed_trades, - ) // TODO: rebenchmark + T::WeightInfo::on_initialize_no_entry() } } @@ -200,4 +199,8 @@ impl AMMHandlers for Price Pallet::::on_trade(amm_transfer.assets, price_entry); } + + fn on_trade_weight() -> Weight { + T::WeightInfo::on_initialize_one_entry() - T::WeightInfo::on_initialize_no_entry() + } } diff --git a/pallets/xyk/src/lib.rs b/pallets/xyk/src/lib.rs index a5b4aeff257..4f4bf9747d0 100644 --- a/pallets/xyk/src/lib.rs +++ b/pallets/xyk/src/lib.rs @@ -467,7 +467,7 @@ pub mod pallet { /// `max_limit` - minimum amount of `asset_out` / amount of asset_out to be obtained from the pool in exchange for `asset_in`. /// /// Emits `SellExecuted` when successful. - #[pallet::weight(::WeightInfo::sell())] + #[pallet::weight(::WeightInfo::sell() + ::AMMHandler::on_trade_weight())] pub fn sell( origin: OriginFor, asset_in: AssetId, diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index c09204c9b41..3e7df9ff6e3 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -18,7 +18,8 @@ #![allow(clippy::upper_case_acronyms)] use frame_support::dispatch; -use frame_support::sp_runtime::{FixedPointNumber, traits::CheckedDiv}; +use frame_support::sp_runtime::{FixedPointNumber, traits::{CheckedDiv, Zero}}; +use frame_support::weights::Weight; use sp_std::vec::Vec; use crate::{Balance, Price}; use crate::asset::AssetPair; @@ -136,11 +137,15 @@ pub trait AMMHandlers // fn on_add_liquidity(asset_pair: AssetPair, amount: Balance); // fn on_remove_liquidity(asset_pair: AssetPair, amount: Balance); fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance); + fn on_trade_weight() -> Weight; } impl AMMHandlers for () { fn on_create_pool(_asset_pair: AssetPair) {} fn on_trade(_amm_transfer: &AMMTransfer, _liq_amount: Balance) {} + fn on_trade_weight() -> Weight { + Weight::zero() + } } pub trait AssetPairAccountIdFor { From a2da0749603a81650d6e8bd8cce9a8de18f4ffb1 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Sun, 15 Aug 2021 23:42:02 +0200 Subject: [PATCH 14/28] rework pricebuffer and average_price calculation --- pallets/price-oracle/src/benchmarking.rs | 38 ++++++----- pallets/price-oracle/src/lib.rs | 73 ++++++++++----------- pallets/price-oracle/src/mock.rs | 7 -- pallets/price-oracle/src/tests.rs | 81 +++++++++++++----------- pallets/price-oracle/src/types.rs | 35 ++++++++-- 5 files changed, 128 insertions(+), 106 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 435c4229b61..6c58a87535b 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -50,9 +50,7 @@ benchmarks! { PriceOracle::::on_create_pool(ASSET_PAIR_A); PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - let mut vec = Vec::new(); - vec.push(PRICE_ENTRY_1); - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(PRICE_ENTRY_1)); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; @@ -60,7 +58,7 @@ benchmarks! { }: { PriceOracle::::on_initialize(block_num.into()); } verify { - assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); @@ -70,14 +68,14 @@ benchmarks! { let block_num: u32 = 5; let a in 1 .. NUM_OF_ITERS; // trade_num PriceOracle::::on_create_pool(ASSET_PAIR_A); - let mut vec = Vec::new(); + let mut price_accumulator = PriceEntry::default(); for i in 0 .. a { PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); + price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); } - assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; @@ -85,7 +83,7 @@ benchmarks! { }: { PriceOracle::::on_initialize(block_num.into()); } verify { - assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); @@ -97,13 +95,13 @@ benchmarks! { PriceOracle::::on_create_pool(ASSET_PAIR_A); frame_system::Pallet::::set_block_number(Zero::zero()); - let mut vec = Vec::new(); + let mut price_accumulator = PriceEntry::default(); for i in 0 .. a { PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); + price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); } - assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); @@ -112,13 +110,13 @@ benchmarks! { frame_system::Pallet::::set_block_number((round - 1) .into()); PriceOracle::::on_initialize((round - 1).into()); - let mut vec = Vec::new(); + let mut price_accumulator = PriceEntry::default(); for i in 0 .. a { PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); + price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); } - assert_eq!(PriceBuffer::::try_get(ASSET_PAIR_A.name()), Ok(vec)); + assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; @@ -129,7 +127,7 @@ benchmarks! { }: { PriceOracle::::on_initialize(block_num.into()); } verify { - assert_eq!(PriceBuffer::::contains_key(ASSET_PAIR_A.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); let bucket_queue = PriceOracle::::price_data_hundred(ASSET_PAIR_A.name()); for i in 0 .. BucketQueue::BUCKET_SIZE { assert_eq!(bucket_queue[i as usize].volume, a as u128 * 1_000); @@ -160,7 +158,7 @@ benchmarks! { verify { for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); @@ -191,7 +189,7 @@ benchmarks! { verify { for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); @@ -221,7 +219,7 @@ benchmarks! { verify { for i in 0 .. NUM_OF_ITERS { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 2_000}); @@ -255,14 +253,14 @@ benchmarks! { }: { PriceOracle::::on_initialize(block_num.into()); } verify { let asset_pair = AssetPair {asset_in: 100, asset_out: 200}; - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: (NUM_OF_ITERS + 1) as u128 * 1_000}); for i in 1 .. NUM_OF_ITERS { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceBuffer::::contains_key(asset_pair.name()), false); + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index b2497851f67..25b826a8a30 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -17,15 +17,13 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::dispatch::{DispatchError, DispatchResult}; -use frame_support::sp_runtime::traits::Zero; use frame_support::pallet_prelude::Weight; +use frame_support::sp_runtime::traits::Zero; use primitives::{ asset::AssetPair, traits::{AMMHandlers, AMMTransfer}, AssetId, Balance, }; -use sp_std::convert::TryInto; use sp_std::marker::PhantomData; use sp_std::prelude::*; @@ -41,7 +39,7 @@ pub use types::*; pub mod weights; use weights::WeightInfo; -mod benchmarking; // TODO: rebenchmark +mod benchmarking; // TODO: rebenchmark // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; @@ -76,12 +74,13 @@ pub mod pallet { #[pallet::event] pub enum Event {} - /// Data generated by trades stored for processing. - /// In every block, all accumulated entries are processed and then removed from the storage. + /// Processed or partially processed data generated by trades. + /// Data generated by trades are processed sequentially. + /// Each new entry is combined with the previous value to produce new intermediate value. + /// The last entry creates the resulting average price and volume. #[pallet::storage] - #[pallet::getter(fn price_buffer)] - pub type PriceBuffer = StorageMap<_, Blake2_128Concat, AssetPairId, Vec, ValueQuery>; - + #[pallet::getter(fn price_accumulator)] + pub type PriceDataAccumulator = StorageMap<_, Blake2_128Concat, AssetPairId, PriceEntry, ValueQuery>; #[pallet::storage] #[pallet::getter(fn price_data_ten)] @@ -98,8 +97,8 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { - let (num_of_processed_buckets, num_of_processed_trades) = Self::update_data().unwrap_or_else(|_| (0, 0)); - PriceBuffer::::remove_all(None); + Self::update_data(); + PriceDataAccumulator::::remove_all(None); T::WeightInfo::on_initialize_no_entry() } } @@ -117,58 +116,56 @@ impl Pallet { } pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) { - PriceBuffer::::append(asset_pair.name(), price_entry); - } + let maybe_price = PriceDataAccumulator::::try_get(asset_pair.name()); + let previous_entry = if let Ok(previous_price) = maybe_price { + previous_price + } else { + PriceEntry::default() + }; - fn update_data() -> Result<(u32, u32), DispatchError> { - let mut num_of_processed_buckets = 0u32; - let mut num_of_processed_trades = 0u32; + if let Some(new_entry) = previous_entry.calculate_new_price_entry(&price_entry) { + PriceDataAccumulator::::insert(asset_pair.name(), new_entry); + } + } - PriceDataTen::::mutate(|data_ten| -> DispatchResult { + fn update_data() { + PriceDataTen::::mutate(|data_ten| { for (asset_pair_id, data) in data_ten.iter_mut() { - let maybe_price = >::try_get(asset_pair_id); - let result = if let Ok(prices) = maybe_price { - num_of_processed_buckets - .checked_add(1) - .ok_or(Error::::UpdateDataOverflow)?; - num_of_processed_trades - .checked_add(prices.len().try_into().map_err(|_| Error::::UpdateDataOverflow)?) - .ok_or(Error::::UpdateDataOverflow)?; - PriceInfo::calculate_price_info(prices.as_slice()).ok_or(Error::::PriceComputationError)? + let maybe_price = PriceDataAccumulator::::try_get(asset_pair_id); + let result = if let Ok(price_entry) = maybe_price { + PriceInfo { + avg_price: price_entry.price, + volume: price_entry.trade_amount, + } } else { data.get_last() }; data.update_last(result); } - - Ok(()) - })?; + }); let now = >::block_number(); if now.is_zero() { - return Ok((num_of_processed_buckets, num_of_processed_trades)); + return; + // return Ok((num_of_processed_buckets, num_of_processed_trades)); } // TODO: delete me. It is here just to make testing easier. if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { for element_from_ten in PriceDataTen::::get().iter() { - PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| -> DispatchResult { + PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| { data.update_last(element_from_ten.1.calculate_average()); - Ok(()) - })?; + }); } } if (now % T::BlockNumber::from(BUCKET_SIZE.pow(2))) == T::BlockNumber::from(BUCKET_SIZE.pow(2) - 1) { for element_from_hundred in PriceDataHundred::::iter() { - PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| -> DispatchResult { + PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| { data.update_last(element_from_hundred.1.calculate_average()); - Ok(()) - })?; + }); } } - - Ok((num_of_processed_buckets, num_of_processed_trades)) } } diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 442a1e99134..532f19f165a 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -18,7 +18,6 @@ use crate as price_oracle; use crate::Config; use frame_support::parameter_types; -use frame_support::traits::OnInitialize; use frame_system; use orml_traits::parameter_type_with_key; use price_oracle::{PriceEntry, PriceOracleHandler}; @@ -31,7 +30,6 @@ use sp_runtime::{ }; pub type Amount = i128; -pub type AccountId = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -165,8 +163,3 @@ impl ExtBuilder { sp_io::TestExternalities::from(storage) } } - -fn next_block() { - System::set_block_number(System::block_number() + 1); - PriceOracle::on_initialize(System::block_number()); -} diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 658a86e2817..71cc85b7a85 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -20,27 +20,13 @@ pub use crate::mock::{ Event as TestEvent, ExtBuilder, Origin, PriceOracle, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, PRICE_ENTRY_2, }; -use frame_support::{assert_noop, assert_storage_noop, assert_ok, traits::OnInitialize}; +use frame_support::{assert_storage_noop, traits::OnInitialize}; pub fn new_test_ext() -> sp_io::TestExternalities { let ext = ExtBuilder.build(); ext } -fn last_events(n: usize) -> Vec { - frame_system::Pallet::::events() - .into_iter() - .rev() - .take(n) - .rev() - .map(|e| e.event) - .collect() -} - -fn expect_events(e: Vec) { - assert_eq!(last_events(e.len()), e); -} - #[test] fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { @@ -71,20 +57,18 @@ fn add_existing_asset_pair_should_not_work() { #[test] fn on_trade_should_work() { new_test_ext().execute_with(|| { - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); - let mut vec = Vec::new(); - vec.push(PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_2); - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); + let price_entry = PRICE_ENTRY_1.calculate_new_price_entry(&PRICE_ENTRY_2); + assert_eq!(>::try_get(ASSET_PAIR_A.name()).ok(), price_entry); }); } #[test] fn on_trade_handler_should_work() { new_test_ext().execute_with(|| { - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); let amm_transfer = AMMTransfer { origin: 1, assets: ASSET_PAIR_A, @@ -96,16 +80,14 @@ fn on_trade_handler_should_work() { }; PriceOracleHandler::::on_trade(&amm_transfer, 2_000); - let mut vec = Vec::new(); - vec.push(PRICE_ENTRY_1); - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(vec)); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(PRICE_ENTRY_1)); }); } #[test] fn price_normalization_should_work() { new_test_ext().execute_with(|| { - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); + assert_eq!(>::try_get(ASSET_PAIR_A.name()), Err(())); let amm_transfer = AMMTransfer { origin: 1, @@ -195,11 +177,27 @@ fn price_normalization_should_work() { }; PriceOracleHandler::::on_trade(&amm_transfer, 2_000); - let data = PriceBuffer::::get(ASSET_PAIR_A.name()); - assert_eq!(data[0].price, Price::from(340282366920938463463)); - assert_eq!(data[1].price, Price::from(2_000)); - assert_eq!(data[2].price, Price::from_float(0.0005)); - assert_eq!(data.len(), 3); + let price_entry = PriceDataAccumulator::::get(ASSET_PAIR_A.name()); + let first_entry = PriceEntry { + price: Price::from(340282366920938463463), + trade_amount: 340282366920938463463, + liquidity_amount: 2_000, + }; + + let second_entry = PriceEntry { + price: Price::from(2_000), + trade_amount: 2_000_000, + liquidity_amount: 2_000, + }; + + let third_entry = PriceEntry { + price: Price::from_float(0.0005), + trade_amount: 1_000, + liquidity_amount: 2_000, + }; + + let result = PriceEntry::default().calculate_new_price_entry(&first_entry).unwrap().calculate_new_price_entry(&second_entry).unwrap().calculate_new_price_entry(&third_entry).unwrap(); + assert_eq!(price_entry, result); }); } @@ -215,7 +213,7 @@ fn update_data_should_work() { PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); PriceOracle::on_trade(ASSET_PAIR_B, PRICE_ENTRY_1); - assert_ok!(PriceOracle::update_data()); + PriceOracle::update_data(); let data_ten_a = PriceOracle::price_data_ten() .iter() @@ -258,8 +256,19 @@ fn update_data_with_incorrect_input_should_not_work() { liquidity_amount: Zero::zero(), }); - assert_noop!(PriceOracle::update_data(), - Error::::PriceComputationError, + PriceOracle::update_data(); + + let data_ten = PriceOracle::price_data_ten() + .iter() + .find(|&x| x.0 == ASSET_PAIR_A.name()) + .unwrap() + .1; + assert_eq!( + data_ten.get_last(), + PriceInfo { + avg_price: Zero::zero(), + volume: Zero::zero() + } ); }); } @@ -272,7 +281,7 @@ fn update_empty_data_should_work() { for i in 0..1002 { System::set_block_number(i); - assert_ok!(PriceOracle::update_data()); + PriceOracle::update_data(); } let data_ten = PriceOracle::price_data_ten() @@ -429,7 +438,7 @@ fn stable_price_should_work() { let num_of_iters = BucketQueue::BUCKET_SIZE.pow(3); PriceOracle::on_create_pool(ASSET_PAIR_A); - for i in 0 .. num_of_iters { + for i in num_of_iters - 2 .. 2 * num_of_iters + 2{ System::set_block_number(i.into()); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); PriceOracle::on_initialize(i.into()); @@ -470,7 +479,7 @@ fn stable_price_should_work() { PriceOracle::on_initialize(i.into()); } - let data_ten_a = PriceOracle::price_data_ten() + let data_ten = PriceOracle::price_data_ten() .iter() .find(|&x| x.0 == ASSET_PAIR_A.name()) .unwrap() diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs index b7a038bcb31..6461f448e26 100644 --- a/pallets/price-oracle/src/types.rs +++ b/pallets/price-oracle/src/types.rs @@ -16,7 +16,7 @@ // limitations under the License. use codec::{Decode, Encode}; -use frame_support::sp_runtime::traits::{CheckedDiv, CheckedMul, Zero}; +use frame_support::sp_runtime::traits::{CheckedAdd, CheckedDiv, CheckedMul, Zero}; use frame_support::sp_runtime::RuntimeDebug; pub use primitives::{Balance, Price}; use sp_std::iter::Sum; @@ -86,6 +86,25 @@ impl<'a> Sum<&'a Self> for PriceEntry { } } +impl PriceEntry { + pub fn calculate_new_price_entry(&self, previous_price_entry: &Self) -> Option { + let total_liquidity = previous_price_entry + .liquidity_amount + .checked_add(self.liquidity_amount)?; + let product_of_old_values = previous_price_entry + .price + .checked_mul(&Price::from_inner(previous_price_entry.liquidity_amount))?; + let product_of_new_values = self.price.checked_mul(&Price::from_inner(self.liquidity_amount))?; + Some(Self { + price: product_of_old_values + .checked_add(&product_of_new_values)? + .checked_div(&Price::from_inner(total_liquidity))?, + trade_amount: previous_price_entry.trade_amount.checked_add(self.trade_amount)?, + liquidity_amount: total_liquidity, + }) + } +} + pub const BUCKET_SIZE: u32 = 10; pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; @@ -143,8 +162,8 @@ impl PriceInfoCalculation for PriceInfo { let price = x.price.checked_mul(&Price::from(x.liquidity_amount))?; Some(PriceEntry { price, - trade_amount: x.trade_amount, - liquidity_amount: x.liquidity_amount, + trade_amount: x.trade_amount, + liquidity_amount: x.liquidity_amount, }) }) .collect::>>()?; @@ -198,8 +217,14 @@ impl BucketQueueT for BucketQueue { fn calculate_average(&self) -> PriceInfo { let sum = self.bucket.iter().sum::(); PriceInfo { - avg_price: sum.avg_price.checked_div(&Price::from(Self::BUCKET_SIZE as u128)).expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), - volume: sum.volume.checked_div(Self::BUCKET_SIZE as u128).expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), + avg_price: sum + .avg_price + .checked_div(&Price::from(Self::BUCKET_SIZE as u128)) + .expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), + volume: sum + .volume + .checked_div(Self::BUCKET_SIZE as u128) + .expect("avg_price is valid value; BUCKET_SIZE is non-zero integer; qed"), } } } From fdbad0689e58d71aae9f707323ecb2985ed02e22 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Mon, 16 Aug 2021 13:48:30 +0200 Subject: [PATCH 15/28] add comments --- pallets/price-oracle/src/lib.rs | 10 ++++++++++ pallets/price-oracle/src/types.rs | 33 ++++++------------------------- pallets/xyk/src/lib.rs | 2 +- primitives/src/traits.rs | 30 +++++++++++++++++----------- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 25b826a8a30..09c40a5591c 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -82,14 +82,19 @@ pub mod pallet { #[pallet::getter(fn price_accumulator)] pub type PriceDataAccumulator = StorageMap<_, Blake2_128Concat, AssetPairId, PriceEntry, ValueQuery>; + /// The last ten average values corresponding to the last ten blocks. #[pallet::storage] #[pallet::getter(fn price_data_ten)] pub type PriceDataTen = StorageValue<_, Vec<(AssetPairId, BucketQueue)>, ValueQuery>; + /// The last ten average values corresponding to the last hundred blocks. + /// Each average value corresponds to an interval of length ten blocks. #[pallet::storage] #[pallet::getter(fn price_data_hundred)] pub type PriceDataHundred = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; + /// The last ten average values corresponding to the last thousand blocks. + /// Each average value corresponds to an interval of length hundred blocks. #[pallet::storage] #[pallet::getter(fn price_data_thousand)] pub type PriceDataThousand = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; @@ -97,8 +102,11 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { + // update average values in the storage Self::update_data(); + // clear the price buffer PriceDataAccumulator::::remove_all(None); + T::WeightInfo::on_initialize_no_entry() } } @@ -151,6 +159,7 @@ impl Pallet { // return Ok((num_of_processed_buckets, num_of_processed_trades)); } // TODO: delete me. It is here just to make testing easier. + // check if it's time to update "hundred" values if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { for element_from_ten in PriceDataTen::::get().iter() { PriceDataHundred::::mutate(element_from_ten.0.clone(), |data| { @@ -159,6 +168,7 @@ impl Pallet { } } + // check if it's time to update "thousand" values if (now % T::BlockNumber::from(BUCKET_SIZE.pow(2))) == T::BlockNumber::from(BUCKET_SIZE.pow(2) - 1) { for element_from_hundred in PriceDataHundred::::iter() { PriceDataThousand::::mutate(element_from_hundred.0.clone(), |data| { diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs index 6461f448e26..4f19fd5715f 100644 --- a/pallets/price-oracle/src/types.rs +++ b/pallets/price-oracle/src/types.rs @@ -26,6 +26,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +/// A type representing data produced by a trade. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] pub struct PriceEntry { @@ -87,6 +88,7 @@ impl<'a> Sum<&'a Self> for PriceEntry { } impl PriceEntry { + /// Updates the previous average value with a new entry. pub fn calculate_new_price_entry(&self, previous_price_entry: &Self) -> Option { let total_liquidity = previous_price_entry .liquidity_amount @@ -109,10 +111,6 @@ pub const BUCKET_SIZE: u32 = 10; pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; -pub trait PriceInfoCalculation { - fn calculate_price_info(entries: &[PriceEntry]) -> Option; -} - #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] pub struct PriceInfo { @@ -154,29 +152,7 @@ impl<'a> Sum<&'a Self> for PriceInfo { } } -impl PriceInfoCalculation for PriceInfo { - fn calculate_price_info(entries: &[PriceEntry]) -> Option { - let intermediate_result: Vec = entries - .iter() - .map(|x| -> Option { - let price = x.price.checked_mul(&Price::from(x.liquidity_amount))?; - Some(PriceEntry { - price, - trade_amount: x.trade_amount, - liquidity_amount: x.liquidity_amount, - }) - }) - .collect::>>()?; - - let sum = intermediate_result.iter().sum::(); - let weighted_avg_price = sum.price.checked_div(&Price::from(sum.liquidity_amount as u128))?; - Some(PriceInfo { - avg_price: weighted_avg_price, - volume: sum.trade_amount, - }) - } -} - +/// A circular buffer storing average prices and volumes #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] pub struct BucketQueue { @@ -205,15 +181,18 @@ pub trait BucketQueueT { } impl BucketQueueT for BucketQueue { + /// Add new entry to the front and remove the oldest entry. fn update_last(&mut self, price_info: PriceInfo) { self.last = (self.last + 1) % Self::BUCKET_SIZE; self.bucket[self.last as usize] = price_info; } + /// Get the last entry added fn get_last(&self) -> PriceInfo { self.bucket[self.last as usize] } + /// Calculate average price and volume from all the entries. fn calculate_average(&self) -> PriceInfo { let sum = self.bucket.iter().sum::(); PriceInfo { diff --git a/pallets/xyk/src/lib.rs b/pallets/xyk/src/lib.rs index 4f4bf9747d0..1efc8f305e5 100644 --- a/pallets/xyk/src/lib.rs +++ b/pallets/xyk/src/lib.rs @@ -490,7 +490,7 @@ pub mod pallet { /// `max_limit` - maximum amount of `asset_in` to be sold in exchange for `asset_out`. /// /// Emits `BuyExecuted` when successful. - #[pallet::weight(::WeightInfo::buy())] + #[pallet::weight(::WeightInfo::buy() + ::AMMHandler::on_trade_weight())] pub fn buy( origin: OriginFor, asset_out: AssetId, diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index 3e7df9ff6e3..6fff93e8c7b 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -17,12 +17,15 @@ #![allow(clippy::upper_case_acronyms)] +use crate::asset::AssetPair; +use crate::{Balance, Price}; use frame_support::dispatch; -use frame_support::sp_runtime::{FixedPointNumber, traits::{CheckedDiv, Zero}}; +use frame_support::sp_runtime::{ + traits::{CheckedDiv, Zero}, + FixedPointNumber, +}; use frame_support::weights::Weight; use sp_std::vec::Vec; -use crate::{Balance, Price}; -use crate::asset::AssetPair; /// Hold information to perform amm transfer /// Contains also exact amount which will be sold/bought @@ -38,9 +41,9 @@ pub struct AMMTransfer { impl AMMTransfer where - Balance: Copy + Balance: Copy, { - /// Calculate price from ordered assets + /// Calculate price from ordered assets pub fn normalize_price(&self) -> Option<(Price, Balance)> { let ordered_asset_pair = self.assets.ordered_pair(); let (balance_a, balance_b) = if ordered_asset_pair.0 == self.assets.asset_in { @@ -49,10 +52,10 @@ where (self.amount_out, self.amount) }; - let price_a = Price::checked_from_integer(balance_a)?; + let price_a = Price::checked_from_integer(balance_a)?; let price_b = Price::checked_from_integer(balance_b)?; let price = price_a.checked_div(&price_b); - price.map(|p| (p, balance_a)) + price.map(|p| (p, balance_a)) } } @@ -123,7 +126,7 @@ pub trait AMM { } pub trait Resolver { - /// Resolve an intention directl via AMM pool. + /// Resolve an intention directly via AMM pool. fn resolve_single_intention(intention: &Intention); /// Resolve intentions by either directly trading with each other or via AMM pool. @@ -131,12 +134,15 @@ pub trait Resolver { fn resolve_matched_intentions(pair_account: &AccountId, intention: &Intention, matched: &[Intention]); } -pub trait AMMHandlers -{ +/// AMM handlers used by AMM pools to indicate various events. +pub trait AMMHandlers { + /// Register an asset to by handled by price-oracle pallet. + /// If an asset is not registered, calling `on_trade` results in populating the price buffer in the price oracle pallet, + /// but the entries are ignored and the average price for the asset is not calculated. fn on_create_pool(asset_pair: AssetPair); - // fn on_add_liquidity(asset_pair: AssetPair, amount: Balance); - // fn on_remove_liquidity(asset_pair: AssetPair, amount: Balance); + /// Include a trade in the average price calculation of the price-oracle pallet. fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance); + /// Known `on_trade` overhead. Needs to be specified here if we don't want to make AMM pools tightly coupled with the price oracle pallet. fn on_trade_weight() -> Weight; } From 14e6f315b5e1336e820946a56b13f088c17da761 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Mon, 16 Aug 2021 22:43:46 +0200 Subject: [PATCH 16/28] add num_of_assets storage --- pallets/price-oracle/src/lib.rs | 15 +++++++++++++-- pallets/price-oracle/src/tests.rs | 3 +++ primitives/src/traits.rs | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 09c40a5591c..de2897fd912 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -74,6 +74,11 @@ pub mod pallet { #[pallet::event] pub enum Event {} + /// The number of assets registered and handled by this pallet. + #[pallet::storage] + #[pallet::getter(fn num_of_assets)] + pub type NumOfTrackedAssets = StorageValue<_, u32, ValueQuery>; + /// Processed or partially processed data generated by trades. /// Data generated by trades are processed sequentially. /// Each new entry is combined with the previous value to produce new intermediate value. @@ -107,7 +112,7 @@ pub mod pallet { // clear the price buffer PriceDataAccumulator::::remove_all(None); - T::WeightInfo::on_initialize_no_entry() + T::WeightInfo::on_initialize_one_entry_multiple_tokens(Self::num_of_assets()) } } @@ -119,6 +124,13 @@ impl Pallet { pub fn on_create_pool(asset_pair: AssetPair) { let data = PriceDataTen::::get(); if !data.iter().any(|bucket_tuple| bucket_tuple.0 == asset_pair.name()) { + let incremented_asset_count = if let Some(count) = Self::num_of_assets().checked_add(1) { + count + } else { + return + }; + >::put(incremented_asset_count); + PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); } } @@ -156,7 +168,6 @@ impl Pallet { let now = >::block_number(); if now.is_zero() { return; - // return Ok((num_of_processed_buckets, num_of_processed_trades)); } // TODO: delete me. It is here just to make testing easier. // check if it's time to update "hundred" values diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 71cc85b7a85..950a79ee86e 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -30,11 +30,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities { #[test] fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { + assert_eq!(PriceOracle::num_of_assets(), 0); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); PriceOracle::on_create_pool(ASSET_PAIR_A); + + assert_eq!(PriceOracle::num_of_assets(), 1); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), true diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index 6fff93e8c7b..a841493e28c 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -136,7 +136,7 @@ pub trait Resolver { /// AMM handlers used by AMM pools to indicate various events. pub trait AMMHandlers { - /// Register an asset to by handled by price-oracle pallet. + /// Register an asset to be handled by price-oracle pallet. /// If an asset is not registered, calling `on_trade` results in populating the price buffer in the price oracle pallet, /// but the entries are ignored and the average price for the asset is not calculated. fn on_create_pool(asset_pair: AssetPair); From a38d737f66eb97715fdd426ca5915137cb80c9b1 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 18 Aug 2021 12:35:19 +0200 Subject: [PATCH 17/28] remove useless if statement --- pallets/price-oracle/src/lib.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index de2897fd912..3a42de7afce 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -166,9 +166,6 @@ impl Pallet { }); let now = >::block_number(); - if now.is_zero() { - return; - } // TODO: delete me. It is here just to make testing easier. // check if it's time to update "hundred" values if (now % T::BlockNumber::from(BUCKET_SIZE)) == T::BlockNumber::from(BUCKET_SIZE - 1) { From 6487d3547c8b74fb3c118ca7d7f802a1bd4a936c Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 18 Aug 2021 22:51:39 +0200 Subject: [PATCH 18/28] remove not necessary benchmarks --- pallets/price-oracle/src/benchmarking.rs | 199 +++++------------------ 1 file changed, 42 insertions(+), 157 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 6c58a87535b..7f630fcd222 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -36,7 +36,6 @@ pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { }; pub const NUM_OF_ITERS: u32 = 100; -pub const NUM_OF_NESTED_ITERS: u32 = 100; benchmarks! { on_initialize_no_entry { @@ -45,7 +44,7 @@ benchmarks! { verify { } - on_initialize_one_entry { + on_initialize_one_token { let block_num: u32 = 5; PriceOracle::::on_create_pool(ASSET_PAIR_A); PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); @@ -64,182 +63,79 @@ benchmarks! { assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); } - on_initialize_multiple_entries_one_token { - let block_num: u32 = 5; - let a in 1 .. NUM_OF_ITERS; // trade_num - PriceOracle::::on_create_pool(ASSET_PAIR_A); - let mut price_accumulator = PriceEntry::default(); - - for i in 0 .. a { - PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); - } - - assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); - - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - - }: { PriceOracle::::on_initialize(block_num.into()); } - verify { - assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); - } - - on_initialize_multiple_entries_one_token_all_bucket_levels { + on_initialize_multiple_tokens_all_bucket_levels { let block_num: u32 = BUCKET_SIZE.pow(3); let a in 1 .. NUM_OF_ITERS; // trade_num - PriceOracle::::on_create_pool(ASSET_PAIR_A); frame_system::Pallet::::set_block_number(Zero::zero()); - let mut price_accumulator = PriceEntry::default(); + let asset_pair = AssetPair::default(); for i in 0 .. a { - PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + PriceOracle::::on_create_pool(asset_pair); + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); } - assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); + assert_eq!(PriceDataAccumulator::::try_get(asset_pair.name()), Ok(PRICE_ENTRY_1)); let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + for i in 0 .. a { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); + } for round in 1.. block_num { frame_system::Pallet::::set_block_number((round - 1) .into()); PriceOracle::::on_initialize((round - 1).into()); - let mut price_accumulator = PriceEntry::default(); for i in 0 .. a { - PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - price_accumulator = price_accumulator.calculate_new_price_entry(&PRICE_ENTRY_1).unwrap(); + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); } - assert_eq!(PriceDataAccumulator::::try_get(ASSET_PAIR_A.name()), Ok(price_accumulator)); - - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); + assert_eq!(PriceDataAccumulator::::try_get(asset_pair.name()), Ok(PRICE_ENTRY_1)); } frame_system::Pallet::::set_block_number(block_num.into()); }: { PriceOracle::::on_initialize(block_num.into()); } verify { - assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); - let bucket_queue = PriceOracle::::price_data_hundred(ASSET_PAIR_A.name()); - for i in 0 .. BucketQueue::BUCKET_SIZE { - assert_eq!(bucket_queue[i as usize].volume, a as u128 * 1_000); - } - - let bucket_queue = PriceOracle::::price_data_thousand(ASSET_PAIR_A.name()); - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); - } - - on_initialize_one_entry_multiple_tokens { - let block_num: u32 = 5; - let b in 1 .. NUM_OF_ITERS; // token num - let mut vec = Vec::new(); - let asset_pair = AssetPair::default(); - - for i in 0 .. b { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - PriceOracle::::on_create_pool(asset_pair); - PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); - } - + let asset_pair = AssetPair {asset_in: 1_000, asset_out: 2_000}; + assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - - }: { PriceOracle::::on_initialize(block_num.into()); } - verify { - for i in 0 .. b { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + for i in 0 .. BucketQueue::BUCKET_SIZE { + for j in 0 .. a { + let asset_pair = AssetPair {asset_in: j * 1_000, asset_out: j * 2_000}; + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue[i as usize], PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + } } - } - - on_initialize_multiple_entries_multiple_tokens { - let block_num: u32 = 5; - let b in 1 .. NUM_OF_ITERS; - let a in 1 .. NUM_OF_NESTED_ITERS; - let mut vec = Vec::new(); - let asset_pair = AssetPair::default(); - for i in 0 .. b { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; + let bucket_queue = PriceOracle::::price_data_hundred(asset_pair.name()); + for i in 0 .. BucketQueue::BUCKET_SIZE { for j in 0 .. a { - PriceOracle::::on_create_pool(asset_pair); - PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); + let asset_pair = AssetPair {asset_in: j * 1_000, asset_out: j * 2_000}; + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue[i as usize], PriceInfo{ avg_price: Price::from(2), volume: 1_000}); } - vec.push(PRICE_ENTRY_1); } - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - - }: { PriceOracle::::on_initialize(block_num.into()); } - verify { - for i in 0 .. b { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: a as u128 * 1_000}); + let bucket_queue = PriceOracle::::price_data_thousand(asset_pair.name()); + for i in 0 .. BucketQueue::BUCKET_SIZE { + for j in 0 .. a { + let asset_pair = AssetPair {asset_in: j * 1_000, asset_out: j * 2_000}; + let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; + assert_eq!(bucket_queue[i as usize], PriceInfo{ avg_price: Price::from(2), volume: 1_000}); + } } - } - on_initialize_uniform_distribution { + on_initialize_multiple_tokens { let block_num: u32 = 5; + let b in 1 .. NUM_OF_ITERS; // token num let mut vec = Vec::new(); let asset_pair = AssetPair::default(); - for i in 0 .. NUM_OF_ITERS { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - PriceOracle::::on_create_pool(asset_pair); - // 2 trades - PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); - PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); - } - - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - - }: { PriceOracle::::on_initialize(block_num.into()); } - verify { - for i in 0 .. NUM_OF_ITERS { - let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 2_000}); - } - - } - - // we expect this test to have similar results as the previous test - on_initialize_nonuniform_distribution { - let block_num: u32 = 5; - let mut vec = Vec::new(); - - let asset_pair = AssetPair {asset_in: 100, asset_out: 200}; - for i in 0 .. NUM_OF_ITERS + 1{ - PriceOracle::::on_create_pool(asset_pair); - PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); - vec.push(PRICE_ENTRY_1); - } - - for i in 1 .. NUM_OF_ITERS { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_create_pool(asset_pair); PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); @@ -252,20 +148,13 @@ benchmarks! { }: { PriceOracle::::on_initialize(block_num.into()); } verify { - let asset_pair = AssetPair {asset_in: 100, asset_out: 200}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); - let price_data = PriceOracle::::price_data_ten(); - let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; - assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: (NUM_OF_ITERS + 1) as u128 * 1_000}); - - for i in 1 .. NUM_OF_ITERS { + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); } - } } @@ -279,13 +168,9 @@ mod tests { fn test_benchmarks() { new_test_ext().execute_with(|| { assert_ok!(test_benchmark_on_initialize_no_entry::()); - assert_ok!(test_benchmark_on_initialize_one_entry::()); - assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token::()); - assert_ok!(test_benchmark_on_initialize_multiple_entries_one_token_all_bucket_levels::()); - assert_ok!(test_benchmark_on_initialize_one_entry_multiple_tokens::()); - assert_ok!(test_benchmark_on_initialize_multiple_entries_multiple_tokens::()); - assert_ok!(test_benchmark_on_initialize_uniform_distribution::()); - assert_ok!(test_benchmark_on_initialize_nonuniform_distribution::()); + assert_ok!(test_benchmark_on_initialize_one_token::()); + assert_ok!(test_benchmark_on_initialize_multiple_tokens_all_bucket_levels::()); + assert_ok!(test_benchmark_on_initialize_multiple_tokens::()); }); } } From 7ca20efeb6a25bf6000207944230e4242ee8fbaf Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Thu, 19 Aug 2021 15:54:47 +0200 Subject: [PATCH 19/28] add PoolRegistered event --- pallets/price-oracle/src/lib.rs | 16 +++++++++++++--- pallets/price-oracle/src/tests.rs | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 3a42de7afce..9d857a204fa 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -72,7 +72,11 @@ pub mod pallet { } #[pallet::event] - pub enum Event {} + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event { + /// Pool was registered. [AssetPair] + PoolRegistered(AssetPair), + } /// The number of assets registered and handled by this pallet. #[pallet::storage] @@ -127,11 +131,14 @@ impl Pallet { let incremented_asset_count = if let Some(count) = Self::num_of_assets().checked_add(1) { count } else { + // We don't want to throw an error here because this method is used in different extrinsics. + // We also do not expect to have more than 2^32 assets registered. return }; >::put(incremented_asset_count); PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); + Self::deposit_event(Event::PoolRegistered(asset_pair)); } } @@ -143,6 +150,7 @@ impl Pallet { PriceEntry::default() }; + // Invalid values are ignored and not added to the queue. if let Some(new_entry) = previous_entry.calculate_new_price_entry(&price_entry) { PriceDataAccumulator::::insert(asset_pair.name(), new_entry); } @@ -197,11 +205,13 @@ impl AMMHandlers for Price let (price, amount) = if let Some(price_tuple) = amm_transfer.normalize_price() { price_tuple } else { + // We don't want to throw an error here because this method is used in different extrinsics. + // Invalid prices are ignored and not added to the queue. return; }; - // we assume that zero prices are not valid - // zero values are ignored and not added to the queue + // We assume that zero values are not valid. + // Zero values are ignored and not added to the queue. if price.is_zero() || amount.is_zero() || liq_amount.is_zero() { return; } diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index 950a79ee86e..d23a85bdf31 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -27,6 +27,20 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext } +fn last_events(n: usize) -> Vec { + frame_system::Pallet::::events() + .into_iter() + .rev() + .take(n) + .rev() + .map(|e| e.event) + .collect() +} + +fn expect_events(e: Vec) { + assert_eq!(last_events(e.len()), e); +} + #[test] fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { @@ -42,6 +56,7 @@ fn add_new_asset_pair_should_work() { >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), true ); + expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); }); } @@ -54,6 +69,7 @@ fn add_existing_asset_pair_should_not_work() { ); PriceOracle::on_create_pool(ASSET_PAIR_A); assert_storage_noop!(PriceOracle::on_create_pool(ASSET_PAIR_A)); + expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); }); } From 534465213edee32e2a9ad24466bd1ebf1685b95a Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Thu, 19 Aug 2021 17:08:05 +0200 Subject: [PATCH 20/28] fix event handling in tests --- pallets/price-oracle/src/tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index d23a85bdf31..e35bfd58ca6 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -44,6 +44,7 @@ fn expect_events(e: Vec) { #[test] fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { + System::set_block_number(3); assert_eq!(PriceOracle::num_of_assets(), 0); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), @@ -63,13 +64,14 @@ fn add_new_asset_pair_should_work() { #[test] fn add_existing_asset_pair_should_not_work() { new_test_ext().execute_with(|| { + System::set_block_number(3); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); PriceOracle::on_create_pool(ASSET_PAIR_A); assert_storage_noop!(PriceOracle::on_create_pool(ASSET_PAIR_A)); - expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); + expect_events(vec![]); }); } From 8de92c6b1d7e10690f894b89aacd65004f91e9d8 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Fri, 20 Aug 2021 16:32:11 +0200 Subject: [PATCH 21/28] various improvements --- pallets/price-oracle/src/lib.rs | 52 ++++++++++------ pallets/price-oracle/src/weights.rs | 97 ++++++++++++----------------- pallets/xyk/src/lib.rs | 5 +- primitives/src/traits.rs | 19 ++++-- runtime/src/weights/price_oracle.rs | 45 ++++++------- 5 files changed, 106 insertions(+), 112 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 9d857a204fa..21edff9aa63 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -21,7 +21,7 @@ use frame_support::pallet_prelude::Weight; use frame_support::sp_runtime::traits::Zero; use primitives::{ asset::AssetPair, - traits::{AMMHandlers, AMMTransfer}, + traits::{OnCreatePoolHandler, OnTradeHandler, AMMTransfer}, AssetId, Balance, }; use sp_std::marker::PhantomData; @@ -43,7 +43,11 @@ mod benchmarking; // TODO: rebenchmark // Re-export pallet items so that they can be accessed from the crate namespace. pub use pallet::*; +use sp_runtime::DispatchError; +/// Unique identifier for an asset pair. +/// AMM pools derive their own unique identifiers for asset pairs, +/// but this one is meant to not be bounded to one particular AMM pool. pub type AssetPairId = Vec; #[frame_support::pallet] @@ -81,7 +85,7 @@ pub mod pallet { /// The number of assets registered and handled by this pallet. #[pallet::storage] #[pallet::getter(fn num_of_assets)] - pub type NumOfTrackedAssets = StorageValue<_, u32, ValueQuery>; + pub type TrackedAssetsCount = StorageValue<_, u32, ValueQuery>; /// Processed or partially processed data generated by trades. /// Data generated by trades are processed sequentially. @@ -116,7 +120,7 @@ pub mod pallet { // clear the price buffer PriceDataAccumulator::::remove_all(None); - T::WeightInfo::on_initialize_one_entry_multiple_tokens(Self::num_of_assets()) + T::WeightInfo::on_initialize_multiple_tokens_all_bucket_levels(Self::num_of_assets()) } } @@ -128,14 +132,12 @@ impl Pallet { pub fn on_create_pool(asset_pair: AssetPair) { let data = PriceDataTen::::get(); if !data.iter().any(|bucket_tuple| bucket_tuple.0 == asset_pair.name()) { - let incremented_asset_count = if let Some(count) = Self::num_of_assets().checked_add(1) { - count - } else { + let _ = TrackedAssetsCount::::try_mutate(|value| -> Result<(), DispatchError> { + *value = value.checked_add(1).ok_or(Error::::PriceComputationError)?; + Ok(()) // We don't want to throw an error here because this method is used in different extrinsics. // We also do not expect to have more than 2^32 assets registered. - return - }; - >::put(incremented_asset_count); + }).map_err(|_| panic!("Max number of assets reached!")); PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); Self::deposit_event(Event::PoolRegistered(asset_pair)); @@ -143,17 +145,25 @@ impl Pallet { } pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) { - let maybe_price = PriceDataAccumulator::::try_get(asset_pair.name()); - let previous_entry = if let Ok(previous_price) = maybe_price { - previous_price - } else { - PriceEntry::default() - }; + + let _ = PriceDataAccumulator::::mutate(asset_pair.name(), |previous_price_entry| { + let maybe_new_price_entry = previous_price_entry.calculate_new_price_entry(&price_entry); + if let Some(new_price_entry) = maybe_new_price_entry { + *previous_price_entry = new_price_entry; + } + }); + + // let maybe_price = PriceDataAccumulator::::try_get(asset_pair.name()); + // let previous_entry = if let Ok(previous_price) = maybe_price { + // previous_price + // } else { + // PriceEntry::default() + // }; // Invalid values are ignored and not added to the queue. - if let Some(new_entry) = previous_entry.calculate_new_price_entry(&price_entry) { - PriceDataAccumulator::::insert(asset_pair.name(), new_entry); - } + // if let Some(new_entry) = previous_entry.calculate_new_price_entry(&price_entry) { + // PriceDataAccumulator::::insert(asset_pair.name(), new_entry); + // } } fn update_data() { @@ -196,11 +206,13 @@ impl Pallet { } pub struct PriceOracleHandler(PhantomData); -impl AMMHandlers for PriceOracleHandler { +impl OnCreatePoolHandler for PriceOracleHandler { fn on_create_pool(asset_pair: AssetPair) { Pallet::::on_create_pool(asset_pair); } +} +impl OnTradeHandler for PriceOracleHandler { fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance) { let (price, amount) = if let Some(price_tuple) = amm_transfer.normalize_price() { price_tuple @@ -226,6 +238,6 @@ impl AMMHandlers for Price } fn on_trade_weight() -> Weight { - T::WeightInfo::on_initialize_one_entry() - T::WeightInfo::on_initialize_no_entry() + T::WeightInfo::on_initialize_one_token() - T::WeightInfo::on_initialize_no_entry() } } diff --git a/pallets/price-oracle/src/weights.rs b/pallets/price-oracle/src/weights.rs index 09430531b51..0835e67286c 100644 --- a/pallets/price-oracle/src/weights.rs +++ b/pallets/price-oracle/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_price_oracle //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-08-08, STEPS: [10, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-08-20, STEPS: [100, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -32,7 +32,7 @@ // compiled // --pallet // pallet-price-oracle -// --steps=10 +// --steps=100 // --repeat=20 // --extrinsic // * @@ -53,91 +53,72 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_price_oracle. pub trait WeightInfo { fn on_initialize_no_entry() -> Weight; - fn on_initialize_one_entry() -> Weight; - fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight; - fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight; - fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight; + fn on_initialize_one_token() -> Weight; + fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight; + fn on_initialize_multiple_tokens(b: u32, ) -> Weight; } /// Weights for pallet_price_oracle using the hack.hydraDX node and recommended hardware. pub struct HydraWeight(PhantomData); impl WeightInfo for HydraWeight { fn on_initialize_no_entry() -> Weight { - (38_000_000 as Weight) + (29_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn on_initialize_one_entry() -> Weight { - (43_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + fn on_initialize_one_token() -> Weight { + (48_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { - (43_629_000 as Weight) - // Standard Error: 6_000 - .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { + (44_809_000 as Weight) + // Standard Error: 18_000 + .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 203_000 - .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) } - fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 655_000 - .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 655_000 - .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + fn on_initialize_multiple_tokens(b: u32, ) -> Weight { + (31_504_000 as Weight) + // Standard Error: 17_000 + .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) } } // For backwards compatibility and tests impl WeightInfo for () { fn on_initialize_no_entry() -> Weight { - (38_000_000 as Weight) + (29_000_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } - fn on_initialize_one_entry() -> Weight { - (43_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + fn on_initialize_one_token() -> Weight { + (48_000_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } - fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { - (43_629_000 as Weight) - // Standard Error: 6_000 - .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { + (44_809_000 as Weight) + // Standard Error: 18_000 + .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) - } - fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 203_000 - .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) } - fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 655_000 - .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 655_000 - .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + fn on_initialize_multiple_tokens(b: u32, ) -> Weight { + (31_504_000 as Weight) + // Standard Error: 17_000 + .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) } } \ No newline at end of file diff --git a/pallets/xyk/src/lib.rs b/pallets/xyk/src/lib.rs index 1efc8f305e5..6b75be88940 100644 --- a/pallets/xyk/src/lib.rs +++ b/pallets/xyk/src/lib.rs @@ -36,7 +36,7 @@ use frame_support::{dispatch::DispatchResult, ensure, traits::Get, transactional use frame_system::ensure_signed; use primitives::{ asset::AssetPair, fee, traits::AMM, AssetId, Balance, Price, MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, - MIN_TRADING_LIMIT, traits::AMMHandlers, + MIN_TRADING_LIMIT, traits::{OnCreatePoolHandler, OnTradeHandler}, }; use sp_std::{marker::PhantomData, vec, vec::Vec}; @@ -67,6 +67,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::OriginFor; + use primitives::traits::{OnTradeHandler}; #[pallet::pallet] pub struct Pallet(_); @@ -96,7 +97,7 @@ pub mod pallet { type GetExchangeFee: Get; /// AMM handlers - type AMMHandler: AMMHandlers; + type AMMHandler: OnCreatePoolHandler + OnTradeHandler; } #[pallet::error] diff --git a/primitives/src/traits.rs b/primitives/src/traits.rs index a841493e28c..b6763658bc5 100644 --- a/primitives/src/traits.rs +++ b/primitives/src/traits.rs @@ -134,20 +134,29 @@ pub trait Resolver { fn resolve_matched_intentions(pair_account: &AccountId, intention: &Intention, matched: &[Intention]); } -/// AMM handlers used by AMM pools to indicate various events. -pub trait AMMHandlers { +/// Handler used by AMM pools to perform some tasks when a new pool is created. +pub trait OnCreatePoolHandler { /// Register an asset to be handled by price-oracle pallet. /// If an asset is not registered, calling `on_trade` results in populating the price buffer in the price oracle pallet, /// but the entries are ignored and the average price for the asset is not calculated. fn on_create_pool(asset_pair: AssetPair); +} + +impl OnCreatePoolHandler for () { + fn on_create_pool(_asset_pair: AssetPair) {} +} + +/// Handler used by AMM pools to perform some tasks when a trade is executed. +pub trait OnTradeHandler { /// Include a trade in the average price calculation of the price-oracle pallet. fn on_trade(amm_transfer: &AMMTransfer, liq_amount: Balance); - /// Known `on_trade` overhead. Needs to be specified here if we don't want to make AMM pools tightly coupled with the price oracle pallet. + /// Known overhead for a trade in `on_initialize/on_finalize`. + /// Needs to be specified here if we don't want to make AMM pools tightly coupled with the price oracle pallet, otherwise we can't access the weight. + /// Add this weight to an extrinsic from which you call `on_trade`. fn on_trade_weight() -> Weight; } -impl AMMHandlers for () { - fn on_create_pool(_asset_pair: AssetPair) {} +impl OnTradeHandler for () { fn on_trade(_amm_transfer: &AMMTransfer, _liq_amount: Balance) {} fn on_trade_weight() -> Weight { Weight::zero() diff --git a/runtime/src/weights/price_oracle.rs b/runtime/src/weights/price_oracle.rs index 44b24711db6..c29bc08852e 100644 --- a/runtime/src/weights/price_oracle.rs +++ b/runtime/src/weights/price_oracle.rs @@ -32,7 +32,7 @@ // compiled // --pallet // pallet-price-oracle -// --steps=10 +// --steps=100 // --repeat=20 // --extrinsic // * @@ -56,40 +56,31 @@ use pallet_price_oracle::weights::WeightInfo; pub struct HydraWeight(PhantomData); impl WeightInfo for HydraWeight { fn on_initialize_no_entry() -> Weight { - (38_000_000 as Weight) + (29_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } - fn on_initialize_one_entry() -> Weight { - (43_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) + fn on_initialize_one_token() -> Weight { + (48_000_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } - fn on_initialize_multiple_entries_one_token(t: u32, ) -> Weight { - (43_629_000 as Weight) - // Standard Error: 6_000 - .saturating_add((294_000 as Weight).saturating_mul(t as Weight)) + fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { + (44_809_000 as Weight) + // Standard Error: 18_000 + .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - fn on_initialize_one_entry_multiple_tokens(t: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 203_000 - .saturating_add((17_566_000 as Weight).saturating_mul(t as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) } - fn on_initialize_multiple_entries_multiple_tokens(t: u32, u: u32, ) -> Weight { - (0 as Weight) - // Standard Error: 655_000 - .saturating_add((48_711_000 as Weight).saturating_mul(t as Weight)) - // Standard Error: 655_000 - .saturating_add((31_923_000 as Weight).saturating_mul(u as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(t as Weight))) + fn on_initialize_multiple_tokens(b: u32, ) -> Weight { + (31_504_000 as Weight) + // Standard Error: 17_000 + .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(t as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) } } From c79c08898e0a8d3f0fcd34c712ec798d17791673 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 24 Aug 2021 14:20:24 +0200 Subject: [PATCH 22/28] move price computation from on_initialize to on_finalize --- pallets/price-oracle/src/benchmarking.rs | 62 +++++++--- pallets/price-oracle/src/lib.rs | 61 ++++++---- pallets/price-oracle/src/mock.rs | 4 + pallets/price-oracle/src/tests.rs | 139 +++++++++++++++++++---- 4 files changed, 203 insertions(+), 63 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index 7f630fcd222..ae5b88873d2 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use frame_benchmarking::benchmarks; -use frame_support::traits::OnInitialize; +use frame_support::traits::{OnInitialize, OnFinalize}; use crate::Pallet as PriceOracle; @@ -38,15 +38,22 @@ pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { pub const NUM_OF_ITERS: u32 = 100; benchmarks! { - on_initialize_no_entry { + on_finalize_no_entry { let block_num: u32 = 5; - }: { PriceOracle::::on_initialize(block_num.into()); } + }: { PriceOracle::::on_finalize(block_num.into()); } verify { } - on_initialize_one_token { + on_finalize_one_token { let block_num: u32 = 5; + + frame_system::Pallet::::set_block_number((block_num - 1).into()); + PriceOracle::::on_initialize((block_num - 1).into()); PriceOracle::::on_create_pool(ASSET_PAIR_A); + PriceOracle::::on_finalize((block_num - 1).into()); + + frame_system::Pallet::::set_block_number(block_num.into()); + PriceOracle::::on_initialize(block_num.into()); PriceOracle::::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(PRICE_ENTRY_1)); @@ -55,7 +62,7 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(block_num.into()); } + }: { PriceOracle::::on_finalize(block_num.into()); } verify { assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); let price_data = PriceOracle::::price_data_ten(); @@ -63,15 +70,24 @@ benchmarks! { assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); } - on_initialize_multiple_tokens_all_bucket_levels { - let block_num: u32 = BUCKET_SIZE.pow(3); + on_finalize_multiple_tokens_all_bucket_levels { + let block_num: u32 = BUCKET_SIZE.pow(2); let a in 1 .. NUM_OF_ITERS; // trade_num frame_system::Pallet::::set_block_number(Zero::zero()); + PriceOracle::::on_initialize(Zero::zero()); let asset_pair = AssetPair::default(); for i in 0 .. a { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_create_pool(asset_pair); + } + + PriceOracle::::on_finalize(Zero::zero()); + frame_system::Pallet::::set_block_number((block_num - 1).into()); + PriceOracle::::on_initialize((block_num - 1).into()); + + for i in 0 .. a { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); } @@ -83,9 +99,10 @@ benchmarks! { assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); } - for round in 1.. block_num { - frame_system::Pallet::::set_block_number((round - 1) .into()); - PriceOracle::::on_initialize((round - 1).into()); + for round in block_num .. 2 * block_num - 1 { + PriceOracle::::on_finalize((round - 1).into()); + frame_system::Pallet::::set_block_number(round.into()); + PriceOracle::::on_initialize(round.into()); for i in 0 .. a { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; @@ -97,7 +114,7 @@ benchmarks! { frame_system::Pallet::::set_block_number(block_num.into()); - }: { PriceOracle::::on_initialize(block_num.into()); } + }: { PriceOracle::::on_finalize((2 * block_num - 1).into()); } verify { let asset_pair = AssetPair {asset_in: 1_000, asset_out: 2_000}; assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); @@ -129,15 +146,26 @@ benchmarks! { } } - on_initialize_multiple_tokens { + on_finalize_multiple_tokens { let block_num: u32 = 5; let b in 1 .. NUM_OF_ITERS; // token num let mut vec = Vec::new(); let asset_pair = AssetPair::default(); + frame_system::Pallet::::set_block_number((block_num - 1).into()); + PriceOracle::::on_initialize((block_num - 1).into()); + for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_create_pool(asset_pair); + } + + PriceOracle::::on_finalize((block_num - 1).into()); + frame_system::Pallet::::set_block_number(block_num.into()); + PriceOracle::::on_initialize((block_num).into()); + + for i in 0 .. b { + let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; PriceOracle::::on_trade(asset_pair, PRICE_ENTRY_1); vec.push(PRICE_ENTRY_1); } @@ -146,7 +174,7 @@ benchmarks! { let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::zero(), volume: 0}); - }: { PriceOracle::::on_initialize(block_num.into()); } + }: { PriceOracle::::on_finalize(block_num.into()); } verify { for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; @@ -167,10 +195,10 @@ mod tests { #[test] fn test_benchmarks() { new_test_ext().execute_with(|| { - assert_ok!(test_benchmark_on_initialize_no_entry::()); - assert_ok!(test_benchmark_on_initialize_one_token::()); - assert_ok!(test_benchmark_on_initialize_multiple_tokens_all_bucket_levels::()); - assert_ok!(test_benchmark_on_initialize_multiple_tokens::()); + assert_ok!(test_benchmark_on_finalize_no_entry::()); + assert_ok!(test_benchmark_on_finalize_one_token::()); + assert_ok!(test_benchmark_on_finalize_multiple_tokens_all_bucket_levels::()); + assert_ok!(test_benchmark_on_finalize_multiple_tokens::()); }); } } diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 21edff9aa63..911e03c61b7 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -26,6 +26,7 @@ use primitives::{ }; use sp_std::marker::PhantomData; use sp_std::prelude::*; +use sp_std::convert::TryInto; #[cfg(test)] mod mock; @@ -87,6 +88,14 @@ pub mod pallet { #[pallet::getter(fn num_of_assets)] pub type TrackedAssetsCount = StorageValue<_, u32, ValueQuery>; + /// Sorted array of newly registered assets. + /// All assets are processed and removed from the storage at the end of a block. + /// Trades start to be processed from the next block. + /// All trades in the same block as the asset registration are ignored. + #[pallet::storage] + #[pallet::getter(fn new_assets)] + pub type NewAssets = StorageValue<_, Vec, ValueQuery>; + /// Processed or partially processed data generated by trades. /// Data generated by trades are processed sequentially. /// Each new entry is combined with the previous value to produce new intermediate value. @@ -115,12 +124,29 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { + T::WeightInfo::on_initialize_multiple_tokens_all_bucket_levels(Self::num_of_assets()) + } + + fn on_finalize(_n: T::BlockNumber) { // update average values in the storage Self::update_data(); + // clear the price buffer PriceDataAccumulator::::remove_all(None); - T::WeightInfo::on_initialize_multiple_tokens_all_bucket_levels(Self::num_of_assets()) + // add newly registered assets + let _ = TrackedAssetsCount::::try_mutate(|value| -> Result<(), DispatchError> { + *value = value.checked_add(Self::new_assets().len().try_into().map_err(|_| Error::::PriceComputationError)?).ok_or(Error::::PriceComputationError)?; + Ok(()) + // We don't want to throw an error here because this method is used in different extrinsics. + // We also do not expect to have more than 2^32 assets registered. + }).map_err(|_| panic!("Max number of assets reached!")); + + + for new_asset in Self::new_assets().iter() { + PriceDataTen::::append((new_asset, BucketQueue::default())); + } + NewAssets::::kill(); } } @@ -132,38 +158,29 @@ impl Pallet { pub fn on_create_pool(asset_pair: AssetPair) { let data = PriceDataTen::::get(); if !data.iter().any(|bucket_tuple| bucket_tuple.0 == asset_pair.name()) { - let _ = TrackedAssetsCount::::try_mutate(|value| -> Result<(), DispatchError> { - *value = value.checked_add(1).ok_or(Error::::PriceComputationError)?; - Ok(()) - // We don't want to throw an error here because this method is used in different extrinsics. - // We also do not expect to have more than 2^32 assets registered. - }).map_err(|_| panic!("Max number of assets reached!")); + let _ = NewAssets::::try_mutate(|new_assets| -> Result<(), ()> { + // Keep the NewAssets vector sorted. It makes it easy to find duplicates. + match new_assets.binary_search(&asset_pair.name()) { + Ok(_pos) => Err(()), // new asset is already in vector + Err(pos) => { + new_assets.insert(pos, asset_pair.name()); + Self::deposit_event(Event::PoolRegistered(asset_pair)); + Ok(()) + }, + } + }).map_err(|_| {}); - PriceDataTen::::append((asset_pair.name(), BucketQueue::default())); - Self::deposit_event(Event::PoolRegistered(asset_pair)); } } pub fn on_trade(asset_pair: AssetPair, price_entry: PriceEntry) { - let _ = PriceDataAccumulator::::mutate(asset_pair.name(), |previous_price_entry| { let maybe_new_price_entry = previous_price_entry.calculate_new_price_entry(&price_entry); + // Invalid values are ignored and not added to the queue. if let Some(new_price_entry) = maybe_new_price_entry { *previous_price_entry = new_price_entry; } }); - - // let maybe_price = PriceDataAccumulator::::try_get(asset_pair.name()); - // let previous_entry = if let Ok(previous_price) = maybe_price { - // previous_price - // } else { - // PriceEntry::default() - // }; - - // Invalid values are ignored and not added to the queue. - // if let Some(new_entry) = previous_entry.calculate_new_price_entry(&price_entry) { - // PriceDataAccumulator::::insert(asset_pair.name(), new_entry); - // } } fn update_data() { diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 532f19f165a..67d94c49172 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -41,6 +41,10 @@ pub const ASSET_PAIR_B: AssetPair = AssetPair { asset_in: 1_000, asset_out: 3_000, }; +pub const ASSET_PAIR_C: AssetPair = AssetPair { + asset_in: 1_000, + asset_out: 4_000, +}; pub const PRICE_ENTRY_1: PriceEntry = PriceEntry { price: Price::from_inner(2000000000000000000), diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index e35bfd58ca6..f9555bf165b 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -17,10 +17,13 @@ use super::*; pub use crate::mock::{ - Event as TestEvent, ExtBuilder, Origin, PriceOracle, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, PRICE_ENTRY_1, - PRICE_ENTRY_2, + Event as TestEvent, ExtBuilder, Origin, PriceOracle, System, Test, ASSET_PAIR_A, ASSET_PAIR_B, ASSET_PAIR_C, + PRICE_ENTRY_1, PRICE_ENTRY_2, +}; +use frame_support::{ + assert_storage_noop, + traits::{OnFinalize, OnInitialize}, }; -use frame_support::{assert_storage_noop, traits::OnInitialize}; pub fn new_test_ext() -> sp_io::TestExternalities { let ext = ExtBuilder.build(); @@ -45,19 +48,75 @@ fn expect_events(e: Vec) { fn add_new_asset_pair_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); + PriceOracle::on_initialize(3); + assert_eq!(PriceOracle::num_of_assets(), 0); + assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); + PriceOracle::on_create_pool(ASSET_PAIR_A); + assert_eq!(PriceOracle::num_of_assets(), 0); + assert_eq!(PriceOracle::new_assets(), vec![ASSET_PAIR_A.name()]); + assert_eq!( + >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), + false + ); + + PriceOracle::on_finalize(3); + System::set_block_number(4); + PriceOracle::on_initialize(4); + assert_eq!(PriceOracle::num_of_assets(), 1); assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), true ); - expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); + + assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); + + PriceOracle::on_create_pool(ASSET_PAIR_B); + PriceOracle::on_create_pool(ASSET_PAIR_C); + + assert_eq!(PriceOracle::num_of_assets(), 1); + + let mut vec_assets = vec![ASSET_PAIR_B.name(), ASSET_PAIR_C.name()]; + vec_assets.sort_unstable(); + + assert_eq!(PriceOracle::new_assets(), vec_assets); + assert_eq!( + >::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default())), + false + ); + assert_eq!( + >::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default())), + false + ); + + PriceOracle::on_finalize(4); + System::set_block_number(5); + PriceOracle::on_initialize(5); + + assert_eq!(PriceOracle::num_of_assets(), 3); + assert_eq!( + >::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default())), + true + ); + assert_eq!( + >::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default())), + true + ); + + assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); + + expect_events(vec![ + Event::PoolRegistered(ASSET_PAIR_A).into(), + Event::PoolRegistered(ASSET_PAIR_B).into(), + Event::PoolRegistered(ASSET_PAIR_C).into(), + ]); }); } @@ -65,13 +124,15 @@ fn add_new_asset_pair_should_work() { fn add_existing_asset_pair_should_not_work() { new_test_ext().execute_with(|| { System::set_block_number(3); + PriceOracle::on_initialize(3); + assert_eq!( >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), false ); PriceOracle::on_create_pool(ASSET_PAIR_A); assert_storage_noop!(PriceOracle::on_create_pool(ASSET_PAIR_A)); - expect_events(vec![]); + expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); }); } @@ -82,7 +143,10 @@ fn on_trade_should_work() { PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); let price_entry = PRICE_ENTRY_1.calculate_new_price_entry(&PRICE_ENTRY_2); - assert_eq!(>::try_get(ASSET_PAIR_A.name()).ok(), price_entry); + assert_eq!( + >::try_get(ASSET_PAIR_A.name()).ok(), + price_entry + ); }); } @@ -101,7 +165,10 @@ fn on_trade_handler_should_work() { }; PriceOracleHandler::::on_trade(&amm_transfer, 2_000); - assert_eq!(>::try_get(ASSET_PAIR_A.name()), Ok(PRICE_ENTRY_1)); + assert_eq!( + >::try_get(ASSET_PAIR_A.name()), + Ok(PRICE_ENTRY_1) + ); }); } @@ -217,7 +284,13 @@ fn price_normalization_should_work() { liquidity_amount: 2_000, }; - let result = PriceEntry::default().calculate_new_price_entry(&first_entry).unwrap().calculate_new_price_entry(&second_entry).unwrap().calculate_new_price_entry(&third_entry).unwrap(); + let result = PriceEntry::default() + .calculate_new_price_entry(&first_entry) + .unwrap() + .calculate_new_price_entry(&second_entry) + .unwrap() + .calculate_new_price_entry(&third_entry) + .unwrap(); assert_eq!(price_entry, result); }); } @@ -226,15 +299,22 @@ fn price_normalization_should_work() { fn update_data_should_work() { new_test_ext().execute_with(|| { System::set_block_number(3); + PriceOracle::on_initialize(3); PriceOracle::on_create_pool(ASSET_PAIR_B); PriceOracle::on_create_pool(ASSET_PAIR_A); + PriceOracle::on_finalize(3); + System::set_block_number(4); + PriceOracle::on_initialize(4); + PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_2); PriceOracle::on_trade(ASSET_PAIR_B, PRICE_ENTRY_1); - PriceOracle::update_data(); + PriceOracle::on_finalize(4); + System::set_block_number(5); + PriceOracle::on_initialize(5); let data_ten_a = PriceOracle::price_data_ten() .iter() @@ -268,16 +348,26 @@ fn update_data_should_work() { fn update_data_with_incorrect_input_should_not_work() { new_test_ext().execute_with(|| { System::set_block_number(3); + PriceOracle::on_initialize(3); PriceOracle::on_create_pool(ASSET_PAIR_A); - PriceOracle::on_trade(ASSET_PAIR_A, PriceEntry { - price: Price::from(1), - trade_amount: Zero::zero(), - liquidity_amount: Zero::zero(), - }); + PriceOracle::on_finalize(3); + System::set_block_number(4); + PriceOracle::on_initialize(4); + + PriceOracle::on_trade( + ASSET_PAIR_A, + PriceEntry { + price: Price::from(1), + trade_amount: Zero::zero(), + liquidity_amount: Zero::zero(), + }, + ); - PriceOracle::update_data(); + PriceOracle::on_finalize(4); + System::set_block_number(5); + PriceOracle::on_initialize(5); let data_ten = PriceOracle::price_data_ten() .iter() @@ -297,12 +387,12 @@ fn update_data_with_incorrect_input_should_not_work() { #[test] fn update_empty_data_should_work() { new_test_ext().execute_with(|| { - PriceOracle::on_create_pool(ASSET_PAIR_A); - for i in 0..1002 { + for i in 0..1002 { + PriceOracle::on_initialize(i); System::set_block_number(i); - PriceOracle::update_data(); + PriceOracle::on_finalize(i); } let data_ten = PriceOracle::price_data_ten() @@ -452,17 +542,17 @@ fn continuous_trades_should_work() { }) } - #[test] fn stable_price_should_work() { new_test_ext().execute_with(|| { let num_of_iters = BucketQueue::BUCKET_SIZE.pow(3); PriceOracle::on_create_pool(ASSET_PAIR_A); - for i in num_of_iters - 2 .. 2 * num_of_iters + 2{ + for i in num_of_iters - 2..2 * num_of_iters + 2 { + PriceOracle::on_initialize(i.into()); System::set_block_number(i.into()); PriceOracle::on_trade(ASSET_PAIR_A, PRICE_ENTRY_1); - PriceOracle::on_initialize(i.into()); + PriceOracle::on_finalize(i.into()); } let data_ten = PriceOracle::price_data_ten() @@ -495,9 +585,10 @@ fn stable_price_should_work() { } ); - for i in num_of_iters .. 2 * num_of_iters { - System::set_block_number(i.into()); + for i in num_of_iters..2 * num_of_iters { PriceOracle::on_initialize(i.into()); + System::set_block_number(i.into()); + PriceOracle::on_finalize(i.into()); } let data_ten = PriceOracle::price_data_ten() @@ -530,4 +621,4 @@ fn stable_price_should_work() { } ); }); -} \ No newline at end of file +} From d3d760ba735375dc4bf5ef4d3c323f2969c92880 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 24 Aug 2021 15:13:36 +0200 Subject: [PATCH 23/28] change storage hasher --- pallets/price-oracle/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 911e03c61b7..624eed10288 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -102,7 +102,7 @@ pub mod pallet { /// The last entry creates the resulting average price and volume. #[pallet::storage] #[pallet::getter(fn price_accumulator)] - pub type PriceDataAccumulator = StorageMap<_, Blake2_128Concat, AssetPairId, PriceEntry, ValueQuery>; + pub type PriceDataAccumulator = StorageMap<_, Twox64Concat, AssetPairId, PriceEntry, ValueQuery>; /// The last ten average values corresponding to the last ten blocks. #[pallet::storage] @@ -113,13 +113,13 @@ pub mod pallet { /// Each average value corresponds to an interval of length ten blocks. #[pallet::storage] #[pallet::getter(fn price_data_hundred)] - pub type PriceDataHundred = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; + pub type PriceDataHundred = StorageMap<_, Twox64Concat, AssetPairId, BucketQueue, ValueQuery>; /// The last ten average values corresponding to the last thousand blocks. /// Each average value corresponds to an interval of length hundred blocks. #[pallet::storage] #[pallet::getter(fn price_data_thousand)] - pub type PriceDataThousand = StorageMap<_, Blake2_128Concat, AssetPairId, BucketQueue, ValueQuery>; + pub type PriceDataThousand = StorageMap<_, Twox64Concat, AssetPairId, BucketQueue, ValueQuery>; #[pallet::hooks] impl Hooks for Pallet { From 99cb4f8a25d9fbb9b21243a99a9ca771c3e468c3 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Wed, 25 Aug 2021 15:15:03 +0200 Subject: [PATCH 24/28] update benchmark weights --- pallets/price-oracle/src/lib.rs | 4 +- pallets/price-oracle/src/weights.rs | 78 ++++++++------------------ runtime/src/lib.rs | 2 +- runtime/src/weights/mod.rs | 1 - runtime/src/weights/price_oracle.rs | 86 ----------------------------- 5 files changed, 26 insertions(+), 145 deletions(-) delete mode 100644 runtime/src/weights/price_oracle.rs diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 624eed10288..4569061ef0a 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -124,7 +124,7 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { fn on_initialize(_n: T::BlockNumber) -> Weight { - T::WeightInfo::on_initialize_multiple_tokens_all_bucket_levels(Self::num_of_assets()) + T::WeightInfo::on_finalize_multiple_tokens_all_bucket_levels(Self::num_of_assets()) } fn on_finalize(_n: T::BlockNumber) { @@ -255,6 +255,6 @@ impl OnTradeHandler for Pr } fn on_trade_weight() -> Weight { - T::WeightInfo::on_initialize_one_token() - T::WeightInfo::on_initialize_no_entry() + T::WeightInfo::on_finalize_one_token() - T::WeightInfo::on_finalize_no_entry() } } diff --git a/pallets/price-oracle/src/weights.rs b/pallets/price-oracle/src/weights.rs index 0835e67286c..f51d82542ff 100644 --- a/pallets/price-oracle/src/weights.rs +++ b/pallets/price-oracle/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_price_oracle //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-08-20, STEPS: [100, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] +//! DATE: 2021-08-25, STEPS: [10, ], REPEAT: 4, LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: @@ -32,8 +32,8 @@ // compiled // --pallet // pallet-price-oracle -// --steps=100 -// --repeat=20 +// --steps=10 +// --repeat=4 // --extrinsic // * // --heap-pages=4096 @@ -52,73 +52,41 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_price_oracle. pub trait WeightInfo { - fn on_initialize_no_entry() -> Weight; - fn on_initialize_one_token() -> Weight; - fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight; - fn on_initialize_multiple_tokens(b: u32, ) -> Weight; + fn on_finalize_no_entry() -> Weight; + fn on_finalize_one_token() -> Weight; + fn on_finalize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight; + fn on_finalize_multiple_tokens(b: u32, ) -> Weight; } /// Weights for pallet_price_oracle using the hack.hydraDX node and recommended hardware. pub struct HydraWeight(PhantomData); impl WeightInfo for HydraWeight { - fn on_initialize_no_entry() -> Weight { - (29_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + fn on_finalize_no_entry() -> Weight { + 0 } - fn on_initialize_one_token() -> Weight { - (48_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + fn on_finalize_one_token() -> Weight { + 0 } - fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { - (44_809_000 as Weight) - // Standard Error: 18_000 - .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) + fn on_finalize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { + (a * 0).into() } - fn on_initialize_multiple_tokens(b: u32, ) -> Weight { - (31_504_000 as Weight) - // Standard Error: 17_000 - .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + (b * 0).into() } } // For backwards compatibility and tests impl WeightInfo for () { - fn on_initialize_no_entry() -> Weight { - (29_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + fn on_finalize_no_entry() -> Weight { + 0 } - fn on_initialize_one_token() -> Weight { - (48_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + fn on_finalize_one_token() -> Weight { + 0 } - fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { - (44_809_000 as Weight) - // Standard Error: 18_000 - .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) + fn on_finalize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { + (a * 0).into() } - fn on_initialize_multiple_tokens(b: u32, ) -> Weight { - (31_504_000 as Weight) - // Standard Error: 17_000 - .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) + fn on_finalize_multiple_tokens(b: u32, ) -> Weight { + (b * 0).into() } } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1b24f1a7083..d05c305e573 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -464,7 +464,7 @@ impl pallet_lbp::Config for Runtime { impl pallet_price_oracle::Config for Runtime { type Event = Event; - type WeightInfo = weights::price_oracle::HydraWeight; + type WeightInfo = pallet_price_oracle::weights::HydraWeight; } parameter_types! { diff --git a/runtime/src/weights/mod.rs b/runtime/src/weights/mod.rs index 8d755fc9fd9..2ea98543789 100644 --- a/runtime/src/weights/mod.rs +++ b/runtime/src/weights/mod.rs @@ -3,4 +3,3 @@ pub mod payment; pub mod system; pub mod timestamp; pub mod xyk; -pub mod price_oracle; diff --git a/runtime/src/weights/price_oracle.rs b/runtime/src/weights/price_oracle.rs deleted file mode 100644 index c29bc08852e..00000000000 --- a/runtime/src/weights/price_oracle.rs +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of Basilisk-node. - -// Copyright (C) 2020-2021 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_price_oracle -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-08-08, STEPS: [10, ], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128 - -// Executed Command: -// ./target/release/basilisk -// benchmark -// --chain -// dev -// --execution -// wasm -// --wasm-execution -// compiled -// --pallet -// pallet-price-oracle -// --steps=100 -// --repeat=20 -// --extrinsic -// * -// --heap-pages=4096 -// --template=./.maintain/pallet-weight-template.hbs -// --output=./pallets/price-oracle/src/weights.rs - -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(clippy::unnecessary_cast)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -use pallet_price_oracle::weights::WeightInfo; - -/// Weights for pallet_price_oracle using the hack.hydraDX node and recommended hardware. -pub struct HydraWeight(PhantomData); -impl WeightInfo for HydraWeight { - fn on_initialize_no_entry() -> Weight { - (29_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - fn on_initialize_one_token() -> Weight { - (48_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) - } - fn on_initialize_multiple_tokens_all_bucket_levels(a: u32, ) -> Weight { - (44_809_000 as Weight) - // Standard Error: 18_000 - .saturating_add((14_885_000 as Weight).saturating_mul(a as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(a as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(a as Weight))) - } - fn on_initialize_multiple_tokens(b: u32, ) -> Weight { - (31_504_000 as Weight) - // Standard Error: 17_000 - .saturating_add((14_850_000 as Weight).saturating_mul(b as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight))) - } -} From b98cc8692110816705c6801e6f2ad29b59f04b7f Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 31 Aug 2021 12:06:40 +0200 Subject: [PATCH 25/28] add mising config param to mock --- pallets/price-oracle/src/mock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index ac300844c9f..703cc5a2672 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -144,6 +144,7 @@ impl pallet_asset_registry::Config for Test { type Event = Event; type RegistryOrigin = frame_system::EnsureSigned; type AssetId = AssetId; + type Balance = Balance; type AssetNativeLocation = u8; type StringLimit = RegistryStringLimit; type NativeAssetId = NativeAssetId; From 7e5493ecb09fc9696c3e39bb85ebeb8912a3635a Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 12 Oct 2021 18:12:10 +0200 Subject: [PATCH 26/28] merge master branch --- Cargo.lock | 4 +++- integration-tests/Cargo.toml | 2 ++ node/src/testing_chain_spec.rs | 14 +----------- pallets/exchange/benchmarking/src/mock.rs | 8 ++----- pallets/exchange/src/mock.rs | 8 ++----- pallets/price-oracle/Cargo.toml | 22 +++++++++---------- pallets/price-oracle/src/mock.rs | 12 ++++++++++ .../benchmarking/src/mock.rs | 7 ++---- pallets/transaction-multi-payment/src/mock.rs | 7 ++---- pallets/xyk/src/lib.rs | 7 +++--- pallets/xyk/src/mock.rs | 4 ++-- runtime/basilisk/Cargo.toml | 3 +++ runtime/testing-basilisk/Cargo.toml | 3 +++ runtime/testing-basilisk/src/lib.rs | 6 ++--- 14 files changed, 51 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 283dabdc2ca..a44f9ccb7ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5965,7 +5965,7 @@ dependencies = [ "orml-traits", "pallet-asset-registry", "pallet-xyk", - "parity-scale-codec 2.2.0", + "parity-scale-codec 2.3.1", "primitives", "serde", "sp-core", @@ -8615,6 +8615,7 @@ dependencies = [ "pallet-lbp", "pallet-multi-payment-benchmarking", "pallet-nft", + "pallet-price-oracle", "pallet-scheduler", "pallet-session", "pallet-sudo", @@ -11256,6 +11257,7 @@ dependencies = [ "pallet-lbp", "pallet-multi-payment-benchmarking", "pallet-nft", + "pallet-price-oracle", "pallet-scheduler", "pallet-session", "pallet-sudo", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 4f888185997..db6accac391 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -113,6 +113,8 @@ polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", basilisk-runtime = { path = "../runtime/basilisk", default-features = false} kusama-runtime = { git = "https://github.com/paritytech/polkadot", branch = "release-v0.9.10" } +[features] +default = ["std"] std = [ "codec/std", "serde", diff --git a/node/src/testing_chain_spec.rs b/node/src/testing_chain_spec.rs index 30076225f2b..bc4896ef71e 100644 --- a/node/src/testing_chain_spec.rs +++ b/node/src/testing_chain_spec.rs @@ -308,19 +308,7 @@ fn testnet_parachain_genesis( currencies: vec![], fallback_account: tx_fee_payment_account, }, - tokens: TokensConfig { - balances: endowed_accounts - .iter() - .flat_map(|x| { - vec![ - (x.clone(), 1, 1_000_000_000u128 * BSX), - (x.clone(), 2, 1_000_000_000u128 * BSX), - (x.clone(), 3, 1_000_000_000u128 * BSX), - (x.clone(), 4, 1_000_000_000u128 * BSX), - ] - }) - .collect(), - }, + tokens: TokensConfig { balances: vec![] }, treasury: Default::default(), elections: ElectionsConfig { // Intergalactic elections diff --git a/pallets/exchange/benchmarking/src/mock.rs b/pallets/exchange/benchmarking/src/mock.rs index 01553640fb3..afcd88dfb00 100644 --- a/pallets/exchange/benchmarking/src/mock.rs +++ b/pallets/exchange/benchmarking/src/mock.rs @@ -29,10 +29,9 @@ use sp_runtime::{ }; use frame_system::EnsureSigned; -use pallet_xyk::AssetPairAccountIdFor; use primitives::{ - AssetId, Balance, - traits::{fee, traits::AssetPairAccountIdFor}; + AssetId, Balance, fee, + traits::AssetPairAccountIdFor, constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, }; @@ -158,14 +157,11 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HDXAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; -<<<<<<< HEAD type AMMHandler = (); -======= type MinTradingLimit = MinTradingLimit; type MinPoolLiquidity = MinPoolLiquidity; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; ->>>>>>> master } impl pallet_exchange::Config for Test { diff --git a/pallets/exchange/src/mock.rs b/pallets/exchange/src/mock.rs index cff3d02b8ec..fc920ec59c8 100644 --- a/pallets/exchange/src/mock.rs +++ b/pallets/exchange/src/mock.rs @@ -32,10 +32,9 @@ use pallet_xyk as xyk; use frame_support::traits::GenesisBuild; use frame_support::traits::Get; use frame_system::EnsureSigned; -use pallet_xyk::AssetPairAccountIdFor; use primitives::{ - AssetId, Balance, - traits::{fee, AssetPairAccountIdFor}, + AssetId, Balance, fee, + traits::AssetPairAccountIdFor, constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, }; use std::cell::RefCell; @@ -165,14 +164,11 @@ impl xyk::Config for Test { type NativeAssetId = HDXAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; -<<<<<<< HEAD type AMMHandler = (); -======= type MinTradingLimit = MinTradingLimit; type MinPoolLiquidity = MinPoolLiquidity; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; ->>>>>>> master } impl Config for Test { diff --git a/pallets/price-oracle/Cargo.toml b/pallets/price-oracle/Cargo.toml index 54683a2999a..f2bc0c269d4 100644 --- a/pallets/price-oracle/Cargo.toml +++ b/pallets/price-oracle/Cargo.toml @@ -12,7 +12,7 @@ version = '1.0.0' targets = ['x86_64-unknown-linux-gnu'] [build-dependencies] -substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" } +substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10" } # alias "parity-scale-code" to "codec" [dependencies.codec] @@ -28,19 +28,19 @@ serde = {features = ['derive'], optional = true, version = '1.0.101'} primitives = {path = '../../primitives', default-features = false} # Substrate dependencies -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false } -frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false, optional = true } -sp-core = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.9", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false, optional = true } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } +frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false, optional = true } +sp-core = { git = 'https://github.com/paritytech/substrate', branch = "polkadot-v0.9.10", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } [dev-dependencies] -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } pallet-xyk = {path = '../xyk', default-features = false} -orml-tokens = {default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "8d5432c3458702a7df14b374bddde43a2a20aa43"} -orml-traits = {default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "8d5432c3458702a7df14b374bddde43a2a20aa43"} +orml-tokens = {default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "37e42936c41dbdbaf0117c628c9eab0e06044844"} +orml-traits = {default-features = false, git = "https://github.com/open-web3-stack/open-runtime-module-library", rev = "37e42936c41dbdbaf0117c628c9eab0e06044844"} pallet-asset-registry = {path = '../asset-registry', default-features = false} [features] diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 703cc5a2672..2fdc55da3c4 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -36,6 +36,7 @@ pub type AccountId = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; +use primitives::constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}; pub const HDX: AssetId = 1000; @@ -151,6 +152,13 @@ impl pallet_asset_registry::Config for Test { type WeightInfo = (); } +parameter_types! { + pub const MinTradingLimit: Balance = MIN_TRADING_LIMIT; + pub const MinPoolLiquidity: Balance = MIN_POOL_LIQUIDITY; + pub const MaxInRatio: u128 = MAX_IN_RATIO; + pub const MaxOutRatio: u128 = MAX_OUT_RATIO; +} + impl pallet_xyk::Config for Test { type Event = Event; type AssetRegistry = AssetRegistry; @@ -160,6 +168,10 @@ impl pallet_xyk::Config for Test { type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; type AMMHandler = PriceOracleHandler; + type MinTradingLimit = MinTradingLimit; + type MinPoolLiquidity = MinPoolLiquidity; + type MaxInRatio = MaxInRatio; + type MaxOutRatio = MaxOutRatio; } diff --git a/pallets/transaction-multi-payment/benchmarking/src/mock.rs b/pallets/transaction-multi-payment/benchmarking/src/mock.rs index 67cfb9a1f27..f791937bdc8 100644 --- a/pallets/transaction-multi-payment/benchmarking/src/mock.rs +++ b/pallets/transaction-multi-payment/benchmarking/src/mock.rs @@ -32,8 +32,8 @@ use frame_support::weights::IdentityFee; use orml_currencies::BasicCurrencyAdapter; use pallet_transaction_multi_payment::MultiCurrencyAdapter; use primitives::{ - Amount, AssetId, Balance, - traits::{fee, AssetPairAccountIdFor}, + Amount, AssetId, Balance, fee, + traits::AssetPairAccountIdFor, constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, }; @@ -186,14 +186,11 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HdxAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; -<<<<<<< HEAD type AMMHandler = (); -======= type MinTradingLimit = MinTradingLimit; type MinPoolLiquidity = MinPoolLiquidity; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; ->>>>>>> master } parameter_type_with_key! { diff --git a/pallets/transaction-multi-payment/src/mock.rs b/pallets/transaction-multi-payment/src/mock.rs index 24ffb16bedd..44c14ede546 100644 --- a/pallets/transaction-multi-payment/src/mock.rs +++ b/pallets/transaction-multi-payment/src/mock.rs @@ -32,8 +32,8 @@ use frame_support::weights::IdentityFee; use frame_support::weights::Weight; use orml_currencies::BasicCurrencyAdapter; use primitives::{ - Amount, AssetId, Balance, Price, - traits::{fee, AssetPairAccountIdFor}, + Amount, AssetId, Balance, Price, fee, + traits::AssetPairAccountIdFor, constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, }; @@ -216,14 +216,11 @@ impl pallet_xyk::Config for Test { type NativeAssetId = HdxAssetId; type WeightInfo = (); type GetExchangeFee = ExchangeFeeRate; -<<<<<<< HEAD type AMMHandler = (); -======= type MinTradingLimit = MinTradingLimit; type MinPoolLiquidity = MinPoolLiquidity; type MaxInRatio = MaxInRatio; type MaxOutRatio = MaxOutRatio; ->>>>>>> master } parameter_type_with_key! { diff --git a/pallets/xyk/src/lib.rs b/pallets/xyk/src/lib.rs index e2011b22d3c..877555eae14 100644 --- a/pallets/xyk/src/lib.rs +++ b/pallets/xyk/src/lib.rs @@ -35,10 +35,9 @@ use frame_support::sp_runtime::{ use frame_support::{dispatch::DispatchResult, ensure, traits::Get, transactional}; use frame_system::ensure_signed; use primitives::{ - AssetId, Balance, Price, + AssetId, Balance, Price, fee, asset::AssetPair, - traits::{OnCreatePoolHandler, OnTradeHandler, fee, AMM}, - constants::chain::{MIN_TRADING_LIMIT, MIN_POOL_LIQUIDITY, MAX_IN_RATIO, MAX_OUT_RATIO}, + traits::{OnCreatePoolHandler, OnTradeHandler, AMM}, }; use sp_std::{marker::PhantomData, vec, vec::Vec}; @@ -309,7 +308,7 @@ pub mod pallet { let token_name = asset_pair.name(); let share_token = - T::AssetRegistry::get_or_create_shared_asset(token_name, vec![asset_a, asset_b], MIN_POOL_LIQUIDITY)?; + T::AssetRegistry::get_or_create_shared_asset(token_name, vec![asset_a, asset_b], T::MinPoolLiquidity::get())?; T::AMMHandler::on_create_pool(asset_pair); diff --git a/pallets/xyk/src/mock.rs b/pallets/xyk/src/mock.rs index 4d64044cfc2..153189605f7 100644 --- a/pallets/xyk/src/mock.rs +++ b/pallets/xyk/src/mock.rs @@ -28,8 +28,8 @@ use sp_runtime::{ use frame_support::traits::{GenesisBuild, Get}; use primitives::{ - AssetId, Balance, - traits::{fee, AssetId, Balance, AssetPairAccountIdFor}, + AssetId, Balance, fee, + traits::AssetPairAccountIdFor, constants::chain::{MAX_IN_RATIO, MAX_OUT_RATIO, MIN_POOL_LIQUIDITY, MIN_TRADING_LIMIT}, }; diff --git a/runtime/basilisk/Cargo.toml b/runtime/basilisk/Cargo.toml index bebbabef691..09ab6efe080 100644 --- a/runtime/basilisk/Cargo.toml +++ b/runtime/basilisk/Cargo.toml @@ -38,6 +38,7 @@ pallet-duster= { path = "../../pallets/duster",default-features = false} pallet-xyk-rpc-runtime-api = { path = "../../pallets/xyk/rpc/runtime-api",default-features = false} pallet-nft = { path = "../../pallets/nft", default-features = false } pallet-lbp = { path = "../../pallets/lbp", default-features = false } +pallet-price-oracle = { path = "../../pallets/price-oracle", default-features = false } pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } pallet-democracy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } @@ -128,6 +129,7 @@ runtime-benchmarks = [ "pallet-xyk/runtime-benchmarks", "pallet-exchange-benchmarking", "pallet-lbp/runtime-benchmarks", + "pallet-price-oracle/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -183,6 +185,7 @@ std = [ "pallet-transaction-multi-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-lbp/std", + "pallet-price-oracle/std", "pallet-utility/std", "sp-api/std", "sp-block-builder/std", diff --git a/runtime/testing-basilisk/Cargo.toml b/runtime/testing-basilisk/Cargo.toml index 216488919b9..9ed10b7271b 100644 --- a/runtime/testing-basilisk/Cargo.toml +++ b/runtime/testing-basilisk/Cargo.toml @@ -38,6 +38,7 @@ pallet-duster= { path = "../../pallets/duster",default-features = false} pallet-xyk-rpc-runtime-api = { path = "../../pallets/xyk/rpc/runtime-api",default-features = false} pallet-nft = { path = "../../pallets/nft", default-features = false } pallet-lbp = { path = "../../pallets/lbp", default-features = false } +pallet-price-oracle = { path = "../../pallets/price-oracle", default-features = false } pallet-treasury = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } pallet-democracy = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.10", default-features = false } @@ -128,6 +129,7 @@ runtime-benchmarks = [ "pallet-xyk/runtime-benchmarks", "pallet-exchange-benchmarking", "pallet-lbp/runtime-benchmarks", + "pallet-price-oracle/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", @@ -183,6 +185,7 @@ std = [ "pallet-transaction-multi-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", "pallet-lbp/std", + "pallet-price-oracle/std", "pallet-utility/std", "sp-api/std", "sp-block-builder/std", diff --git a/runtime/testing-basilisk/src/lib.rs b/runtime/testing-basilisk/src/lib.rs index 9017261db3a..359a5e4accf 100644 --- a/runtime/testing-basilisk/src/lib.rs +++ b/runtime/testing-basilisk/src/lib.rs @@ -52,7 +52,7 @@ use sp_version::RuntimeVersion; // A few exports that help ease life for downstream crates. use frame_support::{ construct_runtime, parameter_types, - traits::{EnsureOrigin, Get, U128CurrencyToVote}, + traits::{EnsureOrigin, Get, U128CurrencyToVote, Everything}, weights::{ constants::{BlockExecutionWeight, RocksDbWeight}, DispatchClass, Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, @@ -95,8 +95,8 @@ pub mod opaque { } mod testing { - use super::{parameter_types, BlockNumber, MINUTES}; - pub type BaseFilter = (); + use super::{parameter_types, BlockNumber, Everything, MINUTES}; + pub type BaseFilter = Everything; parameter_types! { pub const LaunchPeriod: BlockNumber = MINUTES; From 12605babb5ead26a6ef6a3b1f74dc222c33923d0 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 12 Oct 2021 18:38:27 +0200 Subject: [PATCH 27/28] satisfy clippy --- pallets/price-oracle/src/benchmarking.rs | 8 ++--- pallets/price-oracle/src/lib.rs | 1 + pallets/price-oracle/src/mock.rs | 7 ++-- pallets/price-oracle/src/tests.rs | 43 +++++------------------- 4 files changed, 16 insertions(+), 43 deletions(-) diff --git a/pallets/price-oracle/src/benchmarking.rs b/pallets/price-oracle/src/benchmarking.rs index e37e2c7dbe7..2a60be9e746 100644 --- a/pallets/price-oracle/src/benchmarking.rs +++ b/pallets/price-oracle/src/benchmarking.rs @@ -20,7 +20,7 @@ use super::*; use frame_benchmarking::benchmarks; -use frame_support::traits::{OnInitialize, OnFinalize}; +use frame_support::traits::{OnFinalize, OnInitialize}; use crate::Pallet as PriceOracle; @@ -64,7 +64,7 @@ benchmarks! { }: { PriceOracle::::on_finalize(block_num.into()); } verify { - assert_eq!(PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name()), false); + assert!(!PriceDataAccumulator::::contains_key(ASSET_PAIR_A.name())); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == ASSET_PAIR_A.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); @@ -117,7 +117,7 @@ benchmarks! { }: { PriceOracle::::on_finalize((2 * block_num - 1).into()); } verify { let asset_pair = AssetPair {asset_in: 1_000, asset_out: 2_000}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); + assert!(!PriceDataAccumulator::::contains_key(asset_pair.name())); let price_data = PriceOracle::::price_data_ten(); for i in 0 .. BucketQueue::BUCKET_SIZE { for j in 0 .. a { @@ -178,7 +178,7 @@ benchmarks! { verify { for i in 0 .. b { let asset_pair = AssetPair {asset_in: i * 1_000, asset_out: i * 2_000}; - assert_eq!(PriceDataAccumulator::::contains_key(asset_pair.name()), false); + assert!(!PriceDataAccumulator::::contains_key(asset_pair.name())); let price_data = PriceOracle::::price_data_ten(); let bucket_queue = price_data.iter().find(|&x| x.0 == asset_pair.name()).unwrap().1; assert_eq!(bucket_queue.get_last(), PriceInfo{ avg_price: Price::from(2), volume: 1_000}); diff --git a/pallets/price-oracle/src/lib.rs b/pallets/price-oracle/src/lib.rs index 4569061ef0a..aa937658a2f 100644 --- a/pallets/price-oracle/src/lib.rs +++ b/pallets/price-oracle/src/lib.rs @@ -37,6 +37,7 @@ mod tests; mod types; pub use types::*; +#[allow(clippy::all)] pub mod weights; use weights::WeightInfo; diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 2fdc55da3c4..26fc8f7bd62 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -19,7 +19,6 @@ use crate as price_oracle; use crate::Config; use frame_support::parameter_types; use frame_support::traits::Get; -use frame_system; use orml_traits::parameter_type_with_key; use price_oracle::{PriceEntry, PriceOracleHandler}; use primitives::asset::AssetPair; @@ -116,11 +115,9 @@ impl AssetPairAccountIdFor for AssetPairAccountIdTest { let mut a = asset_a as u128; let mut b = asset_b as u128; if a > b { - let tmp = a; - a = b; - b = tmp; + std::mem::swap(&mut a, &mut b); } - return (a * 1000 + b) as u64; + (a * 1000 + b) as u64 } } diff --git a/pallets/price-oracle/src/tests.rs b/pallets/price-oracle/src/tests.rs index f9555bf165b..c328448d4d6 100644 --- a/pallets/price-oracle/src/tests.rs +++ b/pallets/price-oracle/src/tests.rs @@ -26,8 +26,7 @@ use frame_support::{ }; pub fn new_test_ext() -> sp_io::TestExternalities { - let ext = ExtBuilder.build(); - ext + ExtBuilder.build() } fn last_events(n: usize) -> Vec { @@ -52,29 +51,20 @@ fn add_new_asset_pair_should_work() { assert_eq!(PriceOracle::num_of_assets(), 0); assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); - assert_eq!( - >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), - false - ); + assert!(!>::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default()))); PriceOracle::on_create_pool(ASSET_PAIR_A); assert_eq!(PriceOracle::num_of_assets(), 0); assert_eq!(PriceOracle::new_assets(), vec![ASSET_PAIR_A.name()]); - assert_eq!( - >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), - false - ); + assert!(!>::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default()))); PriceOracle::on_finalize(3); System::set_block_number(4); PriceOracle::on_initialize(4); assert_eq!(PriceOracle::num_of_assets(), 1); - assert_eq!( - >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), - true - ); + assert!(>::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default()))); assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); @@ -87,28 +77,16 @@ fn add_new_asset_pair_should_work() { vec_assets.sort_unstable(); assert_eq!(PriceOracle::new_assets(), vec_assets); - assert_eq!( - >::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default())), - false - ); - assert_eq!( - >::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default())), - false - ); + assert!(!>::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default()))); + assert!(!>::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default()))); PriceOracle::on_finalize(4); System::set_block_number(5); PriceOracle::on_initialize(5); assert_eq!(PriceOracle::num_of_assets(), 3); - assert_eq!( - >::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default())), - true - ); - assert_eq!( - >::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default())), - true - ); + assert!(>::get().contains(&(ASSET_PAIR_B.name(), BucketQueue::default()))); + assert!(>::get().contains(&(ASSET_PAIR_C.name(), BucketQueue::default()))); assert_eq!(PriceOracle::new_assets(), vec![AssetPairId::new(); 0]); @@ -126,10 +104,7 @@ fn add_existing_asset_pair_should_not_work() { System::set_block_number(3); PriceOracle::on_initialize(3); - assert_eq!( - >::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default())), - false - ); + assert!(!>::get().contains(&(ASSET_PAIR_A.name(), BucketQueue::default()))); PriceOracle::on_create_pool(ASSET_PAIR_A); assert_storage_noop!(PriceOracle::on_create_pool(ASSET_PAIR_A)); expect_events(vec![Event::PoolRegistered(ASSET_PAIR_A).into()]); From 17f54ed443a54adc05eab44b740507db2995b253 Mon Sep 17 00:00:00 2001 From: Roznovjak Date: Tue, 12 Oct 2021 21:08:24 +0200 Subject: [PATCH 28/28] small improvements --- pallets/price-oracle/src/mock.rs | 4 ++-- pallets/price-oracle/src/types.rs | 22 +++------------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/pallets/price-oracle/src/mock.rs b/pallets/price-oracle/src/mock.rs index 26fc8f7bd62..62ce7439c06 100644 --- a/pallets/price-oracle/src/mock.rs +++ b/pallets/price-oracle/src/mock.rs @@ -26,7 +26,7 @@ use primitives::{fee, traits::AssetPairAccountIdFor, AssetId, Balance, Price}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup, Zero}, + traits::{BlakeTwo256, IdentityLookup}, }; use std::cell::RefCell; @@ -174,7 +174,7 @@ impl pallet_xyk::Config for Test { parameter_type_with_key! { pub ExistentialDeposits: |_currency_id: AssetId| -> Balance { - Zero::zero() + 1_000_000 }; } diff --git a/pallets/price-oracle/src/types.rs b/pallets/price-oracle/src/types.rs index 4f19fd5715f..b76b1a71ca4 100644 --- a/pallets/price-oracle/src/types.rs +++ b/pallets/price-oracle/src/types.rs @@ -77,11 +77,7 @@ impl<'a> Sum<&'a Self> for PriceEntry { I: Iterator, { iter.fold( - PriceEntry { - price: Price::zero(), - trade_amount: Balance::zero(), - liquidity_amount: Balance::zero(), - }, + PriceEntry::default(), |a, b| &a + b, ) } @@ -112,21 +108,12 @@ pub const BUCKET_SIZE: u32 = 10; pub type Bucket = [PriceInfo; BUCKET_SIZE as usize]; #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq)] +#[derive(RuntimeDebug, Encode, Decode, Copy, Clone, PartialEq, Eq, Default)] pub struct PriceInfo { pub avg_price: Price, pub volume: Balance, } -impl Default for PriceInfo { - fn default() -> Self { - Self { - avg_price: Price::zero(), - volume: Zero::zero(), - } - } -} - impl Add for &PriceInfo { type Output = PriceInfo; fn add(self, other: Self) -> Self::Output { @@ -143,10 +130,7 @@ impl<'a> Sum<&'a Self> for PriceInfo { I: Iterator, { iter.fold( - PriceInfo { - avg_price: Price::zero(), - volume: Balance::zero(), - }, + PriceInfo::default(), |a, b| &a + b, ) }