diff --git a/Cargo.lock b/Cargo.lock index 2484396..2e240ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,11 +741,13 @@ dependencies = [ "cosmwasm-std", "cw-controllers", "cw-dex", - "cw-it", + "cw-it 0.1.0", + "cw-it 0.2.3", "cw-storage-plus 1.1.0", "cw2 1.1.0", "cw20 1.1.0", "osmosis-testing 0.12.0", + "serde_json", "test-case", "thiserror", ] @@ -773,6 +775,27 @@ dependencies = [ "tonic", ] +[[package]] +name = "cw-it" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f452b759fc448ec05d20dc70f25dda8b83ba0a7c994049d27556fd1813a5ad0d" +dependencies = [ + "anyhow", + "config", + "cosmrs", + "cosmwasm-schema", + "cosmwasm-std", + "osmosis-std 0.19.2", + "osmosis-test-tube", + "prost", + "serde", + "serde_json", + "strum", + "test-tube", + "thiserror", +] + [[package]] name = "cw-storage-plus" version = "0.15.1" @@ -1472,6 +1495,12 @@ dependencies = [ "http", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2087,6 +2116,24 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "osmosis-test-tube" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dde0a21f1323e7c78f46da4bd0b24149d26483785fb5b39f74016f3f524aad" +dependencies = [ + "base64 0.13.1", + "bindgen", + "cosmrs", + "cosmwasm-std", + "osmosis-std 0.19.2", + "prost", + "serde", + "serde_json", + "test-tube", + "thiserror", +] + [[package]] name = "osmosis-testing" version = "0.12.0" @@ -2966,6 +3013,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "subtle" version = "2.5.0" @@ -3180,6 +3249,22 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "test-tube" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04de0d85f2438f0b64a5c135a1524564f2b89263cfbce011542446b6681d006f" +dependencies = [ + "base64 0.13.1", + "cosmrs", + "cosmwasm-std", + "osmosis-std 0.19.2", + "prost", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "testcontainers" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index a653c96..45ac2cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,10 @@ thiserror = "1.0.38" apollo-utils = "0.1.0" [dev-dependencies] +cw-it = { version = "0.2.3", features = ["osmosis-test-tube"] } test-case = "3.0.0" -cw-it = { git = "https://github.com/apollodao/cw-it.git", rev = "1a3eea575ef82e71460db7296a9e0b5ffa98dcff" } +serde_json = "1.0.107" + +# Used in osmosis tests, should migrate to new cw-it at some point +cw-it-old = { package = "cw-it", git = "https://github.com/apollodao/cw-it.git", rev = "1a3eea575ef82e71460db7296a9e0b5ffa98dcff" } osmosis-testing = { git = "https://github.com/apollodao/osmosis-rust", rev = "430236bd63f26d618e11e59709a56c808c4d427c" } diff --git a/tests/migrate_from_0_1_0.rs b/tests/migrate_from_0_1_0.rs new file mode 100644 index 0000000..d969561 --- /dev/null +++ b/tests/migrate_from_0_1_0.rs @@ -0,0 +1,184 @@ +#[cfg(feature = "osmosis")] +mod tests { + use apollo_cw_asset::{AssetInfo, AssetInfoUnchecked, AssetUnchecked}; + use cosmwasm_std::{coin, Coin, Empty, Uint128}; + use cw_dex::osmosis::OsmosisPool; + use cw_dex::Pool; + use cw_dex_router::msg::{ExecuteMsg, MigrateMsg}; + use cw_dex_router::operations::{SwapOperation, SwapOperationsList}; + use cw_it::osmosis_std::types::cosmwasm::wasm::v1::{ + MsgMigrateContract, MsgMigrateContractResponse, + }; + use cw_it::osmosis_test_tube::Gamm; + use cw_it::test_tube::{Account, Module, Runner, SigningAccount, Wasm}; + use cw_it::{ + osmosis_test_tube::OsmosisTestApp, traits::CwItRunner, Artifact, ContractType, + OwnedTestRunner, + }; + + const TEST_ARTIFACTS_DIR: &str = "tests/test_artifacts"; + + const UOSMO: &str = "uosmo"; + const UATOM: &str = "uatom"; + const UION: &str = "uion"; + + const UOSMO_UATOM_PATH: &[(u64, &str, &str); 1] = &[(1, UOSMO, UATOM)]; + const UION_UATOM_PATH: &[(u64, &str, &str); 2] = &[(2, UION, UOSMO), (1, UOSMO, UATOM)]; + + fn osmosis_swap_operations_list_from_vec(vec: &[(u64, &str, &str)]) -> SwapOperationsList { + SwapOperationsList::new( + vec.iter() + .map(|(pool_id, from, to)| SwapOperation { + pool: Pool::Osmosis(OsmosisPool::unchecked(pool_id.to_owned())), + offer_asset_info: AssetInfo::Native(from.to_string()), + ask_asset_info: AssetInfo::Native(to.to_string()), + }) + .collect(), + ) + } + + fn create_basic_pool<'a>( + runner: &'a impl Runner<'a>, + pool_liquidity: Vec, + signer: &SigningAccount, + ) -> OsmosisPool { + let gamm = Gamm::new(runner); + + // Create 1:1 pool + let pool_id = gamm + .create_basic_pool(&pool_liquidity, signer) + .unwrap() + .data + .pool_id; + + OsmosisPool::unchecked(pool_id) + } + + #[test] + fn migrate_from_0_1_0() { + let test_app = OsmosisTestApp::new(); + let runner = OwnedTestRunner::OsmosisTestApp(test_app); + let wasm = Wasm::new(&runner); + let admin = runner.init_default_account().unwrap(); + + // Upload old wasm file + let old_wasm = ContractType::Artifact(Artifact::Local(format!( + "{}/{}.wasm", + TEST_ARTIFACTS_DIR, "cw_dex_router_osmosis_0_1_0" + ))); + let old_code_id = runner.store_code(old_wasm, &admin).unwrap(); + + // Instantiate old contract + let res = wasm + .instantiate( + old_code_id, + &Empty {}, + Some(admin.address().as_str()), + Some("Cw Dex Router"), + &[], + &admin, + ) + .unwrap(); + let contract_addr = res.data.address; + + // Create pools + let pools = vec![(UOSMO, UATOM), (UION, UOSMO)]; + for pool in pools { + let pool_liquidity = vec![ + Coin { + denom: pool.0.to_string(), + amount: Uint128::from(1000000u128), + }, + Coin { + denom: pool.1.to_string(), + amount: Uint128::from(1000000u128), + }, + ]; + let osmo_pool = create_basic_pool(&runner, pool_liquidity, &admin); + println!("osmo pool: {:?}", osmo_pool); + println!("pool: {:?}", pool); + } + + // Store two routes + // OSMO -> ATOM + let execute_msg = ExecuteMsg::SetPath { + offer_asset: AssetInfoUnchecked::Native(UOSMO.to_string()), + ask_asset: AssetInfoUnchecked::Native(UATOM.to_string()), + path: osmosis_swap_operations_list_from_vec(UOSMO_UATOM_PATH).into(), + bidirectional: true, + }; + wasm.execute(&contract_addr, &execute_msg, &[], &admin) + .unwrap(); + // ION -> OSMO -> ATOM + let execute_msg = ExecuteMsg::SetPath { + offer_asset: AssetInfoUnchecked::Native(UION.to_string()), + ask_asset: AssetInfoUnchecked::Native(UATOM.to_string()), + path: osmosis_swap_operations_list_from_vec(UION_UATOM_PATH).into(), + bidirectional: true, + }; + wasm.execute(&contract_addr, &execute_msg, &[], &admin) + .unwrap(); + + // Try basket liquidate swapping ION and OSMO to ATOM, should fail due to overlapping paths bug + let basket_liq_msg = ExecuteMsg::BasketLiquidate { + offer_assets: vec![ + AssetUnchecked::new( + AssetInfoUnchecked::Native(UION.to_string()), + Uint128::new(1000000), + ), + AssetUnchecked::new( + AssetInfoUnchecked::Native(UOSMO.to_string()), + Uint128::new(1000000), + ), + ] + .into(), + receive_asset: AssetInfoUnchecked::Native(UATOM.to_string()), + minimum_receive: None, + to: None, + }; + let res = wasm + .execute( + &contract_addr, + &basket_liq_msg, + &[coin(1000000, UION), coin(1000000, UOSMO)], + &admin, + ) + .unwrap_err(); + println!("res: {:?}", res); + + // Upload new wasm file + let new_wasm = ContractType::Artifact(Artifact::Local(format!( + "{}/{}.wasm", + TEST_ARTIFACTS_DIR, "cw_dex_router_osmosis_0_3_0" + ))); + let new_code_id = runner.store_code(new_wasm, &admin).unwrap(); + + // Migrate contract + let msg = MigrateMsg {}; + runner + .execute::<_, MsgMigrateContractResponse>( + MsgMigrateContract { + sender: admin.address(), + code_id: new_code_id, + msg: serde_json::to_vec(&msg).unwrap(), + contract: contract_addr.clone(), + }, + "/cosmwasm.wasm.v1.MsgMigrateContract", + &admin, + ) + .unwrap(); + + // Try basket liquidate swapping ION and OSMO to ATOM, should succeed + let res = wasm + .execute( + &contract_addr, + &basket_liq_msg, + &[coin(1000000, UION), coin(1000000, UOSMO)], + &admin, + ) + .unwrap(); + res.events.iter().for_each(|event| { + println!("event: {:?}", event); + }); + } +} diff --git a/tests/osmosis_tests.rs b/tests/osmosis_tests.rs index 5a997b2..3383767 100644 --- a/tests/osmosis_tests.rs +++ b/tests/osmosis_tests.rs @@ -21,8 +21,8 @@ mod osmosis_tests { use cw_dex_router::helpers::{CwDexRouter, CwDexRouterUnchecked}; - use cw_it::config::{Contract, TestConfig}; - use cw_it::mock_api::OsmosisMockApi; + use cw_it_old::config::{Contract, TestConfig}; + use cw_it_old::mock_api::OsmosisMockApi; use osmosis_testing::cosmrs::proto::cosmos::bank::v1beta1::QueryBalanceRequest; use osmosis_testing::cosmrs::Any; diff --git a/tests/test_artifacts/cw_dex_router_osmosis_0_1_0.wasm b/tests/test_artifacts/cw_dex_router_osmosis_0_1_0.wasm new file mode 100644 index 0000000..53a3267 Binary files /dev/null and b/tests/test_artifacts/cw_dex_router_osmosis_0_1_0.wasm differ diff --git a/tests/test_artifacts/cw_dex_router_osmosis_0_3_0.wasm b/tests/test_artifacts/cw_dex_router_osmosis_0_3_0.wasm new file mode 100644 index 0000000..d49de98 Binary files /dev/null and b/tests/test_artifacts/cw_dex_router_osmosis_0_3_0.wasm differ