diff --git a/src/contract.rs b/src/contract.rs index f5eafcb..43e30df 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -51,6 +51,8 @@ pub fn instantiate( }; TOKEN2.save(deps.storage, &token2)?; + prevent_duplicate_denoms(&msg.token1_denom, &msg.token2_denom)?; + let owner = msg.owner.map(|h| deps.api.addr_validate(&h)).transpose()?; OWNER.save(deps.storage, &owner)?; @@ -391,6 +393,29 @@ fn validate_input_amount( } } +fn prevent_duplicate_denoms(token1: &Denom, token2: &Denom) -> Result<(), ContractError> { + match token1 { + Denom::Cw20(token1_address) => match token2 { + Denom::Cw20(token2_address) => { + if *token1_address == *token2_address { + return Err(ContractError::DuplicateDenom {}); + } + Ok(()) + } + Denom::Native(_) => Ok(()), + }, + Denom::Native(token1_denom) => match token2 { + Denom::Cw20(_) => Ok(()), + Denom::Native(token2_denom) => { + if token1_denom == token2_denom { + return Err(ContractError::DuplicateDenom {}); + } + Ok(()) + } + }, + } +} + fn get_cw20_transfer_from_msg( owner: &Addr, recipient: &Addr, diff --git a/src/error.rs b/src/error.rs index 92439bf..55eafc8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -69,4 +69,7 @@ pub enum ContractError { #[error("Failed to instantiate lp token")] InstantiateLpTokenError {}, + + #[error("Duplicate denom is not allowed")] + DuplicateDenom {}, } diff --git a/src/integration_test.rs b/src/integration_test.rs index a1cbd24..107b816 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -46,8 +46,8 @@ fn get_info(router: &App, contract_addr: &Addr) -> InfoResponse { fn create_amm( router: &mut App, owner: &Addr, - cash: &Cw20Contract, - native_denom: String, + token1_denom: &Denom, + token2_denom: &Denom, lp_fee_percent: Decimal, protocol_fee_percent: Decimal, protocol_fee_recipient: String, @@ -56,8 +56,8 @@ fn create_amm( let cw20_id = router.store_code(contract_cw20()); let amm_id = router.store_code(contract_amm()); let msg = InstantiateMsg { - token1_denom: Denom::Native(native_denom), - token2_denom: Denom::Cw20(cash.addr()), + token1_denom: token1_denom.clone(), + token2_denom: token2_denom.clone(), lp_token_code_id: cw20_id, owner: Some(owner.to_string()), lp_fee_percent, @@ -129,8 +129,8 @@ fn test_instantiate() { let amm_addr = create_amm( &mut router, &owner, - &cw20_token, - NATIVE_TOKEN_DENOM.into(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(cw20_token.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -199,8 +199,8 @@ fn amm_add_and_remove_liquidity() { let amm_addr = create_amm( &mut router, &owner, - &cw20_token, - NATIVE_TOKEN_DENOM.into(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(cw20_token.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -584,8 +584,8 @@ fn swap_tokens_happy_path() { let amm_addr = create_amm( &mut router, &owner, - &cw20_token, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(cw20_token.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -791,8 +791,8 @@ fn swap_with_fee_split() { let amm_addr = create_amm( &mut router, &owner, - &cw20_token, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(cw20_token.addr()), lp_fee_percent, protocol_fee_percent, protocol_fee_recipient.to_string(), @@ -1015,8 +1015,8 @@ fn update_config() { let amm_addr = create_amm( &mut router, &owner, - &cw20_token, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(cw20_token.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -1334,8 +1334,8 @@ fn token_to_token_swap_with_fee_split() { let amm1 = create_amm( &mut router, &owner, - &token1, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(token1.addr()), lp_fee_percent, protocol_fee_percent, protocol_fee_recipient.to_string(), @@ -1343,8 +1343,8 @@ fn token_to_token_swap_with_fee_split() { let amm2 = create_amm( &mut router, &owner, - &token2, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(token2.addr()), lp_fee_percent, protocol_fee_percent, protocol_fee_recipient.to_string(), @@ -1541,8 +1541,8 @@ fn token_to_token_swap() { let amm1 = create_amm( &mut router, &owner, - &token1, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(token1.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -1550,8 +1550,8 @@ fn token_to_token_swap() { let amm2 = create_amm( &mut router, &owner, - &token2, - NATIVE_TOKEN_DENOM.to_string(), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Cw20(token2.addr()), lp_fee_percent, protocol_fee_percent, owner.to_string(), @@ -1691,3 +1691,130 @@ fn token_to_token_swap() { assert_eq!(info_amm2.token2_reserve, token2_balance); assert_eq!(info_amm2.token1_reserve, amm2_native_balance.amount); } + +#[test] +#[should_panic(expected = "Duplicate denom is not allowed")] +fn cw20_token1_to_token1_swap() { + let mut router = mock_app(); + + const NATIVE_TOKEN_DENOM: &str = "juno"; + + let owner = Addr::unchecked("owner"); + let funds = coins(2000, NATIVE_TOKEN_DENOM); + router.borrow_mut().init_modules(|router, _, storage| { + router.bank.init_balance(storage, &owner, funds).unwrap() + }); + + let cw20_token = create_cw20( + &mut router, + &owner, + "token1".to_string(), + "TOKENONE".to_string(), + Uint128::new(5000), + ); + + let lp_fee_percent = Decimal::from_str("0.3").unwrap(); + let protocol_fee_percent = Decimal::zero(); + let _amm = create_amm( + &mut router, + &owner, + &Denom::Cw20(cw20_token.addr().clone()), + &Denom::Cw20(cw20_token.addr().clone()), + lp_fee_percent, + protocol_fee_percent, + owner.to_string(), + ); +} + +#[test] +fn cw20_token1_token2_swap() { + let mut router = mock_app(); + + const NATIVE_TOKEN_DENOM: &str = "juno"; + + let owner = Addr::unchecked("owner"); + let funds = coins(2000, NATIVE_TOKEN_DENOM); + router.borrow_mut().init_modules(|router, _, storage| { + router.bank.init_balance(storage, &owner, funds).unwrap() + }); + + let token1 = create_cw20( + &mut router, + &owner, + "token1".to_string(), + "TOKENONE".to_string(), + Uint128::new(5000), + ); + + let token2 = create_cw20( + &mut router, + &owner, + "token2".to_string(), + "TOKENTWO".to_string(), + Uint128::new(5000), + ); + + let lp_fee_percent = Decimal::from_str("0.3").unwrap(); + let protocol_fee_percent = Decimal::zero(); + let _amm = create_amm( + &mut router, + &owner, + &Denom::Cw20(token1.addr().clone()), + &Denom::Cw20(token2.addr().clone()), + lp_fee_percent, + protocol_fee_percent, + owner.to_string(), + ); +} + +#[test] +#[should_panic(expected = "Duplicate denom is not allowed")] +fn native_token1_to_token1_swap() { + let mut router = mock_app(); + + const NATIVE_TOKEN_DENOM: &str = "juno"; + + let owner = Addr::unchecked("owner"); + let funds = coins(2000, NATIVE_TOKEN_DENOM); + router.borrow_mut().init_modules(|router, _, storage| { + router.bank.init_balance(storage, &owner, funds).unwrap() + }); + + let lp_fee_percent = Decimal::from_str("0.3").unwrap(); + let protocol_fee_percent = Decimal::zero(); + let _amm = create_amm( + &mut router, + &owner, + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + lp_fee_percent, + protocol_fee_percent, + owner.to_string(), + ); +} + +#[test] +fn native_token1_to_token2_swap() { + let mut router = mock_app(); + + const NATIVE_TOKEN_DENOM: &str = "juno"; + const NATIVE_TOKEN_DENOM2: &str = "stars"; + + let owner = Addr::unchecked("owner"); + let funds = coins(2000, NATIVE_TOKEN_DENOM); + router.borrow_mut().init_modules(|router, _, storage| { + router.bank.init_balance(storage, &owner, funds).unwrap() + }); + + let lp_fee_percent = Decimal::from_str("0.3").unwrap(); + let protocol_fee_percent = Decimal::zero(); + let _amm = create_amm( + &mut router, + &owner, + &Denom::Native(NATIVE_TOKEN_DENOM.into()), + &Denom::Native(NATIVE_TOKEN_DENOM2.into()), + lp_fee_percent, + protocol_fee_percent, + owner.to_string(), + ); +}