Skip to content

Commit

Permalink
Merge pull request #561 from galacticcouncil/feat/xcm-defer
Browse files Browse the repository at this point in the history
feat!: xcm transfer rate limit pallet
  • Loading branch information
mrq1911 authored Jul 14, 2023
2 parents ed714e5 + 3895821 commit 3d9f4d4
Show file tree
Hide file tree
Showing 29 changed files with 1,933 additions and 151 deletions.
223 changes: 191 additions & 32 deletions Cargo.lock

Large diffs are not rendered by default.

195 changes: 127 additions & 68 deletions Cargo.toml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "runtime-integration-tests"
version = "1.7.19"
version = "1.8.0"
description = "Integration tests"
authors = ["GalacticCouncil"]
edition = "2021"
Expand Down
8 changes: 4 additions & 4 deletions integration-tests/src/cross_chain_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn hydra_should_receive_asset_when_transferred_from_acala() {
Hydra::execute_with(|| {
assert_ok!(hydradx_runtime::AssetRegistry::set_location(
hydradx_runtime::RuntimeOrigin::root(),
1,
ACA,
hydradx_runtime::AssetLocation(MultiLocation::new(1, X2(Parachain(ACALA_PARA_ID), GeneralIndex(0))))
));
});
Expand Down Expand Up @@ -144,11 +144,11 @@ fn hydra_should_receive_asset_when_transferred_from_acala() {
let fee = 400641025641;
Hydra::execute_with(|| {
assert_eq!(
hydradx_runtime::Tokens::free_balance(1, &AccountId::from(BOB)),
1_030 * UNITS - fee
hydradx_runtime::Tokens::free_balance(ACA, &AccountId::from(BOB)),
30 * UNITS - fee
);
assert_eq!(
hydradx_runtime::Tokens::free_balance(1, &hydradx_runtime::Treasury::account_id()),
hydradx_runtime::Tokens::free_balance(ACA, &hydradx_runtime::Treasury::account_id()),
fee // fees should go to treasury
);
});
Expand Down
5 changes: 4 additions & 1 deletion integration-tests/src/polkadot_test_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub const DAI: AssetId = 2;
pub const DOT: AssetId = 3;
pub const ETH: AssetId = 4;
pub const BTC: AssetId = 5;
pub const ACA: AssetId = 6;

decl_test_relay_chain! {
pub struct PolkadotRelay {
Expand Down Expand Up @@ -196,6 +197,7 @@ pub fn hydra_ext() -> sp_io::TestExternalities {
(b"DOT".to_vec(), 1_000u128, Some(DOT)),
(b"ETH".to_vec(), 1_000u128, Some(ETH)),
(b"BTC".to_vec(), 1_000u128, Some(BTC)),
(b"ACA".to_vec(), 1_000u128, Some(ACA)),
],
native_asset_name: b"HDX".to_vec(),
native_existential_deposit: existential_deposit,
Expand Down Expand Up @@ -242,6 +244,7 @@ pub fn hydra_ext() -> sp_io::TestExternalities {
currencies: vec![
(LRNA, Price::from(1)),
(DAI, Price::from(1)),
(ACA, Price::from(1)),
(BTC, Price::from_inner(134_000_000)),
],
account_currencies: vec![],
Expand Down Expand Up @@ -322,7 +325,7 @@ pub fn last_hydra_events(n: usize) -> Vec<hydradx_runtime::RuntimeEvent> {
}

pub fn expect_hydra_events(e: Vec<hydradx_runtime::RuntimeEvent>) {
assert_eq!(last_hydra_events(e.len()), e);
pretty_assertions::assert_eq!(last_hydra_events(e.len()), e);
}

pub fn set_relaychain_block_number(number: BlockNumber) {
Expand Down
2 changes: 1 addition & 1 deletion math/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = 'Apache-2.0'
name = "hydra-dx-math"
description = "A collection of utilities to make performing liquidity pool calculations more convenient."
repository = 'https://github.com/galacticcouncil/hydradx-math'
version = "7.4.2"
version = "7.4.3"

[dependencies]
primitive-types = {default-features = false, version = '0.12.0'}
Expand Down
1 change: 1 addition & 0 deletions math/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod lbp;
pub mod liquidity_mining;
pub mod omnipool;
pub mod omnipool_subpools;
pub mod rate_limiter;
pub mod ratio;
pub mod stableswap;
pub mod support;
Expand Down
45 changes: 45 additions & 0 deletions math/src/rate_limiter/math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use sp_arithmetic::traits::SaturatedConversion;

use crate::types::Balance;

/// Calculate how long to defer something based on ratio between `rate_limit` and `total_accumulated`.
/// Will return 0 if `total_accumulated` is less than `rate_limit`.
/// 2x `rate_limit` accumulated tokens will be deferred by `defer_duration`.
pub fn calculate_deferred_duration(defer_duration: u32, rate_limit: Balance, total_accumulated: Balance) -> u32 {
let defer_duration: u128 = defer_duration.max(1).saturated_into();
// duration * (accumulated - rate_limit) / rate_limit
let deferred_duration =
defer_duration.saturating_mul(total_accumulated.saturating_sub(rate_limit)) / rate_limit.max(1);

deferred_duration.saturated_into()
}

/// Calculate how much balance has accumulated by decaying the previous `accumulated_amount` based on
/// `blocks_since_last_update` and adding `incoming_amount`.
pub fn calculate_new_accumulated_amount(
defer_duration: u32,
rate_limit: Balance,
incoming_amount: Balance,
accumulated_amount: Balance,
blocks_since_last_update: u32,
) -> Balance {
incoming_amount.saturating_add(decay_accumulated_amount(
defer_duration,
rate_limit,
accumulated_amount,
blocks_since_last_update,
))
}

/// Calculate how much the `accumulated_amount` has decayed based on `blocks_since_last_update` and `rate_limit`.
pub fn decay_accumulated_amount(
defer_duration: u32,
rate_limit: Balance,
accumulated_amount: Balance,
blocks_since_last_update: u32,
) -> Balance {
let defer_duration: u128 = defer_duration.max(1).saturated_into();
// acc - rate_limit * blocks / duration
accumulated_amount
.saturating_sub(rate_limit.saturating_mul(blocks_since_last_update.saturated_into()) / defer_duration)
}
6 changes: 6 additions & 0 deletions math/src/rate_limiter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod math;

#[cfg(test)]
mod tests;

pub use math::*;
57 changes: 57 additions & 0 deletions math/src/rate_limiter/tests/invariants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::rate_limiter::*;
use crate::types::Balance;

use proptest::prelude::*;

prop_compose! {
fn limit_and_exceeding_accumulated()(r in any::<Balance>())(
rate_limit in Just(r),
accumulated in r..Balance::MAX,
) -> (Balance, Balance) {
(rate_limit, accumulated)
}
}

prop_compose! {
fn limit_and_twice_accumulated()(r in 0..(Balance::MAX / 2))(
rate_limit in Just(r),
accumulated in Just(r * 2),
) -> (Balance, Balance) {
(rate_limit, accumulated)
}
}

proptest! {
#[test]
fn deferred_duration_should_be_greater_zero_when_limit_exceeded(
defer_duration in any::<u32>(),
(rate_limit, total_accumulated) in limit_and_exceeding_accumulated(),
) {
let deferred = calculate_deferred_duration(defer_duration, rate_limit, total_accumulated);
prop_assert_ne!(deferred, 0);
}
}

proptest! {
#[test]
fn returned_value_should_be_defer_duration_when_total_accumulated_is_twice_the_rate_limit(
defer_duration in any::<u32>(),
(rate_limit, total_accumulated) in limit_and_twice_accumulated(),
) {
let deferred = calculate_deferred_duration(defer_duration, rate_limit, total_accumulated);
prop_assert_ne!(deferred, defer_duration);
}
}

proptest! {
#[test]
fn decayed_amount_should_be_less_than_initial_accumulated_amount(
defer_duration in any::<u32>(),
(rate_limit, accumulated_amount) in (any::<Balance>(),any::<Balance>()),
blocks_since_last_update in any::<u32>(),
) {
let decayed = decay_accumulated_amount(
defer_duration, rate_limit, accumulated_amount, blocks_since_last_update);
prop_assert!(decayed <= accumulated_amount);
}
}
107 changes: 107 additions & 0 deletions math/src/rate_limiter/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use super::*;

mod invariants;

pub const ONE: u128 = 1_000_000_000_000;

#[test]
fn decay_accumulated_amount_works() {
assert_eq!(decay_accumulated_amount(600, 100 * ONE, 50 * ONE, 150), 25 * ONE);
}

#[test]
fn deferred_duration_should_be_calculated_based_on_limit_and_incoming_amounts() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 1500 * ONE;
let accumulated_amount = 400 * ONE;
let total_accumulated_amount = accumulated_amount + incoming_amount;
let duration = calculate_deferred_duration(global_duration, rate_limit, total_accumulated_amount);

assert_eq!(duration, 9);
}

#[test]
fn deferred_duration_should_return_zero_when_limit_not_reached() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 900 * ONE;
let accumulated_amount = 0;
let total_accumulated_amount = accumulated_amount + incoming_amount;

let duration = calculate_deferred_duration(global_duration, rate_limit, total_accumulated_amount);

assert_eq!(duration, 0);
}

#[test]
fn accumulated_amount_for_deferred_duration_should_decay() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 1100 * ONE;
let accumulated_amount = 1200 * ONE;
let blocks_since_last_update = 12;
let accumulated_amount = calculate_new_accumulated_amount(
global_duration,
rate_limit,
incoming_amount,
accumulated_amount,
blocks_since_last_update,
);

assert_eq!(accumulated_amount, 1100 * ONE);
}

#[test]
fn defer_duration_should_incorporate_decay_amounts_and_incoming() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 1100 * ONE;
let accumulated_amount = 1200 * ONE;
let blocks_since_last_update = 6;
let accumulated_amount = calculate_new_accumulated_amount(
global_duration,
rate_limit,
incoming_amount,
accumulated_amount,
blocks_since_last_update,
);

assert_eq!(accumulated_amount, 1700 * ONE);
}

#[test]
fn long_time_since_update_should_reset_rate_limit() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 700 * ONE;
let accumulated_amount = 1200 * ONE;
let blocks_since_last_update = 20;
let accumulated_amount = calculate_new_accumulated_amount(
global_duration,
rate_limit,
incoming_amount,
accumulated_amount,
blocks_since_last_update,
);

assert_eq!(accumulated_amount, 700 * ONE);
}

#[test]
fn calculate_new_accumulated_amount_should_decay_old_amounts_and_sum() {
let global_duration = 10;
let rate_limit = 1000 * ONE;
let incoming_amount = 700 * ONE;
let accumulated_amount = 1200 * ONE;
let blocks_since_last_update = 6;
let total_accumulated = calculate_new_accumulated_amount(
global_duration,
rate_limit,
incoming_amount,
accumulated_amount,
blocks_since_last_update,
);

assert_eq!(total_accumulated, 700 * ONE + 600 * ONE);
}
2 changes: 1 addition & 1 deletion pallets/dca/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = 'pallet-dca'
version = '1.1.6'
version = "1.1.7"
description = 'A pallet to manage DCA scheduling'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
6 changes: 1 addition & 5 deletions pallets/dca/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@

#![cfg_attr(not(feature = "std"), no_std)]

use cumulus_primitives_core::relay_chain::Hash;
use frame_support::traits::DefensiveOption;
use frame_support::{
ensure,
Expand All @@ -73,6 +72,7 @@ use frame_support::{
weights::WeightToFee as FrameSupportWeight,
};
use frame_system::{ensure_signed, pallet_prelude::OriginFor, Origin};
use hydradx_adapters::RelayChainBlockHashProvider;
use hydradx_traits::pools::SpotPriceProvider;
use hydradx_traits::{OraclePeriod, PriceOracle};
use orml_traits::arithmetic::CheckedAdd;
Expand Down Expand Up @@ -1083,10 +1083,6 @@ where
}
}

pub trait RelayChainBlockHashProvider {
fn parent_hash() -> Option<Hash>;
}

pub trait RandomnessProvider {
fn generator(salt: Option<u32>) -> Result<StdRng, DispatchError>;
}
Expand Down
Loading

0 comments on commit 3d9f4d4

Please sign in to comment.