diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/helpers/assert.rs b/smart-contracts/osmosis/contracts/cl-vault/src/helpers/assert.rs index f5c92932a..6152fc149 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/helpers/assert.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/helpers/assert.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{coin, Addr, Coin, Deps, MessageInfo, Storage}; +use cosmwasm_std::{coin, Addr, Coin, Deps, Storage}; use crate::{ state::{ADMIN_ADDRESS, RANGE_ADMIN}, @@ -26,25 +26,23 @@ pub fn assert_range_admin(storage: &mut dyn Storage, sender: &Addr) -> Result<() /// Returns the Coin of the needed denoms in the order given in denoms pub(crate) fn must_pay_one_or_two( - info: &MessageInfo, + funds: &[Coin], denoms: (String, String), ) -> Result<(Coin, Coin), ContractError> { - if info.funds.len() != 2 && info.funds.len() != 1 { + if funds.len() != 2 && funds.len() != 1 { return Err(ContractError::IncorrectAmountFunds); } - let token0 = info - .funds - .clone() - .into_iter() + let token0 = funds + .iter() .find(|coin| coin.denom == denoms.0) + .cloned() .unwrap_or(coin(0, denoms.0)); - let token1 = info - .funds - .clone() - .into_iter() + let token1 = funds + .iter() .find(|coin| coin.denom == denoms.1) + .cloned() .unwrap_or(coin(0, denoms.1)); Ok((token0, token1)) @@ -76,7 +74,7 @@ pub(crate) fn must_pay_one_or_two_from_balance( #[cfg(test)] mod tests { - use cosmwasm_std::{coin, Addr}; + use cosmwasm_std::coin; use super::*; @@ -84,12 +82,9 @@ mod tests { fn must_pay_one_or_two_works_ordered() { let expected0 = coin(100, "uatom"); let expected1 = coin(200, "uosmo"); - let info = MessageInfo { - sender: Addr::unchecked("sender"), - funds: vec![expected0.clone(), expected1.clone()], - }; + let funds = vec![expected0.clone(), expected1.clone()]; let (token0, token1) = - must_pay_one_or_two(&info, ("uatom".to_string(), "uosmo".to_string())).unwrap(); + must_pay_one_or_two(&funds, ("uatom".to_string(), "uosmo".to_string())).unwrap(); assert_eq!(expected0, token0); assert_eq!(expected1, token1); } @@ -98,12 +93,9 @@ mod tests { fn must_pay_one_or_two_works_unordered() { let expected0 = coin(100, "uatom"); let expected1 = coin(200, "uosmo"); - let info = MessageInfo { - sender: Addr::unchecked("sender"), - funds: vec![expected1.clone(), expected0.clone()], - }; + let funds = vec![expected0.clone(), expected1.clone()]; let (token0, token1) = - must_pay_one_or_two(&info, ("uatom".to_string(), "uosmo".to_string())).unwrap(); + must_pay_one_or_two(&funds, ("uatom".to_string(), "uosmo".to_string())).unwrap(); assert_eq!(expected0, token0); assert_eq!(expected1, token1); } @@ -112,31 +104,22 @@ mod tests { fn must_pay_one_or_two_rejects_three() { let expected0 = coin(100, "uatom"); let expected1 = coin(200, "uosmo"); - let info = MessageInfo { - sender: Addr::unchecked("sender"), - funds: vec![expected1, expected0, coin(200, "uqsr")], - }; + let funds = vec![expected0, expected1, coin(200, "uqsr")]; let _err = - must_pay_one_or_two(&info, ("uatom".to_string(), "uosmo".to_string())).unwrap_err(); + must_pay_one_or_two(&funds, ("uatom".to_string(), "uosmo".to_string())).unwrap_err(); } #[test] fn must_pay_one_or_two_accepts_second_token() { - let info = MessageInfo { - sender: Addr::unchecked("sender"), - funds: vec![coin(200, "uosmo")], - }; - let res = must_pay_one_or_two(&info, ("uatom".to_string(), "uosmo".to_string())).unwrap(); + let funds = vec![coin(200, "uosmo")]; + let res = must_pay_one_or_two(&funds, ("uatom".to_string(), "uosmo".to_string())).unwrap(); assert_eq!((coin(0, "uatom"), coin(200, "uosmo")), res) } #[test] fn must_pay_one_or_two_accepts_first_token() { - let info = MessageInfo { - sender: Addr::unchecked("sender"), - funds: vec![coin(200, "uatom")], - }; - let res = must_pay_one_or_two(&info, ("uatom".to_string(), "uosmo".to_string())).unwrap(); + let funds = vec![coin(200, "uatom")]; + let res = must_pay_one_or_two(&funds, ("uatom".to_string(), "uosmo".to_string())).unwrap(); assert_eq!((coin(200, "uatom"), coin(0, "uosmo")), res) } } diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/helpers/getters.rs b/smart-contracts/osmosis/contracts/cl-vault/src/helpers/getters.rs index e6ba63f4a..adc3d785c 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/helpers/getters.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/helpers/getters.rs @@ -1,5 +1,3 @@ -use crate::math::tick::tick_to_price; -use crate::state::{PoolConfig, RANGE_ADMIN}; use std::str::FromStr; use osmosis_std::shim::Timestamp as OsmoTimestamp; @@ -7,7 +5,12 @@ use osmosis_std::types::osmosis::poolmanager::v1beta1::PoolmanagerQuerier; use osmosis_std::types::osmosis::twap::v1beta1::TwapQuerier; use crate::vault::concentrated_liquidity::get_position; -use crate::{state::POOL_CONFIG, ContractError}; +use crate::{ + helpers::assert::must_pay_one_or_two, + math::tick::tick_to_price, + state::{PoolConfig, POOL_CONFIG, RANGE_ADMIN}, + ContractError, +}; use cosmwasm_std::{ Addr, Coin, Decimal, Decimal256, Deps, DepsMut, Env, Fraction, QuerierWrapper, Storage, Uint128, Uint256, @@ -64,10 +67,24 @@ pub fn get_twap_price( Ok(Decimal::from_str(&twap_price.arithmetic_twap)?) } -/// Calculate the amount of tokens that can be deposited while maintaining the current position ratio in the vault. #[allow(clippy::type_complexity)] pub fn get_depositable_tokens( - deps: DepsMut, + deps: &DepsMut, + funds: &[Coin], + pool_config: &PoolConfig, +) -> Result<((Uint128, Uint128), (Uint128, Uint128)), ContractError> { + let (token0, token1) = must_pay_one_or_two( + funds, + (pool_config.token0.clone(), pool_config.token1.clone()), + )?; + + get_depositable_tokens_impl(deps, token0, token1) +} + +/// Calculate the amount of tokens that can be deposited while maintaining the current position ratio in the vault. +#[allow(clippy::type_complexity)] +fn get_depositable_tokens_impl( + deps: &DepsMut, token0: Coin, token1: Coin, ) -> Result<((Uint128, Uint128), (Uint128, Uint128)), ContractError> { @@ -247,14 +264,84 @@ pub fn get_tokens_provided( #[cfg(test)] mod tests { - use std::collections::HashMap; - use cosmwasm_std::testing::mock_dependencies; + use std::collections::HashMap; + use std::{marker::PhantomData, str::FromStr}; + + use cosmwasm_std::{ + coin, + testing::{MockApi, MockStorage, MOCK_CONTRACT_ADDR}, + Coin, Decimal256, Empty, OwnedDeps, + }; + + use osmosis_std::types::{ + cosmos::base::v1beta1::Coin as OsmoCoin, + osmosis::concentratedliquidity::v1beta1::{ + FullPositionBreakdown, Position as OsmoPosition, + }, + }; + + use crate::{ + state::{Position, POSITION}, + test_helpers::QuasarQuerier, + }; use crate::math::tick::{build_tick_exp_cache, price_to_tick}; use super::*; + fn mock_deps_with_position( + token0: Option, + token1: Option, + ) -> OwnedDeps { + let position_id = 2; + + let mut deps = OwnedDeps { + storage: MockStorage::default(), + api: MockApi::default(), + querier: QuasarQuerier::new( + FullPositionBreakdown { + position: Some(OsmoPosition { + position_id, + address: MOCK_CONTRACT_ADDR.to_string(), + pool_id: 1, + lower_tick: 100, + upper_tick: 1000, + join_time: None, + liquidity: "1000000.2".to_string(), + }), + asset0: token0.map(|c| c.into()), + asset1: token1.map(|c| c.into()), + claimable_spread_rewards: vec![ + OsmoCoin { + denom: "token0".to_string(), + amount: "100".to_string(), + }, + OsmoCoin { + denom: "token1".to_string(), + amount: "100".to_string(), + }, + ], + claimable_incentives: vec![], + forfeited_incentives: vec![], + }, + 500, + ), + custom_query_type: PhantomData, + }; + POSITION + .save( + deps.as_mut().storage, + &Position { + position_id, + join_time: 0, + claim_after: None, + }, + ) + .unwrap(); + deps + } + #[test] fn test_0_to_1_swap() { let mut deps = mock_dependencies(); @@ -371,4 +458,144 @@ mod tests { assert_eq!(swap_amount, result); } } + + #[test] + fn test_position_in_both_asset() { + let token0 = Coin { + denom: "token0".to_string(), + amount: Uint128::new(1_000_000_000u128), + }; + let token1 = Coin { + denom: "token1".to_string(), + amount: Uint128::new(100_000_000_000_000_000_000_000_000_000u128), + }; + + let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); + let mutdeps = deps.as_mut(); + + let result = get_depositable_tokens_impl(&mutdeps, token0, token1).unwrap(); + assert_eq!( + result, + ( + ( + Uint128::zero(), + Uint128::new(100_000_000_000_000_000_000_000_000_000u128) + ), + (Uint128::new(1_000_000_000u128), Uint128::zero()) + ) + ); + } + + #[test] + fn test_position_in_asset1_only() { + let token0 = Coin { + denom: "token0".to_string(), + amount: Uint128::new(50), + }; + let token1 = Coin { + denom: "token1".to_string(), + amount: Uint128::new(100), + }; + + // Osmosis is not using None for missing assets, but Some with amount 0, so we need to mimic that here + let mut deps = mock_deps_with_position( + Some(Coin { + denom: "token0".to_string(), + amount: Uint128::zero(), + }), + Some(token1.clone()), + ); + + let result = get_depositable_tokens_impl(&deps.as_mut(), token0, token1).unwrap(); + assert_eq!( + result, + ( + (Uint128::zero(), Uint128::new(100)), + (Uint128::new(50), Uint128::zero()) + ) + ); + } + + #[test] + fn test_position_in_asset0_only() { + let token0 = Coin { + denom: "token0".to_string(), + amount: Uint128::new(50), + }; + let token1 = Coin { + denom: "token1".to_string(), + amount: Uint128::new(100), + }; + + // Osmosis is not using None for missing assets, but Some with amount 0, so we need to mimic that here + let mut deps = mock_deps_with_position( + Some(token0.clone()), + Some(Coin { + denom: "token1".to_string(), + amount: Uint128::zero(), + }), + ); + + let result = get_depositable_tokens_impl(&deps.as_mut(), token0, token1).unwrap(); + assert_eq!( + result, + ( + (Uint128::new(50), Uint128::zero()), + (Uint128::zero(), Uint128::new(100)) + ) + ); + } + + #[test] + fn test_both_assets_present_token0_limiting() { + let token0 = Coin { + denom: "token0".to_string(), + amount: Uint128::new(50), + }; + let token1 = Coin { + denom: "token1".to_string(), + amount: Uint128::new(100), + }; + + // we use a ratio of 1/2 + let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); + + let result = + get_depositable_tokens_impl(&deps.as_mut(), coin(2000, "token0"), coin(5000, "token1")) + .unwrap(); + assert_eq!( + result, + ( + (Uint128::new(2000), Uint128::new(4000)), + (Uint128::zero(), Uint128::new(1000)) + ) + ); + } + + #[test] + fn test_both_assets_present_token1_limiting() { + let token0 = Coin { + denom: "token0".to_string(), + amount: Uint128::new(50), + }; + let token1 = Coin { + denom: "token1".to_string(), + amount: Uint128::new(100), + }; + + // we use a ratio of 1/2 + let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); + let mutdeps = deps.as_mut(); + + let result = + get_depositable_tokens_impl(&mutdeps, coin(2000, "token0"), coin(3000, "token1")) + .unwrap(); + assert_eq!( + result, + ( + (Uint128::new(1500), Uint128::new(3000)), + (Uint128::new(500), Uint128::zero()) + ) + ); + } } diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/instantiate.rs b/smart-contracts/osmosis/contracts/cl-vault/src/instantiate.rs index 32d5e5b86..86830cda7 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/instantiate.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/instantiate.rs @@ -81,7 +81,7 @@ pub fn handle_instantiate( .into(); // in order to create the initial position, we need some funds to throw in there, these funds should be seen as burned - let (initial0, initial1) = must_pay_one_or_two(&info, (pool.token0, pool.token1))?; + let (initial0, initial1) = must_pay_one_or_two(&info.funds, (pool.token0, pool.token1))?; let create_position_msg = create_position( deps, diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/vault/any_deposit.rs b/smart-contracts/osmosis/contracts/cl-vault/src/vault/any_deposit.rs index 64a743c7f..92ea665e2 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/vault/any_deposit.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/vault/any_deposit.rs @@ -1,30 +1,24 @@ use cosmwasm_std::{ - attr, coin, Addr, Coin, Decimal, DepsMut, Env, MessageInfo, Response, SubMsg, SubMsgResult, - Uint128, Uint256, -}; -use osmosis_std::types::osmosis::{ - poolmanager::v1beta1::MsgSwapExactAmountInResponse, tokenfactory::v1beta1::MsgMint, + attr, coin, Coin, Decimal, DepsMut, Env, MessageInfo, Response, SubMsg, SubMsgResult, Uint128, }; +use osmosis_std::types::osmosis::poolmanager::v1beta1::MsgSwapExactAmountInResponse; use crate::{ - helpers::{ - assert::must_pay_one_or_two, - getters::{ - get_asset0_value, get_depositable_tokens, get_single_sided_deposit_0_to_1_swap_amount, - get_single_sided_deposit_1_to_0_swap_amount, - }, + helpers::getters::{ + get_depositable_tokens, get_single_sided_deposit_0_to_1_swap_amount, + get_single_sided_deposit_1_to_0_swap_amount, }, - query::{query_total_assets, query_total_vault_token_supply}, reply::Replies, - state::{CURRENT_SWAP_ANY_DEPOSIT, POOL_CONFIG, SHARES, VAULT_DENOM}, + state::{CURRENT_SWAP_ANY_DEPOSIT, POOL_CONFIG}, vault::{ concentrated_liquidity::{get_cl_pool_info, get_position}, + exact_deposit::execute_deposit, swap::{calculate_swap_amount, SwapDirection}, }, ContractError, }; -pub fn execute_any_deposit( +pub(crate) fn execute_any_deposit( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -39,27 +33,21 @@ pub fn execute_any_deposit( .position .ok_or(ContractError::MissingPosition {})?; - let (token0, token1) = must_pay_one_or_two( - &info, - (pool_config.token0.clone(), pool_config.token1.clone()), - )?; - // get the amount of funds we can deposit from this ratio let (deposit_amount_in_ratio, swappable_amount): ((Uint128, Uint128), (Uint128, Uint128)) = - get_depositable_tokens(deps.branch(), token0.clone(), token1.clone())?; + get_depositable_tokens(&deps.branch(), &info.funds, &pool_config)?; if swappable_amount.0.is_zero() && swappable_amount.1.is_zero() { - let (mint_msg, user_shares) = - mint_msg_user_shares(deps, &env, &deposit_amount_in_ratio, &recipient)?; - - return Ok(Response::new() - .add_attribute("method", "execute") - .add_attribute("action", "any_deposit") - .add_attribute("amount0", deposit_amount_in_ratio.0) - .add_attribute("amount1", deposit_amount_in_ratio.1) - .add_message(mint_msg) - .add_attribute("mint_shares_amount", user_shares) - .add_attribute("receiver", recipient.as_str())); + return execute_deposit( + &mut deps, + env, + recipient, + deposit_amount_in_ratio, + ( + coin(0u128, pool_config.token0), + coin(0u128, pool_config.token1), + ), + ); } // Swap logic @@ -143,8 +131,7 @@ pub fn handle_any_deposit_swap_reply( let (swap_direction, left_over_amount, recipient, deposit_amount_in_ratio) = CURRENT_SWAP_ANY_DEPOSIT.load(deps.storage)?; - - let pool_config = POOL_CONFIG.load(deps.storage)?; + CURRENT_SWAP_ANY_DEPOSIT.remove(deps.storage); // get post swap balances to create positions with let (balance0, balance1): (Uint128, Uint128) = match swap_direction { @@ -158,7 +145,7 @@ pub fn handle_any_deposit_swap_reply( ), }; - // Create the tuple for minting coins + let pool_config = POOL_CONFIG.load(deps.storage)?; let coins_to_mint_for = ( Coin { denom: pool_config.token0.clone(), @@ -170,83 +157,14 @@ pub fn handle_any_deposit_swap_reply( }, ); - let (mint_msg, user_shares) = mint_msg_user_shares( - deps.branch(), - &env, - &(coins_to_mint_for.0.amount, coins_to_mint_for.1.amount), - &recipient, - )?; - - CURRENT_SWAP_ANY_DEPOSIT.remove(deps.storage); - - Ok(Response::new() - .add_attribute("method", "reply") - .add_attribute("action", "handle_any_deposit_swap") - .add_attribute("amount0", balance0) - .add_attribute("amount1", balance1) - .add_message(mint_msg) - .add_attribute("mint_shares_amount", user_shares) - .add_attribute("receiver", recipient.as_str())) -} - -fn mint_msg_user_shares( - deps: DepsMut, - env: &Env, - deposit_amount_in_ratio: &(Uint128, Uint128), - recipient: &Addr, -) -> Result<(MsgMint, Uint128), ContractError> { - // calculate the amount of shares we can mint for this - let total_assets = query_total_assets(deps.as_ref(), env.clone())?; - let total_assets_value = get_asset0_value( - deps.storage, - &deps.querier, - total_assets.token0.amount, - total_assets.token1.amount, - )?; - - let vault_denom = VAULT_DENOM.load(deps.storage)?; - let total_vault_shares: Uint256 = query_total_vault_token_supply(deps.as_ref())?.total.into(); - - let user_value = get_asset0_value( - deps.storage, - &deps.querier, - deposit_amount_in_ratio.0, - deposit_amount_in_ratio.1, - )?; - - // total_vault_shares.is_zero() should never be zero. This should ideally always enter the else and we are just sanity checking. - let user_shares: Uint128 = if total_vault_shares.is_zero() { - user_value - } else { - total_vault_shares - .checked_mul(user_value.into())? - .checked_div(total_assets_value.into())? - .try_into()? - }; - - // save the shares in the user map - SHARES.update( - deps.storage, - recipient.clone(), - |old| -> Result { - if let Some(existing_user_shares) = old { - Ok(user_shares + existing_user_shares) - } else { - Ok(user_shares) - } - }, - )?; - - // TODO the locking of minted shares is a band-aid for giving out rewards to users, - // once tokenfactory has send hooks, we can remove the lockup and have the users - // own the shares in their balance - // we mint shares to the contract address here, so we can lock those shares for the user later in the same call - // this is blocked by Osmosis v17 update - let mint_msg = MsgMint { - sender: env.clone().contract.address.to_string(), - amount: Some(coin(user_shares.into(), vault_denom).into()), - mint_to_address: env.clone().contract.address.to_string(), - }; - - Ok((mint_msg, user_shares)) + execute_deposit( + &mut deps, + env, + recipient, + (coins_to_mint_for.0.amount, coins_to_mint_for.1.amount), + ( + coin(0u128, pool_config.token0), + coin(0u128, pool_config.token1), + ), + ) } diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/vault/exact_deposit.rs b/smart-contracts/osmosis/contracts/cl-vault/src/vault/exact_deposit.rs index 8378b3e28..1dee1568d 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/vault/exact_deposit.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/vault/exact_deposit.rs @@ -1,8 +1,7 @@ -use cosmwasm_std::{coin, DepsMut, Env, MessageInfo, Response, Uint128, Uint256}; +use cosmwasm_std::{coin, Addr, Coin, DepsMut, Env, MessageInfo, Response, Uint128, Uint256}; use osmosis_std::types::osmosis::tokenfactory::v1beta1::MsgMint; -use crate::helpers::assert::must_pay_one_or_two; use crate::helpers::getters::{get_asset0_value, get_depositable_tokens}; use crate::helpers::msgs::refund_bank_msg; use crate::query::query_total_vault_token_supply; @@ -12,29 +11,49 @@ use crate::{ ContractError, }; -/// Try to deposit as much user funds as we can in the current ratio of the vault and -/// refund the rest to the caller. pub(crate) fn execute_exact_deposit( mut deps: DepsMut, env: Env, info: MessageInfo, recipient: Option, ) -> Result { - // Unwrap recipient or use caller's address let recipient = recipient.map_or(Ok(info.sender.clone()), |x| deps.api.addr_validate(&x))?; - - let pool = POOL_CONFIG.load(deps.storage)?; - let (token0, token1) = must_pay_one_or_two(&info, (pool.token0.clone(), pool.token1.clone()))?; - + let pool_config = POOL_CONFIG.load(deps.storage)?; // get the amount of funds we can deposit from this ratio let (deposit, refund): ((Uint128, Uint128), (Uint128, Uint128)) = - get_depositable_tokens(deps.branch(), token0.clone(), token1.clone())?; + get_depositable_tokens(&deps, &info.funds, &pool_config)?; + execute_deposit( + &mut deps, + env, + recipient, + deposit, + ( + coin(refund.0.into(), pool_config.token0), + coin(refund.1.into(), pool_config.token1), + ), + ) +} + +/// Try to deposit as much user funds as we can in the current ratio of the vault and +/// refund the rest to the caller. +pub(crate) fn execute_deposit( + deps: &mut DepsMut, + env: Env, + recipient: Addr, + deposit: (Uint128, Uint128), + refund: (Coin, Coin), +) -> Result { let vault_denom = VAULT_DENOM.load(deps.storage)?; let total_vault_shares: Uint256 = query_total_vault_token_supply(deps.as_ref())?.total.into(); let user_value = get_asset0_value(deps.storage, &deps.querier, deposit.0, deposit.1)?; - let refund_value = get_asset0_value(deps.storage, &deps.querier, refund.0, refund.1)?; + let refund_value = get_asset0_value( + deps.storage, + &deps.querier, + refund.0.amount, + refund.1.amount, + )?; // calculate the amount of shares we can mint for this let total_assets = query_total_assets(deps.as_ref(), env.clone())?; @@ -86,18 +105,15 @@ pub(crate) fn execute_exact_deposit( let mut resp = Response::new() .add_attribute("method", "execute") - .add_attribute("action", "exact_deposit") + .add_attribute("action", "deposit") .add_attribute("amount0", deposit.0) .add_attribute("amount1", deposit.1) .add_message(mint_msg) .add_attribute("mint_shares_amount", user_shares) .add_attribute("receiver", recipient.as_str()); - if let Some((bank_msg, bank_attr)) = refund_bank_msg( - recipient, - Some(coin(refund.0.u128(), pool.token0)), - Some(coin(refund.1.u128(), pool.token1)), - )? { + if let Some((bank_msg, bank_attr)) = refund_bank_msg(recipient, Some(refund.0), Some(refund.1))? + { resp = resp.add_message(bank_msg).add_attributes(bank_attr); } @@ -106,167 +122,18 @@ pub(crate) fn execute_exact_deposit( #[cfg(test)] mod tests { - use std::{marker::PhantomData, str::FromStr}; + use std::str::FromStr; - use cosmwasm_std::{ - testing::{mock_env, MockApi, MockStorage, MOCK_CONTRACT_ADDR}, - Addr, BankMsg, Coin, Decimal256, Empty, Fraction, OwnedDeps, Uint256, - }; - - use osmosis_std::types::{ - cosmos::base::v1beta1::Coin as OsmoCoin, - osmosis::concentratedliquidity::v1beta1::{ - FullPositionBreakdown, Position as OsmoPosition, - }, - }; + use cosmwasm_std::{testing::mock_env, Addr, BankMsg, Decimal256, Fraction, Uint256}; use crate::{ - helpers::{getters::get_depositable_tokens, msgs::refund_bank_msg}, + helpers::msgs::refund_bank_msg, state::{Position, POSITION}, - test_helpers::{mock_deps_with_querier, QuasarQuerier}, + test_helpers::mock_deps_with_querier, }; use super::*; - #[test] - fn test_position_in_both_asset() { - let token0 = Coin { - denom: "token0".to_string(), - amount: Uint128::new(1_000_000_000u128), - }; - let token1 = Coin { - denom: "token1".to_string(), - amount: Uint128::new(100_000_000_000_000_000_000_000_000_000u128), - }; - - let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); - let mutdeps = deps.as_mut(); - - let result = get_depositable_tokens(mutdeps, token0, token1).unwrap(); - assert_eq!( - result, - ( - ( - Uint128::zero(), - Uint128::new(100_000_000_000_000_000_000_000_000_000u128) - ), - (Uint128::new(1_000_000_000u128), Uint128::zero()) - ) - ); - } - - #[test] - fn test_position_in_asset1_only() { - let token0 = Coin { - denom: "token0".to_string(), - amount: Uint128::new(50), - }; - let token1 = Coin { - denom: "token1".to_string(), - amount: Uint128::new(100), - }; - - // Osmosis is not using None for missing assets, but Some with amount 0, so we need to mimic that here - let mut deps = mock_deps_with_position( - Some(Coin { - denom: "token0".to_string(), - amount: Uint128::zero(), - }), - Some(token1.clone()), - ); - - let result = get_depositable_tokens(deps.as_mut(), token0, token1).unwrap(); - assert_eq!( - result, - ( - (Uint128::zero(), Uint128::new(100)), - (Uint128::new(50), Uint128::zero()) - ) - ); - } - - #[test] - fn test_position_in_asset0_only() { - let token0 = Coin { - denom: "token0".to_string(), - amount: Uint128::new(50), - }; - let token1 = Coin { - denom: "token1".to_string(), - amount: Uint128::new(100), - }; - - // Osmosis is not using None for missing assets, but Some with amount 0, so we need to mimic that here - let mut deps = mock_deps_with_position( - Some(token0.clone()), - Some(Coin { - denom: "token1".to_string(), - amount: Uint128::zero(), - }), - ); - - let result = get_depositable_tokens(deps.as_mut(), token0, token1).unwrap(); - assert_eq!( - result, - ( - (Uint128::new(50), Uint128::zero()), - (Uint128::zero(), Uint128::new(100)) - ) - ); - } - - #[test] - fn test_both_assets_present_token0_limiting() { - let token0 = Coin { - denom: "token0".to_string(), - amount: Uint128::new(50), - }; - let token1 = Coin { - denom: "token1".to_string(), - amount: Uint128::new(100), - }; - - // we use a ratio of 1/2 - let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); - - let result = - get_depositable_tokens(deps.as_mut(), coin(2000, "token0"), coin(5000, "token1")) - .unwrap(); - assert_eq!( - result, - ( - (Uint128::new(2000), Uint128::new(4000)), - (Uint128::zero(), Uint128::new(1000)) - ) - ); - } - - #[test] - fn test_both_assets_present_token1_limiting() { - let token0 = Coin { - denom: "token0".to_string(), - amount: Uint128::new(50), - }; - let token1 = Coin { - denom: "token1".to_string(), - amount: Uint128::new(100), - }; - - // we use a ratio of 1/2 - let mut deps = mock_deps_with_position(Some(token0.clone()), Some(token1.clone())); - let mutdeps = deps.as_mut(); - - let result = - get_depositable_tokens(mutdeps, coin(2000, "token0"), coin(3000, "token1")).unwrap(); - assert_eq!( - result, - ( - (Uint128::new(1500), Uint128::new(3000)), - (Uint128::new(500), Uint128::zero()) - ) - ); - } - #[test] fn execute_exact_deposit_works() { let mut deps = mock_deps_with_querier(&MessageInfo { @@ -289,20 +156,6 @@ mod tests { ) .unwrap(); - // STRATEGIST_REWARDS - // .save(deps.as_mut().storage, &CoinList::new()) - // .unwrap(); - // POOL_CONFIG - // .save( - // deps.as_mut().storage, - // &PoolConfig { - // pool_id: 1, - // token0: "token0".to_string(), - // token1: "token1".to_string(), - // }, - // ) - // .unwrap(); - execute_exact_deposit( deps.as_mut(), env, @@ -396,56 +249,4 @@ mod tests { } ) } - - fn mock_deps_with_position( - token0: Option, - token1: Option, - ) -> OwnedDeps { - let position_id = 2; - - let mut deps = OwnedDeps { - storage: MockStorage::default(), - api: MockApi::default(), - querier: QuasarQuerier::new( - FullPositionBreakdown { - position: Some(OsmoPosition { - position_id, - address: MOCK_CONTRACT_ADDR.to_string(), - pool_id: 1, - lower_tick: 100, - upper_tick: 1000, - join_time: None, - liquidity: "1000000.2".to_string(), - }), - asset0: token0.map(|c| c.into()), - asset1: token1.map(|c| c.into()), - claimable_spread_rewards: vec![ - OsmoCoin { - denom: "token0".to_string(), - amount: "100".to_string(), - }, - OsmoCoin { - denom: "token1".to_string(), - amount: "100".to_string(), - }, - ], - claimable_incentives: vec![], - forfeited_incentives: vec![], - }, - 500, - ), - custom_query_type: PhantomData, - }; - POSITION - .save( - deps.as_mut().storage, - &Position { - position_id, - join_time: 0, - claim_after: None, - }, - ) - .unwrap(); - deps - } }