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

osmo gamm lper #72

Merged
merged 36 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
dc70f20
init osmosis lper
bekauz Oct 7, 2024
ade7829
clippy
bekauz Oct 7, 2024
8d231a0
wip: osmosis lper integration into valence services; testing suite init
bekauz Oct 8, 2024
d79691e
update optimize.sh
bekauz Oct 8, 2024
f3fcf86
adding osmosis-utils package
bekauz Oct 9, 2024
aa5fa02
wip: osmo uploading contracts debugging
bekauz Oct 9, 2024
a0c35c5
fix unit test setup
bekauz Oct 9, 2024
0fcba94
removing duplicate pool_id field from configs
bekauz Oct 9, 2024
5aaa5f8
providing two sided liquidity
bekauz Oct 9, 2024
a9af62a
extend test suite
bekauz Oct 10, 2024
9b5f2f5
single sided liquidity provision
bekauz Oct 10, 2024
3ccbde2
add OsmosisPoolType
bekauz Oct 10, 2024
2c01e2b
move osmo message related utils to osmosis-utils package
bekauz Oct 10, 2024
30b85be
add pool_types mod
bekauz Oct 11, 2024
9b611a4
concentrated liquidity mod
bekauz Oct 11, 2024
f87cfda
add a shift cl pool helper
bekauz Oct 11, 2024
352753e
cosmwasm pool test setup
bekauz Oct 14, 2024
abaabf9
wip: transmuter
bekauz Oct 14, 2024
6b12943
remove cl & cw osmo lper logic
bekauz Oct 14, 2024
5cc6a72
rename to osmosis-gamm-lper
bekauz Oct 14, 2024
7b7c8b1
cleanup; add lp token transfer from input -> output acc
bekauz Oct 14, 2024
a12327c
add post-lp transfer to single side lp
bekauz Oct 14, 2024
000958d
cleanup osmo gamm lper
bekauz Oct 15, 2024
5f35aaf
add spot price range validation & tests
bekauz Oct 15, 2024
f983b96
add pool denom validation
bekauz Oct 15, 2024
60c6138
readme
bekauz Oct 15, 2024
27e360b
merge main
bekauz Oct 15, 2024
6e06a00
adjust cargo.toml
bekauz Oct 15, 2024
b39e838
Merge branch 'main' into benskey/osmo-gamm-lper
bekauz Oct 15, 2024
53e923c
rearrange utils
bekauz Oct 15, 2024
febc86f
Merge branch 'benskey/osmo-gamm-lper' of github.com:timewave-computer…
bekauz Oct 15, 2024
8ca28d0
remove valence_service_integration & balancer mods; toml updates
bekauz Oct 15, 2024
eed35fe
rename ActionMsgs; schemagen
bekauz Oct 18, 2024
044b0a9
ci
bekauz Oct 18, 2024
8a56293
Merge branch 'main' into benskey/osmo-gamm-lper
bekauz Oct 18, 2024
8c9f7c8
merge
bekauz Oct 18, 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
239 changes: 202 additions & 37 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ overflow-checks = true

