diff --git a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs index db6c7778..bc666c00 100644 --- a/contracts/liquidity_hub/fee_collector/src/tests/integration.rs +++ b/contracts/liquidity_hub/fee_collector/src/tests/integration.rs @@ -410,7 +410,7 @@ fn collect_all_factories_cw20_fees_successfully() { fee_distributor_id, creator.clone().sender, &white_whale_std::fee_distributor::InstantiateMsg { - bonding_contract_addr: "whale_lair".clone().to_string(), + bonding_contract_addr: "whale_lair".to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), grace_period: Uint64::new(1), epoch_config: EpochConfig { @@ -1399,7 +1399,7 @@ fn collect_pools_native_fees_successfully() { fee_distributor_id, creator.clone().sender, &white_whale_std::fee_distributor::InstantiateMsg { - bonding_contract_addr: "whale_lair".clone().to_string(), + bonding_contract_addr: "whale_lair".to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), grace_period: Uint64::new(1), epoch_config: EpochConfig { @@ -2352,7 +2352,7 @@ fn aggregate_fees_for_vault() { fee_distributor_id, creator.clone().sender, &white_whale_std::fee_distributor::InstantiateMsg { - bonding_contract_addr: "whale_lair".clone().to_string(), + bonding_contract_addr: "whale_lair".to_string(), fee_collector_addr: fee_collector_address.clone().to_string(), grace_period: Uint64::new(1), epoch_config: EpochConfig { @@ -3225,7 +3225,7 @@ fn collect_and_distribute_fees_successfully() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -3275,7 +3275,7 @@ fn collect_and_distribute_fees_successfully() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -3289,7 +3289,7 @@ fn collect_and_distribute_fees_successfully() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -3328,7 +3328,7 @@ fn collect_and_distribute_fees_successfully() { &pool_network::pair::ExecuteMsg::Swap { offer_asset: Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(200_000u128), }, @@ -3337,7 +3337,7 @@ fn collect_and_distribute_fees_successfully() { to: None, }, &[Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(200_000u128), }], ) @@ -3642,7 +3642,7 @@ fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_e denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -3692,7 +3692,7 @@ fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_e }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -3706,7 +3706,7 @@ fn collect_and_dist_fees_where_one_bonder_is_increasing_weight_no_claims_until_e amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -4608,7 +4608,7 @@ fn collect_and_distribute_fees_with_expiring_epoch_successfully() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -4658,7 +4658,7 @@ fn collect_and_distribute_fees_with_expiring_epoch_successfully() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -4672,7 +4672,7 @@ fn collect_and_distribute_fees_with_expiring_epoch_successfully() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -5169,7 +5169,7 @@ fn create_epoch_unsuccessfully() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -5219,7 +5219,7 @@ fn create_epoch_unsuccessfully() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -5233,7 +5233,7 @@ fn create_epoch_unsuccessfully() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -5674,7 +5674,7 @@ fn decrease_grace_period_fee_distributor() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -5724,7 +5724,7 @@ fn decrease_grace_period_fee_distributor() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -5738,7 +5738,7 @@ fn decrease_grace_period_fee_distributor() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -6109,7 +6109,7 @@ fn users_cannot_claim_rewards_from_past_epochs() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -6159,7 +6159,7 @@ fn users_cannot_claim_rewards_from_past_epochs() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -6173,7 +6173,7 @@ fn users_cannot_claim_rewards_from_past_epochs() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -6676,7 +6676,7 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -6726,7 +6726,7 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -6740,7 +6740,7 @@ fn user_can_claim_even_when_his_weight_increases_for_past_epochs() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -7335,7 +7335,7 @@ fn user_weight_accounts_for_unbondings() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -7385,7 +7385,7 @@ fn user_weight_accounts_for_unbondings() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -7399,7 +7399,7 @@ fn user_weight_accounts_for_unbondings() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -8191,7 +8191,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -8241,7 +8241,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -8255,7 +8255,7 @@ fn users_can_claim_even_when_global_index_was_taken_after_epoch_was_created() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], @@ -8714,7 +8714,7 @@ fn collect_distribute_with_unbonders() { denom: "uwhale".to_string(), }, AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, ], pool_fees: PoolFee { @@ -8764,7 +8764,7 @@ fn collect_distribute_with_unbonders() { }, Asset { info: AssetInfo::NativeToken { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), }, amount: Uint128::new(500_000u128), }, @@ -8778,7 +8778,7 @@ fn collect_distribute_with_unbonders() { amount: Uint128::new(500_000u128), }, Coin { - denom: native_token.clone().to_string(), + denom: native_token.to_string(), amount: Uint128::new(500_000u128), }, ], diff --git a/contracts/liquidity_hub/pool-network/frontend_helper/src/reply/deposit_pair.rs b/contracts/liquidity_hub/pool-network/frontend_helper/src/reply/deposit_pair.rs index 29f44973..ddf96cd2 100644 --- a/contracts/liquidity_hub/pool-network/frontend_helper/src/reply/deposit_pair.rs +++ b/contracts/liquidity_hub/pool-network/frontend_helper/src/reply/deposit_pair.rs @@ -96,11 +96,15 @@ pub fn deposit_pair(deps: DepsMut, env: Env, msg: Reply) -> Result { add_swap_routes(deps, env, info.sender, swap_routes) } + ExecuteMsg::RemoveSwapRoutes { swap_routes } => { + remove_swap_routes(deps, env, info.sender, swap_routes) + } } } @@ -235,14 +239,7 @@ fn add_swap_routes( sender: Addr, swap_routes: Vec, ) -> Result { - let contract_info = deps - .querier - .query_wasm_contract_info(env.contract.address)?; - if let Some(admin) = contract_info.admin { - if sender != deps.api.addr_validate(admin.as_str())? { - return Err(ContractError::Unauthorized {}); - } - } + assert_admin(deps.as_ref(), &env, &sender)?; let mut attributes = vec![]; @@ -254,18 +251,7 @@ fn add_swap_routes( ) .map_err(|_| ContractError::InvalidSwapRoute(swap_route.clone()))?; - let swap_route_key = SWAP_ROUTES.key(( - swap_route - .clone() - .offer_asset_info - .get_label(&deps.as_ref())? - .as_str(), - swap_route - .clone() - .ask_asset_info - .get_label(&deps.as_ref())? - .as_str(), - )); + let swap_route_key = get_key_from_swap_route(deps.as_ref(), &swap_route)?; swap_route_key.save(deps.storage, &swap_route.clone().swap_operations)?; attributes.push(attr("swap_route", swap_route.clone().to_string())); @@ -276,6 +262,34 @@ fn add_swap_routes( .add_attributes(attributes)) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn remove_swap_routes( + deps: DepsMut, + env: Env, + sender: Addr, + swap_routes: Vec, +) -> Result { + assert_admin(deps.as_ref(), &env, &sender)?; + + let mut attributes = vec![]; + + for swap_route in swap_routes { + let swap_route_key = get_key_from_swap_route(deps.as_ref(), &swap_route)?; + // Remove the swap route if it exists + + if swap_route_key.has(deps.storage) { + swap_route_key.remove(deps.storage); + attributes.push(attr("swap_route", swap_route.clone().to_string())); + } else { + return Err(ContractError::NoRouteFound {}); + } + } + + Ok(Response::new() + .add_attribute("action", "remove_swap_routes") + .add_attributes(attributes)) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { match msg { diff --git a/contracts/liquidity_hub/pool-network/terraswap_router/src/error.rs b/contracts/liquidity_hub/pool-network/terraswap_router/src/error.rs index 4799fa40..c9442f21 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_router/src/error.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_router/src/error.rs @@ -26,7 +26,6 @@ pub enum ContractError { #[error("Invalid swap route: {0}")] InvalidSwapRoute(SwapRoute), - #[error("No swap route found for {offer_asset} -> {ask_asset}")] NoSwapRouteForAssets { offer_asset: String, @@ -46,6 +45,9 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, + + #[error("Cannot remove a route that does not exist")] + NoRouteFound {}, } impl From for ContractError { diff --git a/contracts/liquidity_hub/pool-network/terraswap_router/src/helpers.rs b/contracts/liquidity_hub/pool-network/terraswap_router/src/helpers.rs new file mode 100644 index 00000000..095947a1 --- /dev/null +++ b/contracts/liquidity_hub/pool-network/terraswap_router/src/helpers.rs @@ -0,0 +1,36 @@ +use cosmwasm_std::{Addr, Deps, Env}; +use cw_storage_plus::Path; +use white_whale_std::pool_network::router::{SwapOperation, SwapRoute}; + +use crate::{error::ContractError, state::SWAP_ROUTES}; + +/// This function compares the address of the message sender with the contract admin +/// address. This provides a convenient way to verify if the sender +/// is the admin in a single line. +pub fn assert_admin(deps: Deps, env: &Env, sender: &Addr) -> Result<(), ContractError> { + let contract_info = deps + .querier + .query_wasm_contract_info(env.contract.address.clone())?; + if let Some(admin) = contract_info.admin { + if sender != deps.api.addr_validate(admin.as_str())? { + return Err(ContractError::Unauthorized {}); + } + } + Ok(()) +} + +/// This function returns the key for a given swap route by computing the offer +/// and ask asset labels. +pub fn get_key_from_swap_route( + deps: Deps, + swap_route: &SwapRoute, +) -> Result>, ContractError> { + Ok(SWAP_ROUTES.key(( + swap_route + .clone() + .offer_asset_info + .get_label(&deps)? + .as_str(), + swap_route.clone().ask_asset_info.get_label(&deps)?.as_str(), + ))) +} diff --git a/contracts/liquidity_hub/pool-network/terraswap_router/src/lib.rs b/contracts/liquidity_hub/pool-network/terraswap_router/src/lib.rs index eef0217e..ff4c1659 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_router/src/lib.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_router/src/lib.rs @@ -2,6 +2,7 @@ pub mod contract; mod error; pub mod state; +pub mod helpers; mod operations; #[cfg(test)] diff --git a/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs b/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs index 95571700..46e8a6bf 100644 --- a/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs +++ b/contracts/liquidity_hub/pool-network/terraswap_router/src/testing/tests.rs @@ -16,8 +16,8 @@ use white_whale_std::pool_network::router::{ use crate::contract::{execute, instantiate, migrate, query}; use crate::error::ContractError; +use crate::helpers::get_key_from_swap_route; use crate::operations::asset_into_swap_msg; -use crate::state::SWAP_ROUTES; #[test] fn proper_initialization() { @@ -1128,35 +1128,8 @@ fn add_swap_routes() { ], }; - let swap_route_1_key = SWAP_ROUTES.key(( - swap_route_1 - .clone() - .offer_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - swap_route_1 - .clone() - .ask_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - )); - - let swap_route_2_key = SWAP_ROUTES.key(( - swap_route_2 - .clone() - .offer_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - swap_route_2 - .clone() - .ask_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - )); + let swap_route_1_key = get_key_from_swap_route(deps.as_ref(), &swap_route_1).unwrap(); + let swap_route_2_key = get_key_from_swap_route(deps.as_ref(), &swap_route_2).unwrap(); // verify the keys are not there assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); @@ -1517,35 +1490,8 @@ fn all_swap_routes() { ], }; - let swap_route_1_key = SWAP_ROUTES.key(( - swap_route_1 - .clone() - .offer_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - swap_route_1 - .clone() - .ask_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - )); - - let swap_route_2_key = SWAP_ROUTES.key(( - swap_route_2 - .clone() - .offer_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - swap_route_2 - .clone() - .ask_asset_info - .get_label(&deps.as_ref()) - .unwrap() - .as_str(), - )); + let swap_route_1_key = get_key_from_swap_route(deps.as_ref(), &swap_route_1).unwrap(); + let swap_route_2_key = get_key_from_swap_route(deps.as_ref(), &swap_route_2).unwrap(); // verify the keys are not there assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); @@ -1576,3 +1522,404 @@ fn all_swap_routes() { assert_eq!(res[0].swap_route, swap_route_1.swap_operations); assert_eq!(res[1].swap_route, swap_route_2.swap_operations); } + +#[test] +fn remove_swap_route() { + let mut deps = mock_dependencies(&[]); + let msg = InstantiateMsg { + terraswap_factory: "terraswapfactory".to_string(), + }; + let info = mock_info("creator", &[]); + + // we can just call .unwrap() to assert this was a success + let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + deps.querier.with_pool_factory( + &[ + ( + &"ukrwasset0000".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + contract_addr: "pair0000".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0000".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ( + &"asset0000uluna".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + ], + contract_addr: "pair0001".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0001".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ( + &"ulunauwhale".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + ], + contract_addr: "pair0002".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0002".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ], + &[ + ("ukrw".to_string(), 6u8), + ("uluna".to_string(), 6u8), + ("uwhale".to_string(), 6u8), + ], + ); + let swap_route_1 = SwapRoute { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + swap_operations: vec![ + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + }, + ], + }; + let swap_route_2 = SwapRoute { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + swap_operations: vec![ + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + }, + ], + }; + let swap_route_1_key = get_key_from_swap_route(deps.as_ref(), &swap_route_1).unwrap(); + let swap_route_2_key = get_key_from_swap_route(deps.as_ref(), &swap_route_2).unwrap(); + + // verify the keys are not there + assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); + assert_eq!(None, swap_route_2_key.may_load(&deps.storage).unwrap()); + + // add swap route + let msg = ExecuteMsg::AddSwapRoutes { + swap_routes: vec![swap_route_1.clone(), swap_route_2.clone()], + }; + let res = execute(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + let expected_attributes = vec![ + attr("action", "add_swap_routes"), + attr("swap_route", swap_route_1.to_string()), + attr("swap_route", swap_route_2.to_string()), + ]; + assert_eq!(res.messages.len(), 0usize); + assert_eq!(res.attributes, expected_attributes); + + // query swap route + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_1.offer_asset_info.clone(), + ask_asset_info: swap_route_1.ask_asset_info.clone(), + }; + let res: Vec = + from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + assert_eq!(res, swap_route_1.swap_operations); + + // remove swap route + let msg = ExecuteMsg::RemoveSwapRoutes { + swap_routes: vec![swap_route_1.clone()], + }; + let res = execute(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + let expected_attributes = vec![ + attr("action", "remove_swap_routes"), + attr("swap_route", swap_route_1.to_string()), + ]; + assert_eq!(res.messages.len(), 0usize); + assert_eq!(res.attributes, expected_attributes); + + // verify the key is not there + assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); + + // query swap route 1 should fail + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_1.offer_asset_info.clone(), + ask_asset_info: swap_route_1.ask_asset_info.clone(), + }; + assert!(query(deps.as_ref(), mock_env(), msg).is_err()); + + // query swap route 2 should still work + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_2.offer_asset_info.clone(), + ask_asset_info: swap_route_2.ask_asset_info.clone(), + }; + let res: Vec = + from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + assert_eq!(res, swap_route_2.swap_operations); + + // remove swap route 1 again should fail + let msg = ExecuteMsg::RemoveSwapRoutes { + swap_routes: vec![swap_route_1.clone()], + }; + let res = execute(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg); + assert!(res.is_err()); +} + +#[test] +fn remove_swap_routes() { + let mut deps = mock_dependencies(&[]); + let msg = InstantiateMsg { + terraswap_factory: "terraswapfactory".to_string(), + }; + let info = mock_info("creator", &[]); + + // we can just call .unwrap() to assert this was a success + let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap(); + deps.querier.with_pool_factory( + &[ + ( + &"ukrwasset0000".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ], + contract_addr: "pair0000".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0000".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ( + &"asset0000uluna".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + ], + contract_addr: "pair0001".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0001".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ( + &"ulunauwhale".to_string(), + &PairInfo { + asset_infos: [ + AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + ], + contract_addr: "pair0002".to_string(), + liquidity_token: AssetInfo::Token { + contract_addr: "liquidity0002".to_string(), + }, + asset_decimals: [6u8, 6u8], + pair_type: PairType::ConstantProduct, + }, + ), + ], + &[ + ("ukrw".to_string(), 6u8), + ("uluna".to_string(), 6u8), + ("uwhale".to_string(), 6u8), + ], + ); + let swap_route_1 = SwapRoute { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + swap_operations: vec![ + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + }, + ], + }; + let swap_route_2 = SwapRoute { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + swap_operations: vec![ + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "ukrw".to_string(), + }, + ask_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::Token { + contract_addr: "asset0000".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + }, + SwapOperation::TerraSwap { + offer_asset_info: AssetInfo::NativeToken { + denom: "uluna".to_string(), + }, + ask_asset_info: AssetInfo::NativeToken { + denom: "uwhale".to_string(), + }, + }, + ], + }; + let swap_route_1_key = get_key_from_swap_route(deps.as_ref(), &swap_route_1).unwrap(); + let swap_route_2_key = get_key_from_swap_route(deps.as_ref(), &swap_route_2).unwrap(); + + // verify the keys are not there + assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); + assert_eq!(None, swap_route_2_key.may_load(&deps.storage).unwrap()); + + // add swap route + let msg = ExecuteMsg::AddSwapRoutes { + swap_routes: vec![swap_route_1.clone(), swap_route_2.clone()], + }; + let res = execute(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + let expected_attributes = vec![ + attr("action", "add_swap_routes"), + attr("swap_route", swap_route_1.to_string()), + attr("swap_route", swap_route_2.to_string()), + ]; + assert_eq!(res.messages.len(), 0usize); + assert_eq!(res.attributes, expected_attributes); + + // query swap route + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_1.offer_asset_info.clone(), + ask_asset_info: swap_route_1.ask_asset_info.clone(), + }; + let res: Vec = + from_json(&query(deps.as_ref(), mock_env(), msg).unwrap()).unwrap(); + assert_eq!(res, swap_route_1.swap_operations); + + // remove swap routes + let msg = ExecuteMsg::RemoveSwapRoutes { + swap_routes: vec![swap_route_1.clone(), swap_route_2.clone()], + }; + let res = execute(deps.as_mut(), mock_env(), mock_info("creator", &[]), msg).unwrap(); + let expected_attributes = vec![ + attr("action", "remove_swap_routes"), + attr("swap_route", swap_route_1.to_string()), + attr("swap_route", swap_route_2.to_string()), + ]; + assert_eq!(res.messages.len(), 0usize); + assert_eq!(res.attributes, expected_attributes); + + // verify the keys are not there + assert_eq!(None, swap_route_1_key.may_load(&deps.storage).unwrap()); + assert_eq!(None, swap_route_2_key.may_load(&deps.storage).unwrap()); + + // query swap route 1 should fail + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_1.offer_asset_info.clone(), + ask_asset_info: swap_route_1.ask_asset_info.clone(), + }; + assert!(query(deps.as_ref(), mock_env(), msg).is_err()); + + // query swap route 2 should also fail + let msg = QueryMsg::SwapRoute { + offer_asset_info: swap_route_2.offer_asset_info.clone(), + ask_asset_info: swap_route_2.ask_asset_info.clone(), + }; + assert!(query(deps.as_ref(), mock_env(), msg).is_err()); +} diff --git a/contracts/liquidity_hub/vault-network/vault_router/src/execute/next_loan.rs b/contracts/liquidity_hub/vault-network/vault_router/src/execute/next_loan.rs index 89d78df7..f027a20d 100644 --- a/contracts/liquidity_hub/vault-network/vault_router/src/execute/next_loan.rs +++ b/contracts/liquidity_hub/vault-network/vault_router/src/execute/next_loan.rs @@ -26,7 +26,8 @@ pub fn next_loan( &white_whale_std::vault_network::vault_factory::QueryMsg::Vault { asset_info: source_vault_asset, }, - )? else { + )? + else { return Err(VaultRouterError::Unauthorized {}); }; diff --git a/packages/white-whale-std/src/pool_network/router.rs b/packages/white-whale-std/src/pool_network/router.rs index 20ab68e6..d71651ee 100644 --- a/packages/white-whale-std/src/pool_network/router.rs +++ b/packages/white-whale-std/src/pool_network/router.rs @@ -94,6 +94,10 @@ pub enum ExecuteMsg { AddSwapRoutes { swap_routes: Vec, }, + /// Removes swap routes from the router. + RemoveSwapRoutes { + swap_routes: Vec, + }, } #[cw_serde]