Skip to content

Commit

Permalink
fix: 🐛 overflow (#1064)
Browse files Browse the repository at this point in the history
* fix: 🐛 overflow

* fix: 🐛 rm as_u128
  • Loading branch information
yooml authored Oct 20, 2023
1 parent 7c3119d commit bba770c
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 48 deletions.
147 changes: 102 additions & 45 deletions pallets/stable-asset/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use frame_support::{
};
use orml_traits::MultiCurrency;
use scale_info::TypeInfo;
use sp_core::U512;
use sp_core::{U256, U512};
use sp_runtime::{
traits::{AccountIdConversion, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, Zero},
SaturatedConversion,
Expand Down Expand Up @@ -798,11 +798,23 @@ impl<T: Config> Pallet<T> {
let time_diff_div: T::AtLeast64BitUnsigned = t1.checked_sub(&t0)?.into();
if a1 > a0 {
let diff = a1.checked_sub(&a0)?;
let amount = diff.checked_mul(&time_diff)?.checked_div(&time_diff_div)?;
let amount = u128::try_from(
U256::from(diff.saturated_into::<u128>())
.checked_mul(U256::from(time_diff.saturated_into::<u128>()))?
.checked_div(U256::from(time_diff_div.saturated_into::<u128>()))?,
)
.ok()?
.into();
Some(a0.checked_add(&amount)?)
} else {
let diff = a0.checked_sub(&a1)?;
let amount = diff.checked_mul(&time_diff)?.checked_div(&time_diff_div)?;
let amount = u128::try_from(
U256::from(diff.saturated_into::<u128>())
.checked_mul(U256::from(time_diff.saturated_into::<u128>()))?
.checked_div(U256::from(time_diff_div.saturated_into::<u128>()))?,
)
.ok()?
.into();
Some(a0.checked_sub(&amount)?)
}
} else {
Expand Down Expand Up @@ -962,11 +974,16 @@ impl<T: Config> Pallet<T> {
let mint_fee: T::AtLeast64BitUnsigned = pool_info.mint_fee;

if pool_info.mint_fee > zero {
fee_amount = mint_amount
.checked_mul(&mint_fee)
.ok_or(Error::<T>::Math)?
.checked_div(&fee_denominator)
.ok_or(Error::<T>::Math)?;
fee_amount = u128::try_from(
U256::from(mint_amount.saturated_into::<u128>())
.checked_mul(U256::from(mint_fee.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(fee_denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();

mint_amount = mint_amount.checked_sub(&fee_amount).ok_or(Error::<T>::Math)?;
}

Expand Down Expand Up @@ -1033,11 +1050,15 @@ impl<T: Config> Pallet<T> {
.checked_div(&pool_info.precisions[output_index_usize])
.ok_or(Error::<T>::Math)?;
if pool_info.swap_fee > zero {
let fee_amount: T::AtLeast64BitUnsigned = dy
.checked_mul(&pool_info.swap_fee)
.ok_or(Error::<T>::Math)?
.checked_div(&fee_denominator)
.ok_or(Error::<T>::Math)?;
let fee_amount = u128::try_from(
U256::from(dy.saturated_into::<u128>())
.checked_mul(U256::from(pool_info.swap_fee.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(fee_denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
dy = dy.checked_sub(&fee_amount).ok_or(Error::<T>::Math)?;
}
Ok(SwapResult {
Expand Down Expand Up @@ -1080,7 +1101,13 @@ impl<T: Config> Pallet<T> {
let swap_exact_over_amount = T::SwapExactOverAmount::get();
if pool_info.swap_fee > zero {
let diff = fee_denominator.checked_sub(&pool_info.swap_fee)?;
dy = dy.checked_mul(&fee_denominator)?.checked_div(&diff)?;
dy = u128::try_from(
U256::from(dy.saturated_into::<u128>())
.checked_mul(U256::from(fee_denominator.saturated_into::<u128>()))?
.checked_div(U256::from(diff.saturated_into::<u128>()))?,
)
.ok()?
.into();
}

let a: T::AtLeast64BitUnsigned = Self::get_a(
Expand Down Expand Up @@ -1132,22 +1159,30 @@ impl<T: Config> Pallet<T> {

let mut fee_amount: T::AtLeast64BitUnsigned = zero;
if pool_info.redeem_fee > zero {
fee_amount = amount
.checked_mul(&pool_info.redeem_fee)
.ok_or(Error::<T>::Math)?
.checked_div(&fee_denominator)
.ok_or(Error::<T>::Math)?;
fee_amount = u128::try_from(
U256::from(amount.saturated_into::<u128>())
.checked_mul(U256::from(pool_info.redeem_fee.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(fee_denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
// Redemption fee is charged with pool token before redemption.
amount = amount.checked_sub(&fee_amount).ok_or(Error::<T>::Math)?;
}

for i in 0..pool_info.balances.len() {
let balance_i: T::AtLeast64BitUnsigned = balances[i];
let diff_i: T::AtLeast64BitUnsigned = balance_i
.checked_mul(&amount)
.ok_or(Error::<T>::Math)?
.checked_div(&d)
.ok_or(Error::<T>::Math)?;
let diff_i: T::AtLeast64BitUnsigned = u128::try_from(
U256::from(balance_i.saturated_into::<u128>())
.checked_mul(U256::from(amount.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(d.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
balances[i] = balance_i.checked_sub(&diff_i).ok_or(Error::<T>::Math)?;
let amounts_i: T::AtLeast64BitUnsigned =
diff_i.checked_div(&pool_info.precisions[i]).ok_or(Error::<T>::Math)?;
Expand Down Expand Up @@ -1196,11 +1231,15 @@ impl<T: Config> Pallet<T> {
let mut fee_amount: T::AtLeast64BitUnsigned = zero;

if pool_info.redeem_fee > zero {
fee_amount = amount
.checked_mul(&pool_info.redeem_fee)
.ok_or(Error::<T>::Math)?
.checked_div(&fee_denominator)
.ok_or(Error::<T>::Math)?;
fee_amount = u128::try_from(
U256::from(amount.saturated_into::<u128>())
.checked_mul(U256::from(pool_info.redeem_fee.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(fee_denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
// Redemption fee is charged with pool token before redemption.
amount = amount.checked_sub(&fee_amount).ok_or(Error::<T>::Math)?;
}
Expand Down Expand Up @@ -1270,11 +1309,15 @@ impl<T: Config> Pallet<T> {
let div_amount: T::AtLeast64BitUnsigned = fee_denominator
.checked_sub(&pool_info.redeem_fee)
.ok_or(Error::<T>::Math)?;
redeem_amount = redeem_amount
.checked_mul(&fee_denominator)
.ok_or(Error::<T>::Math)?
.checked_div(&div_amount)
.ok_or(Error::<T>::Math)?;
redeem_amount = u128::try_from(
U256::from(redeem_amount.saturated_into::<u128>())
.checked_mul(U256::from(fee_denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(div_amount.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
let sub_amount: T::AtLeast64BitUnsigned = old_d.checked_sub(&new_d).ok_or(Error::<T>::Math)?;
fee_amount = redeem_amount.checked_sub(&sub_amount).ok_or(Error::<T>::Math)?;
}
Expand Down Expand Up @@ -1306,11 +1349,15 @@ impl<T: Config> Pallet<T> {
let mut balance_of: T::AtLeast64BitUnsigned =
T::Assets::free_balance(pool_info.assets[i], &pool_info.account_id).into();
if let Some((denominator, numerator)) = Self::get_token_rate(pool_info.pool_id, pool_info.assets[i]) {
balance_of = balance_of
.checked_mul(&numerator)
.ok_or(Error::<T>::Math)?
.checked_div(&denominator)
.ok_or(Error::<T>::Math)?;
balance_of = u128::try_from(
U256::from(balance_of.saturated_into::<u128>())
.checked_mul(U256::from(numerator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
}
*balance = balance_of
.checked_mul(&pool_info.precisions[i])
Expand Down Expand Up @@ -1370,11 +1417,15 @@ impl<T: Config> Pallet<T> {
let mut balance_of: T::AtLeast64BitUnsigned =
T::Assets::free_balance(pool_info.assets[i], &pool_info.account_id).into();
if let Some((denominator, numerator)) = Self::get_token_rate(pool_info.pool_id, pool_info.assets[i]) {
balance_of = balance_of
.checked_mul(&numerator)
.ok_or(Error::<T>::Math)?
.checked_div(&denominator)
.ok_or(Error::<T>::Math)?;
balance_of = u128::try_from(
U256::from(balance_of.saturated_into::<u128>())
.checked_mul(U256::from(numerator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?
.checked_div(U256::from(denominator.saturated_into::<u128>()))
.ok_or(Error::<T>::Math)?,
)
.map_err(|_| Error::<T>::Math)?
.into();
}
*balance = balance_of
.checked_mul(&pool_info.precisions[i])
Expand Down Expand Up @@ -2094,7 +2145,13 @@ impl<T: Config> StableAsset for Pallet<T> {
let mut balance_of: T::AtLeast64BitUnsigned =
T::Assets::free_balance(output_asset, &pool_info.account_id).into();
if let Some((denominator, numerator)) = Self::get_token_rate(pool_info.pool_id, output_asset) {
balance_of = balance_of.checked_mul(&numerator)?.checked_div(&denominator)?;
balance_of = u128::try_from(
U256::from(balance_of.saturated_into::<u128>())
.checked_mul(U256::from(numerator.saturated_into::<u128>()))?
.checked_div(U256::from(denominator.saturated_into::<u128>()))?,
)
.ok()?
.into();
}
// make sure pool can affort the output amount
if swap_result.dy <= balance_of.into() {
Expand Down
17 changes: 14 additions & 3 deletions pallets/stable-pool/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use frame_support::{
};
use frame_system::{EnsureRoot, EnsureSignedBy};
pub use node_primitives::{
currency::{MOVR, VMOVR},
AccountId, Balance, CurrencyId, CurrencyIdMapping, SlpOperator, SlpxOperator, TokenSymbol,
ASTR, BNC, DOT, DOT_TOKEN_ID, GLMR, VBNC, VDOT,
};
Expand Down Expand Up @@ -368,6 +369,14 @@ impl Default for ExtBuilder {
}
}

pub fn million_unit(d: u128) -> u128 {
d.saturating_mul(10_u128.pow(18))
}

pub fn unit(d: u128) -> u128 {
d.saturating_mul(10_u128.pow(12))
}

impl ExtBuilder {
pub fn balances(mut self, endowed_accounts: Vec<(u128, CurrencyId, Balance)>) -> Self {
self.endowed_accounts = endowed_accounts;
Expand All @@ -376,10 +385,11 @@ impl ExtBuilder {

pub fn new_test_ext(self) -> Self {
self.balances(vec![
(0, BNC, unit(1000)),
(0, MOVR, million_unit(1_000_000)),
(0, VMOVR, million_unit(1_000_000)),
(1, BNC, 1_000_000_000_000),
// (1, VDOT, 100_000_000),
(1, DOT, 100_000_000_000_000),
// (2, VDOT, 100_000_000_000_000),
(3, DOT, 200_000_000),
(4, DOT, 100_000_000),
(6, BNC, 100_000_000_000_000),
Expand All @@ -398,8 +408,9 @@ impl ExtBuilder {
(DOT, 1_000_000, None),
(ASTR, 10_000_000, None),
(GLMR, 10_000_000, None),
(MOVR, 10_000_000, None),
],
vcurrency: vec![VDOT],
vcurrency: vec![VDOT, VMOVR],
vsbond: vec![],
phantom: Default::default(),
}
Expand Down
39 changes: 39 additions & 0 deletions pallets/stable-pool/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ fn create_pool2() -> (CurrencyId, CurrencyId, CurrencyId, u128) {
(coin0, coin1, pool_asset, 30160825295207673652903702381u128)
}

fn create_movr_pool() -> (CurrencyId, CurrencyId, CurrencyId, u128) {
let coin0 = MOVR;
let coin1 = VMOVR;
let pool_asset: CurrencyId = CurrencyId::BLP(0);

assert_ok!(StablePool::create_pool(
RuntimeOrigin::root(),
vec![coin0, coin1],
vec![1u128, 1u128],
10000000u128,
20000000u128,
50000000u128,
10000u128,
2,
1,
million_unit(1),
));
(coin0, coin1, pool_asset, 30160825295207673652903702381u128)
}

#[test]
fn modify_a_argument_error_failed() {
env_logger::try_init().unwrap_or(());
Expand Down Expand Up @@ -767,3 +787,22 @@ fn edit_token_rate() {
);
});
}

#[test]
fn redeem_movr() {
ExtBuilder::default().new_test_ext().build().execute_with(|| {
let (coin0, coin1, pool_asset, _swap_id) = create_movr_pool();
assert_ok!(StablePool::edit_token_rate(
RuntimeOrigin::root(),
0,
vec![(coin0, (1, 1)), (coin1, (90_000_000, 100_000_000))]
));
let amounts = vec![million_unit(100_000), million_unit(200_000)];
assert_ok!(StablePool::mint_inner(&0, 0, amounts, 0));
assert_eq!(Tokens::free_balance(pool_asset, &0), 321765598211330627258732);
assert_ok!(StablePool::redeem_proportion_inner(&0, 0, million_unit(300_000), vec![0, 0]));
assert_eq!(Tokens::free_balance(pool_asset, &0), 21765598211330627258732);
assert_eq!(Tokens::free_balance(coin0, &0), 992676625984156921892393);
assert_eq!(Tokens::free_balance(coin1, &0), 985353251968313843784786);
});
}

0 comments on commit bba770c

Please sign in to comment.