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

full feature review #1

Open
wants to merge 16 commits into
base: review
Choose a base branch
from
Open

full feature review #1

wants to merge 16 commits into from

Conversation

czarcas7ic
Copy link
Member

No description provided.

Comment on lines +314 to +330
fn get_next_sqrt_price_from_amount0_in_round_up(
liquidity: Decimal256,
sqrt_price_current: Decimal256,
token_in: Decimal256,
) -> Decimal256 {
let numerator = liquidity * sqrt_price_current;
let denominator = liquidity + (token_in * sqrt_price_current);
numerator / denominator
}

fn get_next_sqrt_price_from_amount1_in_round_down(
liquidity: Decimal256,
sqrt_price_current: Decimal256,
token_in: Decimal256,
) -> Decimal256 {
sqrt_price_current + (token_in / liquidity)
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer: I don't know how to properly round Uint256 to mirror the logic in the osmosis codebase, so I left rounding logic off of these methods

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed that it's probably fine for v1 version of the contract. v2 can use similar tricks to uniswap math if ever needed

src/execute.rs Outdated
return sqrt_price * sqrt_price;
}

// fn iterative_naive_approach_new(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewer: This was an initial attempt at iterating to get the perfect ratio, but after discussing with Dev I scrapped this for now

Comment on lines +17 to +20
pub const UFOO: &'static str =
"ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7";
pub const UBAR: &'static str =
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These denoms are used since they are authorized quote assets so I didn't need to fight with adding ufoo and ubar to the params. I did not use uosmo because we pay tx fees with uosmo, so it makes it simpler to calculate how much was actually used in the swap and position creation.

use prost::Message;

#[test]
fn test_single_sided_swap_and_join_amt_0_in() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests do a good job to show how close we get with this naive approach. They aren't actually checking any inequalities, but print the resulting values so you can see how much is left over after all is done.

pub const MIN_INITIALIZED_TICK: i64 = -108000000;
pub const MAX_TICK: i128 = 342000000;

// The methods in this file are copied from the Quasar cl vault contract.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole file was taken from Quasar's vault contract. That's not to say it shouldn't be reviewed, but wanted to highlight this.

Comment on lines +21 to +34
pub fn instantiate(
deps: DepsMut,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

let owner = deps.api.addr_validate(&msg.owner)?;
let state = Config { owner };

CONFIG.save(deps.storage, &state)?;
Ok(Response::new().add_attribute("method", "instantiate"))
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently contract owner doesn't have any special powers. I do think we should add the min tick, max tick, min spot price, max spot price constants as store values that can be changed by the contract owner, but that can be in the next iteration.

@@ -0,0 +1,114 @@
use cosmwasm_std::{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these errors can be removed, I just started with a base one and worked off of it.

src/execute.rs Outdated
let original = pool.current_sqrt_price.to_string();
let parts: Vec<&str> = original.split('.').collect();
let integer_part = parts[0];
let fractional_part = &parts[1][0..18]; // Take only first 18 digits after the decimal point
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make sure that we handle the min spot price lowering where it is allowed to have a spot price lower than 10^-18

I think it's fine to just fail in such a case since this is primarily for vaults. If they have a future need to support a pair with low spot price, we can refactor and upgrade the contract.

src/execute.rs Outdated
let fractional_part = &parts[1][0..18]; // Take only first 18 digits after the decimal point
let rounded = format!("{}.{}", integer_part, fractional_part);

let pool_cur_sqrt_price = Decimal256::from_str(&rounded).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we check to make sure it is not zero and fail if so to handle my comment above

Comment on lines +62 to +97
if token_provided.denom == pool.token0.clone() {
if pool.current_tick > upper_tick {
// If the current tick is greater than the upper tick, we will swap the entire amount of token0 for token1
token_provided_swap_amount = token_provided.amount;
} else {
// Otherwise, we utilize the ratio of assets calculated above to determine how much of token0 to swap for token1
let token_provided_dec =
Decimal256::from_str(token_provided.amount.to_string().as_str())?;
token_provided_swap_amount = token_provided_dec
.checked_mul(asset_1_ratio.checked_add(spread_factor)?)?
.to_uint_floor()
.try_into()?;
}
token_out_denom = pool.token1.clone();
token_in = Coin {
denom: pool.token0.clone(),
amount: token_provided_swap_amount,
};
} else if token_provided.denom == pool.token1.clone() {
if pool.current_tick < lower_tick {
// If the current tick is less than the lower tick, we will swap the entire amount of token1 for token0
token_provided_swap_amount = token_provided.amount;
} else {
// Otherwise, we utilize the ratio of assets calculated above to determine how much of token1 to swap for token0
let token_provided_dec =
Decimal256::from_str(token_provided.amount.to_string().as_str())?;
token_provided_swap_amount = token_provided_dec
.checked_mul(asset_0_ratio.checked_add(spread_factor)?)?
.to_uint_floor()
.try_into()?;
}
token_out_denom = pool.token0.clone();
token_in = Coin {
denom: pool.token1.clone(),
amount: token_provided_swap_amount,
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we could probably reduce code duplication here by extracting a helper. Something like

pub fn helper(all_consumedl: bool, asset_ratio: type) -> ...
        if all_consumed{
          
            token_provided_swap_amount = token_provided.amount;
        } else {
for token0
            let token_provided_dec =
                Decimal256::from_str(token_provided.amount.to_string().as_str())?;
            token_provided_swap_amount = token_provided_dec
                .checked_mul(asset_ratio.checked_add(spread_factor)?)?
                .to_uint_floor()
                .try_into()?;
        }
        token_out_denom = pool.token0.clone();
        token_in = Coin {
            denom: pool.token1.clone(),
            amount: token_provided_swap_amount,
        };

        // return
}

Comment on lines +314 to +330
fn get_next_sqrt_price_from_amount0_in_round_up(
liquidity: Decimal256,
sqrt_price_current: Decimal256,
token_in: Decimal256,
) -> Decimal256 {
let numerator = liquidity * sqrt_price_current;
let denominator = liquidity + (token_in * sqrt_price_current);
numerator / denominator
}

fn get_next_sqrt_price_from_amount1_in_round_down(
liquidity: Decimal256,
sqrt_price_current: Decimal256,
token_in: Decimal256,
) -> Decimal256 {
sqrt_price_current + (token_in / liquidity)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed that it's probably fine for v1 version of the contract. v2 can use similar tricks to uniswap math if ever needed

Ok(delta_y)
}

pub fn calc_asset_ratio_from_ticks(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: comment would be helpful

}
}

fn iterative_naive_approach(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: instead of checking "if token_0_in" twice, suggest doing that once and extracting the common logic into a separate helper. That would help with readability

src/execute.rs Outdated
Comment on lines 396 to 408
if token_0_in {
sqrt_price_next = get_next_sqrt_price_from_amount0_in_round_up(
Decimal256::from_str(pool.current_tick_liquidity.as_str()).unwrap(),
pool_cur_sqrt_price,
Decimal256::from_str(token_to_swap.amount.to_string().as_str()).unwrap(),
);
} else {
sqrt_price_next = get_next_sqrt_price_from_amount1_in_round_down(
Decimal256::from_str(pool.current_tick_liquidity.as_str()).unwrap(),
pool_cur_sqrt_price,
Decimal256::from_str(token_to_swap.amount.to_string().as_str()).unwrap(),
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there are multiple initialized ticks over the range in the direction of the swap?

Then, we use the current tick liquidity to apply to them all but each bucket has different liquidity value.

I think we must use a liquidity net in direction query to correctly calculate the next sqrt price here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants