wip/gobbler-lps
This commit is contained in:
parent
7f1b42f70f
commit
60acc16480
|
@ -2111,6 +2111,9 @@ dependencies = [
|
||||||
"solana-program-test",
|
"solana-program-test",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"spl-associated-token-account 1.1.3",
|
"spl-associated-token-account 1.1.3",
|
||||||
|
"spl-memo 3.0.1",
|
||||||
|
"spl-token 3.5.0",
|
||||||
|
"spl-token-2022 0.9.0",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -5358,7 +5361,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quic-geyser-client"
|
name = "quic-geyser-client"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
source = "git+https://github.com/blockworks-foundation/quic_geyser_plugin.git?branch=router_new_streaming#b0fa0a2f50240ab6c1e7a59b928ea74da9a6785b"
|
source = "git+https://github.com/blockworks-foundation/quic_geyser_plugin.git?branch=router_v1.17.29#594077ccebfde826920192c17b0e501bb185650e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
@ -5375,7 +5378,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quic-geyser-common"
|
name = "quic-geyser-common"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
source = "git+https://github.com/blockworks-foundation/quic_geyser_plugin.git?branch=router_new_streaming#b0fa0a2f50240ab6c1e7a59b928ea74da9a6785b"
|
source = "git+https://github.com/blockworks-foundation/quic_geyser_plugin.git?branch=router_v1.17.29#594077ccebfde826920192c17b0e501bb185650e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
|
|
@ -30,6 +30,10 @@ serde_derive = "1.0"
|
||||||
mango-feeds-connector = { workspace = true }
|
mango-feeds-connector = { workspace = true }
|
||||||
# raydium-cp
|
# raydium-cp
|
||||||
raydium-cp-swap = "0.1.143"
|
raydium-cp-swap = "0.1.143"
|
||||||
|
spl-memo = "3.0.0"
|
||||||
|
spl-token = "3.5.0"
|
||||||
|
spl-token-2022 = "0.9.0"
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
router-test-lib = { path = "../router-test-lib", version = "0.1" }
|
router-test-lib = { path = "../router-test-lib", version = "0.1" }
|
||||||
|
|
|
@ -7,7 +7,9 @@ use anchor_spl::token_2022::spl_token_2022::extension::{
|
||||||
};
|
};
|
||||||
use anchor_spl::token_2022::spl_token_2022::state::Mint;
|
use anchor_spl::token_2022::spl_token_2022::state::Mint;
|
||||||
use mango_feeds_connector::chain_data::AccountData;
|
use mango_feeds_connector::chain_data::AccountData;
|
||||||
use raydium_cp_swap::curve::{ConstantProductCurve, CurveCalculator, Fees, TradeDirection};
|
use raydium_cp_swap::curve::{
|
||||||
|
ConstantProductCurve, CurveCalculator, Fees, TradeDirection,
|
||||||
|
};
|
||||||
use raydium_cp_swap::states::{AmmConfig, PoolState, PoolStatusBitIndex};
|
use raydium_cp_swap::states::{AmmConfig, PoolState, PoolStatusBitIndex};
|
||||||
use solana_program::clock::Clock;
|
use solana_program::clock::Clock;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
|
@ -18,11 +20,30 @@ use std::panic;
|
||||||
|
|
||||||
use router_lib::dex::{DexEdge, DexEdgeIdentifier};
|
use router_lib::dex::{DexEdge, DexEdgeIdentifier};
|
||||||
|
|
||||||
|
/// Enums to represent operations and directions
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Operation {
|
||||||
|
Swap,
|
||||||
|
Deposit,
|
||||||
|
Withdraw,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Direction {
|
||||||
|
AtoB,
|
||||||
|
BtoA,
|
||||||
|
AandBtoLP,
|
||||||
|
LPtoAandB,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modify GobblerEdgeIdentifier to include operation and direction
|
||||||
pub struct GobblerEdgeIdentifier {
|
pub struct GobblerEdgeIdentifier {
|
||||||
pub pool: Pubkey,
|
pub pool: Pubkey,
|
||||||
pub mint_a: Pubkey,
|
pub mint_a: Pubkey,
|
||||||
pub mint_b: Pubkey,
|
pub mint_b: Pubkey,
|
||||||
pub is_a_to_b: bool,
|
pub lp_mint: Pubkey,
|
||||||
|
pub operation: Operation,
|
||||||
|
pub direction: Direction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DexEdgeIdentifier for GobblerEdgeIdentifier {
|
impl DexEdgeIdentifier for GobblerEdgeIdentifier {
|
||||||
|
@ -31,19 +52,41 @@ impl DexEdgeIdentifier for GobblerEdgeIdentifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn desc(&self) -> String {
|
fn desc(&self) -> String {
|
||||||
format!("Gobbler_{}", self.pool)
|
format!(
|
||||||
|
"Gobbler_{}_{}_{}",
|
||||||
|
self.operation_string(),
|
||||||
|
self.direction_string(),
|
||||||
|
self.pool
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_mint(&self) -> Pubkey {
|
fn input_mint(&self) -> Pubkey {
|
||||||
self.mint_a
|
match self.operation {
|
||||||
|
Operation::Swap => match self.direction {
|
||||||
|
Direction::AtoB => self.mint_a,
|
||||||
|
Direction::BtoA => self.mint_b,
|
||||||
|
_ => self.mint_a, // Default case
|
||||||
|
},
|
||||||
|
Operation::Deposit => self.mint_a, // For deposit, input mints are mint_a and mint_b
|
||||||
|
Operation::Withdraw => self.lp_mint,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_mint(&self) -> Pubkey {
|
fn output_mint(&self) -> Pubkey {
|
||||||
self.mint_b
|
match self.operation {
|
||||||
|
Operation::Swap => match self.direction {
|
||||||
|
Direction::AtoB => self.mint_b,
|
||||||
|
Direction::BtoA => self.mint_a,
|
||||||
|
_ => self.mint_b, // Default case
|
||||||
|
},
|
||||||
|
Operation::Deposit => self.lp_mint,
|
||||||
|
Operation::Withdraw => self.mint_a, // For withdraw, output mints are mint_a and mint_b
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accounts_needed(&self) -> usize {
|
fn accounts_needed(&self) -> usize {
|
||||||
11
|
// Adjust based on operation
|
||||||
|
11 // This might vary depending on actual requirements
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
@ -51,6 +94,26 @@ impl DexEdgeIdentifier for GobblerEdgeIdentifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GobblerEdgeIdentifier {
|
||||||
|
fn operation_string(&self) -> &'static str {
|
||||||
|
match self.operation {
|
||||||
|
Operation::Swap => "Swap",
|
||||||
|
Operation::Deposit => "Deposit",
|
||||||
|
Operation::Withdraw => "Withdraw",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn direction_string(&self) -> &'static str {
|
||||||
|
match self.direction {
|
||||||
|
Direction::AtoB => "AtoB",
|
||||||
|
Direction::BtoA => "BtoA",
|
||||||
|
Direction::AandBtoLP => "AandBtoLP",
|
||||||
|
Direction::LPtoAandB => "LPtoAandB",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Modify GobblerEdge to include operation and direction
|
||||||
pub struct GobblerEdge {
|
pub struct GobblerEdge {
|
||||||
pub pool: PoolState,
|
pub pool: PoolState,
|
||||||
pub config: AmmConfig,
|
pub config: AmmConfig,
|
||||||
|
@ -58,6 +121,9 @@ pub struct GobblerEdge {
|
||||||
pub vault_1_amount: u64,
|
pub vault_1_amount: u64,
|
||||||
pub mint_0: Option<TransferFeeConfig>,
|
pub mint_0: Option<TransferFeeConfig>,
|
||||||
pub mint_1: Option<TransferFeeConfig>,
|
pub mint_1: Option<TransferFeeConfig>,
|
||||||
|
pub lp_supply: u64,
|
||||||
|
pub operation: Operation,
|
||||||
|
pub direction: Direction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DexEdge for GobblerEdge {
|
impl DexEdge for GobblerEdge {
|
||||||
|
@ -94,11 +160,8 @@ pub fn swap_base_input(
|
||||||
amount_in: u64,
|
amount_in: u64,
|
||||||
) -> anyhow::Result<(u64, u64, u64)> {
|
) -> anyhow::Result<(u64, u64, u64)> {
|
||||||
let res = panic::catch_unwind(|| {
|
let res = panic::catch_unwind(|| {
|
||||||
let pool_state = pool;
|
let block_timestamp = Clock::get()?.unix_timestamp as u64;
|
||||||
let block_timestamp = pool_state.open_time + 1; // TODO: This should be the actual clock
|
if !pool.get_status_by_bit(PoolStatusBitIndex::Swap) || block_timestamp < pool.open_time {
|
||||||
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Swap)
|
|
||||||
|| block_timestamp < pool_state.open_time
|
|
||||||
{
|
|
||||||
return Err(anyhow!("Pool is not trading"));
|
return Err(anyhow!("Pool is not trading"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,48 +171,53 @@ pub fn swap_base_input(
|
||||||
return Err(anyhow!("Amount too low after transfer fee"));
|
return Err(anyhow!("Amount too low after transfer fee"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (total_input_token_amount, total_output_token_amount) = if input_vault_key == pool_state.token_0_vault
|
let (total_input_token_amount, total_output_token_amount) =
|
||||||
&& output_vault_key == pool_state.token_1_vault
|
if input_vault_key == pool.token_0_vault && output_vault_key == pool.token_1_vault {
|
||||||
{
|
vault_amount_without_fee(pool, input_vault_amount, output_vault_amount)?
|
||||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?
|
} else if input_vault_key == pool.token_1_vault && output_vault_key == pool.token_0_vault
|
||||||
} else if input_vault_key == pool_state.token_1_vault
|
{
|
||||||
&& output_vault_key == pool_state.token_0_vault
|
let (out, inp) =
|
||||||
{
|
vault_amount_without_fee(pool, output_vault_amount, input_vault_amount)?;
|
||||||
let (out, inp) = vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
(inp, out)
|
||||||
(inp, out)
|
} else {
|
||||||
} else {
|
return Err(anyhow!("Invalid vault configuration"));
|
||||||
return Err(anyhow!("Invalid vault configuration"));
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
let (input_token_creator_rate, input_token_lp_rate) =
|
||||||
|
if input_vault_key == pool.token_0_vault {
|
||||||
|
(amm_config.token_0_creator_rate, amm_config.token_0_lp_rate)
|
||||||
|
} else {
|
||||||
|
(amm_config.token_1_creator_rate, amm_config.token_1_lp_rate)
|
||||||
|
};
|
||||||
|
|
||||||
let (input_token_creator_rate, input_token_lp_rate) = if input_vault_key == pool_state.token_0_vault {
|
let protocol_fee = (amm_config.token_0_creator_rate
|
||||||
(amm_config.token_0_creator_rate, amm_config.token_0_lp_rate)
|
|
||||||
} else {
|
|
||||||
(amm_config.token_1_creator_rate, amm_config.token_1_lp_rate)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let protocol_fee = (amm_config.token_0_creator_rate
|
|
||||||
+ amm_config.token_1_creator_rate
|
+ amm_config.token_1_creator_rate
|
||||||
+ amm_config.token_0_lp_rate
|
+ amm_config.token_0_lp_rate
|
||||||
+ amm_config.token_1_lp_rate) / 10000;
|
+ amm_config.token_1_lp_rate)
|
||||||
|
/ 10000;
|
||||||
|
|
||||||
let swap_result = raydium_cp_swap::curve::CurveCalculator::swap_base_input(
|
let swap_result = CurveCalculator::swap_base_input(
|
||||||
actual_amount_in.into(),
|
actual_amount_in.into(),
|
||||||
total_input_token_amount.into(),
|
total_input_token_amount.into(),
|
||||||
total_output_token_amount.into(),
|
total_output_token_amount.into(),
|
||||||
protocol_fee+input_token_creator_rate+input_token_lp_rate,
|
protocol_fee + input_token_creator_rate + input_token_lp_rate,
|
||||||
input_token_creator_rate,
|
input_token_creator_rate,
|
||||||
input_token_lp_rate,
|
input_token_lp_rate,
|
||||||
).ok_or(anyhow!("Swap calculation failed"))?;
|
)
|
||||||
|
.ok_or(anyhow!("Swap calculation failed"))?;
|
||||||
|
|
||||||
let output_transfer_fee = get_transfer_fee(output_mint, swap_result.destination_amount_swapped.try_into()?)?;
|
let output_transfer_fee =
|
||||||
let amount_received = swap_result.destination_amount_swapped.saturating_sub(output_transfer_fee.into());
|
get_transfer_fee(output_mint, swap_result.destination_amount_swapped.try_into()?)?;
|
||||||
|
let amount_received = swap_result
|
||||||
|
.destination_amount_swapped
|
||||||
|
.saturating_sub(output_transfer_fee.into());
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
amount_in,
|
amount_in,
|
||||||
amount_received.try_into().map_err(|e| anyhow!("Failed to convert amount_received: {}", e))?,
|
amount_received.try_into()?,
|
||||||
(protocol_fee+input_token_creator_rate+input_token_lp_rate).try_into().map_err(|e| anyhow!("Failed to convert fees: {}", e))?,
|
(protocol_fee + input_token_creator_rate + input_token_lp_rate)
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| anyhow!("Failed to convert fees: {}", e))?,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -162,18 +230,15 @@ pub fn swap_base_output(
|
||||||
amm_config: &AmmConfig,
|
amm_config: &AmmConfig,
|
||||||
input_vault_key: Pubkey,
|
input_vault_key: Pubkey,
|
||||||
input_vault_amount: u64,
|
input_vault_amount: u64,
|
||||||
_input_mint: &Option<TransferFeeConfig>,
|
input_mint: &Option<TransferFeeConfig>,
|
||||||
output_vault_key: Pubkey,
|
output_vault_key: Pubkey,
|
||||||
output_vault_amount: u64,
|
output_vault_amount: u64,
|
||||||
output_mint: &Option<TransferFeeConfig>,
|
output_mint: &Option<TransferFeeConfig>,
|
||||||
amount_out: u64,
|
amount_out: u64,
|
||||||
) -> anyhow::Result<(u64, u64, u64)> {
|
) -> anyhow::Result<(u64, u64, u64)> {
|
||||||
let res = panic::catch_unwind(|| {
|
let res = panic::catch_unwind(|| {
|
||||||
let pool_state = pool;
|
let block_timestamp = Clock::get()?.unix_timestamp as u64;
|
||||||
let block_timestamp = pool_state.open_time + 1; // TODO: This should be the actual clock
|
if !pool.get_status_by_bit(PoolStatusBitIndex::Swap) || block_timestamp < pool.open_time {
|
||||||
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Swap)
|
|
||||||
|| block_timestamp < pool_state.open_time
|
|
||||||
{
|
|
||||||
return Err(anyhow!("Pool is not trading"));
|
return Err(anyhow!("Pool is not trading"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,53 +247,62 @@ pub fn swap_base_output(
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_transfer_fee = get_transfer_fee(output_mint, amount_out)?;
|
let output_transfer_fee = get_transfer_fee(output_mint, amount_out)?;
|
||||||
let actual_amount_out = amount_out.checked_add(output_transfer_fee)
|
let actual_amount_out = amount_out
|
||||||
|
.checked_add(output_transfer_fee)
|
||||||
.ok_or_else(|| anyhow!("Output amount overflow"))?;
|
.ok_or_else(|| anyhow!("Output amount overflow"))?;
|
||||||
|
|
||||||
let (total_input_token_amount, total_output_token_amount) = if input_vault_key == pool_state.token_0_vault
|
let (total_input_token_amount, total_output_token_amount) =
|
||||||
&& output_vault_key == pool_state.token_1_vault
|
if input_vault_key == pool.token_0_vault && output_vault_key == pool.token_1_vault {
|
||||||
{
|
vault_amount_without_fee(pool, input_vault_amount, output_vault_amount)?
|
||||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?
|
} else if input_vault_key == pool.token_1_vault && output_vault_key == pool.token_0_vault
|
||||||
} else if input_vault_key == pool_state.token_1_vault
|
{
|
||||||
&& output_vault_key == pool_state.token_0_vault
|
let (out, inp) =
|
||||||
{
|
vault_amount_without_fee(pool, output_vault_amount, input_vault_amount)?;
|
||||||
let (out, inp) = vault_amount_without_fee(pool_state, output_vault_amount, input_vault_amount)?;
|
(inp, out)
|
||||||
(inp, out)
|
} else {
|
||||||
} else {
|
return Err(anyhow!("Invalid vault configuration"));
|
||||||
return Err(anyhow!("Invalid vault configuration"));
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if total_output_token_amount < actual_amount_out.into() {
|
if total_output_token_amount < actual_amount_out.into() {
|
||||||
return Err(anyhow!("Insufficient liquidity"));
|
return Err(anyhow!("Insufficient liquidity"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (input_token_creator_rate, input_token_lp_rate) = if input_vault_key == pool_state.token_0_vault {
|
let (input_token_creator_rate, input_token_lp_rate) =
|
||||||
(amm_config.token_0_creator_rate, amm_config.token_0_lp_rate)
|
if input_vault_key == pool.token_0_vault {
|
||||||
} else {
|
(amm_config.token_0_creator_rate, amm_config.token_0_lp_rate)
|
||||||
(amm_config.token_1_creator_rate, amm_config.token_1_lp_rate)
|
} else {
|
||||||
};
|
(amm_config.token_1_creator_rate, amm_config.token_1_lp_rate)
|
||||||
|
};
|
||||||
|
|
||||||
let protocol_fee = (amm_config.token_0_creator_rate
|
let protocol_fee = (amm_config.token_0_creator_rate
|
||||||
+ amm_config.token_1_creator_rate
|
+ amm_config.token_1_creator_rate
|
||||||
+ amm_config.token_0_lp_rate
|
+ amm_config.token_0_lp_rate
|
||||||
+ amm_config.token_1_lp_rate) / 10000;
|
+ amm_config.token_1_lp_rate)
|
||||||
|
/ 10000;
|
||||||
|
|
||||||
let swap_result = raydium_cp_swap::curve::CurveCalculator::swap_base_output(
|
let swap_result = CurveCalculator::swap_base_output(
|
||||||
actual_amount_out.into(),
|
actual_amount_out.into(),
|
||||||
total_input_token_amount.into(),
|
total_input_token_amount.into(),
|
||||||
total_output_token_amount.into(),
|
total_output_token_amount.into(),
|
||||||
protocol_fee+input_token_creator_rate+input_token_lp_rate,
|
protocol_fee + input_token_creator_rate + input_token_lp_rate,
|
||||||
input_token_creator_rate,
|
input_token_creator_rate,
|
||||||
input_token_lp_rate,
|
input_token_lp_rate,
|
||||||
).ok_or(anyhow!("Swap calculation failed"))?;
|
)
|
||||||
|
.ok_or(anyhow!("Swap calculation failed"))?;
|
||||||
|
|
||||||
let input_transfer_fee = get_transfer_fee(_input_mint, swap_result.source_amount_swapped.try_into()?)?;
|
let input_transfer_fee =
|
||||||
let amount_in = swap_result.source_amount_swapped.saturating_add(input_transfer_fee.into());
|
get_transfer_fee(input_mint, swap_result.source_amount_swapped.try_into()?)?;
|
||||||
|
let amount_in = swap_result
|
||||||
|
.source_amount_swapped
|
||||||
|
.saturating_add(input_transfer_fee.into());
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
amount_in.try_into().map_err(|e| anyhow!("Failed to convert amount_in: {}", e))?,
|
amount_in.try_into()?,
|
||||||
amount_out,
|
amount_out,
|
||||||
swap_result.total_fees.try_into().map_err(|e| anyhow!("Failed to convert fees: {}", e))?,
|
swap_result
|
||||||
|
.total_fees
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| anyhow!("Failed to convert fees: {}", e))?,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -242,7 +316,7 @@ pub fn get_transfer_fee(
|
||||||
let fee = if let Some(transfer_fee_config) = mint_info {
|
let fee = if let Some(transfer_fee_config) = mint_info {
|
||||||
transfer_fee_config
|
transfer_fee_config
|
||||||
.calculate_epoch_fee(Clock::get()?.epoch, pre_fee_amount)
|
.calculate_epoch_fee(Clock::get()?.epoch, pre_fee_amount)
|
||||||
.unwrap()
|
.unwrap_or(0)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
@ -257,9 +331,9 @@ pub fn vault_amount_without_fee(
|
||||||
Ok((
|
Ok((
|
||||||
vault_0
|
vault_0
|
||||||
.checked_sub(pool.protocol_fees_token_0 + pool.fund_fees_token_0)
|
.checked_sub(pool.protocol_fees_token_0 + pool.fund_fees_token_0)
|
||||||
.ok_or(anyhow::format_err!("invalid sub"))?,
|
.ok_or(anyhow::format_err!("Invalid subtraction for vault_0"))?,
|
||||||
vault_1
|
vault_1
|
||||||
.checked_sub(pool.protocol_fees_token_1 + pool.fund_fees_token_1)
|
.checked_sub(pool.protocol_fees_token_1 + pool.fund_fees_token_1)
|
||||||
.ok_or(anyhow::format_err!("invalid sub"))?,
|
.ok_or(anyhow::format_err!("Invalid subtraction for vault_1"))?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::edge::{swap_base_input, swap_base_output, GobblerEdge, GobblerEdgeIdentifier};
|
use crate::edge::{ swap_base_input, swap_base_output, Direction, GobblerEdge, GobblerEdgeIdentifier, Operation, _get_transfer_config};
|
||||||
use crate::gobbler_ix_builder;
|
use crate::gobbler_ix_builder;
|
||||||
use anchor_lang::{AccountDeserialize, Discriminator, Id};
|
use anchor_lang::{AccountDeserialize, Discriminator, Id};
|
||||||
use anchor_spl::token::spl_token::state::AccountState;
|
use anchor_spl::token::spl_token::state::AccountState;
|
||||||
use anchor_spl::token::{spl_token, Token};
|
use anchor_spl::token::{spl_token, Token};
|
||||||
use anchor_spl::token_2022::spl_token_2022;
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -43,82 +42,108 @@ impl DexInterface for GobblerDex {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let pools =
|
// Fetch all PoolState accounts
|
||||||
fetch_raydium_account::<PoolState>(rpc, raydium_cp_swap::id(), PoolState::LEN).await?;
|
let pools = fetch_gobbler_pools(rpc).await?;
|
||||||
|
|
||||||
|
// Collect vaults to identify any banned ones (e.g., frozen accounts)
|
||||||
let vaults = pools
|
let vaults = pools
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|x| [x.1.token_0_vault, x.1.token_1_vault])
|
.flat_map(|(_, pool)| vec![pool.token_0_vault, pool.token_1_vault])
|
||||||
.collect::<HashSet<_>>();
|
|
||||||
let vaults = rpc.get_multiple_accounts(&vaults).await?;
|
|
||||||
let banned_vaults = vaults
|
|
||||||
.iter()
|
|
||||||
.filter(|x| {
|
|
||||||
x.1.owner == Token::id()
|
|
||||||
&& spl_token::state::Account::unpack(x.1.data()).unwrap().state
|
|
||||||
== AccountState::Frozen
|
|
||||||
})
|
|
||||||
.map(|x| x.0)
|
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let pools = pools
|
let vault_accounts = rpc.get_multiple_accounts(&vaults).await?;
|
||||||
|
let banned_vaults = vault_accounts
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_pool_pk, pool)| {
|
.filter(|(_, account)| {
|
||||||
pool.token_0_program == Token::id() && pool.token_1_program == Token::id()
|
account.owner == Token::id()
|
||||||
// TODO Remove filter when 2022 are working
|
&& spl_token::state::Account::unpack(account.data())
|
||||||
|
.map(|acc| acc.state == AccountState::Frozen)
|
||||||
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
.filter(|(_pool_pk, pool)| {
|
.map(|(pubkey, _)| *pubkey)
|
||||||
!banned_vaults.contains(&pool.token_0_vault)
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
|
// Filter out pools with banned vaults or unsupported token programs
|
||||||
|
let valid_pools = pools
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, pool)| {
|
||||||
|
pool.token_0_program == Token::id()
|
||||||
|
&& pool.token_1_program == Token::id()
|
||||||
|
&& !banned_vaults.contains(&pool.token_0_vault)
|
||||||
&& !banned_vaults.contains(&pool.token_1_vault)
|
&& !banned_vaults.contains(&pool.token_1_vault)
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let edge_pairs = pools
|
// Create edge identifiers for each pool
|
||||||
.iter()
|
let mut edge_identifiers = Vec::new();
|
||||||
.map(|(pool_pk, pool)| {
|
|
||||||
(
|
for (pool_pk, pool) in &valid_pools {
|
||||||
Arc::new(GobblerEdgeIdentifier {
|
// Swap edges between Token A and Token B
|
||||||
pool: *pool_pk,
|
let swap_a_to_b = Arc::new(GobblerEdgeIdentifier {
|
||||||
mint_a: pool.token_0_mint,
|
pool: *pool_pk,
|
||||||
mint_b: pool.token_1_mint,
|
mint_a: pool.token_0_mint,
|
||||||
is_a_to_b: true,
|
mint_b: pool.token_1_mint,
|
||||||
}),
|
lp_mint: pool.lp_mint,
|
||||||
Arc::new(GobblerEdgeIdentifier {
|
operation: Operation::Swap,
|
||||||
pool: *pool_pk,
|
direction: Direction::AtoB,
|
||||||
mint_a: pool.token_1_mint,
|
});
|
||||||
mint_b: pool.token_0_mint,
|
|
||||||
is_a_to_b: false,
|
let swap_b_to_a = Arc::new(GobblerEdgeIdentifier {
|
||||||
}),
|
pool: *pool_pk,
|
||||||
)
|
mint_a: pool.token_1_mint,
|
||||||
})
|
mint_b: pool.token_0_mint,
|
||||||
.collect_vec();
|
lp_mint: pool.lp_mint,
|
||||||
|
operation: Operation::Swap,
|
||||||
|
direction: Direction::BtoA,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deposit edge from (Token A, Token B) to LP Token
|
||||||
|
let deposit_edge = Arc::new(GobblerEdgeIdentifier {
|
||||||
|
pool: *pool_pk,
|
||||||
|
mint_a: pool.token_0_mint,
|
||||||
|
mint_b: pool.token_1_mint,
|
||||||
|
lp_mint: pool.lp_mint,
|
||||||
|
operation: Operation::Deposit,
|
||||||
|
direction: Direction::AandBtoLP,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Withdrawal edge from LP Token to (Token A, Token B)
|
||||||
|
let withdraw_edge = Arc::new(GobblerEdgeIdentifier {
|
||||||
|
pool: *pool_pk,
|
||||||
|
mint_a: pool.token_0_mint,
|
||||||
|
mint_b: pool.token_1_mint,
|
||||||
|
lp_mint: pool.lp_mint,
|
||||||
|
operation: Operation::Withdraw,
|
||||||
|
direction: Direction::LPtoAandB,
|
||||||
|
});
|
||||||
|
|
||||||
|
edge_identifiers.push(swap_a_to_b);
|
||||||
|
edge_identifiers.push(swap_b_to_a);
|
||||||
|
edge_identifiers.push(deposit_edge);
|
||||||
|
edge_identifiers.push(withdraw_edge);
|
||||||
|
}
|
||||||
|
|
||||||
let mut needed_accounts = HashSet::new();
|
let mut needed_accounts = HashSet::new();
|
||||||
|
let mut edges_per_pk = HashMap::new();
|
||||||
|
|
||||||
let edges_per_pk = {
|
for (pool_pk, pool) in &valid_pools {
|
||||||
let mut map = HashMap::new();
|
// Collect all necessary accounts
|
||||||
for ((pool_pk, pool), (edge_a_to_b, edge_b_to_a)) in pools.iter().zip(edge_pairs.iter())
|
needed_accounts.insert(*pool_pk);
|
||||||
{
|
needed_accounts.insert(pool.amm_config);
|
||||||
let entry = vec![
|
needed_accounts.insert(pool.token_0_vault);
|
||||||
edge_a_to_b.clone() as Arc<dyn DexEdgeIdentifier>,
|
needed_accounts.insert(pool.token_1_vault);
|
||||||
edge_b_to_a.clone(),
|
needed_accounts.insert(pool.lp_mint);
|
||||||
];
|
needed_accounts.insert(pool.token_0_mint);
|
||||||
|
needed_accounts.insert(pool.token_1_mint);
|
||||||
|
|
||||||
utils::insert_or_extend(&mut map, pool_pk, &entry);
|
// Map edges to their corresponding public keys
|
||||||
utils::insert_or_extend(&mut map, &pool.amm_config, &entry);
|
let edges = edge_identifiers
|
||||||
utils::insert_or_extend(&mut map, &pool.token_0_vault, &entry);
|
.iter()
|
||||||
utils::insert_or_extend(&mut map, &pool.token_1_vault, &entry);
|
.filter(|edge| edge.as_any().downcast_ref::<GobblerEdgeIdentifier>().unwrap().pool == *pool_pk)
|
||||||
|
.cloned()
|
||||||
needed_accounts.insert(*pool_pk);
|
.collect::<Vec<_>>();
|
||||||
needed_accounts.insert(pool.amm_config);
|
utils::insert_or_extend(&mut edges_per_pk, pool_pk, &edges.into_iter().map(|edge| edge as Arc<dyn DexEdgeIdentifier>).collect::<Vec<_>>());
|
||||||
needed_accounts.insert(pool.token_0_vault);
|
}
|
||||||
needed_accounts.insert(pool.token_1_vault);
|
|
||||||
// TODO Uncomment for Token-2022
|
|
||||||
// needed_accounts.insert(pool.token_0_mint);
|
|
||||||
// needed_accounts.insert(pool.token_1_mint);
|
|
||||||
}
|
|
||||||
map
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Arc::new(GobblerDex {
|
Ok(Arc::new(GobblerDex {
|
||||||
edges: edges_per_pk,
|
edges: edges_per_pk,
|
||||||
|
@ -132,12 +157,9 @@ impl DexInterface for GobblerDex {
|
||||||
|
|
||||||
fn subscription_mode(&self) -> DexSubscriptionMode {
|
fn subscription_mode(&self) -> DexSubscriptionMode {
|
||||||
DexSubscriptionMode::Mixed(MixedDexSubscription {
|
DexSubscriptionMode::Mixed(MixedDexSubscription {
|
||||||
accounts: Default::default(),
|
accounts: self.needed_accounts.clone(),
|
||||||
programs: HashSet::from([raydium_cp_swap::id()]),
|
programs: HashSet::from([raydium_cp_swap::id()]),
|
||||||
token_accounts_for_owner: HashSet::from([Pubkey::from_str(
|
token_accounts_for_owner: HashSet::new(),
|
||||||
"GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL",
|
|
||||||
)
|
|
||||||
.unwrap()]),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,35 +179,39 @@ impl DexInterface for GobblerDex {
|
||||||
let id = id
|
let id = id
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||||
.unwrap();
|
.context("Invalid edge identifier type")?;
|
||||||
|
|
||||||
let pool_account = chain_data.account(&id.pool)?;
|
let pool_account = chain_data.account(&id.pool)?;
|
||||||
let pool = PoolState::try_deserialize(&mut pool_account.account.data())?;
|
let pool = PoolState::try_deserialize(&mut pool_account.account.data())?;
|
||||||
|
|
||||||
let config_account = chain_data.account(&pool.amm_config)?;
|
let config_account = chain_data.account(&pool.amm_config)?;
|
||||||
let config = AmmConfig::try_deserialize(&mut config_account.account.data())?;
|
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.token_0_vault)?;
|
||||||
let vault_0 = spl_token_2022::state::Account::unpack(vault_0_account.account.data())?;
|
let vault_0 = spl_token::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.token_1_vault)?;
|
||||||
let vault_1 = spl_token_2022::state::Account::unpack(vault_1_account.account.data())?;
|
let vault_1 = spl_token::state::Account::unpack(&vault_1_account.account.data())?;
|
||||||
|
|
||||||
let transfer_0_fee = None;
|
let lp_mint_account = chain_data.account(&pool.lp_mint)?;
|
||||||
let transfer_1_fee = None;
|
let lp_mint = spl_token::state::Mint::unpack(&lp_mint_account.account.data())?;
|
||||||
|
|
||||||
// TODO Uncomment for Token-2022
|
let mint_0_account = chain_data.account(&pool.token_0_mint)?;
|
||||||
// let mint_0_account = chain_data.account(&pool.token_0_mint)?;
|
let mint_1_account = chain_data.account(&pool.token_1_mint)?;
|
||||||
// let mint_1_account = chain_data.account(&pool.token_1_mint)?;
|
|
||||||
// let transfer_0_fee = crate::edge::get_transfer_config(mint_0_account)?;
|
let mint_0 = _get_transfer_config(&mint_0_account)?;
|
||||||
// let transfer_1_fee = crate::edge::get_transfer_config(mint_1_account)?;
|
let mint_1 = _get_transfer_config(&mint_1_account)?;
|
||||||
|
|
||||||
Ok(Arc::new(GobblerEdge {
|
Ok(Arc::new(GobblerEdge {
|
||||||
pool,
|
pool,
|
||||||
config,
|
config,
|
||||||
vault_0_amount: vault_0.amount,
|
vault_0_amount: vault_0.amount,
|
||||||
vault_1_amount: vault_1.amount,
|
vault_1_amount: vault_1.amount,
|
||||||
mint_0: transfer_0_fee,
|
mint_0,
|
||||||
mint_1: transfer_1_fee,
|
mint_1,
|
||||||
|
lp_supply: lp_mint.supply,
|
||||||
|
operation: id.operation,
|
||||||
|
direction: id.direction,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,69 +225,36 @@ impl DexInterface for GobblerDex {
|
||||||
let id = id
|
let id = id
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||||
.unwrap();
|
.context("Invalid edge identifier type")?;
|
||||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().unwrap();
|
let edge = edge.as_any().downcast_ref::<GobblerEdge>().context("Invalid edge type")?;
|
||||||
|
|
||||||
if !edge.pool.get_status_by_bit(PoolStatusBitIndex::Swap) {
|
match id.operation {
|
||||||
return Ok(Quote {
|
Operation::Swap => {
|
||||||
in_amount: 0,
|
// Handle swap operation
|
||||||
out_amount: 0,
|
// Similar to previous implementation
|
||||||
fee_amount: 0,
|
// ...
|
||||||
fee_mint: edge.pool.token_0_mint,
|
}
|
||||||
});
|
Operation::Deposit => {
|
||||||
|
// Handle deposit operation
|
||||||
|
// Calculate LP tokens received for given in_amounts of Token A and Token B
|
||||||
|
// Return a Quote with LP tokens as out_amount
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
Operation::Withdraw => {
|
||||||
|
// Handle withdrawal operation
|
||||||
|
// Calculate amounts of Token A and Token B received for given in_amount of LP tokens
|
||||||
|
// Return a Quote with total value of tokens received as out_amount
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clock = chain_data.account(&Clock::id()).context("read clock")?;
|
// Placeholder return until implementation is provided
|
||||||
let now_ts = clock.account.deserialize_data::<Clock>()?.unix_timestamp as u64;
|
Ok(Quote {
|
||||||
if edge.pool.open_time > now_ts {
|
in_amount: 0,
|
||||||
return Ok(Quote {
|
out_amount: 0,
|
||||||
in_amount: 0,
|
fee_amount: 0,
|
||||||
out_amount: 0,
|
fee_mint: id.mint_a,
|
||||||
fee_amount: 0,
|
})
|
||||||
fee_mint: edge.pool.token_0_mint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let quote = if id.is_a_to_b {
|
|
||||||
let result = swap_base_input(
|
|
||||||
&edge.pool,
|
|
||||||
&edge.config,
|
|
||||||
edge.pool.token_0_vault,
|
|
||||||
edge.vault_0_amount,
|
|
||||||
&edge.mint_0,
|
|
||||||
edge.pool.token_1_vault,
|
|
||||||
edge.vault_1_amount,
|
|
||||||
&edge.mint_1,
|
|
||||||
in_amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Quote {
|
|
||||||
in_amount: result.0,
|
|
||||||
out_amount: result.1,
|
|
||||||
fee_amount: result.2,
|
|
||||||
fee_mint: edge.pool.token_0_mint,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let result = swap_base_input(
|
|
||||||
&edge.pool,
|
|
||||||
&edge.config,
|
|
||||||
edge.pool.token_1_vault,
|
|
||||||
edge.vault_1_amount,
|
|
||||||
&edge.mint_1,
|
|
||||||
edge.pool.token_0_vault,
|
|
||||||
edge.vault_0_amount,
|
|
||||||
&edge.mint_0,
|
|
||||||
in_amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Quote {
|
|
||||||
in_amount: result.0,
|
|
||||||
out_amount: result.1,
|
|
||||||
fee_amount: result.2,
|
|
||||||
fee_mint: edge.pool.token_1_mint,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(quote)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_swap_ix(
|
fn build_swap_ix(
|
||||||
|
@ -276,15 +269,38 @@ impl DexInterface for GobblerDex {
|
||||||
let id = id
|
let id = id
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||||
.unwrap();
|
.context("Invalid edge identifier type")?;
|
||||||
gobbler_ix_builder::build_swap_ix(
|
|
||||||
id,
|
match id.operation {
|
||||||
chain_data,
|
Operation::Swap => {
|
||||||
wallet_pk,
|
gobbler_ix_builder::build_swap_ix(
|
||||||
in_amount,
|
id,
|
||||||
out_amount,
|
chain_data,
|
||||||
max_slippage_bps,
|
wallet_pk,
|
||||||
)
|
in_amount,
|
||||||
|
out_amount,
|
||||||
|
max_slippage_bps,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Operation::Deposit => {
|
||||||
|
gobbler_ix_builder::build_deposit_ix(
|
||||||
|
id,
|
||||||
|
chain_data,
|
||||||
|
wallet_pk,
|
||||||
|
in_amount, // Adjust as needed
|
||||||
|
max_slippage_bps,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Operation::Withdraw => {
|
||||||
|
gobbler_ix_builder::build_withdraw_ix(
|
||||||
|
id,
|
||||||
|
chain_data,
|
||||||
|
wallet_pk,
|
||||||
|
in_amount, // Adjust as needed
|
||||||
|
max_slippage_bps,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supports_exact_out(&self, _id: &Arc<dyn DexEdgeIdentifier>) -> bool {
|
fn supports_exact_out(&self, _id: &Arc<dyn DexEdgeIdentifier>) -> bool {
|
||||||
|
@ -301,81 +317,46 @@ impl DexInterface for GobblerDex {
|
||||||
let id = id
|
let id = id
|
||||||
.as_any()
|
.as_any()
|
||||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||||
.unwrap();
|
.context("Invalid edge identifier type")?;
|
||||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().unwrap();
|
let edge = edge.as_any().downcast_ref::<GobblerEdge>().context("Invalid edge type")?;
|
||||||
|
|
||||||
if !edge.pool.get_status_by_bit(PoolStatusBitIndex::Swap) {
|
match id.operation {
|
||||||
return Ok(Quote {
|
Operation::Swap => {
|
||||||
in_amount: u64::MAX,
|
// Handle swap operation
|
||||||
out_amount: 0,
|
// Similar to previous implementation
|
||||||
fee_amount: 0,
|
// ...
|
||||||
fee_mint: edge.pool.token_0_mint,
|
}
|
||||||
});
|
Operation::Deposit => {
|
||||||
|
// Handle deposit operation
|
||||||
|
// Calculate amounts of Token A and Token B required to receive given out_amount of LP tokens
|
||||||
|
// Return a Quote with total value of tokens required as in_amount
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
Operation::Withdraw => {
|
||||||
|
// Handle withdrawal operation
|
||||||
|
// Calculate in_amount of LP tokens required to receive given out_amounts of Token A and Token B
|
||||||
|
// Return a Quote with LP tokens as in_amount
|
||||||
|
// ...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clock = chain_data.account(&Clock::id()).context("read clock")?;
|
// Placeholder return until implementation is provided
|
||||||
let now_ts = clock.account.deserialize_data::<Clock>()?.unix_timestamp as u64;
|
Ok(Quote {
|
||||||
if edge.pool.open_time > now_ts {
|
in_amount: 0,
|
||||||
return Ok(Quote {
|
out_amount: 0,
|
||||||
in_amount: u64::MAX,
|
fee_amount: 0,
|
||||||
out_amount: 0,
|
fee_mint: id.mint_a,
|
||||||
fee_amount: 0,
|
})
|
||||||
fee_mint: edge.pool.token_0_mint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let quote = if id.is_a_to_b {
|
|
||||||
let result = swap_base_output(
|
|
||||||
&edge.pool,
|
|
||||||
&edge.config,
|
|
||||||
edge.pool.token_0_vault,
|
|
||||||
edge.vault_0_amount,
|
|
||||||
&edge.mint_0,
|
|
||||||
edge.pool.token_1_vault,
|
|
||||||
edge.vault_1_amount,
|
|
||||||
&edge.mint_1,
|
|
||||||
out_amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Quote {
|
|
||||||
in_amount: result.0,
|
|
||||||
out_amount: result.1,
|
|
||||||
fee_amount: result.2,
|
|
||||||
fee_mint: edge.pool.token_0_mint,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let result = swap_base_output(
|
|
||||||
&edge.pool,
|
|
||||||
&edge.config,
|
|
||||||
edge.pool.token_1_vault,
|
|
||||||
edge.vault_1_amount,
|
|
||||||
&edge.mint_1,
|
|
||||||
edge.pool.token_0_vault,
|
|
||||||
edge.vault_0_amount,
|
|
||||||
&edge.mint_0,
|
|
||||||
out_amount,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Quote {
|
|
||||||
in_amount: result.0,
|
|
||||||
out_amount: result.1,
|
|
||||||
fee_amount: result.2,
|
|
||||||
fee_mint: edge.pool.token_1_mint,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(quote)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_raydium_account<T: Discriminator + AccountDeserialize>(
|
async fn fetch_gobbler_pools(
|
||||||
rpc: &mut RouterRpcClient,
|
rpc: &mut RouterRpcClient,
|
||||||
program_id: Pubkey,
|
) -> anyhow::Result<Vec<(Pubkey, PoolState)>> {
|
||||||
len: usize,
|
|
||||||
) -> anyhow::Result<Vec<(Pubkey, T)>> {
|
|
||||||
let config = RpcProgramAccountsConfig {
|
let config = RpcProgramAccountsConfig {
|
||||||
filters: Some(vec![
|
filters: Some(vec![
|
||||||
RpcFilterType::DataSize(len as u64),
|
RpcFilterType::DataSize(PoolState::LEN as u64),
|
||||||
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, T::DISCRIMINATOR.to_vec())),
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, PoolState::DISCRIMINATOR.to_vec())),
|
||||||
]),
|
]),
|
||||||
account_config: RpcAccountInfoConfig {
|
account_config: RpcAccountInfoConfig {
|
||||||
encoding: Some(UiAccountEncoding::Base64),
|
encoding: Some(UiAccountEncoding::Base64),
|
||||||
|
@ -386,16 +367,17 @@ async fn fetch_raydium_account<T: Discriminator + AccountDeserialize>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let snapshot = rpc
|
let snapshot = rpc
|
||||||
.get_program_accounts_with_config(&program_id, config)
|
.get_program_accounts_with_config(&raydium_cp_swap::id(), config)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let result = snapshot
|
let result = snapshot
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|account| {
|
.map(|account| {
|
||||||
let pool: T = T::try_deserialize(&mut account.data.as_slice()).unwrap();
|
let pool: PoolState =
|
||||||
|
PoolState::try_deserialize(&mut account.data.as_slice()).unwrap();
|
||||||
(account.pubkey, pool)
|
(account.pubkey, pool)
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
use crate::edge::GobblerEdgeIdentifier;
|
use crate::edge::{GobblerEdgeIdentifier, Operation, Direction};
|
||||||
use anchor_lang::{AccountDeserialize, Id, InstructionData, ToAccountMetas};
|
use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas};
|
||||||
use anchor_spl::associated_token::get_associated_token_address;
|
use anchor_spl::associated_token::get_associated_token_address;
|
||||||
|
use raydium_cp_swap::AUTH_SEED;
|
||||||
|
use raydium_cp_swap::instruction::{Deposit, Withdraw, SwapBaseInput};
|
||||||
use raydium_cp_swap::program::RaydiumCpSwap;
|
use raydium_cp_swap::program::RaydiumCpSwap;
|
||||||
use raydium_cp_swap::states::PoolState;
|
use raydium_cp_swap::states::PoolState;
|
||||||
use raydium_cp_swap::AUTH_SEED;
|
|
||||||
use router_lib::dex::{AccountProviderView, SwapInstruction};
|
use router_lib::dex::{AccountProviderView, SwapInstruction};
|
||||||
use solana_program::instruction::Instruction;
|
use solana_program::instruction::Instruction;
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use solana_sdk::account::ReadableAccount;
|
use solana_sdk::account::ReadableAccount;
|
||||||
|
use spl_memo::id as spl_memo_id;
|
||||||
|
use spl_token::id as spl_token_id;
|
||||||
|
use spl_token_2022::id as spl_token_2022_id;
|
||||||
|
|
||||||
pub fn build_swap_ix(
|
pub fn build_swap_ix(
|
||||||
id: &GobblerEdgeIdentifier,
|
id: &GobblerEdgeIdentifier,
|
||||||
|
@ -24,58 +28,200 @@ pub fn build_swap_ix(
|
||||||
let other_amount_threshold =
|
let other_amount_threshold =
|
||||||
((out_amount as f64 * (10_000f64 - max_slippage_bps as f64)) / 10_000f64).floor() as u64;
|
((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 {
|
match id.operation {
|
||||||
(pool.token_0_mint, pool.token_1_mint)
|
Operation::Swap => {
|
||||||
} else {
|
// Handle swap operation
|
||||||
(pool.token_1_mint, pool.token_0_mint)
|
let (input_token_mint, output_token_mint) = match id.direction {
|
||||||
};
|
Direction::AtoB => (pool.token_0_mint, pool.token_1_mint),
|
||||||
let (input_token_program, output_token_program) = if id.is_a_to_b {
|
Direction::BtoA => (pool.token_1_mint, pool.token_0_mint),
|
||||||
(pool.token_0_program, pool.token_1_program)
|
_ => return Err(anyhow::anyhow!("Invalid direction for swap operation")),
|
||||||
} else {
|
};
|
||||||
(pool.token_1_program, pool.token_0_program)
|
|
||||||
};
|
let (input_token_program, output_token_program) = match id.direction {
|
||||||
let (input_vault, output_vault) = if id.is_a_to_b {
|
Direction::AtoB => (pool.token_0_program, pool.token_1_program),
|
||||||
(pool.token_0_vault, pool.token_1_vault)
|
Direction::BtoA => (pool.token_1_program, pool.token_0_program),
|
||||||
} else {
|
_ => return Err(anyhow::anyhow!("Invalid direction for swap operation")),
|
||||||
(pool.token_1_vault, pool.token_0_vault)
|
};
|
||||||
|
|
||||||
|
let (input_vault, output_vault) = match id.direction {
|
||||||
|
Direction::AtoB => (pool.token_0_vault, pool.token_1_vault),
|
||||||
|
Direction::BtoA => (pool.token_1_vault, pool.token_0_vault),
|
||||||
|
_ => return Err(anyhow::anyhow!("Invalid direction for swap operation")),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (input_token_account, output_token_account) = (
|
||||||
|
get_associated_token_address(wallet_pk, &input_token_mint),
|
||||||
|
get_associated_token_address(wallet_pk, &output_token_mint),
|
||||||
|
);
|
||||||
|
|
||||||
|
let instruction = SwapBaseInput {
|
||||||
|
amount_in: amount,
|
||||||
|
minimum_amount_out: other_amount_threshold,
|
||||||
|
};
|
||||||
|
let (authority, __bump) =
|
||||||
|
Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &raydium_cp_swap::id());
|
||||||
|
|
||||||
|
let accounts = raydium_cp_swap::accounts::Swap {
|
||||||
|
payer: *wallet_pk,
|
||||||
|
authority,
|
||||||
|
amm_config: pool.amm_config,
|
||||||
|
pool_state: id.pool,
|
||||||
|
input_token_account,
|
||||||
|
output_token_account,
|
||||||
|
input_vault,
|
||||||
|
output_vault,
|
||||||
|
input_token_program,
|
||||||
|
output_token_program,
|
||||||
|
input_token_mint,
|
||||||
|
output_token_mint,
|
||||||
|
observation_state: pool.observation_key,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = SwapInstruction {
|
||||||
|
instruction: Instruction {
|
||||||
|
program_id: raydium_cp_swap::id(),
|
||||||
|
accounts: accounts.to_account_metas(None),
|
||||||
|
data: instruction.data(),
|
||||||
|
},
|
||||||
|
out_pubkey: output_token_account,
|
||||||
|
out_mint: output_token_mint,
|
||||||
|
in_amount_offset: 8,
|
||||||
|
cu_estimate: Some(40_000),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
Operation::Deposit => build_deposit_ix(
|
||||||
|
id,
|
||||||
|
chain_data,
|
||||||
|
wallet_pk,
|
||||||
|
in_amount,
|
||||||
|
max_slippage_bps,
|
||||||
|
),
|
||||||
|
Operation::Withdraw => build_withdraw_ix(
|
||||||
|
id,
|
||||||
|
chain_data,
|
||||||
|
wallet_pk,
|
||||||
|
in_amount,
|
||||||
|
max_slippage_bps,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_deposit_ix(
|
||||||
|
id: &GobblerEdgeIdentifier,
|
||||||
|
chain_data: &AccountProviderView,
|
||||||
|
wallet_pk: &Pubkey,
|
||||||
|
lp_amount: u64,
|
||||||
|
max_slippage_bps: i32,
|
||||||
|
) -> anyhow::Result<SwapInstruction> {
|
||||||
|
let pool_account = chain_data.account(&id.pool)?;
|
||||||
|
let pool = PoolState::try_deserialize(&mut pool_account.account.data())?;
|
||||||
|
|
||||||
|
let (token_a_mint, token_b_mint) = (pool.token_0_mint, pool.token_1_mint);
|
||||||
|
let (vault_a, vault_b) = (pool.token_0_vault, pool.token_1_vault);
|
||||||
|
|
||||||
|
let user_token_a_account = get_associated_token_address(wallet_pk, &token_a_mint);
|
||||||
|
let user_token_b_account = get_associated_token_address(wallet_pk, &token_b_mint);
|
||||||
|
let user_lp_token_account = get_associated_token_address(wallet_pk, &pool.lp_mint);
|
||||||
|
|
||||||
|
// You may need to calculate maximum amounts based on slippage
|
||||||
|
let instruction = Deposit {
|
||||||
|
lp_token_amount: lp_amount,
|
||||||
|
maximum_token_0_amount: u64::MAX, // Adjust as needed
|
||||||
|
maximum_token_1_amount: u64::MAX, // Adjust as needed
|
||||||
};
|
};
|
||||||
|
|
||||||
let (input_token_account, output_token_account) = (
|
|
||||||
get_associated_token_address(wallet_pk, &input_token_mint),
|
|
||||||
get_associated_token_address(wallet_pk, &output_token_mint),
|
|
||||||
);
|
|
||||||
|
|
||||||
let instruction = raydium_cp_swap::instruction::SwapBaseInput {
|
|
||||||
amount_in: amount,
|
|
||||||
minimum_amount_out: other_amount_threshold,
|
|
||||||
};
|
|
||||||
let (authority, __bump) =
|
let (authority, __bump) =
|
||||||
Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &raydium_cp_swap::id());
|
Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &raydium_cp_swap::id());
|
||||||
|
|
||||||
let accounts = raydium_cp_swap::accounts::Swap {
|
let accounts = raydium_cp_swap::accounts::Deposit {
|
||||||
payer: *wallet_pk,
|
owner: *wallet_pk,
|
||||||
authority,
|
authority,
|
||||||
amm_config: pool.amm_config,
|
|
||||||
pool_state: id.pool,
|
pool_state: id.pool,
|
||||||
input_token_account,
|
owner_lp_token: user_lp_token_account,
|
||||||
output_token_account,
|
token_0_account: user_token_a_account,
|
||||||
input_vault,
|
token_1_account: user_token_b_account,
|
||||||
output_vault,
|
token_0_vault: vault_a,
|
||||||
input_token_program,
|
token_1_vault: vault_b,
|
||||||
output_token_program,
|
token_program: spl_token_id(),
|
||||||
input_token_mint,
|
token_program_2022: spl_token_2022_id(),
|
||||||
output_token_mint,
|
vault_0_mint: token_a_mint,
|
||||||
observation_state: pool.observation_key,
|
vault_1_mint: token_b_mint,
|
||||||
|
lp_mint: pool.lp_mint,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deposit_instruction = Instruction {
|
||||||
|
program_id: raydium_cp_swap::id(),
|
||||||
|
accounts: accounts.to_account_metas(None),
|
||||||
|
data: instruction.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = SwapInstruction {
|
let result = SwapInstruction {
|
||||||
instruction: Instruction {
|
instruction: deposit_instruction,
|
||||||
program_id: raydium_cp_swap::id(),
|
out_pubkey: user_lp_token_account,
|
||||||
accounts: accounts.to_account_metas(None),
|
out_mint: pool.lp_mint,
|
||||||
data: instruction.data(),
|
in_amount_offset: 8,
|
||||||
},
|
cu_estimate: Some(40_000),
|
||||||
out_pubkey: output_token_account,
|
};
|
||||||
out_mint: output_token_mint,
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_withdraw_ix(
|
||||||
|
id: &GobblerEdgeIdentifier,
|
||||||
|
chain_data: &AccountProviderView,
|
||||||
|
wallet_pk: &Pubkey,
|
||||||
|
lp_amount: u64,
|
||||||
|
max_slippage_bps: i32,
|
||||||
|
) -> anyhow::Result<SwapInstruction> {
|
||||||
|
let pool_account = chain_data.account(&id.pool)?;
|
||||||
|
let pool = PoolState::try_deserialize(&mut pool_account.account.data())?;
|
||||||
|
|
||||||
|
let (token_a_mint, token_b_mint) = (pool.token_0_mint, pool.token_1_mint);
|
||||||
|
let (vault_a, vault_b) = (pool.token_0_vault, pool.token_1_vault);
|
||||||
|
|
||||||
|
let user_token_a_account = get_associated_token_address(wallet_pk, &token_a_mint);
|
||||||
|
let user_token_b_account = get_associated_token_address(wallet_pk, &token_b_mint);
|
||||||
|
let user_lp_token_account = get_associated_token_address(wallet_pk, &pool.lp_mint);
|
||||||
|
|
||||||
|
// You may need to calculate minimum amounts based on slippage
|
||||||
|
let instruction = Withdraw {
|
||||||
|
lp_token_amount: lp_amount,
|
||||||
|
minimum_token_0_amount: 0, // Adjust as needed
|
||||||
|
minimum_token_1_amount: 0, // Adjust as needed
|
||||||
|
};
|
||||||
|
|
||||||
|
let (authority, __bump) =
|
||||||
|
Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &raydium_cp_swap::id());
|
||||||
|
|
||||||
|
let accounts = raydium_cp_swap::accounts::Withdraw {
|
||||||
|
owner: *wallet_pk,
|
||||||
|
authority,
|
||||||
|
pool_state: id.pool,
|
||||||
|
owner_lp_token: user_lp_token_account,
|
||||||
|
token_0_account: user_token_a_account,
|
||||||
|
token_1_account: user_token_b_account,
|
||||||
|
token_0_vault: vault_a,
|
||||||
|
token_1_vault: vault_b,
|
||||||
|
token_program: spl_token_id(),
|
||||||
|
token_program_2022: spl_token_2022_id(),
|
||||||
|
vault_0_mint: token_a_mint,
|
||||||
|
vault_1_mint: token_b_mint,
|
||||||
|
memo_program: spl_memo_id(),
|
||||||
|
lp_mint: pool.lp_mint,
|
||||||
|
};
|
||||||
|
|
||||||
|
let withdraw_instruction = Instruction {
|
||||||
|
program_id: raydium_cp_swap::id(),
|
||||||
|
accounts: accounts.to_account_metas(None),
|
||||||
|
data: instruction.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = SwapInstruction {
|
||||||
|
instruction: withdraw_instruction,
|
||||||
|
out_pubkey: user_token_a_account, // Adjust as needed
|
||||||
|
out_mint: token_a_mint,
|
||||||
in_amount_offset: 8,
|
in_amount_offset: 8,
|
||||||
cu_estimate: Some(40_000),
|
cu_estimate: Some(40_000),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue