diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index cc202e1..e9a8a28 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -65,7 +65,7 @@ jobs: toolchain: 1.74.0 args: --locked --tests env: - LLVM_PROFILE_FILE: "swap-converter-%p-%m.profraw" + LLVM_PROFILE_FILE: "swap-contract-%p-%m.profraw" RUSTFLAGS: "-Cinstrument-coverage" RUST_BACKTRACE: 1 diff --git a/Cargo.lock b/Cargo.lock index fdbec18..6ccb2d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1311,31 +1311,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "injective-converter" -version = "1.0.0" -dependencies = [ - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-multi-test", - "cw-storage-plus 0.14.0", - "cw-utils 0.14.0", - "cw2 0.14.0", - "injective-cosmwasm", - "injective-math", - "injective-protobuf", - "injective-std", - "injective-test-tube", - "num-traits", - "prost 0.11.9", - "protobuf", - "schemars", - "serde", - "serde-json-wasm", - "thiserror", -] - [[package]] name = "injective-cosmwasm" version = "0.2.18" @@ -2389,6 +2364,32 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" +[[package]] +name = "swap-contract" +version = "1.0.1" +dependencies = [ + "cosmos-sdk-proto", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-multi-test", + "cw-storage-plus 0.14.0", + "cw-utils 0.14.0", + "cw2 0.14.0", + "injective-cosmwasm", + "injective-math", + "injective-protobuf", + "injective-std", + "injective-test-tube", + "num-traits", + "prost 0.11.9", + "protobuf", + "schemars", + "serde", + "serde-json-wasm", + "thiserror", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..431d467 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,47 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +### Added + +- + +### Changed + +- + +### Fixed + +- + +## [Version v1.1.0] - 2024-02-24 + +### Added + +- This Changelog file +- `rust-toolchain` file +- Contract migration templates and migrate function + +### Changed + +- + +### Fixed + +- Correctly round to `min_quantity_tick_size` in intermediate steps for multi-hop buys + +## [Version v1.0.0] - 2024-02-16 + +### Added + +- Versioning into contract + +### Changed + +- Updated to latest CosmWasm version + +### Fixed + +- diff --git a/contracts/swap/Cargo.toml b/contracts/swap/Cargo.toml index d9d0327..aa57bc1 100644 --- a/contracts/swap/Cargo.toml +++ b/contracts/swap/Cargo.toml @@ -1,8 +1,8 @@ [package] authors = [ "Markus Waas " ] edition = "2021" -name = "injective-converter" -version = "1.0.0" +name = "swap-contract" +version = "1.0.1" exclude = [ # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. @@ -45,6 +45,7 @@ serde-json-wasm = "0.5.1" thiserror = { version = "1.0.31" } [dev-dependencies] +cosmos-sdk-proto = { version = "0.19.0", default-features = false } cosmwasm-schema = "1.5.0" cw-multi-test = "0.16.2" injective-std = { version = "0.1.5" } diff --git a/contracts/swap/src/admin.rs b/contracts/swap/src/admin.rs index 79f8a98..1289aa6 100644 --- a/contracts/swap/src/admin.rs +++ b/contracts/swap/src/admin.rs @@ -24,6 +24,8 @@ pub fn save_config( fee_recipient, admin, }; + config.to_owned().validate()?; + CONFIG.save(deps.storage, &config) } diff --git a/contracts/swap/src/contract.rs b/contracts/swap/src/contract.rs index 6592215..3702ecb 100644 --- a/contracts/swap/src/contract.rs +++ b/contracts/swap/src/contract.rs @@ -6,18 +6,18 @@ use cosmwasm_std::{ use cw2::{get_contract_version, set_contract_version}; use crate::admin::{delete_route, save_config, set_route, update_config, withdraw_support_funds}; +use crate::helpers::handle_config_migration; use crate::types::{ConfigResponse, SwapQuantityMode}; use injective_cosmwasm::{InjectiveMsgWrapper, InjectiveQueryWrapper}; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; use crate::queries::{estimate_swap_result, SwapQuantity}; use crate::state::{get_all_swap_routes, get_config, read_swap_route}; use crate::swap::{handle_atomic_order_reply, start_swap_flow}; -// version info for migration info -pub const CONTRACT_NAME: &str = "crates.io:atomic-order-example"; +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const ATOMIC_ORDER_REPLY_ID: u64 = 1u64; @@ -32,6 +32,7 @@ pub fn instantiate( ) -> Result, ContractError> { set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; save_config(deps, env, msg.admin, msg.fee_recipient)?; + Ok(Response::new() .add_attribute("method", "instantiate") .add_attribute("owner", info.sender)) @@ -98,6 +99,53 @@ pub fn reply( } } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + _msg: MigrateMsg, +) -> Result { + let contract_version = get_contract_version(deps.storage)?; + + match contract_version.contract.as_ref() { + // old contract name + "crates.io:atomic-order-example" => match contract_version.version.as_ref() { + "0.1.0" => { + unimplemented!( + "Migration from version {} is no longer supported", + contract_version.version + ); + } + "1.0.0" => { + set_contract_version( + deps.storage, + format!("crates.io:{CONTRACT_NAME}"), + CONTRACT_VERSION, + )?; + + handle_config_migration(deps)?; + } + _ => return Err(ContractError::MigrationError {}), + }, + "crates.io:swap-contract" => match contract_version.version.as_ref() { + "1.0.1" => { + unimplemented!( + "Migration from version {} is no yet supported", + contract_version.version + ); + } + _ => return Err(ContractError::MigrationError {}), + }, + _ => return Err(ContractError::MigrationError {}), + } + + Ok(Response::new() + .add_attribute("previous_contract_name", &contract_version.contract) + .add_attribute("previous_contract_version", &contract_version.version) + .add_attribute("new_contract_name", format!("crates.io:{CONTRACT_NAME}")) + .add_attribute("new_contract_version", CONTRACT_VERSION)) +} + #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { diff --git a/contracts/swap/src/error.rs b/contracts/swap/src/error.rs index f9b18cf..a0037db 100644 --- a/contracts/swap/src/error.rs +++ b/contracts/swap/src/error.rs @@ -16,7 +16,7 @@ pub enum ContractError { #[error("Failure response from submsg: {0}")] SubMsgFailure(String), - #[error("Unrecognised reply id: {0}")] + #[error("Unrecognized reply id: {0}")] UnrecognizedReply(u64), #[error("Invalid reply from sub-message {id}, {err}")] @@ -27,4 +27,7 @@ pub enum ContractError { #[error("Provided amount of {0} is below required amount of {1}")] InsufficientFundsProvided(FPDecimal, FPDecimal), + + #[error("Contract can't be migrated")] + MigrationError {}, } diff --git a/contracts/swap/src/helpers.rs b/contracts/swap/src/helpers.rs index 27d8f17..498bedc 100644 --- a/contracts/swap/src/helpers.rs +++ b/contracts/swap/src/helpers.rs @@ -1,8 +1,11 @@ -use cosmwasm_std::{CosmosMsg, SubMsg}; +use cosmwasm_std::{CosmosMsg, DepsMut, Response, SubMsg}; -use injective_cosmwasm::InjectiveMsgWrapper; +use cw_storage_plus::Item; +use injective_cosmwasm::{InjectiveMsgWrapper, InjectiveQueryWrapper}; use injective_math::FPDecimal; +use crate::{state::CONFIG, types::Config, ContractError}; + pub fn i32_to_dec(source: i32) -> FPDecimal { FPDecimal::from(i128::from(source)) } @@ -49,6 +52,26 @@ pub fn dec_scale_factor() -> FPDecimal { FPDecimal::ONE.scaled(18) } +type V100Config = Config; +const V100CONFIG: Item = Item::new("config"); + +pub fn handle_config_migration( + deps: DepsMut, +) -> Result { + let v100_config = V100CONFIG.load(deps.storage)?; + + let config = Config { + fee_recipient: v100_config.fee_recipient, + admin: v100_config.admin, + }; + + CONFIG.save(deps.storage, &config)?; + + config.validate()?; + + Ok(Response::default()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/contracts/swap/src/msg.rs b/contracts/swap/src/msg.rs index f0c841e..37590b7 100644 --- a/contracts/swap/src/msg.rs +++ b/contracts/swap/src/msg.rs @@ -18,6 +18,9 @@ pub struct InstantiateMsg { pub admin: Addr, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct MigrateMsg {} + #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ExecuteMsg { diff --git a/contracts/swap/src/queries.rs b/contracts/swap/src/queries.rs index 1a2873f..dbce82d 100644 --- a/contracts/swap/src/queries.rs +++ b/contracts/swap/src/queries.rs @@ -230,22 +230,19 @@ fn estimate_execution_buy_from_target( fee_percent: FPDecimal, is_simulation: bool, ) -> StdResult { - if !(target_base_output_quantity.num % market.min_quantity_tick_size.num).is_zero() { - return Err(StdError::generic_err( - "Target quantity must be a multiple of min_quantity_tick_size", - )); - } + let rounded_target_base_output_quantity = + round_up_to_min_tick(target_base_output_quantity, market.min_quantity_tick_size); let orders = querier.query_spot_market_orderbook( &market.market_id, OrderSide::Sell, - Some(target_base_output_quantity), + Some(rounded_target_base_output_quantity), None, )?; let top_orders = get_minimum_liquidity_levels( deps, &orders.sells_price_level, - target_base_output_quantity, + rounded_target_base_output_quantity, |l| l.q, market.min_quantity_tick_size, )?; @@ -255,12 +252,13 @@ fn estimate_execution_buy_from_target( get_average_price_from_orders(&top_orders, market.min_price_tick_size, true); let worst_price = get_worst_price_from_orders(&top_orders); - let expected_exchange_quote_quantity = target_base_output_quantity * average_price; + let expected_exchange_quote_quantity = rounded_target_base_output_quantity * average_price; let fee_estimate = expected_exchange_quote_quantity * fee_percent; let required_input_quote_quantity = expected_exchange_quote_quantity + fee_estimate; // check if user funds + contract funds are enough to create order - let required_funds = worst_price * target_base_output_quantity * (FPDecimal::ONE + fee_percent); + let required_funds = + worst_price * rounded_target_base_output_quantity * (FPDecimal::ONE + fee_percent); let funds_in_contract = deps .querier diff --git a/contracts/swap/src/state.rs b/contracts/swap/src/state.rs index bc90154..bcc85a2 100644 --- a/contracts/swap/src/state.rs +++ b/contracts/swap/src/state.rs @@ -9,6 +9,12 @@ pub const STEP_STATE: Item = Item::new("current_step_cache"); pub const SWAP_RESULTS: Item> = Item::new("swap_results"); pub const CONFIG: Item = Item::new("config"); +impl Config { + pub fn validate(self) -> StdResult<()> { + Ok(()) + } +} + pub fn store_swap_route(storage: &mut dyn Storage, route: &SwapRoute) -> StdResult<()> { let key = route_key(&route.source_denom, &route.target_denom); SWAP_ROUTES.save(storage, key, route) diff --git a/contracts/swap/src/testing/integration_realistic_tests_exact_quantity.rs b/contracts/swap/src/testing/integration_realistic_tests_exact_quantity.rs index 53e129f..c3c3f7b 100644 --- a/contracts/swap/src/testing/integration_realistic_tests_exact_quantity.rs +++ b/contracts/swap/src/testing/integration_realistic_tests_exact_quantity.rs @@ -7,16 +7,18 @@ use injective_math::FPDecimal; use crate::msg::{ExecuteMsg, QueryMsg}; use crate::testing::test_utils::{ are_fpdecimals_approximately_equal, assert_fee_is_as_expected, - create_realistic_atom_usdt_sell_orders_from_spreadsheet, + create_ninja_inj_both_side_orders, create_realistic_atom_usdt_sell_orders_from_spreadsheet, create_realistic_eth_usdt_buy_orders_from_spreadsheet, create_realistic_eth_usdt_sell_orders_from_spreadsheet, - create_realistic_inj_usdt_buy_orders_from_spreadsheet, create_realistic_limit_order, + create_realistic_inj_usdt_buy_orders_from_spreadsheet, + create_realistic_inj_usdt_sell_orders_from_spreadsheet, create_realistic_limit_order, create_realistic_usdt_usdc_both_side_orders, human_to_dec, init_rich_account, init_self_relaying_contract_and_get_address, launch_realistic_atom_usdt_spot_market, - launch_realistic_inj_usdt_spot_market, launch_realistic_usdt_usdc_spot_market, - launch_realistic_weth_usdt_spot_market, must_init_account_with_funds, query_all_bank_balances, - query_bank_balance, set_route_and_assert_success, str_coin, Decimals, OrderSide, ATOM, ETH, - INJ, INJ_2, USDC, USDT, + launch_realistic_inj_usdt_spot_market, launch_realistic_ninja_inj_spot_market, + launch_realistic_usdt_usdc_spot_market, launch_realistic_weth_usdt_spot_market, + must_init_account_with_funds, query_all_bank_balances, query_bank_balance, + set_route_and_assert_success, str_coin, Decimals, OrderSide, ATOM, ETH, INJ, INJ_2, NINJA, + USDC, USDT, }; use crate::types::{FPCoin, SwapEstimationResult}; @@ -894,6 +896,114 @@ fn it_correctly_swaps_between_markets_using_different_quote_assets_self_relaying ); } +#[test] +fn it_correctly_swaps_between_markets_using_different_quote_assets_self_relaying_ninja() { + let app = InjectiveTestApp::new(); + let wasm = Wasm::new(&app); + let exchange = Exchange::new(&app); + let bank = Bank::new(&app); + + let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); + let _validator = app + .get_first_validator_signing_account(INJ.to_string(), 1.2f64) + .unwrap(); + + let owner = must_init_account_with_funds( + &app, + &[ + str_coin("1_000", USDT, Decimals::Six), + str_coin("1_000", USDC, Decimals::Six), + str_coin("1_000", NINJA, Decimals::Six), + str_coin("10_000", INJ, Decimals::Eighteen), + str_coin("101", INJ_2, Decimals::Eighteen), + ], + ); + + let spot_market_1_id = launch_realistic_inj_usdt_spot_market(&exchange, &owner); + let spot_market_2_id = launch_realistic_ninja_inj_spot_market(&exchange, &owner); + + let contr_addr = init_self_relaying_contract_and_get_address( + &wasm, + &owner, + &[ + str_coin("100", INJ_2, Decimals::Eighteen), + str_coin("10", USDC, Decimals::Six), + str_coin("500", USDT, Decimals::Six), + ], + ); + set_route_and_assert_success( + &wasm, + &owner, + &contr_addr, + USDT, + NINJA, + vec![ + spot_market_1_id.as_str().into(), + spot_market_2_id.as_str().into(), + ], + ); + + let trader1 = init_rich_account(&app); + + create_realistic_inj_usdt_sell_orders_from_spreadsheet(&app, &spot_market_1_id, &trader1); + create_ninja_inj_both_side_orders(&app, &spot_market_2_id, &trader1); + + app.increase_time(1); + + let swapper = must_init_account_with_funds( + &app, + &[ + str_coin("1", INJ, Decimals::Eighteen), + str_coin("100000", USDT, Decimals::Six), + ], + ); + + let usdt_to_swap = "100000"; + let to_output_quantity = human_to_dec("501000", Decimals::Six); + + let from_balance_before = query_bank_balance(&bank, USDT, swapper.address().as_str()); + let to_balance_before = query_bank_balance(&bank, NINJA, swapper.address().as_str()); + + wasm.execute( + &contr_addr, + &ExecuteMsg::SwapExactOutput { + target_denom: NINJA.to_string(), + target_output_quantity: to_output_quantity, + }, + &[str_coin(usdt_to_swap, USDT, Decimals::Six)], + &swapper, + ) + .unwrap(); + + let from_balance_after = query_bank_balance(&bank, USDT, swapper.address().as_str()); + let to_balance_after = query_bank_balance(&bank, NINJA, swapper.address().as_str()); + + // from 100000 USDT -> 96201.062128 USDT = 3798.937872 USDT + let expected_from_balance_before = human_to_dec("100000", Decimals::Six); + let expected_from_balance_after = human_to_dec("96201.062128", Decimals::Six); + + // from 0 NINJA to 501000 NINJA + let expected_to_balance_before = human_to_dec("0", Decimals::Six); + let expected_to_balance_after = human_to_dec("501000", Decimals::Six); + + assert_eq!( + from_balance_before, expected_from_balance_before, + "incorrect original amount was left after swap" + ); + assert_eq!( + to_balance_before, expected_to_balance_before, + "incorrect target amount after swap" + ); + assert_eq!( + from_balance_after, expected_from_balance_after, + "incorrect original amount was left after swap" + ); + assert_eq!( + to_balance_after, expected_to_balance_after, + "incorrect target amount after swap" + ); +} + #[test] fn it_doesnt_lose_buffer_if_exact_swap_of_eth_to_atom_is_executed_multiple_times() { let app = InjectiveTestApp::new(); diff --git a/contracts/swap/src/testing/integration_realistic_tests_min_quantity.rs b/contracts/swap/src/testing/integration_realistic_tests_min_quantity.rs index ed4d64a..6213738 100644 --- a/contracts/swap/src/testing/integration_realistic_tests_min_quantity.rs +++ b/contracts/swap/src/testing/integration_realistic_tests_min_quantity.rs @@ -1,4 +1,6 @@ -use injective_test_tube::{Account, Bank, Exchange, InjectiveTestApp, Module, RunnerResult, Wasm}; +use injective_test_tube::{ + Account, Bank, Exchange, InjectiveTestApp, Module, RunnerResult, SigningAccount, Wasm, +}; use std::ops::Neg; use crate::helpers::Scaled; @@ -35,36 +37,14 @@ use crate::types::{FPCoin, SwapEstimationResult}; In all tests contract is configured to self-relay trades and thus receive a 60% fee discount. */ -#[test] -fn happy_path_two_hops_swap_eth_atom_realistic_values_self_relaying() { - let app = InjectiveTestApp::new(); +pub fn happy_path_two_hops_test(app: InjectiveTestApp, owner: SigningAccount, contr_addr: String) { let wasm = Wasm::new(&app); let exchange = Exchange::new(&app); let bank = Bank::new(&app); - let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); - - let _validator = app - .get_first_validator_signing_account(INJ.to_string(), 1.2f64) - .unwrap(); - let owner = must_init_account_with_funds( - &app, - &[ - str_coin("1", ETH, Decimals::Eighteen), - str_coin("1", ATOM, Decimals::Six), - str_coin("1_000", USDT, Decimals::Six), - str_coin("10_000", INJ, Decimals::Eighteen), - ], - ); - let spot_market_1_id = launch_realistic_weth_usdt_spot_market(&exchange, &owner); let spot_market_2_id = launch_realistic_atom_usdt_spot_market(&exchange, &owner); - let contr_addr = init_self_relaying_contract_and_get_address( - &wasm, - &owner, - &[str_coin("1_000", USDT, Decimals::Six)], - ); set_route_and_assert_success( &wasm, &owner, @@ -227,6 +207,35 @@ fn happy_path_two_hops_swap_eth_atom_realistic_values_self_relaying() { ); } +#[test] +fn happy_path_two_hops_swap_eth_atom_realistic_values_self_relaying() { + let app = InjectiveTestApp::new(); + let wasm = Wasm::new(&app); + + let _signer = must_init_account_with_funds(&app, &[str_coin("1", INJ, Decimals::Eighteen)]); + + let _validator = app + .get_first_validator_signing_account(INJ.to_string(), 1.2f64) + .unwrap(); + let owner = must_init_account_with_funds( + &app, + &[ + str_coin("1", ETH, Decimals::Eighteen), + str_coin("1", ATOM, Decimals::Six), + str_coin("1_000", USDT, Decimals::Six), + str_coin("10_000", INJ, Decimals::Eighteen), + ], + ); + + let contr_addr = init_self_relaying_contract_and_get_address( + &wasm, + &owner, + &[str_coin("1_000", USDT, Decimals::Six)], + ); + + happy_path_two_hops_test(app, owner, contr_addr); +} + #[test] fn happy_path_two_hops_swap_inj_eth_realistic_values_self_relaying() { let app = InjectiveTestApp::new(); diff --git a/contracts/swap/src/testing/migration_test.rs b/contracts/swap/src/testing/migration_test.rs new file mode 100644 index 0000000..73dc413 --- /dev/null +++ b/contracts/swap/src/testing/migration_test.rs @@ -0,0 +1,109 @@ +use crate::{ + msg::{FeeRecipient, InstantiateMsg, MigrateMsg}, + testing::{ + integration_realistic_tests_min_quantity::happy_path_two_hops_test, + test_utils::{ + must_init_account_with_funds, store_code, str_coin, Decimals, ATOM, ETH, INJ, USDT, + }, + }, +}; + +use cosmos_sdk_proto::cosmwasm::wasm::v1::{ + MsgMigrateContract, MsgMigrateContractResponse, QueryContractInfoRequest, + QueryContractInfoResponse, +}; +use cosmwasm_std::Addr; +use injective_test_tube::{Account, ExecuteResponse, InjectiveTestApp, Module, Runner, Wasm}; + +type V100InstantiateMsg = InstantiateMsg; + +#[test] +#[cfg_attr(not(feature = "integration"), ignore)] +fn test_migration_v100_to_v101() { + let app = InjectiveTestApp::new(); + let wasm = Wasm::new(&app); + + let wasm_byte_code = + std::fs::read("../../contracts/swap/src/testing/test_artifacts/swap-contract-v100.wasm") + .unwrap(); + + let owner = must_init_account_with_funds( + &app, + &[ + str_coin("1", ETH, Decimals::Eighteen), + str_coin("1", ATOM, Decimals::Six), + str_coin("1_000", USDT, Decimals::Six), + str_coin("10_000", INJ, Decimals::Eighteen), + ], + ); + + let swap_v100_code_id = wasm + .store_code(&wasm_byte_code, None, &owner) + .unwrap() + .data + .code_id; + + let swap_v100_address: String = wasm + .instantiate( + swap_v100_code_id, + &V100InstantiateMsg { + admin: Addr::unchecked(owner.address()), + fee_recipient: FeeRecipient::SwapContract, + }, + Some(&owner.address()), + Some("swap-contract"), + &[str_coin("1_000", USDT, Decimals::Six)], + &owner, + ) + .unwrap() + .data + .address; + + let res: QueryContractInfoResponse = app + .query( + "/cosmwasm.wasm.v1.Query/ContractInfo", + &QueryContractInfoRequest { + address: swap_v100_address.clone(), + }, + ) + .unwrap(); + let contract_info = res.contract_info.unwrap(); + + assert_eq!(res.address, swap_v100_address); + assert_eq!(contract_info.code_id, swap_v100_code_id); + assert_eq!(contract_info.creator, owner.address()); + assert_eq!(contract_info.label, "swap-contract"); + + let swap_v110_code_id = store_code(&wasm, &owner, "swap_contract".to_string()); + + let _res: ExecuteResponse = app + .execute( + MsgMigrateContract { + sender: owner.address(), + contract: swap_v100_address.clone(), + code_id: swap_v110_code_id, + msg: serde_json_wasm::to_vec(&MigrateMsg {}).unwrap(), + }, + "/cosmwasm.wasm.v1.MsgMigrateContract", + &owner, + ) + .unwrap(); + + let res: QueryContractInfoResponse = app + .query( + "/cosmwasm.wasm.v1.Query/ContractInfo", + &QueryContractInfoRequest { + address: swap_v100_address.clone(), + }, + ) + .unwrap(); + + let contract_info = res.contract_info.unwrap(); + + assert_eq!(res.address, swap_v100_address); + assert_eq!(contract_info.code_id, swap_v110_code_id); + assert_eq!(contract_info.creator, owner.address()); + assert_eq!(contract_info.label, "swap-contract"); + + happy_path_two_hops_test(app, owner, swap_v100_address); +} diff --git a/contracts/swap/src/testing/mod.rs b/contracts/swap/src/testing/mod.rs index 91e596c..ed9f565 100644 --- a/contracts/swap/src/testing/mod.rs +++ b/contracts/swap/src/testing/mod.rs @@ -2,6 +2,7 @@ mod config_tests; mod integration_logic_tests; mod integration_realistic_tests_exact_quantity; mod integration_realistic_tests_min_quantity; +mod migration_test; mod queries_tests; mod storage_tests; mod swap_tests; diff --git a/contracts/swap/src/testing/test_artifacts/swap-contract-v100.wasm b/contracts/swap/src/testing/test_artifacts/swap-contract-v100.wasm new file mode 100644 index 0000000..4bdd560 Binary files /dev/null and b/contracts/swap/src/testing/test_artifacts/swap-contract-v100.wasm differ diff --git a/contracts/swap/src/testing/test_utils.rs b/contracts/swap/src/testing/test_utils.rs index 6c1aac2..ccee9a3 100644 --- a/contracts/swap/src/testing/test_utils.rs +++ b/contracts/swap/src/testing/test_utils.rs @@ -45,6 +45,7 @@ pub const USDT: &str = "usdt"; pub const USDC: &str = "usdc"; pub const INJ: &str = "inj"; pub const INJ_2: &str = "inj_2"; +pub const NINJA: &str = "ninja"; pub const DEFAULT_TAKER_FEE: f64 = 0.001; pub const DEFAULT_ATOMIC_MULTIPLIER: f64 = 2.5; @@ -445,6 +446,20 @@ pub fn launch_realistic_usdt_usdc_spot_market( ) } +pub fn launch_realistic_ninja_inj_spot_market( + exchange: &Exchange, + signer: &SigningAccount, +) -> String { + launch_custom_spot_market( + exchange, + signer, + NINJA, + INJ_2, + dec_to_proto(FPDecimal::must_from_str("1000000")).as_str(), + dec_to_proto(FPDecimal::must_from_str("10000000")).as_str(), + ) +} + pub fn create_realistic_eth_usdt_buy_orders_from_spreadsheet( app: &InjectiveTestApp, market_id: &str, @@ -577,6 +592,23 @@ pub fn create_realistic_inj_usdt_buy_orders_from_spreadsheet( ); } +pub fn create_realistic_inj_usdt_sell_orders_from_spreadsheet( + app: &InjectiveTestApp, + market_id: &str, + trader1: &SigningAccount, +) { + create_realistic_limit_order( + app, + trader1, + market_id, + OrderSide::Sell, + "36", + "2821.001", + Decimals::Eighteen, + Decimals::Six, + ); +} + pub fn create_realistic_atom_usdt_sell_orders_from_spreadsheet( app: &InjectiveTestApp, market_id: &str, @@ -657,6 +689,24 @@ pub fn create_realistic_usdt_usdc_both_side_orders( ); } +// not really realistic yet +pub fn create_ninja_inj_both_side_orders( + app: &InjectiveTestApp, + market_id: &str, + trader1: &SigningAccount, +) { + create_realistic_limit_order( + app, + trader1, + market_id, + OrderSide::Sell, + "0.00021", + "1001000", + Decimals::Six, + Decimals::Eighteen, + ); +} + #[derive(PartialEq)] pub enum OrderSide { Buy, @@ -767,7 +817,7 @@ pub fn init_self_relaying_contract_and_get_address( owner: &SigningAccount, initial_balance: &[Coin], ) -> String { - let code_id = store_code(wasm, owner, "injective_converter".to_string()); + let code_id = store_code(wasm, owner, "swap_contract".to_string()); wasm.instantiate( code_id, &InstantiateMsg { @@ -790,7 +840,7 @@ pub fn init_contract_with_fee_recipient_and_get_address( initial_balance: &[Coin], fee_recipient: &SigningAccount, ) -> String { - let code_id = store_code(wasm, owner, "injective_converter".to_string()); + let code_id = store_code(wasm, owner, "swap_contract".to_string()); wasm.instantiate( code_id, &InstantiateMsg { @@ -949,6 +999,8 @@ pub fn init_rich_account(app: &InjectiveTestApp) -> SigningAccount { str_coin("100_000_000", USDT, Decimals::Six), str_coin("100_000_000", USDC, Decimals::Six), str_coin("100_000", INJ, Decimals::Eighteen), + str_coin("100_000", INJ_2, Decimals::Eighteen), + str_coin("100_000_000_000_000_000", NINJA, Decimals::Six), ], ) } @@ -1239,7 +1291,7 @@ mod tests { } #[test] - fn it_scales_integer_values_correctly_for_inj_udst() { + fn it_scales_integer_values_correctly_for_inj_usdt() { let price = "1"; let quantity = "1"; @@ -1259,7 +1311,7 @@ mod tests { } #[test] - fn it_scales_decimal_values_correctly_for_inj_udst() { + fn it_scales_decimal_values_correctly_for_inj_usdt() { let price = "8.782"; let quantity = "1.12"; @@ -1278,7 +1330,7 @@ mod tests { } #[test] - fn it_scales_integer_values_correctly_for_atom_udst() { + fn it_scales_integer_values_correctly_for_atom_usdt() { let price = "1"; let quantity = "1"; @@ -1301,7 +1353,7 @@ mod tests { } #[test] - fn it_scales_decimal_values_correctly_for_atom_udst() { + fn it_scales_decimal_values_correctly_for_atom_usdt() { let price = "1.129"; let quantity = "1.62"; diff --git a/copy_to_devnet_setup.sh b/copy_to_devnet_setup.sh index 38cdad4..2b34097 100755 --- a/copy_to_devnet_setup.sh +++ b/copy_to_devnet_setup.sh @@ -7,7 +7,7 @@ fi COMMIT_HASH=$(git rev-parse --short HEAD) -rm -f ../devnet-setup/wasm-contracts/swap_converter* -cp artifacts/injective_converter$ARCH.wasm ../devnet-setup/wasm-contracts/swap_converter_${COMMIT_HASH}.wasm +rm -f ../devnet-setup/wasm-contracts/swap_contract* +cp artifacts/swap_contract$ARCH.wasm ../devnet-setup/wasm-contracts/swap_contract_${COMMIT_HASH}.wasm -echo "SWAP_CONVERTER_COMMIT_HASH=$COMMIT_HASH" > "../devnet-setup/wasm-contracts/swap_converter.version" \ No newline at end of file +echo "SWAP_CONVERTER_COMMIT_HASH=$COMMIT_HASH" > "../devnet-setup/wasm-contracts/swap_contract.version" \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..f49bdba --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.73.0" +components = [ "rustfmt" ] +profile = "minimal" +targets = [ "wasm32-unknown-unknown" ] diff --git a/rustfmt.toml b/rustfmt.toml index 351fd5f..845635a 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,4 @@ -# stable -newline_style = "unix" -hard_tabs = false -tab_spaces = 4 -max_width = 150 \ No newline at end of file +hard_tabs = false +max_width = 150 +newline_style = "Unix" +tab_spaces = 4