diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/test_helpers.rs b/smart-contracts/osmosis/contracts/cl-vault/src/test_helpers.rs index e92175556..fca9fc6fa 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/test_helpers.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/test_helpers.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; use cosmwasm_std::testing::{mock_info, BankQuerier, MockApi, MockStorage, MOCK_CONTRACT_ADDR}; use cosmwasm_std::{ coin, from_json, to_json_binary, Addr, BankQuery, Binary, Coin, - ContractResult as CwContractResult, Decimal, DepsMut, Empty, Env, MessageInfo, OwnedDeps, - Querier, QuerierResult, QueryRequest, + ContractResult as CwContractResult, Decimal, DepsMut, Empty, Env, OwnedDeps, Querier, + QuerierResult, QueryRequest, }; use osmosis_std::types::cosmos::bank::v1beta1::{QuerySupplyOfRequest, QuerySupplyOfResponse}; use osmosis_std::types::osmosis::concentratedliquidity::v1beta1::Pool; @@ -23,10 +23,7 @@ use osmosis_std::types::{ use crate::contract::instantiate; use crate::math::tick::tick_to_price; use crate::msg::InstantiateMsg; -use crate::state::{ - PoolConfig, Position, VaultConfig, POOL_CONFIG, POSITION, RANGE_ADMIN, VAULT_CONFIG, - VAULT_DENOM, -}; +use crate::state::{Position, VaultConfig, POSITION, VAULT_DENOM}; pub const POOL_ID: u64 = 1; pub const POSITION_ID: u64 = 101; @@ -168,10 +165,12 @@ impl Querier for QuasarQuerier { } pub fn mock_deps_with_querier_with_balance( - info: &MessageInfo, + position_base_amount: u128, + position_quote_amount: u128, + current_tick: i64, balances: &[(&str, &[Coin])], ) -> OwnedDeps { - let mut deps = OwnedDeps { + OwnedDeps { storage: MockStorage::default(), api: MockApi::default(), querier: QuasarQuerier::new_with_balances( @@ -187,11 +186,11 @@ pub fn mock_deps_with_querier_with_balance( }), asset0: Some(OsmoCoin { denom: BASE_DENOM.to_string(), - amount: "1000000".to_string(), + amount: position_base_amount.to_string(), }), asset1: Some(OsmoCoin { denom: QUOTE_DENOM.to_string(), - amount: "1000000".to_string(), + amount: position_quote_amount.to_string(), }), claimable_spread_rewards: vec![ OsmoCoin { @@ -206,50 +205,11 @@ pub fn mock_deps_with_querier_with_balance( claimable_incentives: vec![], forfeited_incentives: vec![], }, - 0, + current_tick, balances, ), custom_query_type: PhantomData, - }; - - let storage = &mut deps.storage; - - RANGE_ADMIN.save(storage, &info.sender).unwrap(); - POOL_CONFIG - .save( - storage, - &PoolConfig { - pool_id: POOL_ID, - token0: BASE_DENOM.to_string(), - token1: QUOTE_DENOM.to_string(), - }, - ) - .unwrap(); - VAULT_CONFIG - .save( - storage, - &VaultConfig { - performance_fee: Decimal::zero(), - treasury: Addr::unchecked("treasure"), - swap_max_slippage: Decimal::from_ratio(1u128, 20u128), - dex_router: Addr::unchecked("dex_router"), - swap_admin: Addr::unchecked("swap_admin"), - twap_window_seconds: 24u64, - }, - ) - .unwrap(); - POSITION - .save( - storage, - &crate::state::Position { - position_id: POSITION_ID, - join_time: 0, - claim_after: None, - }, - ) - .unwrap(); - - deps + } } pub fn mock_deps_with_querier() -> OwnedDeps { diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/vault/range.rs b/smart-contracts/osmosis/contracts/cl-vault/src/vault/range.rs index 0d4fb7aa6..d816ac1a5 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/vault/range.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/vault/range.rs @@ -373,12 +373,15 @@ mod tests { let env = mock_env(); let mut deps = mock_deps_with_querier_with_balance( - &info, + 100_000, + 100_000, + 0, &[( MOCK_CONTRACT_ADDR, &[coin(11000, BASE_DENOM), coin(11234, QUOTE_DENOM)], )], ); + instantiate_contract(deps.as_mut(), env.clone(), info.sender.as_str()); MODIFY_RANGE_STATE .save( diff --git a/smart-contracts/osmosis/contracts/cl-vault/src/vault/withdraw.rs b/smart-contracts/osmosis/contracts/cl-vault/src/vault/withdraw.rs index ada3a7762..cefab1a9b 100644 --- a/smart-contracts/osmosis/contracts/cl-vault/src/vault/withdraw.rs +++ b/smart-contracts/osmosis/contracts/cl-vault/src/vault/withdraw.rs @@ -161,7 +161,9 @@ fn withdraw_msg( mod tests { use crate::{ state::PoolConfig, - test_helpers::{mock_deps_with_querier_with_balance, BASE_DENOM, QUOTE_DENOM}, + test_helpers::{ + instantiate_contract, mock_deps_with_querier_with_balance, BASE_DENOM, QUOTE_DENOM, + }, }; use cosmwasm_std::{ testing::{mock_dependencies, mock_env, mock_info, MOCK_CONTRACT_ADDR}, @@ -174,17 +176,17 @@ mod tests { fn execute_withdraw_works_no_rewards() { let info = mock_info("bolice", &[]); let mut deps = mock_deps_with_querier_with_balance( - &info, + 100_000, + 100_000, + 0, &[( MOCK_CONTRACT_ADDR, &[coin(2000, BASE_DENOM), coin(3000, QUOTE_DENOM)], )], ); let env = mock_env(); + instantiate_contract(deps.as_mut(), env.clone(), info.sender.as_str()); - VAULT_DENOM - .save(deps.as_mut().storage, &"share_token".to_string()) - .unwrap(); SHARES .save( deps.as_mut().storage, diff --git a/smart-contracts/osmosis/packages/quasar-types/src/pool_pair.rs b/smart-contracts/osmosis/packages/quasar-types/src/pool_pair.rs index b6af3a8c2..aceb43cc9 100644 --- a/smart-contracts/osmosis/packages/quasar-types/src/pool_pair.rs +++ b/smart-contracts/osmosis/packages/quasar-types/src/pool_pair.rs @@ -1,6 +1,26 @@ -use cosmwasm_std::Coin; +use cosmwasm_std::{coin, Coin, OverflowError}; +use thiserror::Error; -#[derive(Debug)] +#[derive(Error, Debug, PartialEq)] +pub enum PoolPairError { + #[error("{0}")] + Overflow(#[from] OverflowError), + + #[error("Inconsistent denoms, received: {given}, expected: {expected}")] + InconsistentDenoms { given: String, expected: String }, +} + +fn assert_denom(expected: &str, given: &str) -> Result<(), PoolPairError> { + if expected != given { + return Err(PoolPairError::InconsistentDenoms { + given: given.to_string(), + expected: expected.to_string(), + }); + } + Ok(()) +} + +#[cosmwasm_schema::cw_serde] pub struct PoolPair { pub base: S, pub quote: T, @@ -12,6 +32,27 @@ impl PoolPair { } } +impl PoolPair { + pub fn checked_sub( + &self, + other: &PoolPair, + ) -> Result, PoolPairError> { + assert_denom(&self.base.denom, &other.base.denom)?; + assert_denom(&self.quote.denom, &other.quote.denom)?; + + Ok(PoolPair::new( + coin( + self.base.amount.checked_sub(other.base.amount)?.into(), + self.base.denom.clone(), + ), + coin( + self.quote.amount.checked_sub(other.quote.amount)?.into(), + self.quote.denom.clone(), + ), + )) + } +} + pub trait Contains { fn contains(&self, value: T) -> bool; } @@ -32,7 +73,7 @@ impl Contains<&str> for PoolPair { #[cfg(test)] mod tests { use super::*; - use cosmwasm_std::coin; + use cosmwasm_std::{coin, OverflowOperation::Sub}; #[test] fn test_string_pair() { @@ -63,4 +104,56 @@ mod tests { assert!(!pair.contains(&"other".to_string())); assert!(!pair.contains("other")); } + + #[test] + fn test_coin_pair_sub() { + let base = coin(123u128, "base"); + let quote = coin(456u128, "quote"); + let pair = PoolPair::new(base.clone(), quote.clone()); + let other_base = coin(234u128, "base"); + let other_quote = coin(678u128, "quote"); + let other = PoolPair::new(other_base.clone(), other_quote.clone()); + + let result = other.checked_sub(&pair).unwrap(); + assert_eq!(result.base.amount.u128(), 111); + assert_eq!(result.quote.amount.u128(), 222); + + let err = pair.checked_sub(&other).unwrap_err(); + assert_eq!( + err, + PoolPairError::Overflow(OverflowError { + operation: Sub, + operand1: base.amount.to_string(), + operand2: other_base.amount.to_string(), + }) + ); + } + + #[test] + fn test_coin_pair_sub_denom_check() { + let base = coin(123u128, "base"); + let quote = coin(456u128, "quote"); + let pair = PoolPair::new(base.clone(), quote.clone()); + let invalid_quote = coin(345u128, "invalid_quote"); + let invalid = PoolPair::new(base.clone(), invalid_quote.clone()); + let err = pair.checked_sub(&invalid).unwrap_err(); + assert_eq!( + err, + PoolPairError::InconsistentDenoms { + given: invalid_quote.denom, + expected: quote.denom.clone(), + } + ); + + let invalid_base = coin(12u128, "invalid_base"); + let invalid = PoolPair::new(invalid_base.clone(), quote.clone()); + let err = pair.checked_sub(&invalid).unwrap_err(); + assert_eq!( + err, + PoolPairError::InconsistentDenoms { + given: invalid_base.denom, + expected: base.denom, + } + ); + } }