diff --git a/src/contract.rs b/src/contract.rs index 8b265a1..11c7ee3 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -1,22 +1,20 @@ -use apollo_cw_asset::{Asset, AssetInfo, AssetInfoUnchecked, AssetList, AssetListUnchecked}; +use apollo_cw_asset::{Asset, AssetInfo, AssetInfoUnchecked}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - from_binary, to_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Env, Event, MessageInfo, Order, - Response, StdError, StdResult, Uint128, + from_json, to_json_binary, Addr, Binary, CosmosMsg, Deps, DepsMut, Env, Event, MessageInfo, + Order, Response, StdError, StdResult, Uint128, }; use cw2::set_contract_version; use cw20::Cw20ReceiveMsg; use crate::error::ContractError; -use crate::helpers::{receive_asset, receive_assets}; +use crate::helpers::receive_asset; use crate::msg::{ BestPathForPairResponse, CallbackMsg, Cw20HookMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, }; -use crate::operations::{ - SwapOperation, SwapOperationsList, SwapOperationsListBase, SwapOperationsListUnchecked, -}; +use crate::operations::{SwapOperation, SwapOperationsList, SwapOperationsListUnchecked}; use crate::state::{ADMIN, PATHS}; const CONTRACT_NAME: &str = "crates.io:cw-dex-router"; @@ -129,7 +127,7 @@ pub fn receive_cw20( ) -> Result { let sender = deps.api.addr_validate(&cw20_msg.sender)?; - match from_binary(&cw20_msg.msg)? { + match from_json(&cw20_msg.msg)? { Cw20HookMsg::ExecuteSwapOperations { operations, minimum_receive, @@ -238,7 +236,10 @@ pub fn assert_minimum_receive( let received_amount = recipient_balance.checked_sub(prev_balance)?; if received_amount < minimum_receive { - return Err(ContractError::FailedMinimumReceive); + return Err(ContractError::FailedMinimumReceive { + wanted: Asset::new(asset_info.clone(), minimum_receive), + got: Asset::new(asset_info, received_amount), + }); } Ok(Response::default()) } @@ -363,11 +364,11 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::SimulateSwapOperations { offer_amount, operations, - } => to_binary(&simulate_swap_operations(deps, offer_amount, operations)?), + } => to_json_binary(&simulate_swap_operations(deps, offer_amount, operations)?), // QueryMsg::SimulateBasketLiquidate { // offer_assets, // receive_asset, - // } => to_binary(&simulate_basket_liquidate( + // } => to_json_binary(&simulate_basket_liquidate( // deps, // offer_assets, // receive_asset, @@ -375,7 +376,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { QueryMsg::PathsForPair { offer_asset, ask_asset, - } => to_binary(&query_paths_for_pair( + } => to_json_binary(&query_paths_for_pair( deps, offer_asset.check(deps.api)?, ask_asset.check(deps.api)?, @@ -385,7 +386,7 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { offer_amount, ask_asset, exclude_paths, - } => to_binary(&query_best_path_for_pair( + } => to_json_binary(&query_best_path_for_pair( deps, offer_amount, offer_asset.check(deps.api)?, @@ -393,10 +394,10 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { exclude_paths, )?), QueryMsg::SupportedOfferAssets { ask_asset } => { - to_binary(&query_supported_offer_assets(deps, ask_asset)?) + to_json_binary(&query_supported_offer_assets(deps, ask_asset)?) } QueryMsg::SupportedAskAssets { offer_asset } => { - to_binary(&query_supported_ask_assets(deps, offer_asset)?) + to_json_binary(&query_supported_ask_assets(deps, offer_asset)?) } } } @@ -492,7 +493,7 @@ pub fn query_best_path_for_pair( let swap_paths: Result, ContractError> = paths .into_iter() - .map(|(id, swaps)| { + .map(|(_, swaps)| { let out = simulate_swap_operations(deps, offer_amount, swaps.clone().into())?; Ok(BestPathForPairResponse { operations: swaps, diff --git a/src/error.rs b/src/error.rs index 36a6271..f7ebe32 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use apollo_cw_asset::Asset; use cosmwasm_std::{OverflowError, StdError}; use cw_controllers::AdminError; use cw_dex::CwDexError; @@ -40,8 +41,8 @@ pub enum ContractError { #[error("Paths to check is empty, excluded paths excludes all valid paths")] NoPathsToCheck, - #[error("Did not receive minimum amount")] - FailedMinimumReceive, + #[error("Did not receive minimum amount, wanted: {wanted}, got: {got}")] + FailedMinimumReceive { wanted: Asset, got: Asset }, #[error("No path found for assets {offer:?} -> {ask:?}")] NoPathFound { offer: String, ask: String }, diff --git a/src/helpers.rs b/src/helpers.rs index 8f03b58..e059291 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,12 +1,10 @@ use std::vec; -use apollo_cw_asset::{Asset, AssetInfo, AssetInfoBase, AssetList}; -use apollo_utils::assets::separate_natives_and_cw20s; +use apollo_cw_asset::{Asset, AssetInfo, AssetList}; use cosmwasm_schema::cw_serde; -use cw20::{Cw20Coin, Cw20ExecuteMsg}; use cosmwasm_std::{ - to_binary, Addr, Api, Coin, CosmosMsg, Env, MessageInfo, QuerierWrapper, QueryRequest, + to_json_binary, Addr, Api, Coin, CosmosMsg, Env, MessageInfo, QuerierWrapper, QueryRequest, StdError, StdResult, Uint128, WasmMsg, WasmQuery, }; @@ -48,7 +46,7 @@ impl CwDexRouterUnchecked { Ok(CosmosMsg::Wasm(WasmMsg::Instantiate { code_id, admin, - msg: to_binary(&InstantiateMsg {})?, + msg: to_json_binary(&InstantiateMsg {})?, funds: vec![], label: label.unwrap_or_else(|| "cw-dex-router".to_string()), })) @@ -65,7 +63,7 @@ impl CwDexRouter { } pub fn call>(&self, msg: T, funds: Vec) -> StdResult { - let msg = to_binary(&msg.into())?; + let msg = to_json_binary(&msg.into())?; Ok(WasmMsg::Execute { contract_addr: self.addr().into(), msg, @@ -169,7 +167,7 @@ impl CwDexRouter { ) -> StdResult { querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.0.to_string(), - msg: to_binary(&QueryMsg::SimulateSwapOperations { + msg: to_json_binary(&QueryMsg::SimulateSwapOperations { offer_amount, operations: operations.into(), })?, @@ -199,7 +197,7 @@ impl CwDexRouter { ) -> StdResult { querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.0.to_string(), - msg: to_binary(&QueryMsg::PathsForPair { + msg: to_json_binary(&QueryMsg::PathsForPair { offer_asset: offer_asset.to_owned().into(), ask_asset: ask_asset.to_owned().into(), })?, @@ -213,7 +211,7 @@ impl CwDexRouter { ) -> StdResult> { querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.0.to_string(), - msg: to_binary(&QueryMsg::SupportedOfferAssets { + msg: to_json_binary(&QueryMsg::SupportedOfferAssets { ask_asset: ask_asset.to_owned().into(), })?, })) @@ -226,7 +224,7 @@ impl CwDexRouter { ) -> StdResult> { querier.query(&QueryRequest::Wasm(WasmQuery::Smart { contract_addr: self.0.to_string(), - msg: to_binary(&QueryMsg::SupportedAskAssets { + msg: to_json_binary(&QueryMsg::SupportedAskAssets { offer_asset: offer_asset.to_owned().into(), })?, })) diff --git a/src/msg.rs b/src/msg.rs index cc6ca44..ad7e75f 100644 --- a/src/msg.rs +++ b/src/msg.rs @@ -1,4 +1,4 @@ -use apollo_cw_asset::{AssetInfo, AssetInfoUnchecked, AssetListUnchecked}; +use apollo_cw_asset::{AssetInfo, AssetInfoUnchecked}; use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{wasm_execute, Addr, CosmosMsg, Empty, Env, Uint128}; use cw20::Cw20ReceiveMsg; diff --git a/src/tests/helpers.rs b/src/tests/helpers.rs index 7456bf2..e6f834a 100644 --- a/src/tests/helpers.rs +++ b/src/tests/helpers.rs @@ -1,31 +1,4 @@ -use cosmwasm_std::{Attribute, Coin}; -use osmosis_std::types::cosmwasm::wasm::v1::MsgExecuteContractResponse; -use osmosis_test_tube::ExecuteResponse; - -pub fn create_cl_pool() {} - -pub fn get_event_attributes_by_ty_and_key( - response: &ExecuteResponse, - ty: &str, - keys: Vec<&str>, -) -> Vec { - response - .events - .iter() - .filter(|event| event.ty == ty) - .flat_map(|event| event.attributes.clone()) - .filter(|attribute| keys.contains(&attribute.key.as_str())) - .collect() -} - -pub fn get_event_value_amount_numeric(value: &String) -> u128 { - // Find the position where the non-numeric part starts - let pos = value.find(|c: char| !c.is_numeric()).unwrap_or(value.len()); - // Extract the numeric part from the string - let numeric_part = &value[0..pos]; - // Try to parse the numeric string to u128 - numeric_part.parse::().unwrap() -} +use cosmwasm_std::Coin; pub fn sort_tokens(tokens: Vec) -> Vec { let mut sorted_tokens = tokens; diff --git a/src/tests/initialize.rs b/src/tests/initialize.rs index a55b19e..4a94f5b 100644 --- a/src/tests/initialize.rs +++ b/src/tests/initialize.rs @@ -1,170 +1,159 @@ +use std::str::FromStr; + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Attribute, Coin, Decimal, Uint128}; +use cw_dex::osmosis::OsmosisPool; +use osmosis_std::types::cosmos::base::v1beta1::Coin as OsmoCoin; +use osmosis_std::types::osmosis::concentratedliquidity::v1beta1::{ + CreateConcentratedLiquidityPoolsProposal, Pool, PoolRecord, PoolsRequest, +}; +use osmosis_std::types::osmosis::gamm::v1beta1::MsgJoinPool; +use osmosis_std::types::osmosis::poolmanager::v1beta1::SpotPriceRequest; +use osmosis_std::types::osmosis::tokenfactory::v1beta1::{MsgCreateDenom, MsgMint}; +use osmosis_test_tube::osmosis_std::types::osmosis::gamm::poolmodels::balancer::v1beta1::MsgCreateBalancerPool; +use osmosis_test_tube::osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; +use osmosis_test_tube::Gamm; +use osmosis_test_tube::{ + cosmrs::proto::traits::Message, + osmosis_std::types::osmosis::concentratedliquidity::{ + poolmodel::concentrated::v1beta1::MsgCreateConcentratedPool, v1beta1::MsgCreatePosition, + }, + Account, ConcentratedLiquidity, GovWithAppAccess, Module, OsmosisTestApp, PoolManager, + SigningAccount, TokenFactory, Wasm, +}; + +use crate::msg::{BestPathForPairResponse, ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::operations::{SwapOperationBase, SwapOperationsListUnchecked}; + +pub(crate) const ADMIN_BALANCE_AMOUNT: u128 = 3402823669209384634633746074317682114u128; +pub(crate) const TOKENS_PROVIDED_AMOUNT: &str = "1000000000000"; +pub(crate) const FEE_DENOM: &str = "uosmo"; +pub(crate) const DENOM_BASE: &str = "udydx"; +pub(crate) const DENOM_QUOTE: &str = "uryeth"; +pub(crate) const INTERMEDIATE_BASE: &str = "uinshallah"; +pub(crate) const INTERMEDIATE_QUOTE: &str = "wosmo"; + +#[cw_serde] +pub struct PoolWithDenoms { + pub pool: u64, + pub denom0: String, + pub denom1: String, +} + +pub fn default_init() -> (OsmosisTestApp, Addr, Vec, SigningAccount) { + let app = OsmosisTestApp::new(); + let tf = TokenFactory::new(&app); + + // Create new account with initial funds + let admin = app + .init_account(&[Coin::new(ADMIN_BALANCE_AMOUNT, FEE_DENOM)]) + .unwrap(); - use std::str::FromStr; - - use cosmwasm_schema::cw_serde; - use cosmwasm_std::{coin, Addr, Attribute, Coin, Decimal, Uint128}; - use cw_dex::osmosis::OsmosisPool; - use osmosis_std::types::osmosis::concentratedliquidity::v1beta1::{ - CreateConcentratedLiquidityPoolsProposal, Pool, PoolRecord, PoolsRequest, - }; - use osmosis_std::types::osmosis::gamm::v1beta1::{MsgJoinPool, MsgJoinPoolResponse}; - use osmosis_std::types::osmosis::poolmanager::v1beta1::PoolType; - use osmosis_std::types::osmosis::poolmanager::v1beta1::{ - MsgSwapExactAmountIn, SpotPriceRequest, SwapAmountInRoute, - }; - use osmosis_std::types::osmosis::tokenfactory::v1beta1::{ - MsgCreateDenom, MsgMint, QueryDenomsFromCreatorRequest, - }; - use osmosis_std::types::{ - cosmos::base::v1beta1::Coin as OsmoCoin, osmosis::gamm::v1beta1::MsgExitSwapShareAmountIn, - }; - - use osmosis_test_tube::osmosis_std::types::osmosis::gamm::poolmodels::balancer::v1beta1::MsgCreateBalancerPool; - use osmosis_test_tube::osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; - use osmosis_test_tube::{ - cosmrs::proto::traits::Message, - osmosis_std::types::osmosis::concentratedliquidity::{ - poolmodel::concentrated::v1beta1::MsgCreateConcentratedPool, v1beta1::MsgCreatePosition, - }, - Account, ConcentratedLiquidity, GovWithAppAccess, Module, OsmosisTestApp, PoolManager, - SigningAccount, TokenFactory, Wasm, - }; - use osmosis_test_tube::{ExecuteResponse, Gamm, Runner}; - - use crate::msg::{BestPathForPairResponse, ExecuteMsg, InstantiateMsg, QueryMsg}; - use crate::operations::{ - SwapOperationBase, SwapOperationsListBase, SwapOperationsListUnchecked, - }; - use crate::tests::helpers::{get_event_attributes_by_ty_and_key, sort_tokens}; - - pub(crate) const ADMIN_BALANCE_AMOUNT: u128 = 3402823669209384634633746074317682114u128; - pub(crate) const TOKENS_PROVIDED_AMOUNT: &str = "1000000000000"; - pub(crate) const FEE_DENOM: &str = "uosmo"; - pub(crate) const DENOM_BASE: &str = "udydx"; - pub(crate) const DENOM_QUOTE: &str = "uryeth"; - pub(crate) const INTERMEDIATE_BASE: &str = "uinshallah"; - pub(crate) const INTERMEDIATE_QUOTE: &str = "wosmo"; - - #[cw_serde] - pub struct PoolWithDenoms { - pub pool: u64, - pub denom0: String, - pub denom1: String, - } - - pub fn default_init() -> (OsmosisTestApp, Addr, Vec, SigningAccount) { - let app = OsmosisTestApp::new(); - let tf = TokenFactory::new(&app); - - // Create new account with initial funds - let admin = app - .init_account(&[Coin::new(ADMIN_BALANCE_AMOUNT, FEE_DENOM)]) - .unwrap(); - - let res0 = tf - .create_denom( - MsgCreateDenom { - sender: admin.address().to_string(), - subdenom: DENOM_BASE.to_string(), - }, - &admin, - ) - .unwrap(); - let res1 = tf - .create_denom( - MsgCreateDenom { - sender: admin.address().to_string(), - subdenom: DENOM_QUOTE.to_string(), - }, - &admin, - ) - .unwrap(); - let res2 = tf - .create_denom( - MsgCreateDenom { - sender: admin.address().to_string(), - subdenom: INTERMEDIATE_BASE.to_string(), - }, - &admin, - ) - .unwrap(); - let res3 = tf - .create_denom( - MsgCreateDenom { - sender: admin.address().to_string(), - subdenom: INTERMEDIATE_QUOTE.to_string(), - }, - &admin, - ) - .unwrap(); - - let denom0 = res0.data.new_token_denom; - let denom1 = res1.data.new_token_denom; - let denom2 = res2.data.new_token_denom; - let denom3 = res3.data.new_token_denom; - - tf.mint( - MsgMint { + let res0 = tf + .create_denom( + MsgCreateDenom { sender: admin.address().to_string(), - amount: Some(OsmoCoin { - denom: denom0.clone(), - amount: ADMIN_BALANCE_AMOUNT.to_string(), - }), - mint_to_address: admin.address().to_string(), + subdenom: DENOM_BASE.to_string(), }, &admin, ) .unwrap(); - - tf.mint( - MsgMint { + let res1 = tf + .create_denom( + MsgCreateDenom { sender: admin.address().to_string(), - amount: Some(OsmoCoin { - denom: denom1.clone(), - amount: ADMIN_BALANCE_AMOUNT.to_string(), - }), - mint_to_address: admin.address().to_string(), + subdenom: DENOM_QUOTE.to_string(), }, &admin, ) .unwrap(); - tf.mint( - MsgMint { + let res2 = tf + .create_denom( + MsgCreateDenom { sender: admin.address().to_string(), - amount: Some(OsmoCoin { - denom: denom2.clone(), - amount: ADMIN_BALANCE_AMOUNT.to_string(), - }), - mint_to_address: admin.address().to_string(), + subdenom: INTERMEDIATE_BASE.to_string(), }, &admin, ) .unwrap(); - tf.mint( - MsgMint { + let res3 = tf + .create_denom( + MsgCreateDenom { sender: admin.address().to_string(), - amount: Some(OsmoCoin { - denom: denom3.clone(), - amount: ADMIN_BALANCE_AMOUNT.to_string(), - }), - mint_to_address: admin.address().to_string(), + subdenom: INTERMEDIATE_QUOTE.to_string(), }, &admin, ) .unwrap(); - init_test_contract( - app, - admin, - "./test-tube-build/wasm32-unknown-unknown/release/cw_dex_router.wasm", - vec![MsgCreateConcentratedPool { - sender: "overwritten".to_string(), - denom0: denom0.to_string(), - denom1: denom1.to_string(), - tick_spacing: 100, - spread_factor: Decimal::from_str("0.01").unwrap().atomics().to_string(), - }], - vec![MsgCreateBalancerPool { + let denom0 = res0.data.new_token_denom; + let denom1 = res1.data.new_token_denom; + let denom2 = res2.data.new_token_denom; + let denom3 = res3.data.new_token_denom; + + tf.mint( + MsgMint { + sender: admin.address().to_string(), + amount: Some(OsmoCoin { + denom: denom0.clone(), + amount: ADMIN_BALANCE_AMOUNT.to_string(), + }), + mint_to_address: admin.address().to_string(), + }, + &admin, + ) + .unwrap(); + + tf.mint( + MsgMint { + sender: admin.address().to_string(), + amount: Some(OsmoCoin { + denom: denom1.clone(), + amount: ADMIN_BALANCE_AMOUNT.to_string(), + }), + mint_to_address: admin.address().to_string(), + }, + &admin, + ) + .unwrap(); + tf.mint( + MsgMint { + sender: admin.address().to_string(), + amount: Some(OsmoCoin { + denom: denom2.clone(), + amount: ADMIN_BALANCE_AMOUNT.to_string(), + }), + mint_to_address: admin.address().to_string(), + }, + &admin, + ) + .unwrap(); + tf.mint( + MsgMint { + sender: admin.address().to_string(), + amount: Some(OsmoCoin { + denom: denom3.clone(), + amount: ADMIN_BALANCE_AMOUNT.to_string(), + }), + mint_to_address: admin.address().to_string(), + }, + &admin, + ) + .unwrap(); + + init_test_contract( + app, + admin, + "./test-tube-build/wasm32-unknown-unknown/release/cw_dex_router.wasm", + vec![MsgCreateConcentratedPool { + sender: "overwritten".to_string(), + denom0: denom0.to_string(), + denom1: denom1.to_string(), + tick_spacing: 100, + spread_factor: Decimal::from_str("0.01").unwrap().atomics().to_string(), + }], + vec![ + MsgCreateBalancerPool { sender: "overwritten".to_string(), pool_params: None, pool_assets: vec![ @@ -190,7 +179,8 @@ }, ], future_pool_governor: "overwritten".to_string(), - }, MsgCreateBalancerPool { + }, + MsgCreateBalancerPool { sender: "overwritten".to_string(), pool_params: None, pool_assets: vec![ @@ -216,7 +206,8 @@ }, ], future_pool_governor: "overwritten".to_string(), - }, MsgCreateBalancerPool { + }, + MsgCreateBalancerPool { sender: "overwritten".to_string(), pool_params: None, pool_assets: vec![ @@ -242,153 +233,105 @@ }, ], future_pool_governor: "overwritten".to_string(), - }], + }, + ], + ) +} + +pub fn init_test_contract( + app: OsmosisTestApp, + admin: SigningAccount, + filename: &str, + cl_pools: Vec, + gamm_pools: Vec, +) -> (OsmosisTestApp, Addr, Vec, SigningAccount) { + // Create new osmosis appchain instance + let pm = PoolManager::new(&app); + let cl = ConcentratedLiquidity::new(&app); + let wasm = Wasm::new(&app); + + // Load compiled wasm bytecode + let wasm_byte_code = std::fs::read(filename).unwrap(); + let code_id = wasm + .store_code(&wasm_byte_code, None, &admin) + .unwrap() + .data + .code_id; + + let mut pools: Vec = vec![]; + + let gov = GovWithAppAccess::new(&app); + for cl_pool in cl_pools { + // Setup a dummy CL pool to work with + gov.propose_and_execute( + CreateConcentratedLiquidityPoolsProposal::TYPE_URL.to_string(), + CreateConcentratedLiquidityPoolsProposal { + title: "CL Pool".to_string(), + description: "So that we can trade it".to_string(), + pool_records: vec![PoolRecord { + denom0: cl_pool.denom0.clone(), + denom1: cl_pool.denom1.clone(), + tick_spacing: cl_pool.tick_spacing.clone(), + spread_factor: cl_pool.spread_factor.clone(), + }], + }, + admin.address(), + &admin, ) - } + .unwrap(); - pub fn init_test_contract( - app: OsmosisTestApp, - admin: SigningAccount, - filename: &str, - cl_pools: Vec, - gamm_pools: Vec, - ) -> (OsmosisTestApp, Addr, Vec, SigningAccount) { - // Create new osmosis appchain instance - let pm = PoolManager::new(&app); - let cl = ConcentratedLiquidity::new(&app); - let wasm = Wasm::new(&app); - - // Load compiled wasm bytecode - let wasm_byte_code = std::fs::read(filename).unwrap(); - let code_id = wasm - .store_code(&wasm_byte_code, None, &admin) - .unwrap() - .data - .code_id; - - let mut pools: Vec = vec![]; - - let gov = GovWithAppAccess::new(&app); - for cl_pool in cl_pools { - // Setup a dummy CL pool to work with - gov.propose_and_execute( - CreateConcentratedLiquidityPoolsProposal::TYPE_URL.to_string(), - CreateConcentratedLiquidityPoolsProposal { - title: "CL Pool".to_string(), - description: "So that we can trade it".to_string(), - pool_records: vec![PoolRecord { - denom0: cl_pool.denom0.clone(), - denom1: cl_pool.denom1.clone(), - tick_spacing: cl_pool.tick_spacing.clone(), - spread_factor: cl_pool.spread_factor.clone(), - }], - }, - admin.address(), - &admin, - ) - .unwrap(); + // Get just created pool information by querying all the pools, and taking the first one + let pools_response = cl.query_pools(&PoolsRequest { pagination: None }).unwrap(); + let pool: Pool = Pool::decode(pools_response.pools[0].value.as_slice()).unwrap(); - // Get just created pool information by querying all the pools, and taking the first one - let pools_response = cl.query_pools(&PoolsRequest { pagination: None }).unwrap(); - let pool: Pool = Pool::decode(pools_response.pools[0].value.as_slice()).unwrap(); + let tokens_provided = vec![ + OsmoCoin { + denom: cl_pool.denom0.to_string(), + amount: TOKENS_PROVIDED_AMOUNT.to_string(), + }, + OsmoCoin { + denom: cl_pool.denom1.to_string(), + amount: TOKENS_PROVIDED_AMOUNT.to_string(), + }, + ]; + // Create a first position in the pool with the admin user + cl.create_position( + MsgCreatePosition { + pool_id: pool.id, + sender: admin.address(), + lower_tick: -5000000, // 0.5 spot price + upper_tick: 500000, // 1.5 spot price + tokens_provided: tokens_provided.clone(), + token_min_amount0: "1".to_string(), + token_min_amount1: "1".to_string(), + }, + &admin, + ) + .unwrap(); - let tokens_provided = vec![ - OsmoCoin { - denom: cl_pool.denom0.to_string(), - amount: TOKENS_PROVIDED_AMOUNT.to_string(), - }, - OsmoCoin { - denom: cl_pool.denom1.to_string(), - amount: TOKENS_PROVIDED_AMOUNT.to_string(), - }, - ]; - // Create a first position in the pool with the admin user - cl.create_position( - MsgCreatePosition { - pool_id: pool.id, - sender: admin.address(), - lower_tick: -5000000, // 0.5 spot price - upper_tick: 500000, // 1.5 spot price - tokens_provided: tokens_provided.clone(), - token_min_amount0: "1".to_string(), - token_min_amount1: "1".to_string(), - }, - &admin, - ) + // Get and assert spot price is 1.0 + let spot_price = pm + .query_spot_price(&SpotPriceRequest { + base_asset_denom: tokens_provided[0].denom.to_string(), + quote_asset_denom: tokens_provided[1].denom.to_string(), + pool_id: pool.id, + }) .unwrap(); + assert_eq!(spot_price.spot_price, "1.000000000000000000"); - // Get and assert spot price is 1.0 - let spot_price = pm - .query_spot_price(&SpotPriceRequest { - base_asset_denom: tokens_provided[0].denom.to_string(), - quote_asset_denom: tokens_provided[1].denom.to_string(), - pool_id: pool.id, - }) - .unwrap(); - assert_eq!(spot_price.spot_price, "1.000000000000000000"); - - pools.push(PoolWithDenoms { - pool: pool.id, - denom0: cl_pool.denom0, - denom1: cl_pool.denom1, - }); - } - - for gamm_pool in gamm_pools { - // Create a new pool - let gamm = Gamm::new(&app); - let response = gamm - .create_basic_pool( - &[ - Coin { - denom: gamm_pool.pool_assets[0] - .token - .as_ref() - .unwrap() - .denom - .to_string(), - amount: Uint128::from_str( - &gamm_pool.pool_assets[0].token.as_ref().unwrap().amount, - ) - .unwrap(), - }, - Coin { - denom: gamm_pool.pool_assets[1] - .token - .as_ref() - .unwrap() - .denom - .to_string(), - amount: Uint128::from_str( - &gamm_pool.pool_assets[1].token.as_ref().unwrap().amount, - ) - .unwrap(), - }, - ], - &admin, - ) - .unwrap(); - - let ty = "pool_created"; - let keys = vec!["pool_id"]; - let pool_id: u64 = response - .events - .iter() - .filter(|event| event.ty == ty) - .flat_map(|event| event.attributes.clone()) - .filter(|attribute| keys.contains(&attribute.key.as_str())) - .collect::>() - .first() - .unwrap() - .value - .parse() - .unwrap(); + pools.push(PoolWithDenoms { + pool: pool.id, + denom0: cl_pool.denom0, + denom1: cl_pool.denom1, + }); + } - let add_liq = MsgJoinPool { - sender: admin.address().to_string(), - pool_id: pool_id.clone(), - share_out_amount: "100".to_string(), - token_in_maxs: vec![ + for gamm_pool in gamm_pools { + // Create a new pool + let gamm = Gamm::new(&app); + let response = gamm + .create_basic_pool( + &[ Coin { denom: gamm_pool.pool_assets[0] .token @@ -400,8 +343,7 @@ &gamm_pool.pool_assets[0].token.as_ref().unwrap().amount, ) .unwrap(), - } - .into(), + }, Coin { denom: gamm_pool.pool_assets[1] .token @@ -413,99 +355,151 @@ &gamm_pool.pool_assets[1].token.as_ref().unwrap().amount, ) .unwrap(), - } - .into(), + }, ], - }; - - // let _res: ExecuteResponse = - // app.execute(add_liq, MsgJoinPool::TYPE_URL, &admin).unwrap(); - - pools.push(PoolWithDenoms { - pool: pool_id, - denom0: gamm_pool.pool_assets[0] - .token - .as_ref() - .unwrap() - .denom - .to_string(), - denom1: gamm_pool.pool_assets[1] - .token - .as_ref() - .unwrap() - .denom - .to_string(), - }) - } - - // Instantiate vault - let contract = wasm - .instantiate( - code_id, - &InstantiateMsg {}, - Some(admin.address().as_str()), - Some("cw-dex-router"), - &[], &admin, ) .unwrap(); - // // Sort tokens alphabetically by denom name or Osmosis will return an error - // tokens_provided.sort_by(|a, b| a.denom.cmp(&b.denom)); // can't use helpers.rs::sort_tokens() due to different Coin type - - // // Increment the app time for twaps to function, this is needed to do not fail on querying a twap for a timeframe higher than the chain existence - // app.increase_time(1000000); + let ty = "pool_created"; + let keys = vec!["pool_id"]; + let pool_id: u64 = response + .events + .iter() + .filter(|event| event.ty == ty) + .flat_map(|event| event.attributes.clone()) + .filter(|attribute| keys.contains(&attribute.key.as_str())) + .collect::>() + .first() + .unwrap() + .value + .parse() + .unwrap(); - (app, Addr::unchecked(contract.data.address), pools, admin) + let _ = MsgJoinPool { + sender: admin.address().to_string(), + pool_id: pool_id.clone(), + share_out_amount: "100".to_string(), + token_in_maxs: vec![ + Coin { + denom: gamm_pool.pool_assets[0] + .token + .as_ref() + .unwrap() + .denom + .to_string(), + amount: Uint128::from_str( + &gamm_pool.pool_assets[0].token.as_ref().unwrap().amount, + ) + .unwrap(), + } + .into(), + Coin { + denom: gamm_pool.pool_assets[1] + .token + .as_ref() + .unwrap() + .denom + .to_string(), + amount: Uint128::from_str( + &gamm_pool.pool_assets[1].token.as_ref().unwrap().amount, + ) + .unwrap(), + } + .into(), + ], + }; + + // let _res: ExecuteResponse = + // app.execute(add_liq, MsgJoinPool::TYPE_URL, &admin).unwrap(); + + pools.push(PoolWithDenoms { + pool: pool_id, + denom0: gamm_pool.pool_assets[0] + .token + .as_ref() + .unwrap() + .denom + .to_string(), + denom1: gamm_pool.pool_assets[1] + .token + .as_ref() + .unwrap() + .denom + .to_string(), + }) } - #[test] - fn default_init_works() { - let (app, contract_address, pools, admin) = default_init(); - let wasm = Wasm::new(&app); - - for pool in pools.clone() { - let response = wasm - .execute( - &contract_address.to_string(), - &ExecuteMsg::SetPath { - offer_asset: apollo_cw_asset::AssetInfoBase::Native(pool.denom0.clone()), - ask_asset: apollo_cw_asset::AssetInfoBase::Native(pool.denom1.clone()), - path: SwapOperationsListUnchecked::new(vec![SwapOperationBase { - pool: cw_dex::Pool::Osmosis(OsmosisPool::unchecked(pool.pool.clone())), - offer_asset_info: apollo_cw_asset::AssetInfoBase::Native( - pool.denom0.clone(), - ), - ask_asset_info: apollo_cw_asset::AssetInfoBase::Native(pool.denom1) - .clone(), - }]) - .into(), - bidirectional: true, - }, - &[], - &admin, - ) - .unwrap(); - } - - let resp: BestPathForPairResponse = wasm - .query( + // Instantiate vault + let contract = wasm + .instantiate( + code_id, + &InstantiateMsg {}, + Some(admin.address().as_str()), + Some("cw-dex-router"), + &[], + &admin, + ) + .unwrap(); + + // // Sort tokens alphabetically by denom name or Osmosis will return an error + // tokens_provided.sort_by(|a, b| a.denom.cmp(&b.denom)); // can't use helpers.rs::sort_tokens() due to different Coin type + + // // Increment the app time for twaps to function, this is needed to do not fail on querying a twap for a timeframe higher than the chain existence + // app.increase_time(1000000); + + (app, Addr::unchecked(contract.data.address), pools, admin) +} + +#[test] +fn default_init_works() { + let (app, contract_address, pools, admin) = default_init(); + let wasm = Wasm::new(&app); + + for pool in pools.clone() { + let _ = wasm + .execute( &contract_address.to_string(), - &QueryMsg::BestPathForPair { - offer_asset: apollo_cw_asset::AssetInfoBase::Native( - pools.first().unwrap().denom0.clone(), - ), - ask_asset: apollo_cw_asset::AssetInfoBase::Native( - pools.first().unwrap().denom1.clone(), - ), - offer_amount: Uint128::from(100000000u128), - exclude_paths: None, + &ExecuteMsg::SetPath { + offer_asset: apollo_cw_asset::AssetInfoBase::Native(pool.denom0.clone()), + ask_asset: apollo_cw_asset::AssetInfoBase::Native(pool.denom1.clone()), + path: SwapOperationsListUnchecked::new(vec![SwapOperationBase { + pool: cw_dex::Pool::Osmosis(OsmosisPool::unchecked(pool.pool.clone())), + offer_asset_info: apollo_cw_asset::AssetInfoBase::Native( + pool.denom0.clone(), + ), + ask_asset_info: apollo_cw_asset::AssetInfoBase::Native(pool.denom1).clone(), + }]) + .into(), + bidirectional: true, }, + &[], + &admin, ) .unwrap(); + } + + let resp: BestPathForPairResponse = wasm + .query( + &contract_address.to_string(), + &QueryMsg::BestPathForPair { + offer_asset: apollo_cw_asset::AssetInfoBase::Native( + pools.first().unwrap().denom0.clone(), + ), + ask_asset: apollo_cw_asset::AssetInfoBase::Native( + pools.first().unwrap().denom1.clone(), + ), + offer_amount: Uint128::from(100000000u128), + exclude_paths: None, + }, + ) + .unwrap(); - let mut iter = resp.operations.into_iter(); - // under the default setup, we expect the best path to route over pool 1 - assert_eq!(iter.next().unwrap().pool, cw_dex::Pool::Osmosis(OsmosisPool::unchecked(1))); - assert!(iter.next().is_none()); - } \ No newline at end of file + let mut iter = resp.operations.into_iter(); + // under the default setup, we expect the best path to route over pool 1 + assert_eq!( + iter.next().unwrap().pool, + cw_dex::Pool::Osmosis(OsmosisPool::unchecked(1)) + ); + assert!(iter.next().is_none()); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index fdb7f5e..8872441 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,3 +1,3 @@ mod helpers; mod initialize; -mod multipool_paths; \ No newline at end of file +mod multipool_paths; diff --git a/src/tests/multipool_paths.rs b/src/tests/multipool_paths.rs index 72bee97..a1d4c06 100644 --- a/src/tests/multipool_paths.rs +++ b/src/tests/multipool_paths.rs @@ -1,22 +1,15 @@ use std::str::FromStr; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{coin, Addr, Attribute, Coin, Decimal, Uint128}; +use cosmwasm_std::{Addr, Attribute, Coin, Decimal, Uint128}; use cw_dex::osmosis::OsmosisPool; +use osmosis_std::types::cosmos::base::v1beta1::Coin as OsmoCoin; use osmosis_std::types::osmosis::concentratedliquidity::v1beta1::{ CreateConcentratedLiquidityPoolsProposal, Pool, PoolRecord, PoolsRequest, }; use osmosis_std::types::osmosis::gamm::v1beta1::{MsgJoinPool, MsgJoinPoolResponse}; -use osmosis_std::types::osmosis::poolmanager::v1beta1::{ - MsgSwapExactAmountIn, SpotPriceRequest, SwapAmountInRoute, -}; -use osmosis_std::types::osmosis::poolmanager::v1beta1::{PoolRequest, PoolType}; -use osmosis_std::types::osmosis::tokenfactory::v1beta1::{ - MsgCreateDenom, MsgMint, QueryDenomsFromCreatorRequest, -}; -use osmosis_std::types::{ - cosmos::base::v1beta1::Coin as OsmoCoin, osmosis::gamm::v1beta1::MsgExitSwapShareAmountIn, -}; +use osmosis_std::types::osmosis::poolmanager::v1beta1::PoolRequest; +use osmosis_std::types::osmosis::poolmanager::v1beta1::SpotPriceRequest; +use osmosis_std::types::osmosis::tokenfactory::v1beta1::{MsgCreateDenom, MsgMint}; use osmosis_test_tube::osmosis_std::types::osmosis::gamm::poolmodels::balancer::v1beta1::MsgCreateBalancerPool; use osmosis_test_tube::osmosis_std::types::osmosis::gamm::v1beta1::PoolAsset; @@ -28,11 +21,11 @@ use osmosis_test_tube::{ Account, ConcentratedLiquidity, GovWithAppAccess, Module, OsmosisTestApp, PoolManager, SigningAccount, TokenFactory, Wasm, }; -use osmosis_test_tube::{Bank, ExecuteResponse, Gamm, Runner}; +use osmosis_test_tube::{ExecuteResponse, Gamm, Runner}; use crate::msg::{BestPathForPairResponse, ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::operations::{SwapOperationBase, SwapOperationsListBase, SwapOperationsListUnchecked}; -use crate::tests::helpers::{get_event_attributes_by_ty_and_key, sort_tokens}; +use crate::operations::{SwapOperationBase, SwapOperationsListUnchecked}; +use crate::tests::helpers::sort_tokens; use super::initialize::*; @@ -455,7 +448,7 @@ fn multiple_pools_work() { let (app, contract_address, pools, admin) = multiple_pool_init(); let wasm = Wasm::new(&app); - let alice = app + let _ = app .init_account(&[ Coin::new(100_000_000u128, DENOM_BASE), Coin::new(ADMIN_BALANCE_AMOUNT, FEE_DENOM), @@ -463,7 +456,7 @@ fn multiple_pools_work() { .unwrap(); for pool in pools.clone() { - let response = wasm + let _ = wasm .execute( &contract_address.to_string(), &ExecuteMsg::SetPath { @@ -508,8 +501,10 @@ fn multiple_pools_work() { pools.first().unwrap().denom1.clone() ); - let poolManager = PoolManager::new(&app); - let pool_resp = poolManager.query_pool(&PoolRequest { pool_id: 1 }).unwrap(); + let pool_manager = PoolManager::new(&app); + let pool_resp = pool_manager + .query_pool(&PoolRequest { pool_id: 1 }) + .unwrap(); let pool = Pool::decode(pool_resp.pool.unwrap().value.as_ref()).unwrap(); println!("pool: {:?}", pool); @@ -521,7 +516,7 @@ fn multiple_pools_work() { ); assert!(iter.next().is_none()); - let resp = wasm + let _ = wasm .execute( &contract_address.to_string(), &ExecuteMsg::ExecuteSwapOperations {