Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap non vault funds #715

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6298dea
rebase changes to main
ajansari95 Jul 25, 2024
ebdd6e4
Merge branch 'main' into fix/swap-non-vault-funds
magiodev Jul 26, 2024
b8ca7bd
fix swap non vault funds
magiodev Jul 26, 2024
937b0b3
Merge branch 'main' into fix/swap-non-vault-funds-fix
magiodev Jul 29, 2024
9821d79
merge fixes
magiodev Jul 29, 2024
5885ef5
todo reentrancy state
magiodev Jul 29, 2024
fb2f073
move swap struct to swap.rs
magiodev Jul 29, 2024
9aa41c3
twap_window_seconds
magiodev Jul 29, 2024
0dc33d7
wip: get position balance helper; twap accepting pool and assets params
magiodev Jul 29, 2024
574cfdb
wip: wire new functions
magiodev Jul 29, 2024
fc57f9b
early return position ratio if any 0 amount
magiodev Jul 29, 2024
3db34ae
wip: renaming token_in and min_token_out and adjust accordingly
magiodev Jul 29, 2024
560d50f
wip: wrapping helpers
magiodev Jul 29, 2024
eb66c96
minor changes
magiodev Jul 29, 2024
83c950f
Merge branch 'main' into fix/swap-non-vault-funds-fix
magiodev Jul 29, 2024
03085da
post merge fixes
magiodev Jul 29, 2024
2399d30
calculate_token_in_direction helper
magiodev Jul 29, 2024
71f0844
todos
magiodev Jul 29, 2024
07e25f7
Merge branch 'main' into fix/swap-non-vault-funds-fix
magiodev Jul 29, 2024
6632292
optimizations and merge fix
magiodev Jul 30, 2024
862b417
swapDirections and twap outside calculate_swap
magiodev Jul 30, 2024
77abb83
fix test tube vault libmod visibility
magiodev Jul 30, 2024
65a19bb
optimizations
magiodev Jul 30, 2024
9bfd627
minor arg changes
magiodev Jul 30, 2024
57265ce
remove dbgs
magiodev Jul 30, 2024
b1d9afe
fix position id not found
magiodev Jul 30, 2024
ac00bf2
fix modify_range_state clear for reentrancy
magiodev Jul 30, 2024
d713ae7
fix authz any_deposit attribute on test tube case
magiodev Jul 30, 2024
1232eec
apply tokens_provided pattern
magiodev Jul 30, 2024
4762a21
fix swap_non_vault_funds
magiodev Jul 30, 2024
0870652
remove reentrancy error
magiodev Jul 30, 2024
03ae594
remove unnecessary early returns
magiodev Jul 30, 2024
14809b0
cmts and lint
magiodev Jul 30, 2024
07b8edb
autocompound swap_non_vault_funds test tube case
magiodev Jul 31, 2024
67514f2
remove old comments
magiodev Jul 31, 2024
6501a4c
autocompound test fixed, pending approx_eqs
magiodev Jul 31, 2024
e5eb1e2
unit test swap non vault funds
magiodev Jul 31, 2024
e76303b
revert tuple change
magiodev Jul 31, 2024
0f9b6f2
checked math ops on get position balance and adjust parsing
magiodev Jul 31, 2024
c7e0cdb
rename 0 and 1 token convention and implement base and quote nomencla…
magiodev Jul 31, 2024
6209546
remove useless comment
magiodev Jul 31, 2024
9ba705d
todos
magiodev Jul 31, 2024
805d466
schema
magiodev Jul 31, 2024
456c768
fmt
magiodev Jul 31, 2024
30fe09d
remove comments and rename vars
magiodev Jul 31, 2024
4a0516c
autocompound test tube rename variables
magiodev Jul 31, 2024
92b02c9
remove tuple
magiodev Jul 31, 2024
19706c4
rename autocompound missing vars
magiodev Jul 31, 2024
d0e9da5
test tube clippy
magiodev Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions smart-contracts/osmosis/contracts/cl-vault/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ pub fn execute(
) -> Result<Response, ContractError> {
match msg {
cw_vault_multi_standard::VaultStandardExecuteMsg::AnyDeposit {
amount: _,
asset: _,
recipient,
max_slippage,
.. // asset and amount fields are not used in this implementation, they are for CW20 tokens
} => execute_any_deposit(deps, env, info, recipient, max_slippage),
cw_vault_multi_standard::VaultStandardExecuteMsg::ExactDeposit { recipient } => {
execute_exact_deposit(deps, env, info, recipient)
Expand Down Expand Up @@ -137,9 +136,10 @@ pub fn execute(
claim_after,
)?,
),
crate::msg::ExtensionExecuteMsg::SwapNonVaultFunds { swap_operations } => {
execute_swap_non_vault_funds(deps, env, info, swap_operations)
}
crate::msg::ExtensionExecuteMsg::SwapNonVaultFunds {
swap_operations,
twap_window_seconds,
} => execute_swap_non_vault_funds(deps, env, swap_operations, twap_window_seconds),
crate::msg::ExtensionExecuteMsg::CollectRewards {} => {
execute_collect_rewards(deps, env)
}
Expand Down
6 changes: 6 additions & 0 deletions smart-contracts/osmosis/contracts/cl-vault/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub enum ContractError {
#[error("ratio_of_swappable_funds_to_use should be >0 and <=1")]
InvalidRatioOfSwappableFundsToUse,

#[error("Invalid swap direction")]
InvalidSwapDirection,

#[error("Cannot do two swaps at the same time")]
SwapInProgress,

Expand All @@ -48,6 +51,9 @@ pub enum ContractError {
#[error("This message does no accept funds")]
NonPayable {},

#[error("Modify range state already exists")]
ModifyRangeStateAlreadyExists {},

// Add any other custom errors you like here.
// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.

Expand Down
89 changes: 62 additions & 27 deletions smart-contracts/osmosis/contracts/cl-vault/src/helpers/getters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
};
use cosmwasm_std::{
Addr, Coin, Decimal, Decimal256, Deps, DepsMut, Env, Fraction, QuerierWrapper, Storage,
Uint128, Uint256,
Timestamp, Uint128, Uint256,
};

use super::coinlist::CoinList;
Expand All @@ -27,8 +27,7 @@ pub fn get_range_admin(deps: Deps) -> Result<Addr, ContractError> {
pub fn get_asset0_value(
storage: &dyn Storage,
querier: &QuerierWrapper,
token0: Uint128,
token1: Uint128,
tokens_provided: (Uint128, Uint128),
magiodev marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<Uint128, ContractError> {
let pool_config = POOL_CONFIG.load(storage)?;

Expand All @@ -38,26 +37,56 @@ pub fn get_asset0_value(
.spot_price
.parse()?;

let total = token0
.checked_add(token1.multiply_ratio(spot_price.denominator(), spot_price.numerator()))?;
let total = tokens_provided.0.checked_add(
tokens_provided
.1
.multiply_ratio(spot_price.denominator(), spot_price.numerator()),
)?;

Ok(total)
}

pub fn get_twap_price(
pub fn get_position_balance(
storage: &dyn Storage,
querier: &QuerierWrapper,
env: &Env,
) -> Result<(f64, f64), ContractError> {
let position = get_position(storage, querier)?;
let asset0_amount = Uint128::from_str(&position.clone().asset0.unwrap_or_default().amount)?;
let asset1_amount = Uint128::from_str(&position.clone().asset1.unwrap_or_default().amount)?;

// Handle cases where either asset amount is zero
if asset0_amount.is_zero() && asset1_amount.is_zero() {
magiodev marked this conversation as resolved.
Show resolved Hide resolved
return Ok((0.0, 0.0));
} else if asset0_amount.is_zero() {
return Ok((0.0, 1.0));
} else if asset1_amount.is_zero() {
return Ok((1.0, 0.0));
}

// Get the total amount of the vault's position in asset0 denom
let asset_0_value = get_asset0_value(storage, querier, (asset0_amount, asset1_amount))?;

// Calculate the ratio of the vault's position in asset0 and asset1
let asset_0_ratio = asset0_amount.u128() as f64 / asset_0_value.u128() as f64;
let asset_1_ratio = asset1_amount.u128() as f64 / asset_0_value.u128() as f64;

Ok((asset_0_ratio, asset_1_ratio))
}

pub fn get_twap_price(
querier: &QuerierWrapper,
block_time: Timestamp,
twap_window_seconds: u64,
pool_id: u64,
token0_denom: String,
token1_denom: String,
) -> Result<Decimal, ContractError> {
let pool_config = POOL_CONFIG.load(storage)?;

let twap_querier = TwapQuerier::new(querier);
let start_of_window = env.block.time.minus_seconds(twap_window_seconds);
let start_of_window = block_time.minus_seconds(twap_window_seconds);
let twap_price = twap_querier.arithmetic_twap_to_now(
pool_config.pool_id,
pool_config.token0,
pool_config.token1,
pool_id,
token0_denom,
token1_denom,
Some(OsmoTimestamp {
seconds: start_of_window.seconds().try_into()?,
nanos: 0,
Expand Down Expand Up @@ -101,16 +130,15 @@ pub fn get_depositable_tokens(
position.asset0.unwrap_or_default().try_into()?,
position.asset1.unwrap_or_default().try_into()?,
);
compute_deposit_and_refund_tokens(&assets, token0, token1)
compute_deposit_and_refund_tokens(&assets, (token0.amount, token1.amount))
}

fn compute_deposit_and_refund_tokens(
assets: &PoolAssets,
provided_base: Coin,
provided_quote: Coin,
tokens_provided: (Uint128, Uint128),
) -> Result<DepositInfo, ContractError> {
let provided_base_amount: Uint256 = provided_base.amount.into();
let provided_quote_amount: Uint256 = provided_quote.amount.into();
let provided_base_amount: Uint256 = tokens_provided.0.into();
let provided_quote_amount: Uint256 = tokens_provided.1.into();

let base_deposit = if assets.quote.amount.is_zero() {
provided_base_amount
Expand Down Expand Up @@ -393,7 +421,8 @@ mod tests {
base: token0.clone(),
quote: token1.clone(),
};
let result = compute_deposit_and_refund_tokens(&assets, token0, token1).unwrap();
let result =
compute_deposit_and_refund_tokens(&assets, (token0.amount, token1.amount)).unwrap();
assert_eq!(
result,
DepositInfo {
Expand Down Expand Up @@ -423,7 +452,8 @@ mod tests {
},
quote: token1.clone(),
};
let result = compute_deposit_and_refund_tokens(&assets, token0, token1).unwrap();
let result =
compute_deposit_and_refund_tokens(&assets, (token0.amount, token1.amount)).unwrap();
assert_eq!(
result,
DepositInfo {
Expand Down Expand Up @@ -453,7 +483,8 @@ mod tests {
},
base: token0.clone(),
};
let result = compute_deposit_and_refund_tokens(&assets, token0, token1).unwrap();
let result =
compute_deposit_and_refund_tokens(&assets, (token0.amount, token1.amount)).unwrap();
assert_eq!(
result,
DepositInfo {
Expand All @@ -480,9 +511,11 @@ mod tests {
base: token0.clone(),
quote: token1.clone(),
};
let result =
compute_deposit_and_refund_tokens(&assets, coin(2000, "token0"), coin(5000, "token1"))
.unwrap();
let result = compute_deposit_and_refund_tokens(
&assets,
(coin(2000, "token0").amount, coin(5000, "token1").amount),
)
.unwrap();
assert_eq!(
result,
DepositInfo {
Expand All @@ -509,9 +542,11 @@ mod tests {
base: token0.clone(),
quote: token1.clone(),
};
let result =
compute_deposit_and_refund_tokens(&assets, coin(2000, "token0"), coin(3000, "token1"))
.unwrap();
let result = compute_deposit_and_refund_tokens(
&assets,
(coin(2000, "token0").amount, coin(3000, "token1").amount),
)
.unwrap();
assert_eq!(
result,
DepositInfo {
Expand Down
54 changes: 23 additions & 31 deletions smart-contracts/osmosis/contracts/cl-vault/src/helpers/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use cosmwasm_std::{
WasmMsg,
};
use dex_router_osmosis::msg::ExecuteMsg as DexRouterExecuteMsg;
use osmosis_std::types::{
cosmos::base::v1beta1::Coin as OsmoCoin,
osmosis::{
use osmosis_std::{
cosmwasm_to_proto_coins,
types::osmosis::{
concentratedliquidity::v1beta1::{MsgCollectIncentives, MsgCollectSpreadRewards},
poolmanager::v1beta1::SwapAmountInRoute,
},
Expand Down Expand Up @@ -58,52 +58,49 @@ pub fn refund_bank_msg(

/// swap will always swap over the CL pool. In the future we may expand the
/// feature such that it chooses best swaps over all routes
pub fn swap_msg(deps: &DepsMut, env: &Env, params: SwapParams) -> Result<CosmosMsg, ContractError> {
pub fn swap_msg(
deps: &DepsMut,
contract_address: Addr,
params: SwapParams,
) -> Result<CosmosMsg, ContractError> {
// let pool_config = POOL_CONFIG.load(deps.storage)?;
let dex_router = DEX_ROUTER.may_load(deps.storage)?;

// we will only ever have a route length of one, this will likely change once we start selecting different routes
let pool_route = SwapAmountInRoute {
pool_id: params.pool_id,
token_out_denom: params.token_out_denom.to_string(),
token_out_denom: params.min_token_out.denom.to_string(),
};

// if we don't have a dex_router, we will always swap over the osmosis pool
if dex_router.is_none() {
return Ok(osmosis_swap_exact_amount_in_msg(
env,
contract_address,
pool_route,
params.token_in_amount,
&params.token_in_denom.to_string(),
params.token_out_min_amount,
params.token_in,
params.min_token_out.amount,
));
}

// we know we have a dex_router, so we can unwrap it and execute the swap
cw_dex_execute_swap_operations_msg(
dex_router.clone().unwrap(),
params.forced_swap_route,
params.token_in_denom.to_string(),
params.token_in_amount,
params.token_out_denom.to_string(),
params.token_out_min_amount,
params.token_in,
params.min_token_out,
)
}

fn osmosis_swap_exact_amount_in_msg(
env: &Env,
contract_address: Addr,
pool_route: SwapAmountInRoute,
token_in_amount: Uint128,
token_in_denom: &String,
token_in: Coin,
token_out_min_amount: Uint128,
) -> CosmosMsg {
osmosis_std::types::osmosis::poolmanager::v1beta1::MsgSwapExactAmountIn {
sender: env.contract.address.to_string(),
sender: contract_address.to_string(),
routes: vec![pool_route],
token_in: Some(OsmoCoin {
denom: token_in_denom.to_string(),
amount: token_in_amount.to_string(),
}),
token_in: cosmwasm_to_proto_coins([token_in]).first().cloned(),
token_out_min_amount: token_out_min_amount.to_string(),
}
.into()
Expand All @@ -112,22 +109,17 @@ fn osmosis_swap_exact_amount_in_msg(
fn cw_dex_execute_swap_operations_msg(
dex_router_address: Addr,
path: Option<Vec<SwapAmountInRoute>>,
token_in_denom: String,
token_in_amount: Uint128,
token_out_denom: String,
token_out_min_amount: Uint128,
token_in: Coin,
min_token_out: Coin,
) -> Result<CosmosMsg, ContractError> {
let swap_msg: CosmosMsg = WasmMsg::Execute {
contract_addr: dex_router_address.to_string(),
msg: to_json_binary(&DexRouterExecuteMsg::Swap {
path,
out_denom: token_out_denom,
minimum_receive: Some(token_out_min_amount),
out_denom: min_token_out.denom,
minimum_receive: Some(min_token_out.amount),
})?,
funds: vec![Coin {
denom: token_in_denom,
amount: token_in_amount,
}],
funds: vec![token_in],
}
.into();

Expand Down
6 changes: 4 additions & 2 deletions smart-contracts/osmosis/contracts/cl-vault/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ pub fn handle_instantiate_create_position_reply(
let asset_value = get_asset0_value(
deps.storage,
&deps.querier,
assets[0].amount + free_asset0.amount,
assets[1].amount + free_asset1.amount,
(
assets[0].amount + free_asset0.amount,
assets[1].amount + free_asset1.amount,
),
)?;

let vault_denom = VAULT_DENOM.load(deps.storage)?;
Expand Down
2 changes: 1 addition & 1 deletion smart-contracts/osmosis/contracts/cl-vault/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub mod msg;
pub mod query;
mod reply;
pub mod state;
mod vault;
pub mod vault;

pub use crate::error::ContractError;

Expand Down
20 changes: 8 additions & 12 deletions smart-contracts/osmosis/contracts/cl-vault/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use crate::query::{
AssetsBalanceResponse, PoolResponse, PositionResponse, RangeAdminResponse,
UserSharesBalanceResponse, VerifyTickCacheResponse,
};
use crate::state::{Metadata, VaultConfig};
use crate::{
state::{Metadata, VaultConfig},
vault::swap::SwapOperation,
};

/// Extension execute messages for an apollo autocompounding vault
#[cw_serde]
Expand All @@ -29,7 +32,10 @@ pub enum ExtensionExecuteMsg {
/// MigrationStep
MigrationStep { amount_of_users: Uint128 },
/// SwapNonVaultFunds
SwapNonVaultFunds { swap_operations: Vec<SwapOperation> },
SwapNonVaultFunds {
swap_operations: Vec<SwapOperation>,
twap_window_seconds: Option<u64>,
},
}

/// Extension messages for Authz. This interface basically reexports certain vault functionality
Expand Down Expand Up @@ -96,16 +102,6 @@ pub struct MergePositionMsg {
pub position_ids: Vec<u64>,
}

// struct used by swap.rs on swap non vault funds
#[cw_serde]
pub struct SwapOperation {
pub token_in_denom: String,
pub pool_id_0: u64, // the osmosis pool_id as mandatory to have at least the chance to swap on CL pools
pub pool_id_1: u64, // the osmosis pool_id as mandatory to have at least the chance to swap on CL pools
pub forced_swap_route_token_0: Option<Vec<SwapAmountInRoute>>,
pub forced_swap_route_token_1: Option<Vec<SwapAmountInRoute>>,
}

/// Extension query messages for an apollo autocompounding vault
#[cw_serde]
pub enum ExtensionQueryMsg {
Expand Down
Loading
Loading