wip/gobbler-lps
This commit is contained in:
parent
7f1b42f70f
commit
60acc16480
|
@ -2111,6 +2111,9 @@ dependencies = [
|
|||
"solana-program-test",
|
||||
"solana-sdk",
|
||||
"spl-associated-token-account 1.1.3",
|
||||
"spl-memo 3.0.1",
|
||||
"spl-token 3.5.0",
|
||||
"spl-token-2022 0.9.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -5358,7 +5361,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "quic-geyser-client"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
@ -5375,7 +5378,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "quic-geyser-common"
|
||||
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 = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
|
|
@ -30,6 +30,10 @@ serde_derive = "1.0"
|
|||
mango-feeds-connector = { workspace = true }
|
||||
# raydium-cp
|
||||
raydium-cp-swap = "0.1.143"
|
||||
spl-memo = "3.0.0"
|
||||
spl-token = "3.5.0"
|
||||
spl-token-2022 = "0.9.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
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 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 solana_program::clock::Clock;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
|
@ -18,11 +20,30 @@ use std::panic;
|
|||
|
||||
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 pool: Pubkey,
|
||||
pub mint_a: 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 {
|
||||
|
@ -31,19 +52,41 @@ impl DexEdgeIdentifier for GobblerEdgeIdentifier {
|
|||
}
|
||||
|
||||
fn desc(&self) -> String {
|
||||
format!("Gobbler_{}", self.pool)
|
||||
format!(
|
||||
"Gobbler_{}_{}_{}",
|
||||
self.operation_string(),
|
||||
self.direction_string(),
|
||||
self.pool
|
||||
)
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
11
|
||||
// Adjust based on operation
|
||||
11 // This might vary depending on actual requirements
|
||||
}
|
||||
|
||||
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 pool: PoolState,
|
||||
pub config: AmmConfig,
|
||||
|
@ -58,6 +121,9 @@ pub struct GobblerEdge {
|
|||
pub vault_1_amount: u64,
|
||||
pub mint_0: Option<TransferFeeConfig>,
|
||||
pub mint_1: Option<TransferFeeConfig>,
|
||||
pub lp_supply: u64,
|
||||
pub operation: Operation,
|
||||
pub direction: Direction,
|
||||
}
|
||||
|
||||
impl DexEdge for GobblerEdge {
|
||||
|
@ -94,11 +160,8 @@ pub fn swap_base_input(
|
|||
amount_in: u64,
|
||||
) -> anyhow::Result<(u64, u64, u64)> {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let pool_state = pool;
|
||||
let block_timestamp = pool_state.open_time + 1; // TODO: This should be the actual clock
|
||||
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Swap)
|
||||
|| block_timestamp < pool_state.open_time
|
||||
{
|
||||
let block_timestamp = Clock::get()?.unix_timestamp as u64;
|
||||
if !pool.get_status_by_bit(PoolStatusBitIndex::Swap) || block_timestamp < pool.open_time {
|
||||
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"));
|
||||
}
|
||||
|
||||
let (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
|
||||
{
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?
|
||||
} 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_state, output_vault_amount, input_vault_amount)?;
|
||||
(inp, out)
|
||||
} else {
|
||||
return Err(anyhow!("Invalid vault configuration"));
|
||||
};
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
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)?
|
||||
} else if input_vault_key == pool.token_1_vault && output_vault_key == pool.token_0_vault
|
||||
{
|
||||
let (out, inp) =
|
||||
vault_amount_without_fee(pool, output_vault_amount, input_vault_amount)?;
|
||||
(inp, out)
|
||||
} else {
|
||||
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 {
|
||||
(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
|
||||
let protocol_fee = (amm_config.token_0_creator_rate
|
||||
+ amm_config.token_1_creator_rate
|
||||
+ amm_config.token_0_lp_rate
|
||||
+ amm_config.token_1_lp_rate) / 10000;
|
||||
+ amm_config.token_0_lp_rate
|
||||
+ 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(),
|
||||
total_input_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_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 amount_received = swap_result.destination_amount_swapped.saturating_sub(output_transfer_fee.into());
|
||||
let output_transfer_fee =
|
||||
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((
|
||||
amount_in,
|
||||
amount_received.try_into().map_err(|e| anyhow!("Failed to convert amount_received: {}", e))?,
|
||||
(protocol_fee+input_token_creator_rate+input_token_lp_rate).try_into().map_err(|e| anyhow!("Failed to convert fees: {}", 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))?,
|
||||
))
|
||||
});
|
||||
|
||||
|
@ -162,18 +230,15 @@ pub fn swap_base_output(
|
|||
amm_config: &AmmConfig,
|
||||
input_vault_key: Pubkey,
|
||||
input_vault_amount: u64,
|
||||
_input_mint: &Option<TransferFeeConfig>,
|
||||
input_mint: &Option<TransferFeeConfig>,
|
||||
output_vault_key: Pubkey,
|
||||
output_vault_amount: u64,
|
||||
output_mint: &Option<TransferFeeConfig>,
|
||||
amount_out: u64,
|
||||
) -> anyhow::Result<(u64, u64, u64)> {
|
||||
let res = panic::catch_unwind(|| {
|
||||
let pool_state = pool;
|
||||
let block_timestamp = pool_state.open_time + 1; // TODO: This should be the actual clock
|
||||
if !pool_state.get_status_by_bit(PoolStatusBitIndex::Swap)
|
||||
|| block_timestamp < pool_state.open_time
|
||||
{
|
||||
let block_timestamp = Clock::get()?.unix_timestamp as u64;
|
||||
if !pool.get_status_by_bit(PoolStatusBitIndex::Swap) || block_timestamp < pool.open_time {
|
||||
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 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"))?;
|
||||
|
||||
let (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
|
||||
{
|
||||
vault_amount_without_fee(pool_state, input_vault_amount, output_vault_amount)?
|
||||
} 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_state, output_vault_amount, input_vault_amount)?;
|
||||
(inp, out)
|
||||
} else {
|
||||
return Err(anyhow!("Invalid vault configuration"));
|
||||
};
|
||||
let (total_input_token_amount, total_output_token_amount) =
|
||||
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)?
|
||||
} else if input_vault_key == pool.token_1_vault && output_vault_key == pool.token_0_vault
|
||||
{
|
||||
let (out, inp) =
|
||||
vault_amount_without_fee(pool, output_vault_amount, input_vault_amount)?;
|
||||
(inp, out)
|
||||
} else {
|
||||
return Err(anyhow!("Invalid vault configuration"));
|
||||
};
|
||||
|
||||
if total_output_token_amount < actual_amount_out.into() {
|
||||
return Err(anyhow!("Insufficient liquidity"));
|
||||
}
|
||||
|
||||
let (input_token_creator_rate, input_token_lp_rate) = if input_vault_key == pool_state.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.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 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_0_lp_rate
|
||||
+ amm_config.token_1_lp_rate) / 10000;
|
||||
+ amm_config.token_0_lp_rate
|
||||
+ 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(),
|
||||
total_input_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_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 amount_in = swap_result.source_amount_swapped.saturating_add(input_transfer_fee.into());
|
||||
let input_transfer_fee =
|
||||
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((
|
||||
amount_in.try_into().map_err(|e| anyhow!("Failed to convert amount_in: {}", e))?,
|
||||
amount_in.try_into()?,
|
||||
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 {
|
||||
transfer_fee_config
|
||||
.calculate_epoch_fee(Clock::get()?.epoch, pre_fee_amount)
|
||||
.unwrap()
|
||||
.unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
@ -257,9 +331,9 @@ pub fn vault_amount_without_fee(
|
|||
Ok((
|
||||
vault_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
|
||||
.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 anchor_lang::{AccountDeserialize, Discriminator, Id};
|
||||
use anchor_spl::token::spl_token::state::AccountState;
|
||||
use anchor_spl::token::{spl_token, Token};
|
||||
use anchor_spl::token_2022::spl_token_2022;
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use itertools::Itertools;
|
||||
|
@ -43,82 +42,108 @@ impl DexInterface for GobblerDex {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let pools =
|
||||
fetch_raydium_account::<PoolState>(rpc, raydium_cp_swap::id(), PoolState::LEN).await?;
|
||||
// Fetch all PoolState accounts
|
||||
let pools = fetch_gobbler_pools(rpc).await?;
|
||||
|
||||
// Collect vaults to identify any banned ones (e.g., frozen accounts)
|
||||
let vaults = pools
|
||||
.iter()
|
||||
.flat_map(|x| [x.1.token_0_vault, x.1.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)
|
||||
.flat_map(|(_, pool)| vec![pool.token_0_vault, pool.token_1_vault])
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let pools = pools
|
||||
let vault_accounts = rpc.get_multiple_accounts(&vaults).await?;
|
||||
let banned_vaults = vault_accounts
|
||||
.iter()
|
||||
.filter(|(_pool_pk, pool)| {
|
||||
pool.token_0_program == Token::id() && pool.token_1_program == Token::id()
|
||||
// TODO Remove filter when 2022 are working
|
||||
.filter(|(_, account)| {
|
||||
account.owner == Token::id()
|
||||
&& spl_token::state::Account::unpack(account.data())
|
||||
.map(|acc| acc.state == AccountState::Frozen)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.filter(|(_pool_pk, pool)| {
|
||||
!banned_vaults.contains(&pool.token_0_vault)
|
||||
.map(|(pubkey, _)| *pubkey)
|
||||
.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)
|
||||
})
|
||||
.collect_vec();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let edge_pairs = pools
|
||||
.iter()
|
||||
.map(|(pool_pk, pool)| {
|
||||
(
|
||||
Arc::new(GobblerEdgeIdentifier {
|
||||
pool: *pool_pk,
|
||||
mint_a: pool.token_0_mint,
|
||||
mint_b: pool.token_1_mint,
|
||||
is_a_to_b: true,
|
||||
}),
|
||||
Arc::new(GobblerEdgeIdentifier {
|
||||
pool: *pool_pk,
|
||||
mint_a: pool.token_1_mint,
|
||||
mint_b: pool.token_0_mint,
|
||||
is_a_to_b: false,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
// Create edge identifiers for each pool
|
||||
let mut edge_identifiers = Vec::new();
|
||||
|
||||
for (pool_pk, pool) in &valid_pools {
|
||||
// Swap edges between Token A and Token B
|
||||
let swap_a_to_b = 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::Swap,
|
||||
direction: Direction::AtoB,
|
||||
});
|
||||
|
||||
let swap_b_to_a = Arc::new(GobblerEdgeIdentifier {
|
||||
pool: *pool_pk,
|
||||
mint_a: pool.token_1_mint,
|
||||
mint_b: pool.token_0_mint,
|
||||
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 edges_per_pk = HashMap::new();
|
||||
|
||||
let edges_per_pk = {
|
||||
let mut map = HashMap::new();
|
||||
for ((pool_pk, pool), (edge_a_to_b, edge_b_to_a)) in pools.iter().zip(edge_pairs.iter())
|
||||
{
|
||||
let entry = vec![
|
||||
edge_a_to_b.clone() as Arc<dyn DexEdgeIdentifier>,
|
||||
edge_b_to_a.clone(),
|
||||
];
|
||||
for (pool_pk, pool) in &valid_pools {
|
||||
// Collect all necessary accounts
|
||||
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.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);
|
||||
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);
|
||||
|
||||
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);
|
||||
// TODO Uncomment for Token-2022
|
||||
// needed_accounts.insert(pool.token_0_mint);
|
||||
// needed_accounts.insert(pool.token_1_mint);
|
||||
}
|
||||
map
|
||||
};
|
||||
// Map edges to their corresponding public keys
|
||||
let edges = edge_identifiers
|
||||
.iter()
|
||||
.filter(|edge| edge.as_any().downcast_ref::<GobblerEdgeIdentifier>().unwrap().pool == *pool_pk)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
utils::insert_or_extend(&mut edges_per_pk, pool_pk, &edges.into_iter().map(|edge| edge as Arc<dyn DexEdgeIdentifier>).collect::<Vec<_>>());
|
||||
}
|
||||
|
||||
Ok(Arc::new(GobblerDex {
|
||||
edges: edges_per_pk,
|
||||
|
@ -132,12 +157,9 @@ impl DexInterface for GobblerDex {
|
|||
|
||||
fn subscription_mode(&self) -> DexSubscriptionMode {
|
||||
DexSubscriptionMode::Mixed(MixedDexSubscription {
|
||||
accounts: Default::default(),
|
||||
accounts: self.needed_accounts.clone(),
|
||||
programs: HashSet::from([raydium_cp_swap::id()]),
|
||||
token_accounts_for_owner: HashSet::from([Pubkey::from_str(
|
||||
"GpMZbSM2GgvTKHJirzeGfMFoaZ8UR2X7F4v8vHTvxFbL",
|
||||
)
|
||||
.unwrap()]),
|
||||
token_accounts_for_owner: HashSet::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -157,35 +179,39 @@ impl DexInterface for GobblerDex {
|
|||
let id = id
|
||||
.as_any()
|
||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||
.unwrap();
|
||||
.context("Invalid edge identifier type")?;
|
||||
|
||||
let pool_account = chain_data.account(&id.pool)?;
|
||||
let pool = PoolState::try_deserialize(&mut pool_account.account.data())?;
|
||||
|
||||
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 = 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 = 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 transfer_1_fee = None;
|
||||
let lp_mint_account = chain_data.account(&pool.lp_mint)?;
|
||||
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_1_account = chain_data.account(&pool.token_1_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)?;
|
||||
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 = _get_transfer_config(&mint_0_account)?;
|
||||
let mint_1 = _get_transfer_config(&mint_1_account)?;
|
||||
|
||||
Ok(Arc::new(GobblerEdge {
|
||||
pool,
|
||||
config,
|
||||
vault_0_amount: vault_0.amount,
|
||||
vault_1_amount: vault_1.amount,
|
||||
mint_0: transfer_0_fee,
|
||||
mint_1: transfer_1_fee,
|
||||
mint_0,
|
||||
mint_1,
|
||||
lp_supply: lp_mint.supply,
|
||||
operation: id.operation,
|
||||
direction: id.direction,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -199,69 +225,36 @@ impl DexInterface for GobblerDex {
|
|||
let id = id
|
||||
.as_any()
|
||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||
.unwrap();
|
||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().unwrap();
|
||||
.context("Invalid edge identifier type")?;
|
||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().context("Invalid edge type")?;
|
||||
|
||||
if !edge.pool.get_status_by_bit(PoolStatusBitIndex::Swap) {
|
||||
return Ok(Quote {
|
||||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
});
|
||||
match id.operation {
|
||||
Operation::Swap => {
|
||||
// Handle swap operation
|
||||
// Similar to previous implementation
|
||||
// ...
|
||||
}
|
||||
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")?;
|
||||
let now_ts = clock.account.deserialize_data::<Clock>()?.unix_timestamp as u64;
|
||||
if edge.pool.open_time > now_ts {
|
||||
return Ok(Quote {
|
||||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
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)
|
||||
// Placeholder return until implementation is provided
|
||||
Ok(Quote {
|
||||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: id.mint_a,
|
||||
})
|
||||
}
|
||||
|
||||
fn build_swap_ix(
|
||||
|
@ -276,15 +269,38 @@ impl DexInterface for GobblerDex {
|
|||
let id = id
|
||||
.as_any()
|
||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||
.unwrap();
|
||||
gobbler_ix_builder::build_swap_ix(
|
||||
id,
|
||||
chain_data,
|
||||
wallet_pk,
|
||||
in_amount,
|
||||
out_amount,
|
||||
max_slippage_bps,
|
||||
)
|
||||
.context("Invalid edge identifier type")?;
|
||||
|
||||
match id.operation {
|
||||
Operation::Swap => {
|
||||
gobbler_ix_builder::build_swap_ix(
|
||||
id,
|
||||
chain_data,
|
||||
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 {
|
||||
|
@ -301,81 +317,46 @@ impl DexInterface for GobblerDex {
|
|||
let id = id
|
||||
.as_any()
|
||||
.downcast_ref::<GobblerEdgeIdentifier>()
|
||||
.unwrap();
|
||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().unwrap();
|
||||
.context("Invalid edge identifier type")?;
|
||||
let edge = edge.as_any().downcast_ref::<GobblerEdge>().context("Invalid edge type")?;
|
||||
|
||||
if !edge.pool.get_status_by_bit(PoolStatusBitIndex::Swap) {
|
||||
return Ok(Quote {
|
||||
in_amount: u64::MAX,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: edge.pool.token_0_mint,
|
||||
});
|
||||
match id.operation {
|
||||
Operation::Swap => {
|
||||
// Handle swap operation
|
||||
// Similar to previous implementation
|
||||
// ...
|
||||
}
|
||||
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")?;
|
||||
let now_ts = clock.account.deserialize_data::<Clock>()?.unix_timestamp as u64;
|
||||
if edge.pool.open_time > now_ts {
|
||||
return Ok(Quote {
|
||||
in_amount: u64::MAX,
|
||||
out_amount: 0,
|
||||
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)
|
||||
// Placeholder return until implementation is provided
|
||||
Ok(Quote {
|
||||
in_amount: 0,
|
||||
out_amount: 0,
|
||||
fee_amount: 0,
|
||||
fee_mint: id.mint_a,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_raydium_account<T: Discriminator + AccountDeserialize>(
|
||||
async fn fetch_gobbler_pools(
|
||||
rpc: &mut RouterRpcClient,
|
||||
program_id: Pubkey,
|
||||
len: usize,
|
||||
) -> anyhow::Result<Vec<(Pubkey, T)>> {
|
||||
) -> anyhow::Result<Vec<(Pubkey, PoolState)>> {
|
||||
let config = RpcProgramAccountsConfig {
|
||||
filters: Some(vec![
|
||||
RpcFilterType::DataSize(len as u64),
|
||||
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, T::DISCRIMINATOR.to_vec())),
|
||||
RpcFilterType::DataSize(PoolState::LEN as u64),
|
||||
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, PoolState::DISCRIMINATOR.to_vec())),
|
||||
]),
|
||||
account_config: RpcAccountInfoConfig {
|
||||
encoding: Some(UiAccountEncoding::Base64),
|
||||
|
@ -386,16 +367,17 @@ async fn fetch_raydium_account<T: Discriminator + AccountDeserialize>(
|
|||
};
|
||||
|
||||
let snapshot = rpc
|
||||
.get_program_accounts_with_config(&program_id, config)
|
||||
.get_program_accounts_with_config(&raydium_cp_swap::id(), config)
|
||||
.await?;
|
||||
|
||||
let result = snapshot
|
||||
.iter()
|
||||
.into_iter()
|
||||
.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)
|
||||
})
|
||||
.collect_vec();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use crate::edge::GobblerEdgeIdentifier;
|
||||
use anchor_lang::{AccountDeserialize, Id, InstructionData, ToAccountMetas};
|
||||
use crate::edge::{GobblerEdgeIdentifier, Operation, Direction};
|
||||
use anchor_lang::{AccountDeserialize, InstructionData, ToAccountMetas};
|
||||
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::states::PoolState;
|
||||
use raydium_cp_swap::AUTH_SEED;
|
||||
use router_lib::dex::{AccountProviderView, SwapInstruction};
|
||||
use solana_program::instruction::Instruction;
|
||||
use solana_program::pubkey::Pubkey;
|
||||
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(
|
||||
id: &GobblerEdgeIdentifier,
|
||||
|
@ -24,58 +28,200 @@ pub fn build_swap_ix(
|
|||
let other_amount_threshold =
|
||||
((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)
|
||||
} else {
|
||||
(pool.token_1_mint, pool.token_0_mint)
|
||||
};
|
||||
let (input_token_program, output_token_program) = if id.is_a_to_b {
|
||||
(pool.token_0_program, pool.token_1_program)
|
||||
} else {
|
||||
(pool.token_1_program, pool.token_0_program)
|
||||
};
|
||||
let (input_vault, output_vault) = if id.is_a_to_b {
|
||||
(pool.token_0_vault, pool.token_1_vault)
|
||||
} else {
|
||||
(pool.token_1_vault, pool.token_0_vault)
|
||||
match id.operation {
|
||||
Operation::Swap => {
|
||||
// Handle swap operation
|
||||
let (input_token_mint, output_token_mint) = match id.direction {
|
||||
Direction::AtoB => (pool.token_0_mint, pool.token_1_mint),
|
||||
Direction::BtoA => (pool.token_1_mint, pool.token_0_mint),
|
||||
_ => return Err(anyhow::anyhow!("Invalid direction for swap operation")),
|
||||
};
|
||||
|
||||
let (input_token_program, output_token_program) = match id.direction {
|
||||
Direction::AtoB => (pool.token_0_program, pool.token_1_program),
|
||||
Direction::BtoA => (pool.token_1_program, pool.token_0_program),
|
||||
_ => return Err(anyhow::anyhow!("Invalid direction for swap operation")),
|
||||
};
|
||||
|
||||
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) =
|
||||
Pubkey::find_program_address(&[AUTH_SEED.as_bytes()], &raydium_cp_swap::id());
|
||||
|
||||
let accounts = raydium_cp_swap::accounts::Swap {
|
||||
payer: *wallet_pk,
|
||||
let accounts = raydium_cp_swap::accounts::Deposit {
|
||||
owner: *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,
|
||||
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,
|
||||
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 {
|
||||
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,
|
||||
instruction: deposit_instruction,
|
||||
out_pubkey: user_lp_token_account,
|
||||
out_mint: pool.lp_mint,
|
||||
in_amount_offset: 8,
|
||||
cu_estimate: Some(40_000),
|
||||
};
|
||||
|
||||
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,
|
||||
cu_estimate: Some(40_000),
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue