Skip to content

Commit

Permalink
Merge pull request #15 from InjectiveLabs/b/fix-multihop-buys
Browse files Browse the repository at this point in the history
fix/feat: add fix for multi-hop buys and contract migration system
  • Loading branch information
gorgos authored Feb 25, 2024
2 parents 24d2450 + c827e7f commit be2f46d
Show file tree
Hide file tree
Showing 20 changed files with 504 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 26 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 47 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -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

-
5 changes: 3 additions & 2 deletions contracts/swap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
authors = [ "Markus Waas <[email protected]>" ]
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.
Expand Down Expand Up @@ -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" }
Expand Down
2 changes: 2 additions & 0 deletions contracts/swap/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub fn save_config(
fee_recipient,
admin,
};
config.to_owned().validate()?;

CONFIG.save(deps.storage, &config)
}

Expand Down
54 changes: 51 additions & 3 deletions contracts/swap/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +32,7 @@ pub fn instantiate(
) -> Result<Response<InjectiveMsgWrapper>, 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))
Expand Down Expand Up @@ -98,6 +99,53 @@ pub fn reply(
}
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
deps: DepsMut<InjectiveQueryWrapper>,
_env: Env,
_msg: MigrateMsg,
) -> Result<Response, ContractError> {
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<InjectiveQueryWrapper>, env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
Expand Down
5 changes: 4 additions & 1 deletion contracts/swap/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}")]
Expand All @@ -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 {},
}
27 changes: 25 additions & 2 deletions contracts/swap/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -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))
}
Expand Down Expand Up @@ -49,6 +52,26 @@ pub fn dec_scale_factor() -> FPDecimal {
FPDecimal::ONE.scaled(18)
}

type V100Config = Config;
const V100CONFIG: Item<V100Config> = Item::new("config");

pub fn handle_config_migration(
deps: DepsMut<InjectiveQueryWrapper>,
) -> Result<Response, ContractError> {
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::*;
Expand Down
3 changes: 3 additions & 0 deletions contracts/swap/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
16 changes: 7 additions & 9 deletions contracts/swap/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,19 @@ fn estimate_execution_buy_from_target(
fee_percent: FPDecimal,
is_simulation: bool,
) -> StdResult<StepExecutionEstimate> {
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,
)?;
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions contracts/swap/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub const STEP_STATE: Item<CurrentSwapStep> = Item::new("current_step_cache");
pub const SWAP_RESULTS: Item<Vec<SwapResults>> = Item::new("swap_results");
pub const CONFIG: Item<Config> = 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)
Expand Down
Loading

0 comments on commit be2f46d

Please sign in to comment.