From bba770c1d8f226bd497d3cb670eeb56f64f2d634 Mon Sep 17 00:00:00 2001 From: yooml Date: Fri, 20 Oct 2023 14:33:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20overflow=20(#1064)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 🐛 overflow * fix: 🐛 rm as_u128 --- pallets/stable-asset/src/lib.rs | 147 +++++++++++++++++++++---------- pallets/stable-pool/src/mock.rs | 17 +++- pallets/stable-pool/src/tests.rs | 39 ++++++++ 3 files changed, 155 insertions(+), 48 deletions(-) diff --git a/pallets/stable-asset/src/lib.rs b/pallets/stable-asset/src/lib.rs index e94516df1..e6c6acb12 100644 --- a/pallets/stable-asset/src/lib.rs +++ b/pallets/stable-asset/src/lib.rs @@ -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, @@ -798,11 +798,23 @@ impl Pallet { 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::()) + .checked_mul(U256::from(time_diff.saturated_into::()))? + .checked_div(U256::from(time_diff_div.saturated_into::()))?, + ) + .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::()) + .checked_mul(U256::from(time_diff.saturated_into::()))? + .checked_div(U256::from(time_diff_div.saturated_into::()))?, + ) + .ok()? + .into(); Some(a0.checked_sub(&amount)?) } } else { @@ -962,11 +974,16 @@ impl Pallet { 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::::Math)? - .checked_div(&fee_denominator) - .ok_or(Error::::Math)?; + fee_amount = u128::try_from( + U256::from(mint_amount.saturated_into::()) + .checked_mul(U256::from(mint_fee.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(fee_denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); + mint_amount = mint_amount.checked_sub(&fee_amount).ok_or(Error::::Math)?; } @@ -1033,11 +1050,15 @@ impl Pallet { .checked_div(&pool_info.precisions[output_index_usize]) .ok_or(Error::::Math)?; if pool_info.swap_fee > zero { - let fee_amount: T::AtLeast64BitUnsigned = dy - .checked_mul(&pool_info.swap_fee) - .ok_or(Error::::Math)? - .checked_div(&fee_denominator) - .ok_or(Error::::Math)?; + let fee_amount = u128::try_from( + U256::from(dy.saturated_into::()) + .checked_mul(U256::from(pool_info.swap_fee.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(fee_denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); dy = dy.checked_sub(&fee_amount).ok_or(Error::::Math)?; } Ok(SwapResult { @@ -1080,7 +1101,13 @@ impl Pallet { 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::()) + .checked_mul(U256::from(fee_denominator.saturated_into::()))? + .checked_div(U256::from(diff.saturated_into::()))?, + ) + .ok()? + .into(); } let a: T::AtLeast64BitUnsigned = Self::get_a( @@ -1132,22 +1159,30 @@ impl Pallet { 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::::Math)? - .checked_div(&fee_denominator) - .ok_or(Error::::Math)?; + fee_amount = u128::try_from( + U256::from(amount.saturated_into::()) + .checked_mul(U256::from(pool_info.redeem_fee.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(fee_denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); // Redemption fee is charged with pool token before redemption. amount = amount.checked_sub(&fee_amount).ok_or(Error::::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::::Math)? - .checked_div(&d) - .ok_or(Error::::Math)?; + let diff_i: T::AtLeast64BitUnsigned = u128::try_from( + U256::from(balance_i.saturated_into::()) + .checked_mul(U256::from(amount.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(d.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); balances[i] = balance_i.checked_sub(&diff_i).ok_or(Error::::Math)?; let amounts_i: T::AtLeast64BitUnsigned = diff_i.checked_div(&pool_info.precisions[i]).ok_or(Error::::Math)?; @@ -1196,11 +1231,15 @@ impl Pallet { 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::::Math)? - .checked_div(&fee_denominator) - .ok_or(Error::::Math)?; + fee_amount = u128::try_from( + U256::from(amount.saturated_into::()) + .checked_mul(U256::from(pool_info.redeem_fee.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(fee_denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); // Redemption fee is charged with pool token before redemption. amount = amount.checked_sub(&fee_amount).ok_or(Error::::Math)?; } @@ -1270,11 +1309,15 @@ impl Pallet { let div_amount: T::AtLeast64BitUnsigned = fee_denominator .checked_sub(&pool_info.redeem_fee) .ok_or(Error::::Math)?; - redeem_amount = redeem_amount - .checked_mul(&fee_denominator) - .ok_or(Error::::Math)? - .checked_div(&div_amount) - .ok_or(Error::::Math)?; + redeem_amount = u128::try_from( + U256::from(redeem_amount.saturated_into::()) + .checked_mul(U256::from(fee_denominator.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(div_amount.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); let sub_amount: T::AtLeast64BitUnsigned = old_d.checked_sub(&new_d).ok_or(Error::::Math)?; fee_amount = redeem_amount.checked_sub(&sub_amount).ok_or(Error::::Math)?; } @@ -1306,11 +1349,15 @@ impl Pallet { 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::::Math)? - .checked_div(&denominator) - .ok_or(Error::::Math)?; + balance_of = u128::try_from( + U256::from(balance_of.saturated_into::()) + .checked_mul(U256::from(numerator.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); } *balance = balance_of .checked_mul(&pool_info.precisions[i]) @@ -1370,11 +1417,15 @@ impl Pallet { 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::::Math)? - .checked_div(&denominator) - .ok_or(Error::::Math)?; + balance_of = u128::try_from( + U256::from(balance_of.saturated_into::()) + .checked_mul(U256::from(numerator.saturated_into::())) + .ok_or(Error::::Math)? + .checked_div(U256::from(denominator.saturated_into::())) + .ok_or(Error::::Math)?, + ) + .map_err(|_| Error::::Math)? + .into(); } *balance = balance_of .checked_mul(&pool_info.precisions[i]) @@ -2094,7 +2145,13 @@ impl StableAsset for Pallet { 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::()) + .checked_mul(U256::from(numerator.saturated_into::()))? + .checked_div(U256::from(denominator.saturated_into::()))?, + ) + .ok()? + .into(); } // make sure pool can affort the output amount if swap_result.dy <= balance_of.into() { diff --git a/pallets/stable-pool/src/mock.rs b/pallets/stable-pool/src/mock.rs index 9a8ead303..d0b82188f 100644 --- a/pallets/stable-pool/src/mock.rs +++ b/pallets/stable-pool/src/mock.rs @@ -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, }; @@ -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; @@ -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), @@ -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(), } diff --git a/pallets/stable-pool/src/tests.rs b/pallets/stable-pool/src/tests.rs index a7ab1b8b7..92063177d 100644 --- a/pallets/stable-pool/src/tests.rs +++ b/pallets/stable-pool/src/tests.rs @@ -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(()); @@ -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); + }); +}