feat: add Meteora DAMM V2 support and bump to v3.0.1
Add Meteora DAMM V2 trading protocol with instruction builder, type definitions, and fixed_output_token_amount parameter for precise output control. Update all examples and documentation.
This commit is contained in:
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sol-trade-sdk"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"William <byteblock6@gmail.com>",
|
||||
@@ -32,6 +32,7 @@ members = [
|
||||
"examples/seed_trading",
|
||||
"examples/cli_trading",
|
||||
"examples/gas_fee_strategy",
|
||||
"examples/meteora_damm_v2_direct_trading",
|
||||
]
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<strong>Integrate PumpFun, PumpSwap, Bonk, and Raydium trading functionality into your applications with powerful tools and unified interfaces.</strong>
|
||||
<strong>Integrate PumpFun, PumpSwap, Bonk, Raydium, and Meteora trading functionality into your applications with powerful tools and unified interfaces.</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -66,7 +66,7 @@
|
||||
3. **Bonk Trading**: Support for Bonk trading operations
|
||||
4. **Raydium CPMM Trading**: Support for Raydium CPMM (Concentrated Pool Market Maker) trading operations
|
||||
5. **Raydium AMM V4 Trading**: Support for Raydium AMM V4 (Automated Market Maker) trading operations
|
||||
6. **Event Subscription**: SDK integrates solana-streamer SDK, supports subscribing to PumpFun, PumpSwap, Bonk, Raydium CPMM, and Raydium AMM V4 program trading events, the description of this SDK can be found in [solana-streamer SDK](https://github.com/0xfnzero/solana-streamer).
|
||||
6. **Meteora DAMM V2 Trading**: Support for Meteora DAMM V2 (Dynamic AMM) trading operations
|
||||
7. **Multiple MEV Protection**: Support for Jito, Nextblock, ZeroSlot, Temporal, Bloxroute, FlashBlock, BlockRazor, Node1, Astralane and other services
|
||||
8. **Concurrent Trading**: Send transactions using multiple MEV services simultaneously; the fastest succeeds while others fail
|
||||
9. **Unified Trading Interface**: Use unified trading protocol enums for trading operations
|
||||
@@ -87,14 +87,14 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.0.0" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.0.1" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
sol-trade-sdk = "3.0.0"
|
||||
sol-trade-sdk = "3.0.1"
|
||||
```
|
||||
|
||||
## 🛠️ Usage Examples
|
||||
@@ -185,6 +185,7 @@ Please ensure that the parameters your trading logic depends on are available in
|
||||
| PumpSwap trading operations | `cargo run --package pumpswap_trading` | [examples/pumpswap_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpswap_trading/src/main.rs) |
|
||||
| Raydium CPMM trading operations | `cargo run --package raydium_cpmm_trading` | [examples/raydium_cpmm_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_cpmm_trading/src/main.rs) |
|
||||
| Raydium AMM V4 trading operations | `cargo run --package raydium_amm_v4_trading` | [examples/raydium_amm_v4_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_amm_v4_trading/src/main.rs) |
|
||||
| Meteora DAMM V2 trading operations | `cargo run --package meteora_damm_v2_direct_trading` | [examples/meteora_damm_v2_direct_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/meteora_damm_v2_direct_trading/src/main.rs) |
|
||||
| Bonk token sniping trading | `cargo run --package bonk_sniper_trading` | [examples/bonk_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_sniper_trading/src/main.rs) |
|
||||
| Bonk token copy trading | `cargo run --package bonk_copy_trading` | [examples/bonk_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_copy_trading/src/main.rs) |
|
||||
| Custom instruction middleware example | `cargo run --package middleware_system` | [examples/middleware_system](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/middleware_system/src/main.rs) |
|
||||
|
||||
+5
-4
@@ -4,7 +4,7 @@
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<strong>将 PumpFun、PumpSwap、Bonk 和 Raydium 交易功能集成到您的应用程序中,提供强大的工具和统一的接口。</strong>
|
||||
<strong>将 PumpFun、PumpSwap、Bonk、Raydium 和 Meteora 交易功能集成到您的应用程序中,提供强大的工具和统一的接口。</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -66,7 +66,7 @@
|
||||
3. **Bonk 交易**: 支持 Bonk 的交易操作
|
||||
4. **Raydium CPMM 交易**: 支持 Raydium CPMM (Concentrated Pool Market Maker) 的交易操作
|
||||
5. **Raydium AMM V4 交易**: 支持 Raydium AMM V4 (Automated Market Maker) 的交易操作
|
||||
6. **事件订阅**: SDK 集成了 solana-streamer SDK,支持引用该 SDK 订阅 PumpFun、PumpSwap、Bonk、Raydium CPMM 和 Raydium AMM V4 程序的交易事件,该 SDK 的说明可以查阅:[solana-streamer SDK](https://github.com/0xfnzero/solana-streamer)。
|
||||
6. **Meteora DAMM V2 交易**: 支持 Meteora DAMM V2 (Dynamic AMM) 的交易操作
|
||||
7. **多种 MEV 保护**: 支持 Jito、Nextblock、ZeroSlot、Temporal、Bloxroute、FlashBlock、BlockRazor、Node1、Astralane 等服务
|
||||
8. **并发交易**: 同时使用多个 MEV 服务发送交易,最快的成功,其他失败
|
||||
9. **统一交易接口**: 使用统一的交易协议枚举进行交易操作
|
||||
@@ -87,14 +87,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.0.0" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.0.1" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = "3.0.0"
|
||||
sol-trade-sdk = "3.0.1"
|
||||
```
|
||||
|
||||
## 🛠️ 使用示例
|
||||
@@ -186,6 +186,7 @@ client.buy(buy_params).await?;
|
||||
| PumpSwap 交易操作 | `cargo run --package pumpswap_trading` | [examples/pumpswap_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpswap_trading/src/main.rs) |
|
||||
| Raydium CPMM 交易操作 | `cargo run --package raydium_cpmm_trading` | [examples/raydium_cpmm_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_cpmm_trading/src/main.rs) |
|
||||
| Raydium AMM V4 交易操作 | `cargo run --package raydium_amm_v4_trading` | [examples/raydium_amm_v4_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/raydium_amm_v4_trading/src/main.rs) |
|
||||
| Meteora DAMM V2 交易操作 | `cargo run --package meteora_damm_v2_direct_trading` | [examples/meteora_damm_v2_direct_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/meteora_damm_v2_direct_trading/src/main.rs) |
|
||||
| Bonk 代币狙击交易 | `cargo run --package bonk_sniper_trading` | [examples/bonk_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_sniper_trading/src/main.rs) |
|
||||
| Bonk 代币跟单交易 | `cargo run --package bonk_copy_trading` | [examples/bonk_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/bonk_copy_trading/src/main.rs) |
|
||||
| 自定义指令中间件示例 | `cargo run --package middleware_system` | [examples/middleware_system](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/middleware_system/src/main.rs) |
|
||||
|
||||
@@ -17,7 +17,7 @@ The `TradeBuyParams` struct contains all parameters required for executing buy o
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `dex_type` | `DexType` | ✅ | The trading protocol to use (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4) |
|
||||
| `dex_type` | `DexType` | ✅ | The trading protocol to use (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4, MeteoraDammV2) |
|
||||
| `input_token_type` | `TradeTokenType` | ✅ | The type of input token to use (SOL, WSOL, USD1) |
|
||||
| `mint` | `Pubkey` | ✅ | The public key of the token mint to purchase |
|
||||
| `input_token_amount` | `u64` | ✅ | Amount of input token to spend (in smallest token units) |
|
||||
@@ -36,6 +36,7 @@ The `TradeBuyParams` struct contains all parameters required for executing buy o
|
||||
| `create_mint_ata` | `bool` | ✅ | Whether to create token mint ATA |
|
||||
| `open_seed_optimize` | `bool` | ✅ | Whether to use seed optimization for reduced CU consumption |
|
||||
| `durable_nonce` | `Option<DurableNonceInfo>` | ❌ | Durable nonce information containing nonce account and current nonce value |
|
||||
| `fixed_output_token_amount` | `Option<u64>` | ❌ | Optional fixed output token amount. If set, this value will be directly assigned to the output amount instead of being calculated (required for Meteora DAMM V2) |
|
||||
|
||||
|
||||
## TradeSellParams
|
||||
@@ -46,7 +47,7 @@ The `TradeSellParams` struct contains all parameters required for executing sell
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `dex_type` | `DexType` | ✅ | The trading protocol to use (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4) |
|
||||
| `dex_type` | `DexType` | ✅ | The trading protocol to use (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4, MeteoraDammV2) |
|
||||
| `output_token_type` | `TradeTokenType` | ✅ | The type of output token to receive (SOL, WSOL, USD1) |
|
||||
| `mint` | `Pubkey` | ✅ | The public key of the token mint to sell |
|
||||
| `input_token_amount` | `u64` | ✅ | Amount of tokens to sell (in smallest token units) |
|
||||
@@ -65,6 +66,7 @@ The `TradeSellParams` struct contains all parameters required for executing sell
|
||||
| `close_output_token_ata` | `bool` | ✅ | Whether to close output token ATA after transaction |
|
||||
| `open_seed_optimize` | `bool` | ✅ | Whether to use seed optimization for reduced CU consumption |
|
||||
| `durable_nonce` | `Option<DurableNonceInfo>` | ❌ | Durable nonce information containing nonce account and current nonce value |
|
||||
| `fixed_output_token_amount` | `Option<u64>` | ❌ | Optional fixed output token amount. If set, this value will be directly assigned to the output amount instead of being calculated (required for Meteora DAMM V2) |
|
||||
|
||||
|
||||
## Parameter Categories
|
||||
@@ -152,5 +154,6 @@ Each DEX protocol requires specific `extension_params`:
|
||||
- **Bonk**: `BonkParams`
|
||||
- **Raydium CPMM**: `RaydiumCpmmParams`
|
||||
- **Raydium AMM V4**: `RaydiumAmmV4Params`
|
||||
- **Meteora DAMM V2**: `MeteoraDammV2Params`
|
||||
|
||||
Refer to the respective protocol documentation for detailed parameter specifications.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
|------|------|------|------|
|
||||
| `dex_type` | `DexType` | ✅ | 要使用的交易协议 (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4) |
|
||||
| `dex_type` | `DexType` | ✅ | 要使用的交易协议 (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4, MeteoraDammV2) |
|
||||
| `input_token_type` | `TradeTokenType` | ✅ | 要使用的输入代币类型 (SOL, WSOL, USD1) |
|
||||
| `mint` | `Pubkey` | ✅ | 要购买的代币 mint 公钥 |
|
||||
| `input_token_amount` | `u64` | ✅ | 要花费的输入代币数量(最小代币单位) |
|
||||
@@ -36,6 +36,7 @@
|
||||
| `create_mint_ata` | `bool` | ✅ | 是否创建代币 mint ATA |
|
||||
| `open_seed_optimize` | `bool` | ✅ | 是否使用 seed 优化以减少 CU 消耗 |
|
||||
| `durable_nonce` | `Option<DurableNonceInfo>` | ❌ | 持久 nonce 信息,包含 nonce 账户和当前 nonce 值 |
|
||||
| `fixed_output_token_amount` | `Option<u64>` | ❌ | 可选的固定输出代币数量。如果设置,此值将直接分配给输出数量而不是通过计算得出(Meteora DAMM V2 必需) |
|
||||
|
||||
|
||||
## TradeSellParams
|
||||
@@ -46,7 +47,7 @@
|
||||
|
||||
| 参数 | 类型 | 必需 | 描述 |
|
||||
|------|------|------|------|
|
||||
| `dex_type` | `DexType` | ✅ | 要使用的交易协议 (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4) |
|
||||
| `dex_type` | `DexType` | ✅ | 要使用的交易协议 (PumpFun, PumpSwap, Bonk, RaydiumCpmm, RaydiumAmmV4, MeteoraDammV2) |
|
||||
| `output_token_type` | `TradeTokenType` | ✅ | 要接收的输出代币类型 (SOL, WSOL, USD1) |
|
||||
| `mint` | `Pubkey` | ✅ | 要出售的代币 mint 公钥 |
|
||||
| `input_token_amount` | `u64` | ✅ | 要出售的代币数量(最小代币单位) |
|
||||
@@ -65,6 +66,7 @@
|
||||
| `close_output_token_ata` | `bool` | ✅ | 交易后是否关闭输出代币 ATA |
|
||||
| `open_seed_optimize` | `bool` | ✅ | 是否使用 seed 优化以减少 CU 消耗 |
|
||||
| `durable_nonce` | `Option<DurableNonceInfo>` | ❌ | 持久 nonce 信息,包含 nonce 账户和当前 nonce 值 |
|
||||
| `fixed_output_token_amount` | `Option<u64>` | ❌ | 可选的固定输出代币数量。如果设置,此值将直接分配给输出数量而不是通过计算得出(Meteora DAMM V2 必需) |
|
||||
|
||||
|
||||
## 参数分类
|
||||
@@ -152,5 +154,6 @@
|
||||
- **Bonk**: `BonkParams`
|
||||
- **Raydium CPMM**: `RaydiumCpmmParams`
|
||||
- **Raydium AMM V4**: `RaydiumAmmV4Params`
|
||||
- **Meteora DAMM V2**: `MeteoraDammV2Params`
|
||||
|
||||
请参阅相应的协议文档了解详细的参数规格。
|
||||
|
||||
@@ -168,6 +168,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ async fn bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()>
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -205,6 +206,7 @@ async fn bonk_copy_trade_with_grpc(trade_info: BonkTradeEvent) -> AnyResult<()>
|
||||
durable_nonce: None,
|
||||
create_output_token_ata: false,
|
||||
close_output_token_ata: false,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ async fn bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -168,6 +169,7 @@ async fn bonk_sniper_trade_with_shreds(trade_info: BonkTradeEvent) -> AnyResult<
|
||||
open_seed_optimize: false,
|
||||
with_tip: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -629,6 +629,7 @@ async fn handle_buy_pumpfun(
|
||||
create_mint_ata: create_mint_ata,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.buy(buy_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -678,6 +679,7 @@ async fn handle_buy_pumpswap(
|
||||
create_mint_ata: create_mint_ata,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.buy(buy_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -726,6 +728,7 @@ async fn handle_buy_bonk(
|
||||
create_mint_ata: create_mint_ata,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.buy(buy_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -778,6 +781,7 @@ async fn handle_buy_raydium_v4(
|
||||
create_mint_ata: create_mint_ata,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.buy(buy_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -830,6 +834,7 @@ async fn handle_buy_raydium_cpmm(
|
||||
create_mint_ata: create_mint_ata,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.buy(buy_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -992,6 +997,7 @@ async fn handle_sell_pumpfun(
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
|
||||
match client.sell(sell_params).await {
|
||||
@@ -1044,6 +1050,7 @@ async fn handle_sell_pumpswap(
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.sell(sell_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -1095,6 +1102,7 @@ async fn handle_sell_bonk(
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.sell(sell_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -1149,6 +1157,7 @@ async fn handle_sell_raydium_v4(
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.sell(sell_params).await {
|
||||
Ok((_, signature)) => {
|
||||
@@ -1203,6 +1212,7 @@ async fn handle_sell_raydium_cpmm(
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: use_seed,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
match client.sell(sell_params).await {
|
||||
Ok((_, signature)) => {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "meteora_damm_v2_direct_trading"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
||||
@@ -0,0 +1,98 @@
|
||||
use sol_trade_sdk::{
|
||||
common::{
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id, AnyResult,
|
||||
TradeConfig,
|
||||
},
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::MeteoraDammV2Params, factory::DexType},
|
||||
SolanaTrade, TradeTokenType,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{pubkey::Pubkey, signer::Signer};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Testing Metaora Damm V2 trading...");
|
||||
|
||||
let client = create_solana_trade_client().await?;
|
||||
let slippage_basis_points = Some(100);
|
||||
let recent_blockhash = client.rpc.get_latest_blockhash().await?;
|
||||
let pool = Pubkey::from_str("35EFyWd9cH8pdHxVgXHF68L1oZxSWd1FfJASSNTUtoTC").unwrap();
|
||||
let mint_pubkey = Pubkey::from_str("FhTRoy63ZiLcjLEVCMCTLc5Cu5ozrzouNg6cDp1ASZMC").unwrap();
|
||||
|
||||
// Buy tokens
|
||||
println!("Buying tokens from Metaora Damm V2...");
|
||||
let buy_sol_amount = 100_000;
|
||||
let buy_params = sol_trade_sdk::TradeBuyParams {
|
||||
dex_type: DexType::MeteoraDammV2,
|
||||
input_token_type: TradeTokenType::WSOL,
|
||||
mint: mint_pubkey,
|
||||
input_token_amount: buy_sol_amount,
|
||||
slippage_basis_points: slippage_basis_points,
|
||||
recent_blockhash: Some(recent_blockhash),
|
||||
extension_params: Box::new(
|
||||
MeteoraDammV2Params::from_pool_address_by_rpc(&client.rpc, &pool).await?,
|
||||
),
|
||||
lookup_table_key: None,
|
||||
wait_transaction_confirmed: true,
|
||||
create_input_token_ata: true,
|
||||
close_input_token_ata: true,
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: Some(1),
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
// Sell tokens
|
||||
println!("Selling tokens from Metaora Damm V2...");
|
||||
|
||||
let rpc = client.rpc.clone();
|
||||
let payer = client.payer.pubkey();
|
||||
let program_id = sol_trade_sdk::constants::TOKEN_PROGRAM;
|
||||
let account = get_associated_token_address_with_program_id(&payer, &mint_pubkey, &program_id);
|
||||
let balance = rpc.get_token_account_balance(&account).await?;
|
||||
let amount_token = balance.amount.parse::<u64>().unwrap();
|
||||
println!("Token balance: {}", amount_token);
|
||||
let sell_params = sol_trade_sdk::TradeSellParams {
|
||||
dex_type: DexType::MeteoraDammV2,
|
||||
output_token_type: TradeTokenType::WSOL,
|
||||
mint: mint_pubkey,
|
||||
input_token_amount: amount_token,
|
||||
slippage_basis_points: slippage_basis_points,
|
||||
recent_blockhash: Some(recent_blockhash),
|
||||
with_tip: false,
|
||||
extension_params: Box::new(
|
||||
MeteoraDammV2Params::from_pool_address_by_rpc(&client.rpc, &pool).await?,
|
||||
),
|
||||
lookup_table_key: None,
|
||||
wait_transaction_confirmed: true,
|
||||
create_output_token_ata: true,
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: Some(1),
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create SolanaTrade client
|
||||
/// Initializes a new SolanaTrade client with configuration
|
||||
async fn create_solana_trade_client() -> AnyResult<SolanaTrade> {
|
||||
println!("🚀 Initializing SolanaTrade client...");
|
||||
let payer = Keypair::from_base58_string("use_your_payer_keypair_here");
|
||||
let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
|
||||
let commitment = CommitmentConfig::confirmed();
|
||||
let swqos_configs: Vec<SwqosConfig> = vec![SwqosConfig::Default(rpc_url.clone())];
|
||||
let trade_config = TradeConfig::new(rpc_url, swqos_configs, commitment);
|
||||
let solana_trade = SolanaTrade::new(Arc::new(payer), trade_config).await;
|
||||
// set global strategy
|
||||
sol_trade_sdk::common::GasFeeStrategy::set_global_fee_strategy(150000, 500000, 0.001, 0.001);
|
||||
println!("✅ SolanaTrade client initialized successfully!");
|
||||
Ok(solana_trade)
|
||||
}
|
||||
@@ -99,6 +99,7 @@ async fn test_middleware() -> AnyResult<()> {
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
println!("tip: This transaction will not succeed because we're using a test account. You can modify the code to initialize the payer with your own private key");
|
||||
|
||||
@@ -155,6 +155,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: Some(durable_nonce),
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -191,6 +192,7 @@ async fn pumpfun_copy_trade_with_grpc(trade_info: PumpFunTradeEvent) -> AnyResul
|
||||
close_output_token_ata: false,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ async fn pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyR
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -146,6 +147,7 @@ async fn pumpfun_sniper_trade_with_shreds(trade_info: PumpFunTradeEvent) -> AnyR
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -71,6 +72,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ async fn pumpswap_trade_with_grpc(mint_pubkey: Pubkey, params: PumpSwapParams) -
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -232,6 +233,7 @@ async fn pumpswap_trade_with_grpc(mint_pubkey: Pubkey, params: PumpSwapParams) -
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent)
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -187,6 +188,7 @@ async fn raydium_amm_v4_copy_trade_with_grpc(trade_info: RaydiumAmmV4SwapEvent)
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ async fn raydium_cpmm_copy_trade_with_grpc(trade_info: RaydiumCpmmSwapEvent) ->
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -180,6 +181,7 @@ async fn raydium_cpmm_copy_trade_with_grpc(trade_info: RaydiumCpmmSwapEvent) ->
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: false,
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
create_mint_ata: true,
|
||||
open_seed_optimize: true, // ❗️❗️❗️❗️ open seed optimize
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.buy(buy_params).await?;
|
||||
|
||||
@@ -78,6 +79,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
close_output_token_ata: true,
|
||||
open_seed_optimize: true, // ❗️❗️❗️❗️ open seed optimize
|
||||
durable_nonce: None,
|
||||
fixed_output_token_amount: None,
|
||||
};
|
||||
client.sell(sell_params).await?;
|
||||
|
||||
|
||||
+22
-16
@@ -69,14 +69,17 @@ impl InstructionBuilder for BonkInstructionBuilder {
|
||||
// ========================================
|
||||
let amount_in: u64 = params.input_amount.unwrap_or(0);
|
||||
let share_fee_rate: u64 = 0;
|
||||
let minimum_amount_out: u64 = get_buy_token_amount_from_sol_amount(
|
||||
amount_in,
|
||||
protocol_params.virtual_base,
|
||||
protocol_params.virtual_quote,
|
||||
protocol_params.real_base,
|
||||
protocol_params.real_quote,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128,
|
||||
);
|
||||
let minimum_amount_out: u64 = match params.fixed_output_amount {
|
||||
Some(fixed_amount) => fixed_amount,
|
||||
None => get_buy_token_amount_from_sol_amount(
|
||||
amount_in,
|
||||
protocol_params.virtual_base,
|
||||
protocol_params.virtual_quote,
|
||||
protocol_params.real_base,
|
||||
protocol_params.real_quote,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128,
|
||||
),
|
||||
};
|
||||
|
||||
let user_base_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
@@ -226,14 +229,17 @@ impl InstructionBuilder for BonkInstructionBuilder {
|
||||
// Trade calculation and account address preparation
|
||||
// ========================================
|
||||
let share_fee_rate: u64 = 0;
|
||||
let minimum_amount_out: u64 = get_sell_sol_amount_from_token_amount(
|
||||
amount,
|
||||
protocol_params.virtual_base,
|
||||
protocol_params.virtual_quote,
|
||||
protocol_params.real_base,
|
||||
protocol_params.real_quote,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128,
|
||||
);
|
||||
let minimum_amount_out: u64 = match params.fixed_output_amount {
|
||||
Some(fixed_amount) => fixed_amount,
|
||||
None => get_sell_sol_amount_from_token_amount(
|
||||
amount,
|
||||
protocol_params.virtual_base,
|
||||
protocol_params.virtual_quote,
|
||||
protocol_params.real_base,
|
||||
protocol_params.real_quote,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE) as u128,
|
||||
),
|
||||
};
|
||||
|
||||
let user_base_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
use crate::{
|
||||
instruction::utils::meteora_damm_v2::{accounts, get_event_authority_pda, SWAP_DISCRIMINATOR},
|
||||
trading::core::{
|
||||
params::{MeteoraDammV2Params, SwapParams},
|
||||
traits::InstructionBuilder,
|
||||
},
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use solana_sdk::{
|
||||
instruction::{AccountMeta, Instruction},
|
||||
signer::Signer,
|
||||
};
|
||||
|
||||
/// Instruction builder for RaydiumCpmm protocol
|
||||
pub struct MeteoraDammV2InstructionBuilder;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InstructionBuilder for MeteoraDammV2InstructionBuilder {
|
||||
async fn build_buy_instructions(&self, params: &SwapParams) -> Result<Vec<Instruction>> {
|
||||
// ========================================
|
||||
// Parameter validation and basic data preparation
|
||||
// ========================================
|
||||
if params.input_amount.unwrap_or(0) == 0 {
|
||||
return Err(anyhow!("Amount cannot be zero"));
|
||||
}
|
||||
let protocol_params = params
|
||||
.protocol_params
|
||||
.as_any()
|
||||
.downcast_ref::<MeteoraDammV2Params>()
|
||||
.ok_or_else(|| anyhow!("Invalid protocol params for RaydiumCpmm"))?;
|
||||
|
||||
// ========================================
|
||||
// Trade calculation and account address preparation
|
||||
// ========================================
|
||||
let is_a_in = protocol_params.token_a_mint == crate::constants::WSOL_TOKEN_ACCOUNT;
|
||||
let amount_in: u64 = params.input_amount.unwrap_or(0);
|
||||
let minimum_amount_out: u64 = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => return Err(anyhow!("fixed_output_amount must be set for MeteoraDammV2 swap")),
|
||||
};
|
||||
|
||||
let input_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.input_mint,
|
||||
if is_a_in {
|
||||
&protocol_params.token_a_program
|
||||
} else {
|
||||
&protocol_params.token_b_program
|
||||
},
|
||||
params.open_seed_optimize,
|
||||
);
|
||||
let output_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.output_mint,
|
||||
if is_a_in {
|
||||
&protocol_params.token_b_program
|
||||
} else {
|
||||
&protocol_params.token_a_program
|
||||
},
|
||||
params.open_seed_optimize,
|
||||
);
|
||||
|
||||
// ========================================
|
||||
// Build instructions
|
||||
// ========================================
|
||||
let mut instructions = Vec::with_capacity(6);
|
||||
|
||||
if params.create_input_mint_ata {
|
||||
instructions
|
||||
.extend(crate::trading::common::handle_wsol(¶ms.payer.pubkey(), amount_in));
|
||||
}
|
||||
|
||||
if params.create_output_mint_ata {
|
||||
instructions.extend(
|
||||
crate::common::fast_fn::create_associated_token_account_idempotent_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.output_mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
params.open_seed_optimize,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Create buy instruction
|
||||
let accounts: [AccountMeta; 14] = [
|
||||
accounts::AUTHORITY_META, // Pool Authority (readonly)
|
||||
AccountMeta::new(protocol_params.pool, false), // Pool
|
||||
AccountMeta::new(input_token_account, false), // Input Token Account
|
||||
AccountMeta::new(output_token_account, false), // Output Token Account
|
||||
AccountMeta::new(protocol_params.token_a_vault, false), // Token A Vault
|
||||
AccountMeta::new(protocol_params.token_b_vault, false), // Token B Vault
|
||||
AccountMeta::new_readonly(protocol_params.token_a_mint, false), // Token A Mint (readonly)
|
||||
AccountMeta::new_readonly(protocol_params.token_b_mint, false), // Token B Mint (readonly)
|
||||
AccountMeta::new(params.payer.pubkey(), true), // User Transfer Authority
|
||||
AccountMeta::new_readonly(protocol_params.token_a_program, false), // Token Program (readonly)
|
||||
AccountMeta::new_readonly(protocol_params.token_b_program, false), // Token Program (readonly)
|
||||
accounts::METEORA_DAMM_V2_META, // Referral Token Account (readonly)
|
||||
AccountMeta::new_readonly(get_event_authority_pda(), false), // Event Authority (readonly)
|
||||
accounts::METEORA_DAMM_V2_META, // Program (readonly)
|
||||
];
|
||||
// Create instruction data
|
||||
let mut data = [0u8; 24];
|
||||
data[..8].copy_from_slice(&SWAP_DISCRIMINATOR);
|
||||
data[8..16].copy_from_slice(&amount_in.to_le_bytes());
|
||||
data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes());
|
||||
|
||||
instructions.push(Instruction::new_with_bytes(
|
||||
accounts::METEORA_DAMM_V2,
|
||||
&data,
|
||||
accounts.to_vec(),
|
||||
));
|
||||
|
||||
if params.close_input_mint_ata {
|
||||
// Close wSOL ATA account, reclaim rent
|
||||
instructions.extend(crate::trading::common::close_wsol(¶ms.payer.pubkey()));
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
async fn build_sell_instructions(&self, params: &SwapParams) -> Result<Vec<Instruction>> {
|
||||
// ========================================
|
||||
// Parameter validation and basic data preparation
|
||||
// ========================================
|
||||
let protocol_params = params
|
||||
.protocol_params
|
||||
.as_any()
|
||||
.downcast_ref::<MeteoraDammV2Params>()
|
||||
.ok_or_else(|| anyhow!("Invalid protocol params for RaydiumCpmm"))?;
|
||||
|
||||
if params.input_amount.is_none() || params.input_amount.unwrap_or(0) == 0 {
|
||||
return Err(anyhow!("Token amount is not set"));
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Trade calculation and account address preparation
|
||||
// ========================================
|
||||
let is_a_in = protocol_params.token_b_mint == crate::constants::WSOL_TOKEN_ACCOUNT;
|
||||
let minimum_amount_out: u64 = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => return Err(anyhow!("fixed_output_amount must be set for MeteoraDammV2 swap")),
|
||||
};
|
||||
|
||||
let input_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.input_mint,
|
||||
if is_a_in {
|
||||
&protocol_params.token_a_program
|
||||
} else {
|
||||
&protocol_params.token_b_program
|
||||
},
|
||||
params.open_seed_optimize,
|
||||
);
|
||||
let output_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
¶ms.output_mint,
|
||||
if is_a_in {
|
||||
&protocol_params.token_b_program
|
||||
} else {
|
||||
&protocol_params.token_a_program
|
||||
},
|
||||
params.open_seed_optimize,
|
||||
);
|
||||
|
||||
// ========================================
|
||||
// Build instructions
|
||||
// ========================================
|
||||
let mut instructions = Vec::with_capacity(3);
|
||||
|
||||
if params.create_output_mint_ata {
|
||||
instructions.extend(crate::trading::common::create_wsol_ata(¶ms.payer.pubkey()));
|
||||
}
|
||||
|
||||
// Create buy instruction
|
||||
let accounts: [AccountMeta; 14] = [
|
||||
accounts::AUTHORITY_META, // Pool Authority (readonly)
|
||||
AccountMeta::new(protocol_params.pool, false), // Pool
|
||||
AccountMeta::new(input_token_account, false), // Input Token Account
|
||||
AccountMeta::new(output_token_account, false), // Output Token Account
|
||||
AccountMeta::new(protocol_params.token_a_vault, false), // Token A Vault
|
||||
AccountMeta::new(protocol_params.token_b_vault, false), // Token B Vault
|
||||
AccountMeta::new_readonly(protocol_params.token_a_mint, false), // Token A Mint (readonly)
|
||||
AccountMeta::new_readonly(protocol_params.token_b_mint, false), // Token B Mint (readonly)
|
||||
AccountMeta::new(params.payer.pubkey(), true), // User Transfer Authority
|
||||
AccountMeta::new_readonly(protocol_params.token_a_program, false), // Token Program (readonly)
|
||||
AccountMeta::new_readonly(protocol_params.token_b_program, false), // Token Program (readonly)
|
||||
accounts::METEORA_DAMM_V2_META, // Referral Token Account (readonly)
|
||||
AccountMeta::new_readonly(get_event_authority_pda(), false), // Event Authority (readonly)
|
||||
accounts::METEORA_DAMM_V2_META, // Program (readonly)
|
||||
];
|
||||
// Create instruction data
|
||||
let mut data = [0u8; 24];
|
||||
data[..8].copy_from_slice(&SWAP_DISCRIMINATOR);
|
||||
data[8..16].copy_from_slice(¶ms.input_amount.unwrap_or_default().to_le_bytes());
|
||||
data[16..24].copy_from_slice(&minimum_amount_out.to_le_bytes());
|
||||
|
||||
instructions.push(Instruction::new_with_bytes(
|
||||
accounts::METEORA_DAMM_V2,
|
||||
&data,
|
||||
accounts.to_vec(),
|
||||
));
|
||||
|
||||
if params.close_output_mint_ata {
|
||||
instructions.extend(crate::trading::common::close_wsol(¶ms.payer.pubkey()));
|
||||
}
|
||||
|
||||
Ok(instructions)
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub mod pumpswap;
|
||||
pub mod bonk;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod meteora_damm_v2;
|
||||
pub mod utils;
|
||||
+21
-13
@@ -1,8 +1,10 @@
|
||||
use crate::{
|
||||
common::spl_token::close_account, constants::trade::trade::DEFAULT_SLIPPAGE, trading::core::{
|
||||
common::spl_token::close_account,
|
||||
constants::trade::trade::DEFAULT_SLIPPAGE,
|
||||
trading::core::{
|
||||
params::{PumpFunParams, SwapParams},
|
||||
traits::InstructionBuilder,
|
||||
}
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
instruction::utils::pumpfun::{
|
||||
@@ -44,13 +46,16 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
// ========================================
|
||||
// Trade calculation and account address preparation
|
||||
// ========================================
|
||||
let buy_token_amount = get_buy_token_amount_from_sol_amount(
|
||||
bonding_curve.virtual_token_reserves as u128,
|
||||
bonding_curve.virtual_sol_reserves as u128,
|
||||
bonding_curve.real_token_reserves as u128,
|
||||
creator,
|
||||
params.input_amount.unwrap_or(0),
|
||||
);
|
||||
let buy_token_amount = match params.fixed_output_amount {
|
||||
Some(amount) => amount,
|
||||
None => get_buy_token_amount_from_sol_amount(
|
||||
bonding_curve.virtual_token_reserves as u128,
|
||||
bonding_curve.virtual_sol_reserves as u128,
|
||||
bonding_curve.real_token_reserves as u128,
|
||||
creator,
|
||||
params.input_amount.unwrap_or(0),
|
||||
),
|
||||
};
|
||||
|
||||
let max_sol_cost = calculate_with_slippage_buy(
|
||||
params.input_amount.unwrap_or(0),
|
||||
@@ -169,10 +174,13 @@ impl InstructionBuilder for PumpFunInstructionBuilder {
|
||||
token_amount,
|
||||
);
|
||||
|
||||
let min_sol_output = calculate_with_slippage_sell(
|
||||
sol_amount,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
let min_sol_output = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => calculate_with_slippage_sell(
|
||||
sol_amount,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
),
|
||||
};
|
||||
|
||||
let bonding_curve_addr = if bonding_curve.account == Pubkey::default() {
|
||||
get_bonding_curve_pda(¶ms.input_mint).unwrap()
|
||||
|
||||
@@ -68,7 +68,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
creator = params_coin_creator_vault_authority;
|
||||
}
|
||||
|
||||
let (token_amount, sol_amount) = if quote_mint_is_wsol {
|
||||
let (mut token_amount, sol_amount) = if quote_mint_is_wsol {
|
||||
let result = buy_quote_input_internal(
|
||||
params.input_amount.unwrap_or(0),
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
@@ -92,6 +92,10 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
(result.min_quote, params.input_amount.unwrap_or(0))
|
||||
};
|
||||
|
||||
if params.fixed_output_amount.is_some() {
|
||||
token_amount = params.fixed_output_amount.unwrap();
|
||||
}
|
||||
|
||||
let user_base_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
@@ -233,7 +237,7 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
creator = params_coin_creator_vault_authority;
|
||||
}
|
||||
|
||||
let (token_amount, sol_amount) = if quote_mint_is_wsol {
|
||||
let (token_amount, mut sol_amount) = if quote_mint_is_wsol {
|
||||
let result = sell_base_input_internal(
|
||||
params.input_amount.unwrap(),
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
@@ -257,6 +261,10 @@ impl InstructionBuilder for PumpSwapInstructionBuilder {
|
||||
(result.max_quote, result.base)
|
||||
};
|
||||
|
||||
if params.fixed_output_amount.is_some() {
|
||||
sol_amount = params.fixed_output_amount.unwrap();
|
||||
}
|
||||
|
||||
let fee_recipient_ata = fee_recipient_ata(accounts::FEE_RECIPIENT, quote_mint);
|
||||
let user_base_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
|
||||
@@ -43,7 +43,10 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder {
|
||||
amount_in,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
let minimum_amount_out = swap_result.min_amount_out;
|
||||
let minimum_amount_out = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => swap_result.min_amount_out,
|
||||
};
|
||||
|
||||
let user_source_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
@@ -147,7 +150,10 @@ impl InstructionBuilder for RaydiumAmmV4InstructionBuilder {
|
||||
params.input_amount.unwrap_or(0),
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
let minimum_amount_out = swap_result.min_amount_out;
|
||||
let minimum_amount_out = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => swap_result.min_amount_out,
|
||||
};
|
||||
|
||||
let user_source_token_account =
|
||||
crate::common::fast_fn::get_associated_token_address_with_program_id_fast_use_seed(
|
||||
|
||||
@@ -65,7 +65,10 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder {
|
||||
amount_in,
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
);
|
||||
let minimum_amount_out = result.min_amount_out;
|
||||
let minimum_amount_out = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => result.min_amount_out,
|
||||
};
|
||||
|
||||
let wsol_token_account = get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
@@ -188,14 +191,19 @@ impl InstructionBuilder for RaydiumCpmmInstructionBuilder {
|
||||
protocol_params.quote_token_program
|
||||
};
|
||||
|
||||
let minimum_amount_out: u64 = compute_swap_amount(
|
||||
protocol_params.base_reserve,
|
||||
protocol_params.quote_reserve,
|
||||
is_base_in,
|
||||
params.input_amount.unwrap_or(0),
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
)
|
||||
.min_amount_out;
|
||||
let minimum_amount_out: u64 = match params.fixed_output_amount {
|
||||
Some(fixed) => fixed,
|
||||
None => {
|
||||
compute_swap_amount(
|
||||
protocol_params.base_reserve,
|
||||
protocol_params.quote_reserve,
|
||||
is_base_in,
|
||||
params.input_amount.unwrap_or(0),
|
||||
params.slippage_basis_points.unwrap_or(DEFAULT_SLIPPAGE),
|
||||
)
|
||||
.min_amount_out
|
||||
}
|
||||
};
|
||||
|
||||
let wsol_token_account = get_associated_token_address_with_program_id_fast_use_seed(
|
||||
¶ms.payer.pubkey(),
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
use crate::{
|
||||
common::SolanaRpcClient,
|
||||
instruction::utils::meteora_damm_v2_types::{pool_decode, Pool},
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
/// Constants used as seeds for deriving PDAs (Program Derived Addresses)
|
||||
pub mod seeds {
|
||||
pub const EVENT_AUTHORITY_SEED: &[u8] = b"__event_authority";
|
||||
}
|
||||
|
||||
/// Constants related to program accounts and authorities
|
||||
pub mod accounts {
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const AUTHORITY: Pubkey = pubkey!("HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC");
|
||||
pub const METEORA_DAMM_V2: Pubkey = pubkey!("cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG");
|
||||
|
||||
// META
|
||||
|
||||
pub const METEORA_DAMM_V2_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta {
|
||||
pubkey: METEORA_DAMM_V2,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
};
|
||||
|
||||
pub const AUTHORITY_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta {
|
||||
pubkey: AUTHORITY,
|
||||
is_signer: false,
|
||||
is_writable: false,
|
||||
};
|
||||
}
|
||||
|
||||
pub const SWAP_DISCRIMINATOR: &[u8] = &[248, 198, 158, 145, 225, 117, 135, 200];
|
||||
|
||||
pub async fn fetch_pool(
|
||||
rpc: &SolanaRpcClient,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<Pool, anyhow::Error> {
|
||||
let account = rpc.get_account(pool_address).await?;
|
||||
if account.owner != accounts::METEORA_DAMM_V2 {
|
||||
return Err(anyhow!("Account is not owned by Meteora Damm V2 program"));
|
||||
}
|
||||
let pool = pool_decode(&account.data[8..]).ok_or_else(|| anyhow!("Failed to decode pool"))?;
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_event_authority_pda() -> Pubkey {
|
||||
Pubkey::find_program_address(&[seeds::EVENT_AUTHORITY_SEED], &accounts::METEORA_DAMM_V2).0
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
use borsh::BorshDeserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct BaseFeeStruct {
|
||||
pub cliff_fee_numerator: u64,
|
||||
pub fee_scheduler_mode: u8,
|
||||
pub padding_0: [u8; 5],
|
||||
pub number_of_period: u16,
|
||||
pub period_frequency: u64,
|
||||
pub reduction_factor: u64,
|
||||
pub padding_1: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct DynamicFeeStruct {
|
||||
pub initialized: u8,
|
||||
pub padding: [u8; 7],
|
||||
pub max_volatility_accumulator: u32,
|
||||
pub variable_fee_control: u32,
|
||||
pub bin_step: u16,
|
||||
pub filter_period: u16,
|
||||
pub decay_period: u16,
|
||||
pub reduction_factor: u16,
|
||||
pub last_update_timestamp: u64,
|
||||
pub bin_step_u128: u128,
|
||||
pub sqrt_price_reference: u128,
|
||||
pub volatility_accumulator: u128,
|
||||
pub volatility_reference: u128,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PoolFeesStruct {
|
||||
pub base_fee: BaseFeeStruct,
|
||||
pub protocol_fee_percent: u8,
|
||||
pub partner_fee_percent: u8,
|
||||
pub referral_fee_percent: u8,
|
||||
pub padding_0: [u8; 5],
|
||||
pub dynamic_fee: DynamicFeeStruct,
|
||||
pub padding_1: [u64; 2],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct PoolMetrics {
|
||||
pub total_lp_a_fee: u128,
|
||||
pub total_lp_b_fee: u128,
|
||||
pub total_protocol_a_fee: u64,
|
||||
pub total_protocol_b_fee: u64,
|
||||
pub total_partner_a_fee: u64,
|
||||
pub total_partner_b_fee: u64,
|
||||
pub total_position: u64,
|
||||
pub padding: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct RewardInfo {
|
||||
pub initialized: u8,
|
||||
pub reward_token_flag: u8,
|
||||
pub padding_0: [u8; 6],
|
||||
pub padding_1: [u8; 8],
|
||||
pub mint: Pubkey,
|
||||
pub vault: Pubkey,
|
||||
pub funder: Pubkey,
|
||||
pub reward_duration: u64,
|
||||
pub reward_duration_end: u64,
|
||||
pub reward_rate: u128,
|
||||
pub reward_per_token_stored: [u8; 32],
|
||||
pub last_update_time: u64,
|
||||
pub cumulative_seconds_with_empty_liquidity_reward: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, BorshDeserialize)]
|
||||
pub struct Pool {
|
||||
pub pool_fees: PoolFeesStruct,
|
||||
pub token_a_mint: Pubkey,
|
||||
pub token_b_mint: Pubkey,
|
||||
pub token_a_vault: Pubkey,
|
||||
pub token_b_vault: Pubkey,
|
||||
pub whitelisted_vault: Pubkey,
|
||||
pub partner: Pubkey,
|
||||
pub liquidity: u128,
|
||||
pub padding: u128,
|
||||
pub protocol_a_fee: u64,
|
||||
pub protocol_b_fee: u64,
|
||||
pub partner_a_fee: u64,
|
||||
pub partner_b_fee: u64,
|
||||
pub sqrt_min_price: u128,
|
||||
pub sqrt_max_price: u128,
|
||||
pub sqrt_price: u128,
|
||||
pub activation_point: u64,
|
||||
pub activation_type: u8,
|
||||
pub pool_status: u8,
|
||||
pub token_a_flag: u8,
|
||||
pub token_b_flag: u8,
|
||||
pub collect_fee_mode: u8,
|
||||
pub pool_type: u8,
|
||||
pub padding_0: [u8; 2],
|
||||
pub fee_a_per_liquidity: [u8; 32],
|
||||
pub fee_b_per_liquidity: [u8; 32],
|
||||
pub permanent_lock_liquidity: u128,
|
||||
pub metrics: PoolMetrics,
|
||||
pub padding_1: [u64; 10],
|
||||
pub reward_infos: [RewardInfo; 2],
|
||||
}
|
||||
|
||||
pub const POOL_SIZE: usize = 1104;
|
||||
|
||||
pub fn pool_decode(data: &[u8]) -> Option<Pool> {
|
||||
if data.len() < POOL_SIZE {
|
||||
return None;
|
||||
}
|
||||
borsh::from_slice::<Pool>(&data[..POOL_SIZE]).ok()
|
||||
}
|
||||
@@ -3,9 +3,11 @@ pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod meteora_damm_v2;
|
||||
|
||||
// types
|
||||
pub mod bonk_types;
|
||||
pub mod pumpswap_types;
|
||||
pub mod raydium_amm_v4_types;
|
||||
pub mod raydium_cpmm_types;
|
||||
pub mod raydium_cpmm_types;
|
||||
pub mod meteora_damm_v2_types;
|
||||
+13
@@ -14,6 +14,7 @@ use crate::swqos::SwqosClient;
|
||||
use crate::swqos::SwqosConfig;
|
||||
use crate::swqos::TradeType;
|
||||
use crate::trading::core::params::BonkParams;
|
||||
use crate::trading::core::params::MeteoraDammV2Params;
|
||||
use crate::trading::core::params::PumpFunParams;
|
||||
use crate::trading::core::params::PumpSwapParams;
|
||||
use crate::trading::core::params::RaydiumAmmV4Params;
|
||||
@@ -104,6 +105,8 @@ pub struct TradeBuyParams {
|
||||
pub open_seed_optimize: bool,
|
||||
/// Durable nonce information
|
||||
pub durable_nonce: Option<DurableNonceInfo>,
|
||||
/// Optional fixed output token amount (If this value is set, it will be directly assigned to the output amount instead of being calculated)
|
||||
pub fixed_output_token_amount: Option<u64>,
|
||||
}
|
||||
|
||||
/// Parameters for executing sell orders across different DEX protocols
|
||||
@@ -142,6 +145,8 @@ pub struct TradeSellParams {
|
||||
pub open_seed_optimize: bool,
|
||||
/// Durable nonce information
|
||||
pub durable_nonce: Option<DurableNonceInfo>,
|
||||
/// Optional fixed output token amount (If this value is set, it will be directly assigned to the output amount instead of being calculated)
|
||||
pub fixed_output_token_amount: Option<u64>,
|
||||
}
|
||||
|
||||
impl SolanaTrade {
|
||||
@@ -300,6 +305,7 @@ impl SolanaTrade {
|
||||
close_input_mint_ata: params.close_input_token_ata,
|
||||
create_output_mint_ata: params.create_mint_ata,
|
||||
close_output_mint_ata: false,
|
||||
fixed_output_amount: params.fixed_output_token_amount,
|
||||
};
|
||||
|
||||
// Validate protocol params
|
||||
@@ -315,6 +321,9 @@ impl SolanaTrade {
|
||||
DexType::RaydiumAmmV4 => {
|
||||
protocol_params.as_any().downcast_ref::<RaydiumAmmV4Params>().is_some()
|
||||
}
|
||||
DexType::MeteoraDammV2 => {
|
||||
protocol_params.as_any().downcast_ref::<MeteoraDammV2Params>().is_some()
|
||||
}
|
||||
};
|
||||
|
||||
if !is_valid_params {
|
||||
@@ -389,6 +398,7 @@ impl SolanaTrade {
|
||||
close_input_mint_ata: false,
|
||||
create_output_mint_ata: params.create_output_token_ata,
|
||||
close_output_mint_ata: params.close_output_token_ata,
|
||||
fixed_output_amount: params.fixed_output_token_amount,
|
||||
};
|
||||
|
||||
// Validate protocol params
|
||||
@@ -404,6 +414,9 @@ impl SolanaTrade {
|
||||
DexType::RaydiumAmmV4 => {
|
||||
protocol_params.as_any().downcast_ref::<RaydiumAmmV4Params>().is_some()
|
||||
}
|
||||
DexType::MeteoraDammV2 => {
|
||||
protocol_params.as_any().downcast_ref::<MeteoraDammV2Params>().is_some()
|
||||
}
|
||||
};
|
||||
|
||||
if !is_valid_params {
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::common::bonding_curve::BondingCurveAccount;
|
||||
use crate::common::nonce_cache::DurableNonceInfo;
|
||||
use crate::common::spl_associated_token_account::get_associated_token_address_with_program_id;
|
||||
use crate::common::SolanaRpcClient;
|
||||
use crate::constants::TOKEN_PROGRAM;
|
||||
use crate::swqos::{SwqosClient, TradeType};
|
||||
use crate::trading::common::get_multi_token_balances;
|
||||
use crate::trading::MiddlewareManager;
|
||||
@@ -36,6 +37,7 @@ pub struct SwapParams {
|
||||
pub close_input_mint_ata: bool,
|
||||
pub create_output_mint_ata: bool,
|
||||
pub close_output_mint_ata: bool,
|
||||
pub fixed_output_amount: Option<u64>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SwapParams {
|
||||
@@ -647,3 +649,65 @@ impl ProtocolParams for RaydiumAmmV4Params {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// MeteoraDammV2 protocol specific parameters
|
||||
/// Configuration parameters specific to Meteora Damm V2 trading protocol
|
||||
#[derive(Clone)]
|
||||
pub struct MeteoraDammV2Params {
|
||||
pub pool: Pubkey,
|
||||
pub token_a_vault: Pubkey,
|
||||
pub token_b_vault: Pubkey,
|
||||
pub token_a_mint: Pubkey,
|
||||
pub token_b_mint: Pubkey,
|
||||
pub token_a_program: Pubkey,
|
||||
pub token_b_program: Pubkey,
|
||||
}
|
||||
|
||||
impl MeteoraDammV2Params {
|
||||
pub fn new(
|
||||
pool: Pubkey,
|
||||
token_a_vault: Pubkey,
|
||||
token_b_vault: Pubkey,
|
||||
token_a_mint: Pubkey,
|
||||
token_b_mint: Pubkey,
|
||||
token_a_program: Pubkey,
|
||||
token_b_program: Pubkey,
|
||||
) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
token_a_vault,
|
||||
token_b_vault,
|
||||
token_a_mint,
|
||||
token_b_mint,
|
||||
token_a_program,
|
||||
token_b_program,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_pool_address_by_rpc(
|
||||
rpc: &SolanaRpcClient,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<Self, anyhow::Error> {
|
||||
let pool_data =
|
||||
crate::instruction::utils::meteora_damm_v2::fetch_pool(rpc, pool_address).await?;
|
||||
Ok(Self {
|
||||
pool: pool_address.clone(),
|
||||
token_a_vault: pool_data.token_a_vault,
|
||||
token_b_vault: pool_data.token_b_vault,
|
||||
token_a_mint: pool_data.token_a_mint,
|
||||
token_b_mint: pool_data.token_b_mint,
|
||||
token_a_program: TOKEN_PROGRAM,
|
||||
token_b_program: TOKEN_PROGRAM,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ProtocolParams for MeteoraDammV2Params {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn ProtocolParams> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -1,9 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::instruction::{
|
||||
bonk::BonkInstructionBuilder, pumpfun::PumpFunInstructionBuilder,
|
||||
pumpswap::PumpSwapInstructionBuilder, raydium_amm_v4::RaydiumAmmV4InstructionBuilder,
|
||||
raydium_cpmm::RaydiumCpmmInstructionBuilder,
|
||||
bonk::BonkInstructionBuilder, meteora_damm_v2::MeteoraDammV2InstructionBuilder,
|
||||
pumpfun::PumpFunInstructionBuilder, pumpswap::PumpSwapInstructionBuilder,
|
||||
raydium_amm_v4::RaydiumAmmV4InstructionBuilder, raydium_cpmm::RaydiumCpmmInstructionBuilder,
|
||||
};
|
||||
|
||||
use super::core::{executor::GenericTradeExecutor, traits::TradeExecutor};
|
||||
@@ -16,6 +16,7 @@ pub enum DexType {
|
||||
Bonk,
|
||||
RaydiumCpmm,
|
||||
RaydiumAmmV4,
|
||||
MeteoraDammV2,
|
||||
}
|
||||
|
||||
/// 交易工厂 - 用于创建不同协议的交易执行器
|
||||
@@ -30,6 +31,7 @@ impl TradeFactory {
|
||||
DexType::Bonk => Self::bonk_executor(),
|
||||
DexType::RaydiumCpmm => Self::raydium_cpmm_executor(),
|
||||
DexType::RaydiumAmmV4 => Self::raydium_amm_v4_executor(),
|
||||
DexType::MeteoraDammV2 => Self::meteora_damm_v2_executor(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,4 +85,14 @@ impl TradeFactory {
|
||||
});
|
||||
INSTANCE.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn meteora_damm_v2_executor() -> Arc<dyn TradeExecutor> {
|
||||
static INSTANCE: std::sync::LazyLock<Arc<dyn TradeExecutor>> =
|
||||
std::sync::LazyLock::new(|| {
|
||||
let instruction_builder = Arc::new(MeteoraDammV2InstructionBuilder);
|
||||
Arc::new(GenericTradeExecutor::new(instruction_builder, "MeteoraDammV2"))
|
||||
});
|
||||
INSTANCE.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user