Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: stableswap gradual amplification change #629

Merged
merged 22 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
13a78c4
feat: use nonzerou16 type for amplification
enthusiastmartin Jul 3, 2023
97c113f
add test of amplication 0
enthusiastmartin Jul 3, 2023
204711d
reword amplification calculation - gradual change
enthusiastmartin Jul 4, 2023
174766b
fix amp math
enthusiastmartin Jul 4, 2023
7ebc401
remove amp change from update pool call
enthusiastmartin Jul 4, 2023
2fb6409
rename pool params
enthusiastmartin Jul 4, 2023
cb43244
use better error message
enthusiastmartin Jul 4, 2023
683c6a5
reformat
enthusiastmartin Jul 4, 2023
ff4b2b3
use correct non-std
enthusiastmartin Jul 4, 2023
7c89def
temporarily remove update pool benchmark - need to be updated for fee…
enthusiastmartin Jul 4, 2023
c551c55
Merge branch 'master' into feat/stableswap-amplification-rework
enthusiastmartin Jul 4, 2023
1daf5d5
renamed amplification fields
enthusiastmartin Jul 4, 2023
45addda
add additional checks when chagning amplification
enthusiastmartin Jul 4, 2023
717e4b5
add ampl change error test scenarios
enthusiastmartin Jul 4, 2023
9225cbe
add test of changing amplification across 1000 blocks
enthusiastmartin Jul 4, 2023
86113c3
add invariants tests while amp is changing
enthusiastmartin Jul 4, 2023
0958cf6
add ampl update event to create pool
enthusiastmartin Jul 4, 2023
a4581c1
allow change of ongoiing amplification change
enthusiastmartin Jul 5, 2023
a2c2fbd
update branch to reflect recent change in amplifcation change
enthusiastmartin Jul 5, 2023
3c038a0
happy clippy happy life
enthusiastmartin Jul 5, 2023
2ed11b8
update weights calls
enthusiastmartin Jul 5, 2023
e574d29
simplify condition
enthusiastmartin Jul 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions pallets/stableswap/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@

/// Future block number is in the past.
InvalidBlock,
enthusiastmartin marked this conversation as resolved.
Show resolved Hide resolved

/// Current amplification change has not completed yet.
AmplificationChangeNotCompleted,

/// New amplification is equal to the previous value.
SameAmplification,
}

#[pallet::call]
Expand Down Expand Up @@ -397,16 +403,17 @@
///
/// Emits `AmplificationUpdated` event if successful.
#[pallet::call_index(2)]
#[pallet::weight(<T as Config>::WeightInfo::update_pool())]

Check warning on line 406 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L406