[workspace.dependencies]
anyhow = "1.0.86"
cosmwasm-std = { version = "2.1.3", features = ["cosmwasm_1_4"] }
cosmwasm-std = { version = "2.1.3" }
cosmwasm-schema = "2.1.3"
cw-denom = { package = "cw-denom", git = "https://github.com/DA0-DA0/dao-contracts", branch = "cw-std-2" }
cw-ownable = "2.0.0"
Expand All @@ -49,6 +49,7 @@ serde = { version = "1.0.207", default-features = false, features = [
serde_json = "1.0.125"
sha2 = "0.10.8"
thiserror = "1.0.63"
osmosis-std = "0.26.0"

# our contracts
valence-authorization = { path = "contracts/authorization", features = ["library"] }
Expand All @@ -62,10 +63,12 @@ valence-astroport-lper = { path = "contracts/services/astroport-lper",
valence-forwarder-service = { path = "contracts/services/forwarder", features = ["library"] }
valence-astroport-withdrawer = { path = "contracts/services/astroport-withdrawer", features = ["library"] }
valence-reverse-splitter-service = { path = "contracts/services/reverse-splitter", features = ["library"] }
valence-osmosis-gamm-lper = { path = "contracts/services/osmosis-gamm-lper", features = ["library"] }

# our packages
valence-account-utils = { path = "packages/account-utils" }
valence-astroport-utils = { path = "packages/astroport-utils" }
valence-osmosis-utils = { path = "packages/osmosis-utils" }
valence-authorization-utils = { path = "packages/authorization-utils" }
valence-macros = { path = "packages/valence-macros" }
valence-polytone-utils = { path = "packages/polytone-utils" }
Expand All @@ -83,3 +86,4 @@ hex = "0.4.3"
margined-neutron-std = "4.2.0"
neutron-test-tube = "4.2.0"
tokio = "1.40.0"
osmosis-test-tube = { version = "25.0.0" }
bekauz marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion contracts/accounts/base_account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ optimize = """docker run --rm -v "$(pwd)":/code \

[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-std = { workspace = true, features = ["stargate"] }

cw-ownable = { workspace = true }
cw-storage-plus = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion contracts/authorization/src/tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub fn store_and_instantiate_authorization_with_processor_contract(
let salt = hex::encode("authorization");
let predicted_address = extended_wasm
.query_build_address(
hex::encode(&checksum),
hex::encode(checksum),
signer.address().to_string(),
salt.clone(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn create_withdraw_liquidity_msgs(
let token = query_liquidity_token(deps, cfg)?;

// Query the balance of the account that is going to withdraw
let balance = deps.querier.query_balance(&cfg.input_addr, &token)?;
let balance = deps.querier.query_balance(&cfg.input_addr, token)?;
if balance.amount.is_zero() {
return Err(ServiceError::ExecutionError(
"Nothing to withdraw".to_string(),
Expand Down
3 changes: 3 additions & 0 deletions contracts/services/osmosis-gamm-lper/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
schema = "run --bin schema"
31 changes: 31 additions & 0 deletions contracts/services/osmosis-gamm-lper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "valence-osmosis-gamm-lper"
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
version = { workspace = true }
repository = { workspace = true }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
# use library feature to disable all instantiate/execute/query exports
library = []

[dependencies]
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-ownable = { workspace = true }
valence-macros = { workspace = true }
valence-service-utils = { workspace = true }
valence-service-base = { workspace = true }
osmosis-std = { workspace = true }
valence-account-utils = { workspace = true }
valence-osmosis-utils = { workspace = true }

[dev-dependencies]
cosmwasm-std-polytone = { package = "cosmwasm-std", version = "1.5.7" }
cw20 = { workspace = true }
osmosis-test-tube = { workspace = true }
valence-osmosis-utils = { workspace = true, features = ["testing"] }
1 change: 1 addition & 0 deletions contracts/services/osmosis-gamm-lper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Osmosis GAMM liquidity provider service
219 changes: 219 additions & 0 deletions contracts/services/osmosis-gamm-lper/src/balancer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use std::str::FromStr;

use cosmwasm_std::{
coin, coins, BankMsg, Coin, CosmosMsg, Decimal, DepsMut, Empty, Fraction, Response, StdError,
StdResult, Uint128,
};
use osmosis_std::{
cosmwasm_to_proto_coins,
types::osmosis::{
gamm::v1beta1::{GammQuerier, Pool},
poolmanager::v1beta1::PoolmanagerQuerier,
},
};
use valence_osmosis_utils::utils::{
get_provide_liquidity_msg, get_provide_ss_liquidity_msg, DecimalRange,
};
use valence_service_utils::{error::ServiceError, execute_on_behalf_of};

use crate::{msg::LiquidityProviderConfig, valence_service_integration::Config};

pub fn provide_single_sided_liquidity(
deps: DepsMut,
cfg: Config,
asset: String,
limit: Uint128,
expected_spot_price: Option<DecimalRange>,
) -> Result<Response, ServiceError> {
// first we assert the input account balance
let input_acc_asset_bal = deps.querier.query_balance(&cfg.input_addr, &asset)?;
let pm_querier = PoolmanagerQuerier::new(&deps.querier);
let pool = pm_querier.get_pool_response(&cfg.lp_config)?;
let pool_ratio = pm_querier.query_spot_price(&cfg.lp_config)?;

// assert the spot price to be within our expectations,
// if expectations are set.
if let Some(acceptable_spot_price_range) = expected_spot_price {
acceptable_spot_price_range.contains(pool_ratio)?;
}

// if the input balance is greater than the limit, we provision the limit amount.
// otherwise we provision the full input balance.
let provision_amount = if input_acc_asset_bal.amount > limit {
limit
} else {
input_acc_asset_bal.amount
};
bekauz marked this conversation as resolved.
Show resolved Hide resolved

let share_out_amt = calculate_share_out_amt_swap(
&deps,
cfg.lp_config.pool_id,
coins(provision_amount.u128(), asset.to_string()),
)?;

let liquidity_provision_msg = get_provide_ss_liquidity_msg(
cfg.input_addr.as_str(),
cfg.lp_config.pool_id,
coin(provision_amount.u128(), asset),
share_out_amt.to_string(),
)?;

let transfer_lp_tokens_msg = BankMsg::Send {
to_address: cfg.output_addr.to_string(),
amount: coins(
share_out_amt.u128(),
pool.total_shares
.ok_or_else(|| StdError::generic_err("failed to get shares"))?
.denom,
),
};

let delegated_input_acc_msgs = execute_on_behalf_of(
vec![liquidity_provision_msg, transfer_lp_tokens_msg.into()],
&cfg.input_addr.clone(),
)?;

Ok(Response::default().add_message(delegated_input_acc_msgs))
}

pub fn provide_double_sided_liquidity(
deps: DepsMut,
cfg: Config,
expected_spot_price: Option<DecimalRange>,
) -> Result<Response, ServiceError> {
// first we assert the input account balances
let bal_asset_1 = deps
.querier
.query_balance(&cfg.input_addr, &cfg.lp_config.pool_asset_1)?;
let bal_asset_2 = deps
.querier
.query_balance(&cfg.input_addr, &cfg.lp_config.pool_asset_2)?;

let pm_querier = PoolmanagerQuerier::new(&deps.querier);

let pool_ratio = pm_querier.query_spot_price(&cfg.lp_config)?;
let pool = pm_querier.get_pool_response(&cfg.lp_config)?;

// assert the spot price to be within our expectations,
// if expectations are set.
if let Some(acceptable_spot_price_range) = expected_spot_price {
acceptable_spot_price_range.contains(pool_ratio)?;
}

let provision_coins = calculate_provision_amounts(bal_asset_1, bal_asset_2, pool_ratio)?;

let share_out_amt =
calculate_share_out_amt_no_swap(&deps, cfg.lp_config.pool_id, provision_coins.clone())?;

let liquidity_provision_msg: CosmosMsg = get_provide_liquidity_msg(
cfg.input_addr.as_str(),
cfg.lp_config.pool_id,
provision_coins,
share_out_amt.to_string(),
)?;

let transfer_lp_tokens_msg = BankMsg::Send {
to_address: cfg.output_addr.to_string(),
amount: coins(
share_out_amt.u128(),
pool.total_shares
.ok_or_else(|| StdError::generic_err("failed to get shares"))?
.denom,
),
};

let delegated_msgs = execute_on_behalf_of(
vec![liquidity_provision_msg, transfer_lp_tokens_msg.into()],
&cfg.input_addr.clone(),
)?;

Ok(Response::default().add_message(delegated_msgs))
}

pub trait ValenceLiquidPooler {
fn query_spot_price(&self, lp_config: &LiquidityProviderConfig) -> StdResult<Decimal>;
fn get_pool_response(&self, lp_config: &LiquidityProviderConfig) -> StdResult<Pool>;
}

impl ValenceLiquidPooler for PoolmanagerQuerier<'_, Empty> {
fn query_spot_price(&self, lp_config: &LiquidityProviderConfig) -> StdResult<Decimal> {
let spot_price_response = self.spot_price(
lp_config.pool_id,
lp_config.pool_asset_1.to_string(),
lp_config.pool_asset_2.to_string(),
)?;

let pool_ratio = Decimal::from_str(&spot_price_response.spot_price)?;

Ok(pool_ratio)
}

fn get_pool_response(&self, lp_config: &LiquidityProviderConfig) -> StdResult<Pool> {
let pool_response = self.pool(lp_config.pool_id)?;

let pool: Pool = pool_response
.pool
.ok_or_else(|| StdError::generic_err("failed to get pool"))?
.try_into()
.map_err(|_| StdError::generic_err("failed to decode proto"))?;

Ok(pool)
}
}

bekauz marked this conversation as resolved.
Show resolved Hide resolved
pub fn calculate_share_out_amt_no_swap(
deps: &DepsMut,
pool_id: u64,
coins_in: Vec<Coin>,
) -> StdResult<Uint128> {
let gamm_querier = GammQuerier::new(&deps.querier);
let shares_out = gamm_querier
.calc_join_pool_no_swap_shares(pool_id, cosmwasm_to_proto_coins(coins_in))?
.shares_out;

let shares_u128 = Uint128::from_str(&shares_out)?;

Ok(shares_u128)
}

pub fn calculate_share_out_amt_swap(
deps: &DepsMut,
pool_id: u64,
coin_in: Vec<Coin>,
) -> StdResult<Uint128> {
let gamm_querier = GammQuerier::new(&deps.querier);
let shares_out = gamm_querier
.calc_join_pool_shares(pool_id, cosmwasm_to_proto_coins(coin_in))?
.share_out_amount;

let shares_u128 = Uint128::from_str(&shares_out)?;

Ok(shares_u128)
}

pub fn calculate_provision_amounts(
mut asset_1_bal: Coin,
mut asset_2_bal: Coin,
pool_ratio: Decimal,
) -> StdResult<Vec<Coin>> {
// first we assume that we are going to provide all of asset_1 and up to all of asset_2
// then we get the expected amount of asset_2 tokens to provide
let expected_asset_2_provision_amt = asset_1_bal
.amount
.checked_multiply_ratio(pool_ratio.numerator(), pool_ratio.denominator())
.map_err(|e| StdError::generic_err(e.to_string()))?;

// then we check if the expected amount of asset_2 tokens is greater than the available balance
if expected_asset_2_provision_amt > asset_2_bal.amount {
// if it is, we calculate the amount of asset_1 tokens to provide
asset_1_bal.amount = asset_2_bal
.amount
.checked_multiply_ratio(pool_ratio.denominator(), pool_ratio.numerator())
.map_err(|e| StdError::generic_err(e.to_string()))?;
} else {
// if it is not, we provide all of asset_1 and the expected amount of asset_2
asset_2_bal.amount = expected_asset_2_provision_amt;
}

Ok(vec![asset_1_bal, asset_2_bal])
}
15 changes: 15 additions & 0 deletions contracts/services/osmosis-gamm-lper/src/bin/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use cosmwasm_schema::write_api;

use valence_osmosis_gamm_lper::{
msg::{ActionsMsgs, QueryMsg},
valence_service_integration::{OptionalServiceConfig, ServiceConfig},
};
use valence_service_utils::msg::{ExecuteMsg, InstantiateMsg};

fn main() {
write_api! {
instantiate: InstantiateMsg<ServiceConfig>,
execute: ExecuteMsg<ActionsMsgs, OptionalServiceConfig>,
query: QueryMsg,
}
}
Loading
Loading