remove gamma git dependency
This commit is contained in:
parent
164df24fd2
commit
44fb77ce17
|
@ -123,7 +123,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5f619f1d04f53621925ba8a2e633ba5a6081f2ae14758cbb67f38fd823e0a3e"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
|
@ -135,7 +135,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7f2a3e1df4685f18d12a943a9f2a7456305401af21a07c9fe076ef9ecd6e400"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"bs58 0.5.1",
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
|
@ -148,7 +148,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9423945cb55627f0b30903288e78baf6f62c6c8ab28fb344b6b25f1ffee3dca7"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
@ -159,7 +159,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93ed12720033cc3c3bf3cfa293349c2275cd5ab99936e33dd4bf283aaad3e241"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
@ -170,7 +170,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eef4dc0371eba2d8c8b54794b0b0eb786a234a559b77593d6f80825b6d2c77a2"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
|
@ -182,7 +182,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b18c4f191331e078d4a6a080954d1576241c29c56638783322a18d308ab27e4f"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
@ -212,7 +212,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5de10d6e9620d3bcea56c56151cad83c5992f50d5960b3a9bebc4a50390ddc3c"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
@ -223,7 +223,7 @@ version = "0.29.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4e2e5be518ec6053d90a2a7f26843dbee607583c779e6c8395951b9739bdfbe"
|
||||
dependencies = [
|
||||
"anchor-syn",
|
||||
"anchor-syn 0.29.0",
|
||||
"borsh-derive-internal 0.10.3",
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
|
@ -241,6 +241,52 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-gen"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a3b9def91d1f0c23b99be210afea0990f931a1edae439ea30e0da50baeaea8f"
|
||||
dependencies = [
|
||||
"anchor-generate-cpi-crate",
|
||||
"anchor-generate-cpi-interface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-generate-cpi-crate"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03e8733d55b8492bde0d6f219c25769786758ff9e5e90a16e6a348f91284af50"
|
||||
dependencies = [
|
||||
"anchor-idl",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-generate-cpi-interface"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7092a50cf959ebf53460162cb07e88d4cc8ea7fa8c45292e80503a3186033daf"
|
||||
dependencies = [
|
||||
"anchor-idl",
|
||||
"darling 0.14.4",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-idl"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3de2665dc06ee0bd3c7d08f47f136a3d5b1e34b7ac1bc632c86a812add04d68b"
|
||||
dependencies = [
|
||||
"anchor-syn 0.24.2",
|
||||
"darling 0.14.4",
|
||||
"heck 0.4.1",
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
"serde_json",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-lang"
|
||||
version = "0.29.0"
|
||||
|
@ -280,6 +326,25 @@ dependencies = [
|
|||
"spl-token-2022 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-syn"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03549dc2eae0b20beba6333b14520e511822a6321cdb1760f841064a69347316"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58 0.3.1",
|
||||
"heck 0.3.3",
|
||||
"proc-macro2 1.0.86",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote 1.0.35",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"syn 1.0.109",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anchor-syn"
|
||||
version = "0.29.0"
|
||||
|
@ -2114,6 +2179,7 @@ name = "dex-gamma"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anchor-client",
|
||||
"anchor-gen",
|
||||
"anchor-lang",
|
||||
"anchor-spl",
|
||||
"anyhow",
|
||||
|
@ -2901,16 +2967,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "gamma"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GooseFX1/gamma-swap/?branch=testing#38fdd63239a3be9ea73a1bddb6813c74672150ef"
|
||||
dependencies = [
|
||||
"anchor-gen",
|
||||
"anchor-lang",
|
||||
"anchor-spl",
|
||||
"referral",
|
||||
"solana-security-txt",
|
||||
"spl-math",
|
||||
"spl-memo 4.0.0",
|
||||
"spl-token 4.0.0",
|
||||
"uint",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4574,9 +4634,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg 1.2.0",
|
||||
]
|
||||
|
@ -5275,6 +5335,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.86",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
"version_check 0.9.4",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.13.4"
|
||||
|
@ -5876,15 +5949,6 @@ dependencies = [
|
|||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "referral"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/GooseFX1/gfx-referral.git?branch=make-types-public#3d353a8027bcaca1de6c7062b7e90450030e56ba"
|
||||
dependencies = [
|
||||
"anchor-lang",
|
||||
"anchor-spl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
|
@ -10525,6 +10589,12 @@ dependencies = [
|
|||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yasna"
|
||||
version = "0.5.2"
|
||||
|
|
|
@ -22,7 +22,6 @@ reqwest = { version = "0.11.27", features = ["json"] }
|
|||
whirlpools-client = { git = "https://github.com/blockworks-foundation/whirlpools-client/", features = ["no-entrypoint"] }
|
||||
openbook-v2 = { git = "https://github.com/openbook-dex/openbook-v2", tag = "v0.2.7", features = ["no-entrypoint", "client"] }
|
||||
raydium-cp-swap = { git = "https://github.com/raydium-io/raydium-cp-swap/", features = ["no-entrypoint", "client"] }
|
||||
gamma = { git = "https://github.com/GooseFX1/gamma-swap/", features = ["no-entrypoint", "client"], branch="testing" }
|
||||
stable-swap = { version = "1.8.1", features = ["no-entrypoint", "client"] }
|
||||
stable-swap-client = { version = "1.8.1" }
|
||||
stable-swap-math = { version = "1.8.1" }
|
||||
|
|
|
@ -53,6 +53,7 @@ cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::te
|
|||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_orca
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_cropper
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_raydium_cp
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_gamma
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_raydium
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_openbook_v2
|
||||
cargo test-sbf --package simulator -- --nocapture cases::test_swap_from_dump::test_quote_match_swap_for_infinity
|
||||
|
|
|
@ -15,6 +15,7 @@ solana-logger = "1.17"
|
|||
solana-program = "1.17"
|
||||
solana-program-test = "1.17"
|
||||
anchor-lang = "0.29.0"
|
||||
anchor-gen = "0.3.1"
|
||||
anchor-client = "0.29.0"
|
||||
anchor-spl = "0.29.0"
|
||||
anyhow = "1.0.86"
|
||||
|
@ -29,7 +30,7 @@ serde_derive = "1.0"
|
|||
mango-feeds-connector = { workspace = true }
|
||||
|
||||
# gamma
|
||||
gamma = { workspace = true }
|
||||
gamma = { path="../gamma" }
|
||||
|
||||
[dev-dependencies]
|
||||
router-test-lib = { path = "../router-test-lib", version = "0.1" }
|
|
@ -5,8 +5,8 @@ use anchor_spl::token_2022::spl_token_2022::extension::{
|
|||
BaseStateWithExtensions, StateWithExtensions,
|
||||
};
|
||||
use anchor_spl::token_2022::spl_token_2022::state::Mint;
|
||||
use gamma::curve::{CurveCalculator, TradeDirection};
|
||||
use gamma::states::{AmmConfig, ObservationState, PoolState, PoolStatusBitIndex};
|
||||
use gamma::curve::CurveCalculator;
|
||||
use gamma::{AmmConfig, ObservationState, PoolState, PoolStatusBitIndex};
|
||||
use mango_feeds_connector::chain_data::AccountData;
|
||||
use solana_program::clock::Clock;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
@ -114,32 +114,24 @@ pub fn swap_base_input(
|
|||
|
||||
// Calculate the trade amounts
|
||||
|
||||
let (trade_direction, total_input_token_amount, total_output_token_amount) =
|
||||
if input_vault_key == pool_state.token_0_vault
|
||||
&& output_vault_key == pool_state.token_1_vault
|
||||
{
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?;
|
||||
let (total_input_token_amount, total_output_token_amount) = if input_vault_key
|
||||
== pool_state.token0_vault
|
||||
&& output_vault_key == pool_state.token1_vault
|
||||
{
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?;
|
||||
|
||||
(
|
||||
TradeDirection::ZeroForOne,
|
||||
total_input_token_amount,
|
||||
total_output_token_amount,
|
||||
)
|
||||
} else if input_vault_key == pool_state.token_1_vault
|
||||
&& output_vault_key == pool_state.token_0_vault
|
||||
{
|
||||
let (total_output_token_amount, total_input_token_amount) =
|
||||
vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
||||
(total_input_token_amount, total_output_token_amount)
|
||||
} else if input_vault_key == pool_state.token1_vault
|
||||
&& output_vault_key == pool_state.token0_vault
|
||||
{
|
||||
let (total_output_token_amount, total_input_token_amount) =
|
||||
vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
||||
|
||||
(
|
||||
TradeDirection::OneForZero,
|
||||
total_input_token_amount,
|
||||
total_output_token_amount,
|
||||
)
|
||||
} else {
|
||||
anyhow::bail!("Invalid vault");
|
||||
};
|
||||
(total_input_token_amount, total_output_token_amount)
|
||||
} else {
|
||||
anyhow::bail!("Invalid vault");
|
||||
};
|
||||
|
||||
let Ok(result) = CurveCalculator::swap_base_input(
|
||||
u128::from(actual_amount_in),
|
||||
|
@ -150,7 +142,6 @@ pub fn swap_base_input(
|
|||
amm_config.fund_fee_rate,
|
||||
block_timestamp,
|
||||
&observation_state,
|
||||
trade_direction,
|
||||
) else {
|
||||
anyhow::bail!("Can't swap");
|
||||
};
|
||||
|
@ -210,32 +201,24 @@ pub fn swap_base_output(
|
|||
};
|
||||
|
||||
// Calculate the trade amounts
|
||||
let (trade_direction, total_input_token_amount, total_output_token_amount) =
|
||||
if input_vault_key == pool_state.token_0_vault
|
||||
&& output_vault_key == pool_state.token_1_vault
|
||||
{
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?;
|
||||
let (total_input_token_amount, total_output_token_amount) = if input_vault_key
|
||||
== pool_state.token0_vault
|
||||
&& output_vault_key == pool_state.token1_vault
|
||||
{
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?;
|
||||
|
||||
(
|
||||
TradeDirection::ZeroForOne,
|
||||
total_input_token_amount,
|
||||
total_output_token_amount,
|
||||
)
|
||||
} else if input_vault_key == pool_state.token_1_vault
|
||||
&& output_vault_key == pool_state.token_0_vault
|
||||
{
|
||||
let (total_output_token_amount, total_input_token_amount) =
|
||||
vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
||||
(total_input_token_amount, total_output_token_amount)
|
||||
} else if input_vault_key == pool_state.token1_vault
|
||||
&& output_vault_key == pool_state.token0_vault
|
||||
{
|
||||
let (total_output_token_amount, total_input_token_amount) =
|
||||
vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
||||
|
||||
(
|
||||
TradeDirection::OneForZero,
|
||||
total_input_token_amount,
|
||||
total_output_token_amount,
|
||||
)
|
||||
} else {
|
||||
anyhow::bail!("Invalid vault");
|
||||
};
|
||||
(total_input_token_amount, total_output_token_amount)
|
||||
} else {
|
||||
anyhow::bail!("Invalid vault");
|
||||
};
|
||||
|
||||
if total_output_token_amount < output_amount {
|
||||
anyhow::bail!("Vault does not contain enough tokens");
|
||||
|
@ -250,7 +233,6 @@ pub fn swap_base_output(
|
|||
amm_config.fund_fee_rate,
|
||||
block_timestamp,
|
||||
observation_state,
|
||||
trade_direction,
|
||||
) else {
|
||||
anyhow::bail!("Can't swap");
|
||||
};
|
||||
|
@ -292,10 +274,10 @@ pub fn vault_amount_without_fee(
|
|||
) -> anyhow::Result<(u64, u64)> {
|
||||
Ok((
|
||||
vault_0
|
||||
.checked_sub(pool.protocol_fees_token_0 + pool.fund_fees_token_0)
|
||||
.checked_sub(pool.protocol_fees_token0 + pool.fund_fees_token0)
|
||||
.ok_or(anyhow::format_err!("invalid sub"))?,
|
||||
vault_1
|
||||
.checked_sub(pool.protocol_fees_token_1 + pool.fund_fees_token_1)
|
||||
.checked_sub(pool.protocol_fees_token1 + pool.fund_fees_token1)
|
||||
.ok_or(anyhow::format_err!("invalid sub"))?,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use anchor_spl::token_2022::spl_token_2022;
|
|||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use gamma::program::Gamma;
|
||||
use gamma::states::{AmmConfig, ObservationState, PoolState, PoolStatusBitIndex};
|
||||
use gamma::{AmmConfig, ObservationState, PoolState, PoolStatusBitIndex};
|
||||
use itertools::Itertools;
|
||||
use router_feed_lib::router_rpc_client::{RouterRpcClient, RouterRpcClientTrait};
|
||||
use router_lib::dex::{
|
||||
|
@ -50,7 +50,7 @@ impl DexInterface for GammaCpDex {
|
|||
|
||||
let vaults = pools
|
||||
.iter()
|
||||
.flat_map(|x| [x.1.token_0_vault, x.1.token_1_vault])
|
||||
.flat_map(|x| [x.1.token0_vault, x.1.token1_vault])
|
||||
.collect::<HashSet<_>>();
|
||||
let vaults = rpc.get_multiple_accounts(&vaults).await?;
|
||||
let banned_vaults = vaults
|
||||
|
@ -66,12 +66,12 @@ impl DexInterface for GammaCpDex {
|
|||
let pools = pools
|
||||
.iter()
|
||||
.filter(|(_pool_pk, pool)| {
|
||||
pool.token_0_program == Token::id() && pool.token_1_program == Token::id()
|
||||
pool.token0_program == Token::id() && pool.token1_program == Token::id()
|
||||
// TODO Remove filter when 2022 are working
|
||||
})
|
||||
.filter(|(_pool_pk, pool)| {
|
||||
!banned_vaults.contains(&pool.token_0_vault)
|
||||
&& !banned_vaults.contains(&pool.token_1_vault)
|
||||
!banned_vaults.contains(&pool.token0_vault)
|
||||
&& !banned_vaults.contains(&pool.token1_vault)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
|
@ -81,14 +81,14 @@ impl DexInterface for GammaCpDex {
|
|||
(
|
||||
Arc::new(GammaEdgeIdentifier {
|
||||
pool: *pool_pk,
|
||||
mint_a: pool.token_0_mint,
|
||||
mint_b: pool.token_1_mint,
|
||||
mint_a: pool.token0_mint,
|
||||
mint_b: pool.token1_mint,
|
||||
is_a_to_b: true,
|
||||
}),
|
||||
Arc::new(GammaEdgeIdentifier {
|
||||
pool: *pool_pk,
|
||||
mint_a: pool.token_1_mint,
|
||||
mint_b: pool.token_0_mint,
|
||||
mint_a: pool.token1_mint,
|
||||
mint_b: pool.token0_mint,
|
||||
is_a_to_b: false,
|
||||
}),
|
||||
)
|
||||
|
@ -108,15 +108,15 @@ impl DexInterface for GammaCpDex {
|
|||
|
||||
utils::insert_or_extend(&mut map, pool_pk, &entry);
|
||||
utils::insert_or_extend(&mut map, &pool.amm_config, &entry);
|
||||
utils::insert_or_extend(&mut map, &pool.token_0_vault, &entry);
|
||||
utils::insert_or_extend(&mut map, &pool.token_1_vault, &entry);
|
||||
utils::insert_or_extend(&mut map, &pool.token0_vault, &entry);
|
||||
utils::insert_or_extend(&mut map, &pool.token1_vault, &entry);
|
||||
|
||||
needed_accounts.insert(*pool_pk);
|
||||
needed_accounts.insert(pool.amm_config);
|
||||
needed_accounts.insert(pool.token_0_vault);
|
||||
needed_accounts.insert(pool.token_1_vault);
|
||||
needed_accounts.insert(pool.token_0_mint);
|
||||
needed_accounts.insert(pool.token_1_mint);
|
||||
needed_accounts.insert(pool.token0_vault);
|
||||
needed_accounts.insert(pool.token1_vault);
|
||||
needed_accounts.insert(pool.token0_mint);
|
||||
needed_accounts.insert(pool.token1_mint);
|
||||
}
|
||||
map
|
||||
};
|
||||
|
@ -162,14 +162,14 @@ impl DexInterface for GammaCpDex {
|
|||
let config_account = chain_data.account(&pool.amm_config)?;
|
||||
let config = AmmConfig::try_deserialize(&mut config_account.account.data())?;
|
||||
|
||||
let vault_0_account = chain_data.account(&pool.token_0_vault)?;
|
||||
let vault_0_account = chain_data.account(&pool.token0_vault)?;
|
||||
let vault_0 = spl_token_2022::state::Account::unpack(vault_0_account.account.data())?;
|
||||
|
||||
let vault_1_account = chain_data.account(&pool.token_1_vault)?;
|
||||
let vault_1_account = chain_data.account(&pool.token1_vault)?;
|
||||
let vault_1 = spl_token_2022::state::Account::unpack(vault_1_account.account.data())?;
|
||||
|
||||
let mint_0_account = chain_data.account(&pool.token_0_mint)?;
|
||||
let mint_1_account = chain_data.account(&pool.token_1_mint)?;
|
||||
let mint_0_account = chain_data.account(&pool.token0_mint)?;
|
||||
let mint_1_account = chain_data.account(&pool.token1_mint)?;
|
||||
let transfer_0_fee = crate::edge::get_transfer_config(&mint_0_account)?;
|
||||
let transfer_1_fee = crate::edge::get_transfer_config(&mint_1_account)?;
|
||||
|
||||
|
@ -203,7 +203,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -214,7 +214,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -223,10 +223,10 @@ impl DexInterface for GammaCpDex {
|
|||
&edge.pool,
|
||||
&edge.config,
|
||||
&edge.observation_state,
|
||||
edge.pool.token_0_vault,
|
||||
edge.pool.token0_vault,
|
||||
edge.vault_0_amount,
|
||||
&edge.mint_0,
|
||||
edge.pool.token_1_vault,
|
||||
edge.pool.token1_vault,
|
||||
edge.vault_1_amount,
|
||||
&edge.mint_1,
|
||||
in_amount,
|
||||
|
@ -237,17 +237,17 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: result.0,
|
||||
out_amount: result.1,
|
||||
fee_amount: result.2,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
}
|
||||
} else {
|
||||
let result = swap_base_input(
|
||||
&edge.pool,
|
||||
&edge.config,
|
||||
&edge.observation_state,
|
||||
edge.pool.token_1_vault,
|
||||
edge.pool.token1_vault,
|
||||
edge.vault_1_amount,
|
||||
&edge.mint_1,
|
||||
edge.pool.token_0_vault,
|
||||
edge.pool.token0_vault,
|
||||
edge.vault_0_amount,
|
||||
&edge.mint_0,
|
||||
in_amount,
|
||||
|
@ -258,7 +258,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: result.0,
|
||||
out_amount: result.1,
|
||||
fee_amount: result.2,
|
||||
fee_mint: edge.pool.token_1_mint,
|
||||
fee_mint: edge.pool.token1_mint,
|
||||
}
|
||||
};
|
||||
Ok(quote)
|
||||
|
@ -303,7 +303,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: u64::MAX,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -314,7 +314,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: u64::MAX,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -323,10 +323,10 @@ impl DexInterface for GammaCpDex {
|
|||
&edge.pool,
|
||||
&edge.config,
|
||||
&edge.observation_state,
|
||||
edge.pool.token_0_vault,
|
||||
edge.pool.token0_vault,
|
||||
edge.vault_0_amount,
|
||||
&edge.mint_0,
|
||||
edge.pool.token_1_vault,
|
||||
edge.pool.token1_vault,
|
||||
edge.vault_1_amount,
|
||||
&edge.mint_1,
|
||||
out_amount,
|
||||
|
@ -337,17 +337,17 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: result.0,
|
||||
out_amount: result.1,
|
||||
fee_amount: result.2,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
fee_mint: edge.pool.token0_mint,
|
||||
}
|
||||
} else {
|
||||
let result = swap_base_output(
|
||||
&edge.pool,
|
||||
&edge.config,
|
||||
&edge.observation_state,
|
||||
edge.pool.token_1_vault,
|
||||
edge.pool.token1_vault,
|
||||
edge.vault_1_amount,
|
||||
&edge.mint_1,
|
||||
edge.pool.token_0_vault,
|
||||
edge.pool.token0_vault,
|
||||
edge.vault_0_amount,
|
||||
&edge.mint_0,
|
||||
out_amount,
|
||||
|
@ -358,7 +358,7 @@ impl DexInterface for GammaCpDex {
|
|||
in_amount: result.0,
|
||||
out_amount: result.1,
|
||||
fee_amount: result.2,
|
||||
fee_mint: edge.pool.token_1_mint,
|
||||
fee_mint: edge.pool.token1_mint,
|
||||
}
|
||||
};
|
||||
Ok(quote)
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::edge::GammaEdgeIdentifier;
|
|||
use anchor_lang::{AccountDeserialize, Id, InstructionData, ToAccountMetas};
|
||||
use anchor_spl::associated_token::get_associated_token_address;
|
||||
use gamma::program::Gamma;
|
||||
use gamma::states::PoolState;
|
||||
use gamma::PoolState;
|
||||
use gamma::AUTH_SEED;
|
||||
use router_lib::dex::{AccountProviderView, SwapInstruction};
|
||||
use solana_program::instruction::Instruction;
|
||||
|
@ -25,19 +25,19 @@ pub fn build_swap_ix(
|
|||
((out_amount as f64 * (10_000f64 - max_slippage_bps as f64)) / 10_000f64).floor() as u64;
|
||||
|
||||
let (input_token_mint, output_token_mint) = if id.is_a_to_b {
|
||||
(pool.token_0_mint, pool.token_1_mint)
|
||||
(pool.token0_mint, pool.token1_mint)
|
||||
} else {
|
||||
(pool.token_1_mint, pool.token_0_mint)
|
||||
(pool.token1_mint, pool.token0_mint)
|
||||
};
|
||||
let (input_token_program, output_token_program) = if id.is_a_to_b {
|
||||
(pool.token_0_program, pool.token_1_program)
|
||||
(pool.token0_program, pool.token1_program)
|
||||
} else {
|
||||
(pool.token_1_program, pool.token_0_program)
|
||||
(pool.token1_program, pool.token0_program)
|
||||
};
|
||||
let (input_vault, output_vault) = if id.is_a_to_b {
|
||||
(pool.token_0_vault, pool.token_1_vault)
|
||||
(pool.token0_vault, pool.token1_vault)
|
||||
} else {
|
||||
(pool.token_1_vault, pool.token_0_vault)
|
||||
(pool.token1_vault, pool.token0_vault)
|
||||
};
|
||||
|
||||
let (input_token_account, output_token_account) = (
|
||||
|
@ -46,12 +46,12 @@ pub fn build_swap_ix(
|
|||
);
|
||||
|
||||
let instruction = gamma::instruction::SwapBaseInput {
|
||||
amount_in: amount,
|
||||
minimum_amount_out: other_amount_threshold,
|
||||
_amount_in: amount,
|
||||
_minimum_amount_out: other_amount_threshold,
|
||||
};
|
||||
let (authority, __bump) = Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &Gamma::id());
|
||||
|
||||
let accounts = gamma::accounts::Swap {
|
||||
let accounts = gamma::accounts::SwapBaseInput {
|
||||
payer: *wallet_pk,
|
||||
authority,
|
||||
amm_config: pool.amm_config,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "gamma"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "lib"]
|
||||
name = "gamma"
|
||||
|
||||
[features]
|
||||
no-entrypoint = []
|
||||
cpi = ["no-entrypoint"]
|
||||
|
||||
[dependencies]
|
||||
anchor-lang = "0.29.0"
|
||||
anchor-gen = "0.3.1"
|
||||
num-traits = "0.2.19"
|
|
@ -0,0 +1,134 @@
|
|||
use crate::fees::{DynamicFee, FeeType};
|
||||
use crate::GammaError;
|
||||
use crate::ObservationState;
|
||||
use crate::{curve::constant_product::ConstantProductCurve, fees::StaticFee};
|
||||
use anchor_lang::prelude::*;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum TradeDirection {
|
||||
ZeroForOne,
|
||||
OneForZero,
|
||||
}
|
||||
|
||||
impl TradeDirection {
|
||||
pub fn opposite(&self) -> TradeDirection {
|
||||
match self {
|
||||
TradeDirection::ZeroForOne => TradeDirection::OneForZero,
|
||||
TradeDirection::OneForZero => TradeDirection::ZeroForOne,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SwapResult {
|
||||
pub new_swap_source_amount: u128,
|
||||
pub new_swap_destination_amount: u128,
|
||||
pub source_amount_swapped: u128,
|
||||
pub destination_amount_swapped: u128,
|
||||
pub dynamic_fee: u128,
|
||||
pub protocol_fee: u128,
|
||||
pub fund_fee: u128,
|
||||
}
|
||||
|
||||
/// Concrete struct to wrap around the trait object which performs calculation.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct CurveCalculator {}
|
||||
|
||||
impl CurveCalculator {
|
||||
pub fn swap_base_input(
|
||||
source_amount_to_be_swapped: u128,
|
||||
swap_source_amount: u128,
|
||||
swap_destination_amount: u128,
|
||||
trade_fee_rate: u64,
|
||||
protocol_fee_rate: u64,
|
||||
fund_fee_rate: u64,
|
||||
block_timestamp: u64,
|
||||
observation_state: &ObservationState,
|
||||
// TODO: add fee type here once that is configurable on pool level/ or we can use it from pool_state
|
||||
) -> Result<SwapResult> {
|
||||
let dynamic_fee = DynamicFee::dynamic_fee(
|
||||
source_amount_to_be_swapped,
|
||||
block_timestamp,
|
||||
observation_state,
|
||||
FeeType::Volatility,
|
||||
trade_fee_rate,
|
||||
)?;
|
||||
|
||||
let protocol_fee = StaticFee::protocol_fee(dynamic_fee, protocol_fee_rate)
|
||||
.ok_or(GammaError::InvalidFee)?;
|
||||
let fund_fee =
|
||||
StaticFee::fund_fee(dynamic_fee, fund_fee_rate).ok_or(GammaError::InvalidFee)?;
|
||||
|
||||
let source_amount_after_fees = source_amount_to_be_swapped
|
||||
.checked_sub(dynamic_fee)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let destination_amount_swapped = ConstantProductCurve::swap_base_input_without_fees(
|
||||
source_amount_after_fees,
|
||||
swap_source_amount,
|
||||
swap_destination_amount,
|
||||
)?;
|
||||
|
||||
Ok(SwapResult {
|
||||
new_swap_source_amount: swap_source_amount
|
||||
.checked_add(source_amount_to_be_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?,
|
||||
new_swap_destination_amount: swap_destination_amount
|
||||
.checked_sub(destination_amount_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?,
|
||||
source_amount_swapped: source_amount_to_be_swapped,
|
||||
destination_amount_swapped,
|
||||
dynamic_fee,
|
||||
protocol_fee,
|
||||
fund_fee,
|
||||
})
|
||||
}
|
||||
|
||||
/// Subtract fees and calculate how much source token will be required
|
||||
pub fn swap_base_output(
|
||||
destination_amount_to_be_swapped: u128,
|
||||
swap_source_amount: u128,
|
||||
swap_destination_amount: u128,
|
||||
trade_fee_rate: u64,
|
||||
protocol_fee_rate: u64,
|
||||
fund_fee_rate: u64,
|
||||
block_timestamp: u64,
|
||||
observation_state: &ObservationState,
|
||||
) -> Result<SwapResult> {
|
||||
let source_amount_swapped = ConstantProductCurve::swap_base_output_without_fees(
|
||||
destination_amount_to_be_swapped,
|
||||
swap_source_amount,
|
||||
swap_destination_amount,
|
||||
)?;
|
||||
|
||||
let source_amount = DynamicFee::calculate_pre_fee_amount(
|
||||
block_timestamp,
|
||||
source_amount_swapped,
|
||||
observation_state,
|
||||
FeeType::Volatility,
|
||||
trade_fee_rate,
|
||||
)?;
|
||||
|
||||
let dynamic_fee = source_amount
|
||||
.checked_sub(source_amount_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let protocol_fee = StaticFee::protocol_fee(dynamic_fee, protocol_fee_rate)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let fund_fee =
|
||||
StaticFee::fund_fee(dynamic_fee, fund_fee_rate).ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
Ok(SwapResult {
|
||||
new_swap_source_amount: swap_source_amount
|
||||
.checked_add(source_amount)
|
||||
.ok_or(GammaError::MathOverflow)?,
|
||||
new_swap_destination_amount: swap_destination_amount
|
||||
.checked_sub(destination_amount_to_be_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?,
|
||||
source_amount_swapped: source_amount,
|
||||
destination_amount_swapped: destination_amount_to_be_swapped,
|
||||
protocol_fee,
|
||||
fund_fee,
|
||||
dynamic_fee,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
use crate::utils::math::CheckedCeilDiv;
|
||||
use crate::GammaError;
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct ConstantProductCurve;
|
||||
|
||||
impl ConstantProductCurve {
|
||||
pub fn swap_base_input_without_fees(
|
||||
source_amount_to_be_swapped: u128,
|
||||
swap_source_amount: u128,
|
||||
swap_destination_amount: u128,
|
||||
) -> Result<u128> {
|
||||
let numerator = source_amount_to_be_swapped
|
||||
.checked_mul(swap_destination_amount)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let denominator = swap_source_amount
|
||||
.checked_add(source_amount_to_be_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let destination_amount_swapped = numerator
|
||||
.checked_div(denominator)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
Ok(destination_amount_swapped)
|
||||
}
|
||||
|
||||
pub fn swap_base_output_without_fees(
|
||||
destination_amount_to_be_swapped: u128,
|
||||
swap_source_amount: u128,
|
||||
swap_destination_amount: u128,
|
||||
) -> Result<u128> {
|
||||
let numerator = swap_source_amount
|
||||
.checked_mul(destination_amount_to_be_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let denominator = swap_destination_amount
|
||||
.checked_sub(destination_amount_to_be_swapped)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let (source_amount_swapped, _) = numerator
|
||||
.checked_ceil_div(denominator)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
Ok(source_amount_swapped)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod calculator;
|
||||
pub mod constant_product;
|
||||
|
||||
pub use calculator::*;
|
||||
pub use constant_product::*;
|
|
@ -0,0 +1,218 @@
|
|||
use super::{ceil_div, FEE_RATE_DENOMINATOR_VALUE};
|
||||
use crate::GammaError;
|
||||
use crate::{Observation, ObservationState};
|
||||
use anchor_lang::prelude::*;
|
||||
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use num_traits::identities::Zero;
|
||||
|
||||
pub const OBSERVATION_NUM: usize = 100;
|
||||
|
||||
pub const MAX_FEE_VOLATILITY: u64 = 10000; // 1% max fee
|
||||
pub const VOLATILITY_WINDOW: u64 = 3600; // 1 hour window for volatility calculation
|
||||
|
||||
const MAX_FEE: u64 = 100000; // 10% max fee
|
||||
const VOLATILITY_FACTOR: u64 = 300_000; // Adjust based on desired sensitivity
|
||||
|
||||
pub enum FeeType {
|
||||
Volatility,
|
||||
}
|
||||
|
||||
struct ObservationWithIndex {
|
||||
observation: Observation,
|
||||
index: u16,
|
||||
}
|
||||
|
||||
pub struct DynamicFee {}
|
||||
|
||||
impl DynamicFee {
|
||||
pub fn dynamic_fee(
|
||||
amount: u128,
|
||||
block_timestamp: u64,
|
||||
observation_state: &ObservationState,
|
||||
fee_type: FeeType,
|
||||
base_fees: u64,
|
||||
) -> Result<u128> {
|
||||
let dynamic_fee_rate =
|
||||
Self::calculate_dynamic_fee(block_timestamp, observation_state, fee_type, base_fees)?;
|
||||
|
||||
Ok(ceil_div(
|
||||
amount,
|
||||
u128::from(dynamic_fee_rate),
|
||||
u128::from(FEE_RATE_DENOMINATOR_VALUE),
|
||||
)
|
||||
.ok_or(GammaError::MathOverflow)?)
|
||||
}
|
||||
|
||||
fn calculate_dynamic_fee(
|
||||
block_timestamp: u64,
|
||||
observation_state: &ObservationState,
|
||||
fee_type: FeeType,
|
||||
base_fees: u64,
|
||||
) -> Result<u64> {
|
||||
match fee_type {
|
||||
FeeType::Volatility => {
|
||||
Self::calculate_volatile_fee(block_timestamp, observation_state, base_fees)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_volatile_fee(
|
||||
block_timestamp: u64,
|
||||
observation_state: &ObservationState,
|
||||
base_fees: u64,
|
||||
) -> Result<u64> {
|
||||
let (min_price, max_price, twap_price) =
|
||||
Self::get_price_range(observation_state, block_timestamp, VOLATILITY_WINDOW)?;
|
||||
if min_price == 0 || max_price == 0 || twap_price == 0 || twap_price == 1 {
|
||||
return Ok(base_fees);
|
||||
}
|
||||
|
||||
let log_max_price = (max_price as f64).ln();
|
||||
let log_min_price = (min_price as f64).ln();
|
||||
let log_twap_price = (twap_price as f64).ln();
|
||||
|
||||
let volatility_numerator = (log_max_price - log_min_price).abs();
|
||||
let volatility_denominator = log_twap_price.abs();
|
||||
|
||||
if volatility_denominator.is_zero() {
|
||||
return Ok(base_fees);
|
||||
}
|
||||
|
||||
let volatility = volatility_numerator / volatility_denominator;
|
||||
|
||||
let volatility_component_calculated = (VOLATILITY_FACTOR as f64 * volatility)
|
||||
.to_u64()
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
let dynamic_fee = base_fees
|
||||
.checked_add(volatility_component_calculated)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
Ok(std::cmp::min(dynamic_fee, MAX_FEE))
|
||||
}
|
||||
|
||||
fn get_price_range(
|
||||
observation_state: &ObservationState,
|
||||
current_time: u64,
|
||||
window: u64,
|
||||
) -> Result<(u128, u128, u128)> {
|
||||
let mut min_price = u128::MAX;
|
||||
let mut max_price = 0u128;
|
||||
let mut descending_order_observations = observation_state
|
||||
.observations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, observation)| {
|
||||
observation.block_timestamp != 0
|
||||
&& observation.cumulative_token0_price_x32 != 0
|
||||
&& observation.cumulative_token1_price_x32 != 0
|
||||
&& current_time.saturating_sub(observation.block_timestamp) <= window
|
||||
})
|
||||
.map(|(index, observation)| ObservationWithIndex {
|
||||
index: index as u16,
|
||||
observation: *observation,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort observations by timestamp (newest first)
|
||||
descending_order_observations.sort_by(|a, b| {
|
||||
{ b.observation.block_timestamp }.cmp(&{ a.observation.block_timestamp })
|
||||
});
|
||||
|
||||
if descending_order_observations.len() < 2 {
|
||||
return Ok((0, 0, 0));
|
||||
}
|
||||
|
||||
let newest_obs = descending_order_observations.first().unwrap();
|
||||
let oldest_obs = descending_order_observations.last().unwrap();
|
||||
|
||||
let total_time_delta = newest_obs
|
||||
.observation
|
||||
.block_timestamp
|
||||
.saturating_sub(oldest_obs.observation.block_timestamp)
|
||||
as u128;
|
||||
|
||||
if total_time_delta == 0 {
|
||||
return Ok((0, 0, 0));
|
||||
}
|
||||
|
||||
let twap_price = newest_obs
|
||||
.observation
|
||||
.cumulative_token0_price_x32
|
||||
.checked_sub(oldest_obs.observation.cumulative_token0_price_x32)
|
||||
.ok_or(GammaError::MathOverflow)?
|
||||
.checked_div(total_time_delta)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
for observation_with_index in descending_order_observations {
|
||||
let last_observation_index = if observation_with_index.index == 0 {
|
||||
OBSERVATION_NUM - 1
|
||||
} else {
|
||||
observation_with_index.index as usize - 1
|
||||
};
|
||||
|
||||
if observation_state.observations[last_observation_index].block_timestamp == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if observation_state.observations[last_observation_index].block_timestamp
|
||||
> observation_with_index.observation.block_timestamp
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let obs = observation_state.observations[last_observation_index];
|
||||
let next_obs = observation_with_index.observation;
|
||||
|
||||
let time_delta = next_obs.block_timestamp.saturating_sub(obs.block_timestamp) as u128;
|
||||
|
||||
if time_delta == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let price = next_obs
|
||||
.cumulative_token0_price_x32
|
||||
.checked_sub(obs.cumulative_token0_price_x32)
|
||||
.ok_or(GammaError::MathOverflow)?
|
||||
.checked_div(time_delta)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
min_price = min_price.min(price);
|
||||
max_price = max_price.max(price);
|
||||
}
|
||||
|
||||
Ok((min_price, max_price, twap_price))
|
||||
}
|
||||
|
||||
pub fn calculate_pre_fee_amount(
|
||||
block_timestamp: u64,
|
||||
post_fee_amount: u128,
|
||||
observation_state: &ObservationState,
|
||||
fee_type: FeeType,
|
||||
base_fees: u64,
|
||||
) -> Result<u128> {
|
||||
let dynamic_fee_rate =
|
||||
Self::calculate_dynamic_fee(block_timestamp, observation_state, fee_type, base_fees)?;
|
||||
if dynamic_fee_rate == 0 {
|
||||
Ok(post_fee_amount)
|
||||
} else {
|
||||
let numerator = post_fee_amount
|
||||
.checked_mul(u128::from(FEE_RATE_DENOMINATOR_VALUE))
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
let denominator = u128::from(FEE_RATE_DENOMINATOR_VALUE)
|
||||
.checked_sub(u128::from(dynamic_fee_rate))
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
let result = numerator
|
||||
.checked_add(denominator)
|
||||
.ok_or(GammaError::MathOverflow)?
|
||||
.checked_sub(1)
|
||||
.ok_or(GammaError::MathOverflow)?
|
||||
.checked_div(denominator)
|
||||
.ok_or(GammaError::MathOverflow)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
pub mod dynamic_fee;
|
||||
pub mod static_fees;
|
||||
|
||||
pub use dynamic_fee::*;
|
||||
pub use static_fees::*;
|
||||
|
||||
pub const FEE_RATE_DENOMINATOR_VALUE: u64 = 1_000_000;
|
||||
|
||||
pub fn ceil_div(token_amount: u128, fee_numerator: u128, fee_denominator: u128) -> Option<u128> {
|
||||
token_amount
|
||||
.checked_mul(fee_numerator)?
|
||||
.checked_add(fee_denominator)?
|
||||
.checked_sub(1)?
|
||||
.checked_div(fee_denominator)
|
||||
}
|
||||
|
||||
pub fn floor_div(token_amount: u128, fee_numerator: u128, fee_denominator: u128) -> Option<u128> {
|
||||
token_amount
|
||||
.checked_mul(fee_numerator)?
|
||||
.checked_div(fee_denominator)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
use super::{floor_div, FEE_RATE_DENOMINATOR_VALUE};
|
||||
|
||||
pub struct StaticFee {}
|
||||
|
||||
impl StaticFee {
|
||||
pub fn protocol_fee(amount: u128, protocol_fee_rate: u64) -> Option<u128> {
|
||||
floor_div(
|
||||
amount,
|
||||
u128::from(protocol_fee_rate),
|
||||
u128::from(FEE_RATE_DENOMINATOR_VALUE),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fund_fee(amount: u128, fund_fee_rate: u64) -> Option<u128> {
|
||||
floor_div(
|
||||
amount,
|
||||
u128::from(fund_fee_rate),
|
||||
u128::from(FEE_RATE_DENOMINATOR_VALUE),
|
||||
)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
pub mod curve;
|
||||
use std::ops::BitAnd;
|
||||
pub mod fees;
|
||||
pub mod utils;
|
||||
|
||||
declare_id!("GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT");
|
||||
|
||||
anchor_gen::generate_cpi_crate!("src/gamma.json");
|
||||
|
||||
pub const AUTH_SEED: &str = "vault_and_lp_mint_auth_seed";
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum PoolStatusBitFlag {
|
||||
Enable,
|
||||
Disable,
|
||||
}
|
||||
|
||||
pub enum PoolStatusBitIndex {
|
||||
Deposit,
|
||||
Withdraw,
|
||||
Swap,
|
||||
}
|
||||
|
||||
impl PoolState {
|
||||
pub const LEN: usize = 8 + 10 * 32 + 5 * 1 + 7 * 8 + 16 * 4 + 23 * 8;
|
||||
|
||||
pub fn get_status_by_bit(&self, bit: PoolStatusBitIndex) -> bool {
|
||||
let status = u8::from(1) << (bit as u8);
|
||||
self.status.bitand(status) == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[error_code]
|
||||
pub enum GammaError {
|
||||
#[msg("Math overflow")]
|
||||
MathOverflow,
|
||||
#[msg("Invalid fee")]
|
||||
InvalidFee,
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
pub trait CheckedCeilDiv: Sized {
|
||||
/// Perform ceiling division
|
||||
fn checked_ceil_div(&self, rhs: Self) -> Option<(Self, Self)>;
|
||||
}
|
||||
|
||||
impl CheckedCeilDiv for u128 {
|
||||
fn checked_ceil_div(&self, mut rhs: Self) -> Option<(Self, Self)> {
|
||||
let mut quotient = self.checked_div(rhs)?;
|
||||
if quotient == 0 {
|
||||
if self.checked_mul(2 as u128)? >= rhs {
|
||||
return Some((1, 0));
|
||||
} else {
|
||||
return Some((0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
let remainder = self.checked_rem(rhs)?;
|
||||
if remainder > 0 {
|
||||
quotient = quotient.checked_add(1)?;
|
||||
rhs = self.checked_div(quotient)?;
|
||||
let remainder = self.checked_rem(quotient)?;
|
||||
if remainder > 0 {
|
||||
rhs = rhs.checked_add(1)?;
|
||||
}
|
||||
}
|
||||
Some((quotient, rhs))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod math;
|
||||
pub use math::*;
|
Loading…
Reference in New Issue