diff --git a/contracts/swapper/astroport/tests/test_swap.rs b/contracts/swapper/astroport/tests/test_swap.rs index 002f25c2e..b2ab8870b 100644 --- a/contracts/swapper/astroport/tests/test_swap.rs +++ b/contracts/swapper/astroport/tests/test_swap.rs @@ -54,9 +54,11 @@ const DEFAULT_LIQ: [u128; 2] = [10000000000000000u128, 10000000000000000u128]; #[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &DEFAULT_LIQ, &[6,6], Decimal::percent(5), true => panics ; "stable swap no route")] #[test_case(PoolType::Xyk {}, "uatom", &DEFAULT_LIQ, &[10,6], Decimal::percent(1), false; "xyk 10:6 decimals, even pool")] #[test_case(PoolType::Xyk {}, "uatom", &DEFAULT_LIQ, &[6,18], Decimal::percent(1), false; "xyk 6:18 decimals, even pool")] -#[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &[100000000000,10000000000000], &[6,8], Decimal::percent(50), false; "stable 6:8 decimals, even adjusted pool")] -#[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &[1000000000000,100000000000], &[7,6], Decimal::percent(50), false; "stable 8:6 decimals, even adjusted pool")] +#[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &[100000000000,10000000000000], &[6,8], Decimal::percent(10), false; "stable 6:8 decimals, even adjusted pool")] +#[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &[1000000000000,100000000000], &[7,6], Decimal::percent(10), false; "stable 8:6 decimals, even adjusted pool")] #[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &[100000000000,100000000000000000000000], &[6,18], Decimal::percent(5), false; "stable 6:18 decimals, even adjusted pool")] +#[test_case(PoolType::Xyk {}, "uatom", &DEFAULT_LIQ, &[6,6], Decimal::percent(11), false => panics ; "xyk max slippage exceeded")] +#[test_case(PoolType::Stable { amp: 10u64 }, "uatom", &DEFAULT_LIQ, &[6,6], Decimal::percent(11), false => panics ; "stable max slippage exceeded")] fn swap( pool_type: PoolType, denom_out: &str, diff --git a/contracts/swapper/base/src/contract.rs b/contracts/swapper/base/src/contract.rs index 21408efc6..f597aa0e6 100644 --- a/contracts/swapper/base/src/contract.rs +++ b/contracts/swapper/base/src/contract.rs @@ -14,6 +14,9 @@ use mars_red_bank_types::swapper::{ use crate::{ContractError, ContractResult, Route}; +// Max allowed slippage percentage for swap +const MAX_SLIPPAGE_PERCENTAGE: u64 = 10; + pub struct SwapBase<'a, Q, M, R> where Q: CustomQuery, @@ -161,6 +164,14 @@ where denom_out: String, slippage: Decimal, ) -> ContractResult> { + let max_slippage = Decimal::percent(MAX_SLIPPAGE_PERCENTAGE); + if slippage > max_slippage { + return Err(ContractError::MaxSlippageExceeded { + max_slippage, + slippage, + }); + } + let swap_msg = self .routes .load(deps.storage, (coin_in.denom.clone(), denom_out.clone())) diff --git a/contracts/swapper/base/src/error.rs b/contracts/swapper/base/src/error.rs index f1e8e59ba..57047c5b4 100644 --- a/contracts/swapper/base/src/error.rs +++ b/contracts/swapper/base/src/error.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, + CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, Decimal, DecimalRangeExceeded, OverflowError, StdError, }; use mars_owner::OwnerError; @@ -50,6 +50,12 @@ pub enum ContractError { from: String, to: String, }, + + #[error("Max slippage of {max_slippage} exceeded. Slippage is {slippage}")] + MaxSlippageExceeded { + max_slippage: Decimal, + slippage: Decimal, + }, } pub type ContractResult = Result; diff --git a/contracts/swapper/osmosis/tests/test_swap.rs b/contracts/swapper/osmosis/tests/test_swap.rs index ec1c7bc1b..713209ad6 100644 --- a/contracts/swapper/osmosis/tests/test_swap.rs +++ b/contracts/swapper/osmosis/tests/test_swap.rs @@ -46,6 +46,41 @@ fn transfer_callback_only_internal() { ); } +#[test] +fn max_slippage_exeeded() { + let app = OsmosisTestApp::new(); + let wasm = Wasm::new(&app); + + let accs = app + .init_accounts(&[coin(1_000_000_000_000, "uosmo"), coin(1_000_000_000_000, "umars")], 2) + .unwrap(); + let owner = &accs[0]; + let other_guy = &accs[1]; + + let contract_addr = instantiate_contract(&wasm, owner); + + let res_err = wasm + .execute( + &contract_addr, + &ExecuteMsg::::SwapExactIn { + coin_in: coin(1_000_000, "umars"), + denom_out: "uosmo".to_string(), + slippage: Decimal::percent(11), + }, + &[coin(1_000_000, "umars")], + other_guy, + ) + .unwrap_err(); + + assert_err( + res_err, + ContractError::MaxSlippageExceeded { + max_slippage: Decimal::percent(10), + slippage: Decimal::percent(11), + }, + ); +} + #[test] fn swap_exact_in_slippage_too_high() { let app = OsmosisTestApp::new();