-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: review
Are you sure you want to change the base?
Conversation
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) | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
pub const UFOO: &'static str = | ||
"ibc/0CD3A0285E1341859B5E86B6AB7682F023D03E97607CCC1DC95706411D866DF7"; | ||
pub const UBAR: &'static str = | ||
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"; |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
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")) | ||
} |
There was a problem hiding this comment.
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::{ |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
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, | ||
}; |
There was a problem hiding this comment.
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
}
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) | ||
} |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
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(), | ||
); | ||
} |
There was a problem hiding this comment.
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
No description provided.