Added line #L406 was not covered by tests
enthusiastmartin marked this conversation as resolved.
Show resolved Hide resolved
#[transactional]
pub fn update_amplification(
enthusiastmartin marked this conversation as resolved.
Show resolved Hide resolved
origin: OriginFor<T>,
pool_id: T::AssetId,
future_amplification: u16,
final_amplification: u16,
start_block: T::BlockNumber,
end_block: T::BlockNumber,
) -> DispatchResult {
T::AuthorityOrigin::ensure_origin(origin)?;

let current_block = T::BlockNumberProvider::current_block_number();
ensure!(
end_block > start_block && end_block > current_block && start_block >= current_block,
Expand All @@ -416,15 +423,24 @@
Pools::<T>::try_mutate(pool_id, |maybe_pool| -> DispatchResult {
let mut pool = maybe_pool.as_mut().ok_or(Error::<T>::PoolNotFound)?;

ensure!(
pool.final_block <= current_block,
Error::<T>::AmplificationChangeNotCompleted
enthusiastmartin marked this conversation as resolved.
Show resolved Hide resolved
);
ensure!(
pool.final_amplification.get() != final_amplification,
Error::<T>::SameAmplification

Check warning on line 432 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L432

Added line #L432 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

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

what if you want to speed up or slow down ramp up? this check seems not very effective as it could be bypassed by just changing final amp by 1

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this just ensures that you dont set the same value as there is now as it does not make any change

Copy link
Member

@mrq1911 mrq1911 Jul 5, 2023

Choose a reason for hiding this comment

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

you can still change it however with the same final amp, as you can set different end block therefore slow down or hasten the change

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes. now it is different - this was a case when you had to wait for previous change to finish.

now it makes sense to ensure that the new final amplification != initial ampfliciation, isnt it ?

);

pool.initial_amplification = pool.final_amplification;
pool.final_amplification =
NonZeroU16::new(future_amplification).ok_or(Error::<T>::InvalidAmplification)?;
NonZeroU16::new(final_amplification).ok_or(Error::<T>::InvalidAmplification)?;
pool.initial_block = start_block;
pool.final_block = end_block;

ensure!(
T::AmplificationRange::get().contains(&pool.final_amplification),
Error::<T>::InvalidAmplification

Check warning on line 443 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L443

Added line #L443 was not covered by tests
);
Self::deposit_event(Event::AmplificationChanging {
pool_id,
Expand Down Expand Up @@ -543,7 +559,7 @@
share_amount,
asset_idx,
share_issuance,
amplification,

Check warning on line 562 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L562

Added line #L562 was not covered by tests
pool.withdraw_fee,
)
.ok_or(ArithmeticError::Overflow)?;
Expand Down Expand Up @@ -749,7 +765,7 @@
index_in,
index_out,
amount_in,
amplification,

Check warning on line 768 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L768

Added line #L768 was not covered by tests
pool.trade_fee,
)
.ok_or_else(|| ArithmeticError::Overflow.into())
Expand Down Expand Up @@ -785,7 +801,7 @@
index_in,
index_out,
amount_out,
amplification,

Check warning on line 804 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L804

Added line #L804 was not covered by tests
pool.trade_fee,
)
.ok_or_else(|| ArithmeticError::Overflow.into())
Expand Down Expand Up @@ -890,7 +906,7 @@
let share_amount = hydra_dx_math::stableswap::calculate_shares::<D_ITERATIONS>(
&initial_reserves,
&updated_reserves,
amplification,

Check warning on line 909 in pallets/stableswap/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/stableswap/src/lib.rs#L909

Added line #L909 was not covered by tests
share_issuance,
)
.ok_or(ArithmeticError::Overflow)?;
Expand Down
223 changes: 223 additions & 0 deletions pallets/stableswap/src/tests/amplification.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
use crate::tests::mock::*;
use crate::types::PoolInfo;
use crate::{Error, Pools};
use frame_support::{assert_noop, assert_ok};
use sp_runtime::traits::BlockNumberProvider;
use sp_runtime::Permill;
use std::num::NonZeroU16;

#[test]
fn update_amplification_should_work_when_correct_params_are_provided() {
let asset_a: AssetId = 1;
let asset_b: AssetId = 2;
let pool_id: AssetId = 100;

ExtBuilder::default()
.with_endowed_accounts(vec![(ALICE, asset_a, 200 * ONE), (ALICE, asset_b, 200 * ONE)])
.with_registered_asset("pool".as_bytes().to_vec(), pool_id)
.with_registered_asset("one".as_bytes().to_vec(), asset_a)
.with_registered_asset("two".as_bytes().to_vec(), asset_b)
.build()
.execute_with(|| {
assert_ok!(Stableswap::create_pool(
RuntimeOrigin::signed(ALICE),
pool_id,
vec![asset_a, asset_b],
100,
Permill::from_percent(10),
Permill::from_percent(20),
));

System::set_block_number(2);
let b = System::current_block_number();
dbg!(b);

assert_ok!(Stableswap::update_amplification(
RuntimeOrigin::signed(ALICE),
pool_id,
1000,
10,
1000,
));

assert_eq!(
<Pools<Test>>::get(pool_id).unwrap(),
PoolInfo {
assets: vec![asset_a, asset_b].try_into().unwrap(),
initial_amplification: NonZeroU16::new(100).unwrap(),
final_amplification: NonZeroU16::new(1000).unwrap(),
initial_block: 10,
final_block: 1000,
trade_fee: Permill::from_percent(10),
withdraw_fee: Permill::from_percent(20)
}
);
});
}

#[test]
fn update_amplification_should_fail_when_end_block_is_before_current_block() {
let asset_a: AssetId = 1;
let asset_b: AssetId = 2;
let pool_id: AssetId = 100;

ExtBuilder::default()
.with_endowed_accounts(vec![(ALICE, asset_a, 200 * ONE), (ALICE, asset_b, 200 * ONE)])
.with_registered_asset("pool".as_bytes().to_vec(), pool_id)
.with_registered_asset("one".as_bytes().to_vec(), asset_a)
.with_registered_asset("two".as_bytes().to_vec(), asset_b)
.build()
.execute_with(|| {
assert_ok!(Stableswap::create_pool(
RuntimeOrigin::signed(ALICE),
pool_id,
vec![asset_a, asset_b],
100,
Permill::from_percent(10),
Permill::from_percent(20),
));

System::set_block_number(5000);

assert_noop!(
Stableswap::update_amplification(RuntimeOrigin::signed(ALICE), pool_id, 1000, 10, 1000),
Error::<Test>::InvalidBlock
);
});
}

#[test]
fn update_amplification_should_fail_when_end_block_is_smaller_than_start_block() {
let asset_a: AssetId = 1;
let asset_b: AssetId = 2;
let pool_id: AssetId = 100;

ExtBuilder::default()
.with_endowed_accounts(vec![(ALICE, asset_a, 200 * ONE), (ALICE, asset_b, 200 * ONE)])
.with_registered_asset("pool".as_bytes().to_vec(), pool_id)
.with_registered_asset("one".as_bytes().to_vec(), asset_a)
.with_registered_asset("two".as_bytes().to_vec(), asset_b)
.build()
.execute_with(|| {
assert_ok!(Stableswap::create_pool(
RuntimeOrigin::signed(ALICE),
pool_id,
vec![asset_a, asset_b],
100,
Permill::from_percent(10),
Permill::from_percent(20),
));

System::set_block_number(5000);

assert_noop!(
Stableswap::update_amplification(RuntimeOrigin::signed(ALICE), pool_id, 1000, 20_000, 10_000),
Error::<Test>::InvalidBlock
);
});
}

#[test]
fn update_amplification_should_fail_when_start_block_before_current_block() {
let asset_a: AssetId = 1;
let asset_b: AssetId = 2;
let pool_id: AssetId = 100;

ExtBuilder::default()
.with_endowed_accounts(vec![(ALICE, asset_a, 200 * ONE), (ALICE, asset_b, 200 * ONE)])
.with_registered_asset("pool".as_bytes().to_vec(), pool_id)
.with_registered_asset("one".as_bytes().to_vec(), asset_a)
.with_registered_asset("two".as_bytes().to_vec(), asset_b)
.build()
.execute_with(|| {
assert_ok!(Stableswap::create_pool(
RuntimeOrigin::signed(ALICE),
pool_id,
vec![asset_a, asset_b],
100,
Permill::from_percent(10),
Permill::from_percent(20),
));

System::set_block_number(5000);

assert_noop!(
Stableswap::update_amplification(RuntimeOrigin::signed(ALICE), pool_id, 1000, 4000, 10_000),
Error::<Test>::InvalidBlock
);
});
}

#[test]
fn update_amplification_should_work_when_current_change_has_not_completed() {
let asset_a: AssetId = 1;
let asset_b: AssetId = 2;
let pool_id: AssetId = 100;

ExtBuilder::default()
.with_endowed_accounts(vec![(ALICE, asset_a, 200 * ONE), (ALICE, asset_b, 200 * ONE)])
.with_registered_asset("pool".as_bytes().to_vec(), pool_id)
.with_registered_asset("one".as_bytes().to_vec(), asset_a)
.with_registered_asset("two".as_bytes().to_vec(), asset_b)
.build()
.execute_with(|| {
assert_ok!(Stableswap::create_pool(
RuntimeOrigin::signed(ALICE),
pool_id,
vec![asset_a, asset_b],
100,
Permill::from_percent(10),
Permill::from_percent(20),
));

System::set_block_number(1);

assert_ok!(Stableswap::update_amplification(
RuntimeOrigin::signed(ALICE),
pool_id,
1000,
10,
1000,
));

assert_eq!(
<Pools<Test>>::get(pool_id).unwrap(),
PoolInfo {
assets: vec![asset_a, asset_b].try_into().unwrap(),
initial_amplification: NonZeroU16::new(100).unwrap(),
final_amplification: NonZeroU16::new(1000).unwrap(),
initial_block: 10,
final_block: 1000,
trade_fee: Permill::from_percent(10),
withdraw_fee: Permill::from_percent(20)
}
);
System::set_block_number(500);
assert_noop!(
Stableswap::update_amplification(RuntimeOrigin::signed(ALICE), pool_id, 5000, 5010, 6000,),
Error::<Test>::AmplificationChangeNotCompleted
);

System::set_block_number(5000);
assert_ok!(Stableswap::update_amplification(
RuntimeOrigin::signed(ALICE),
pool_id,
5000,
5010,
6000,
));

assert_eq!(
<Pools<Test>>::get(pool_id).unwrap(),
PoolInfo {
assets: vec![asset_a, asset_b].try_into().unwrap(),
initial_amplification: NonZeroU16::new(1000).unwrap(),
final_amplification: NonZeroU16::new(5000).unwrap(),
initial_block: 5010,
final_block: 6000,
trade_fee: Permill::from_percent(10),
withdraw_fee: Permill::from_percent(20)
}
);
});
}
1 change: 1 addition & 0 deletions pallets/stableswap/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod add_liquidity;
mod amplification;
mod creation;
mod invariants;
pub(crate) mod mock;
Expand Down
Loading