feat: upgrade to v2.0.0 with major dependency updates
- Upgrade Solana dependencies from 2.3.x to 3.0.0 series - Upgrade solana-streamer-sdk from 0.4.13 to 0.5.0 - Upgrade yellowstone-grpc from 8.0.0 to 9.0.0 - Add new SPL token utility modules (spl_token.rs, spl_token_2022.rs, spl_associated_token_account.rs) - Remove deprecated gRPC protocol definitions (protos/ directory) - Remove event_subscription example and update all example dependencies - Update README documentation to reflect v2.0.0 changes - Refactor imports to use new dependency structure across examples BREAKING CHANGES: - Solana SDK upgraded to 3.0.0 with API changes - Removed gRPC protocol definitions - Updated import paths for SPL token operations
This commit is contained in:
+27
-25
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sol-trade-sdk"
|
||||
version = "1.2.3"
|
||||
version = "2.0.0"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"William <byteblock6@gmail.com>",
|
||||
@@ -17,7 +17,6 @@ readme = "README.md"
|
||||
[workspace]
|
||||
members = [
|
||||
"examples/trading_client",
|
||||
"examples/event_subscription",
|
||||
"examples/middleware_system",
|
||||
"examples/pumpfun_copy_trading",
|
||||
"examples/pumpfun_sniper_trading",
|
||||
@@ -39,22 +38,24 @@ members = [
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
solana-streamer-sdk = "0.4.13"
|
||||
solana-sdk = "2.3.0"
|
||||
solana-client = "2.3.6"
|
||||
solana-program = "2.3.0"
|
||||
solana-rpc-client = "2.3.6"
|
||||
solana-rpc-client-api = "2.3.6"
|
||||
solana-transaction-status = "2.3.6"
|
||||
solana-account-decoder = "2.3.6"
|
||||
solana-hash = "2.3.0"
|
||||
solana-entry = "2.3.6"
|
||||
solana-rpc-client-nonce-utils = "2.3.6"
|
||||
solana-perf = "2.3.6"
|
||||
solana-metrics = "2.3.6"
|
||||
spl-token = "8.0.0"
|
||||
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
||||
spl-associated-token-account = "7.0.0"
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-client = "3.0.0"
|
||||
solana-program = "3.0.0"
|
||||
solana-rpc-client = "3.0.0"
|
||||
solana-rpc-client-api = "3.0.0"
|
||||
solana-transaction-status = "3.0.0"
|
||||
solana-account-decoder = "3.0.0"
|
||||
solana-hash = "3.0.0"
|
||||
solana-entry = "3.0.0"
|
||||
solana-rpc-client-nonce-utils = "3.0.0"
|
||||
solana-perf = "3.0.0"
|
||||
solana-metrics = "3.0.0"
|
||||
solana-nonce = "3.0.0"
|
||||
solana-address-lookup-table-interface = "3.0.0"
|
||||
solana-compute-budget-interface = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
|
||||
borsh = { version = "1.5.3", features = ["derive"] }
|
||||
isahc = "1.7.2"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
@@ -66,11 +67,11 @@ bs58 = "0.5.1"
|
||||
rand = "0.9.0"
|
||||
bincode = "1.3.3"
|
||||
anyhow = "1.0.90"
|
||||
yellowstone-grpc-client = { version = "8.0.0" }
|
||||
yellowstone-grpc-proto = { version = "8.0.0" }
|
||||
yellowstone-grpc-client = { version = "9.0.0" }
|
||||
yellowstone-grpc-proto = { version = "9.0.0" }
|
||||
reqwest = { version = "0.12.12", features = ["json", "multipart"] }
|
||||
tokio = { version = "1.42.0" , features = ["full", "rt-multi-thread"]}
|
||||
tonic = { version = "0.12.3", features = ["tls", "tls-roots", "tls-webpki-roots"] }
|
||||
tonic = { version = "0.14.2", features = ["transport"] }
|
||||
rustls = { version = "0.23.23", features = ["ring"] }
|
||||
rustls-native-certs = "0.8.1"
|
||||
tokio-rustls = "0.26.1"
|
||||
@@ -83,8 +84,8 @@ thiserror = "2.0.11"
|
||||
async-trait = "0.1.86"
|
||||
lazy_static = "1.5.0"
|
||||
once_cell = "1.20.3"
|
||||
prost = "0.13.5"
|
||||
prost-types = "0.13.5"
|
||||
prost = "0.14.1"
|
||||
prost-types = "0.14.1"
|
||||
num_enum = "0.7.3"
|
||||
num-derive = "0.4.2"
|
||||
num-traits = "0.2.19"
|
||||
@@ -93,11 +94,12 @@ bytemuck = { version = "1.4.0" }
|
||||
arrayref = "0.3.6"
|
||||
borsh-derive = "1.5.5"
|
||||
indicatif = "0.18.0"
|
||||
solana-system-interface = { version = "1.0.0", features = ["bincode"] }
|
||||
solana-system-interface = { version = "2.0.0", features = ["bincode"] }
|
||||
fnv = "1.0.7"
|
||||
dashmap = "6.1.0"
|
||||
clru = "0.6"
|
||||
smallvec = "1.15.1"
|
||||
parking_lot = "0.12"
|
||||
arc-swap = "1.7"
|
||||
sha2 = "0.10"
|
||||
sha2 = "0.10"
|
||||
tonic-prost = "0.14.2"
|
||||
|
||||
@@ -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 = "1.2.3" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "2.0.0" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
sol-trade-sdk = "1.2.3"
|
||||
sol-trade-sdk = "2.0.0"
|
||||
```
|
||||
|
||||
## 🛠️ Usage Examples
|
||||
@@ -179,7 +179,6 @@ Please ensure that the parameters your trading logic depends on are available in
|
||||
|
||||
| Description | Run Command | Source Code |
|
||||
|-------------|-------------|-------------|
|
||||
| Monitor token trading events | `cargo run --package event_subscription` | [examples/event_subscription](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/event_subscription/src/main.rs) |
|
||||
| Create and configure SolanaTrade instance | `cargo run --package trading_client` | [examples/trading_client](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/trading_client/src/main.rs) |
|
||||
| PumpFun token sniping trading | `cargo run --package pumpfun_sniper_trading` | [examples/pumpfun_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_sniper_trading/src/main.rs) |
|
||||
| PumpFun token copy trading | `cargo run --package pumpfun_copy_trading` | [examples/pumpfun_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_copy_trading/src/main.rs) |
|
||||
@@ -272,7 +271,6 @@ src/
|
||||
├── constants/ # Constant definitions
|
||||
├── instruction/ # Instruction building
|
||||
│ └── utils/ # Instruction utilities
|
||||
├── protos/ # gRPC protocol definitions
|
||||
├── swqos/ # MEV service clients
|
||||
├── trading/ # Unified trading engine
|
||||
│ ├── common/ # Common trading tools
|
||||
|
||||
+2
-4
@@ -87,14 +87,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "1.2.3" }
|
||||
sol-trade-sdk = { path = "./sol-trade-sdk", version = "2.0.0" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
sol-trade-sdk = "1.2.3"
|
||||
sol-trade-sdk = "2.0.0"
|
||||
```
|
||||
|
||||
## 🛠️ 使用示例
|
||||
@@ -180,7 +180,6 @@ client.buy(buy_params).await?;
|
||||
|
||||
| 描述 | 运行命令 | 源码路径 |
|
||||
|------|---------|----------|
|
||||
| 监听代币交易事件 | `cargo run --package event_subscription` | [examples/event_subscription](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/event_subscription/src/main.rs) |
|
||||
| 创建和配置 SolanaTrade 实例 | `cargo run --package trading_client` | [examples/trading_client](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/trading_client/src/main.rs) |
|
||||
| PumpFun 代币狙击交易 | `cargo run --package pumpfun_sniper_trading` | [examples/pumpfun_sniper_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_sniper_trading/src/main.rs) |
|
||||
| PumpFun 代币跟单交易 | `cargo run --package pumpfun_copy_trading` | [examples/pumpfun_copy_trading](https://github.com/0xfnzero/sol-trade-sdk/tree/main/examples/pumpfun_copy_trading/src/main.rs) |
|
||||
@@ -273,7 +272,6 @@ src/
|
||||
├── constants/ # 常量定义
|
||||
├── instruction/ # 指令构建
|
||||
│ └── utils/ # 指令工具函数
|
||||
├── protos/ # gRPC 协议定义
|
||||
├── swqos/ # MEV 服务客户端
|
||||
├── trading/ # 统一交易引擎
|
||||
│ ├── common/ # 通用交易工具
|
||||
|
||||
@@ -5,7 +5,9 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-address-lookup-table-interface = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1.0.94"
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
use sol_trade_sdk::common::address_lookup_cache::AddressLookupTableCache;
|
||||
use sol_trade_sdk::common::SolanaRpcClient;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpFunParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_streamer_sdk::match_event;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use std::{
|
||||
str::FromStr,
|
||||
sync::{
|
||||
@@ -6,31 +26,6 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::{
|
||||
common::address_lookup_cache::AddressLookupTableCache,
|
||||
solana_streamer_sdk::streaming::event_parser::common::EventType,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpFunParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
common::SolanaRpcClient,
|
||||
solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter,
|
||||
};
|
||||
use sol_trade_sdk::{common::TradeConfig, solana_streamer_sdk::match_event};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -3,25 +3,25 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::common::spl_associated_token_account::get_associated_token_address;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::BonkParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{common::TradeConfig, solana_streamer_sdk::match_event};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use solana_streamer_sdk::match_event;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use sol_trade_sdk::common::spl_associated_token_account::get_associated_token_address;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::BonkParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
|
||||
@@ -5,8 +5,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
solana-system-interface = "2.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
spl-token = "8.0.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
@@ -1,8 +1,14 @@
|
||||
use clap::Parser;
|
||||
use sol_trade_sdk::{
|
||||
common::{
|
||||
fast_fn::get_associated_token_address_with_program_id_fast_use_seed, AnyResult, TradeConfig,
|
||||
fast_fn::get_associated_token_address_with_program_id_fast_use_seed,
|
||||
spl_associated_token_account::{
|
||||
create_associated_token_account_idempotent, get_associated_token_address,
|
||||
},
|
||||
spl_token::{self, close_account},
|
||||
AnyResult, TradeConfig,
|
||||
},
|
||||
constants::WSOL_TOKEN_ACCOUNT,
|
||||
swqos::SwqosConfig,
|
||||
trading::{
|
||||
core::params::{
|
||||
@@ -12,10 +18,15 @@ use sol_trade_sdk::{
|
||||
},
|
||||
SolanaTrade, TradeBuyParams, TradeSellParams, TradeTokenType,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, native_token::sol_str_to_lamports, pubkey::Pubkey,
|
||||
signature::Keypair, signer::Signer,
|
||||
message::{AccountMeta, Instruction},
|
||||
native_token::sol_str_to_lamports,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
signer::Signer,
|
||||
};
|
||||
use solana_system_interface::instruction::transfer;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
@@ -1238,7 +1249,7 @@ async fn handle_close_wsol() -> Result<(), Box<dyn std::error::Error>> {
|
||||
match initialize_real_client().await {
|
||||
Ok(client) => {
|
||||
// Check WSOL balance first
|
||||
let wsol_mint = spl_token::native_mint::id();
|
||||
let wsol_mint = WSOL_TOKEN_ACCOUNT;
|
||||
match client.get_payer_token_balance(&wsol_mint).await {
|
||||
Ok(wsol_balance) => {
|
||||
if wsol_balance == 0 {
|
||||
@@ -1332,29 +1343,27 @@ async fn wrap_sol_real(
|
||||
let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap();
|
||||
let instructions = vec![
|
||||
// Create associated token account for WSOL
|
||||
spl_associated_token_account::instruction::create_associated_token_account_idempotent(
|
||||
create_associated_token_account_idempotent(
|
||||
&client.get_payer_pubkey(),
|
||||
&client.get_payer_pubkey(),
|
||||
&wsol_mint,
|
||||
&spl_token::ID,
|
||||
),
|
||||
// Transfer SOL to WSOL account
|
||||
solana_sdk::system_instruction::transfer(
|
||||
transfer(
|
||||
&client.get_payer_pubkey(),
|
||||
&spl_associated_token_account::get_associated_token_address(
|
||||
&client.get_payer_pubkey(),
|
||||
&wsol_mint,
|
||||
),
|
||||
&get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint),
|
||||
amount_lamports,
|
||||
),
|
||||
// Sync native (convert SOL to WSOL tokens)
|
||||
spl_token::instruction::sync_native(
|
||||
&spl_token::ID,
|
||||
&spl_associated_token_account::get_associated_token_address(
|
||||
&client.get_payer_pubkey(),
|
||||
&wsol_mint,
|
||||
),
|
||||
)?,
|
||||
Instruction {
|
||||
program_id: sol_trade_sdk::constants::TOKEN_PROGRAM,
|
||||
accounts: vec![AccountMeta::new(
|
||||
get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint),
|
||||
false,
|
||||
)],
|
||||
data: vec![17],
|
||||
},
|
||||
];
|
||||
|
||||
// Build and send transaction
|
||||
@@ -1375,10 +1384,7 @@ async fn close_wsol_real(client: &SolanaTrade) -> Result<(), Box<dyn std::error:
|
||||
use solana_sdk::transaction::Transaction;
|
||||
|
||||
let wsol_mint = "So11111111111111111111111111111111111111112".parse().unwrap();
|
||||
let wsol_account = spl_associated_token_account::get_associated_token_address(
|
||||
&client.get_payer_pubkey(),
|
||||
&wsol_mint,
|
||||
);
|
||||
let wsol_account = get_associated_token_address(&client.get_payer_pubkey(), &wsol_mint);
|
||||
|
||||
// Check if WSOL account exists
|
||||
let account_info = client.rpc.get_account(&wsol_account).await;
|
||||
@@ -1387,7 +1393,7 @@ async fn close_wsol_real(client: &SolanaTrade) -> Result<(), Box<dyn std::error:
|
||||
}
|
||||
|
||||
// Close WSOL account instruction
|
||||
let close_instruction = spl_token::instruction::close_account(
|
||||
let close_instruction = close_account(
|
||||
&spl_token::ID,
|
||||
&wsol_account,
|
||||
&client.get_payer_pubkey(),
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
[package]
|
||||
name = "event_subscription"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
@@ -1,182 +0,0 @@
|
||||
use sol_trade_sdk::solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
common::{filter::EventTypeFilter, EventType},
|
||||
protocols::{
|
||||
bonk::{parser::BONK_PROGRAM_ID, BonkPoolCreateEvent, BonkTradeEvent},
|
||||
pumpfun::{parser::PUMPFUN_PROGRAM_ID, PumpFunCreateTokenEvent, PumpFunTradeEvent},
|
||||
pumpswap::{
|
||||
parser::PUMPSWAP_PROGRAM_ID, PumpSwapBuyEvent, PumpSwapCreatePoolEvent,
|
||||
PumpSwapDepositEvent, PumpSwapSellEvent, PumpSwapWithdrawEvent,
|
||||
},
|
||||
raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID,
|
||||
raydium_clmm::parser::RAYDIUM_CLMM_PROGRAM_ID,
|
||||
raydium_cpmm::{parser::RAYDIUM_CPMM_PROGRAM_ID, RaydiumCpmmSwapEvent},
|
||||
},
|
||||
Protocol, UnifiedEvent,
|
||||
},
|
||||
yellowstone_grpc::{AccountFilter, TransactionFilter},
|
||||
ShredStreamGrpc, YellowstoneGrpc,
|
||||
},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("This example demonstrates how to subscribe to events using Yellowstone gRPC and ShredStream.");
|
||||
println!("You can choose which example to run by uncommenting the relevant function call.");
|
||||
|
||||
// Uncomment one of these to run the example:
|
||||
test_grpc().await?; // Use public Yellowstone gRPC endpoint
|
||||
// test_shreds().await?; // Use local ShredStream endpoint
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Subscribe to events using Yellowstone gRPC
|
||||
async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to GRPC events...");
|
||||
|
||||
// Initialize gRPC client with public endpoint
|
||||
let grpc = YellowstoneGrpc::new(
|
||||
"https://solana-yellowstone-grpc.publicnode.com:443".to_string(),
|
||||
None, // No auth token needed
|
||||
)?;
|
||||
|
||||
let callback = create_event_callback();
|
||||
|
||||
// Define protocols to monitor
|
||||
let protocols =
|
||||
vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk, Protocol::RaydiumCpmm];
|
||||
|
||||
// Define program IDs to monitor
|
||||
let account_include = vec![
|
||||
PUMPFUN_PROGRAM_ID.to_string(), // Listen to pumpfun program ID
|
||||
PUMPSWAP_PROGRAM_ID.to_string(), // Listen to pumpswap program ID
|
||||
BONK_PROGRAM_ID.to_string(), // Listen to bonk program ID
|
||||
RAYDIUM_CPMM_PROGRAM_ID.to_string(), // Listen to raydium_cpmm program ID
|
||||
RAYDIUM_CLMM_PROGRAM_ID.to_string(), // Listen to raydium_clmm program ID
|
||||
RAYDIUM_AMM_V4_PROGRAM_ID.to_string(), // Listen to raydium_amm_v4 program ID
|
||||
];
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
|
||||
// Configure transaction filter
|
||||
let transaction_filter = TransactionFilter {
|
||||
account_include: account_include.clone(),
|
||||
account_exclude,
|
||||
account_required,
|
||||
};
|
||||
|
||||
// Configure account filter for program-owned accounts
|
||||
let account_filter =
|
||||
AccountFilter { account: vec![], owner: account_include.clone(), filters: vec![] };
|
||||
|
||||
// Configure event type filter (all events)
|
||||
let event_type_filter = None;
|
||||
// For specific events only:
|
||||
// let event_type_filter =
|
||||
// EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
|
||||
// Start subscription
|
||||
grpc.subscribe_events_immediate(
|
||||
protocols,
|
||||
None,
|
||||
vec![transaction_filter],
|
||||
vec![account_filter],
|
||||
event_type_filter,
|
||||
None,
|
||||
callback,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Wait for termination signal
|
||||
tokio::signal::ctrl_c().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Subscribe to events using ShredStream
|
||||
async fn test_shreds() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Subscribing to ShredStream events...");
|
||||
|
||||
// Initialize ShredStream client with local endpoint
|
||||
let shred_stream = ShredStreamGrpc::new("http://127.0.0.1:10800".to_string()).await?;
|
||||
|
||||
let callback = create_event_callback();
|
||||
|
||||
// Define protocols to monitor
|
||||
let protocols = vec![Protocol::PumpFun, Protocol::PumpSwap, Protocol::Bonk];
|
||||
|
||||
// Configure event type filter (all events)
|
||||
let event_type_filter = None;
|
||||
// For specific events only:
|
||||
// let event_type_filter =
|
||||
// EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
|
||||
// Start subscription
|
||||
shred_stream
|
||||
.shredstream_subscribe(
|
||||
protocols,
|
||||
None, // No slot range specified
|
||||
event_type_filter,
|
||||
callback,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Wait for termination signal
|
||||
tokio::signal::ctrl_c().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create an event callback function that handles different types of events
|
||||
fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
// Process events using match_event! macro
|
||||
match_event!(event, {
|
||||
// Bonk protocol events
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
println!("BonkPoolCreateEvent: {:?}", e.base_mint_param.symbol);
|
||||
},
|
||||
BonkTradeEvent => |e: BonkTradeEvent| {
|
||||
println!("BonkTradeEvent: {:?}", e);
|
||||
},
|
||||
|
||||
// PumpFun protocol events
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {:?}", e);
|
||||
},
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {:?}", e);
|
||||
},
|
||||
|
||||
// PumpSwap protocol events
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {:?}", e);
|
||||
},
|
||||
PumpSwapSellEvent => |e: PumpSwapSellEvent| {
|
||||
println!("Sell event: {:?}", e);
|
||||
},
|
||||
PumpSwapCreatePoolEvent => |e: PumpSwapCreatePoolEvent| {
|
||||
println!("CreatePool event: {:?}", e);
|
||||
},
|
||||
PumpSwapDepositEvent => |e: PumpSwapDepositEvent| {
|
||||
println!("Deposit event: {:?}", e);
|
||||
},
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {:?}", e);
|
||||
},
|
||||
|
||||
// Raydium protocol events
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {:?}", e);
|
||||
},
|
||||
// For more events and documentation, please refer to https://github.com/0xfnzero/solana-streamer
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
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"] }
|
||||
anyhow = "1.0.79"
|
||||
@@ -8,10 +8,8 @@ use sol_trade_sdk::{
|
||||
},
|
||||
SolanaTrade, TradeTokenType,
|
||||
};
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig, instruction::Instruction, pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signature::Keypair};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -5,7 +5,9 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
solana-streamer-sdk = "0.5.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
spl-associated-token-account = "7.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1.0.94"
|
||||
|
||||
@@ -3,28 +3,25 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::{
|
||||
common::nonce_cache::NonceCache,
|
||||
solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID,
|
||||
};
|
||||
use sol_trade_sdk::common::nonce_cache::NonceCache;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpFunParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{common::TradeConfig, solana_streamer_sdk::match_event};
|
||||
use sol_trade_sdk::{
|
||||
solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter, TradeTokenType,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_streamer_sdk::match_event;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -3,27 +3,27 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
use sol_trade_sdk::common::{
|
||||
spl_associated_token_account::get_associated_token_address, TradeConfig,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpFunParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{common::TradeConfig, solana_streamer_sdk::match_event};
|
||||
use sol_trade_sdk::{
|
||||
solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter, TradeTokenType,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use solana_streamer_sdk::match_event;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::parser::PUMPFUN_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
use sol_trade_sdk::common::spl_associated_token_account::get_associated_token_address;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use sol_trade_sdk::solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
@@ -11,10 +7,14 @@ use sol_trade_sdk::{
|
||||
trading::{core::params::PumpFunParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use std::mem::take;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::{match_event, streaming::ShredStreamGrpc};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
|
||||
@@ -5,8 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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= "8.0.0"
|
||||
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
||||
@@ -1,12 +1,15 @@
|
||||
use sol_trade_sdk::{
|
||||
common::{AnyResult, TradeConfig},
|
||||
common::{
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id, AnyResult,
|
||||
TradeConfig,
|
||||
},
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpSwapParams, factory::DexType},
|
||||
SolanaTrade, TradeTokenType,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{pubkey::Pubkey, signer::Signer};
|
||||
use spl_associated_token_account::get_associated_token_address_with_program_id;
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
#[tokio::main]
|
||||
@@ -47,7 +50,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let rpc = client.rpc.clone();
|
||||
let payer = client.payer.pubkey();
|
||||
let program_id = spl_token_2022::ID;
|
||||
let program_id = sol_trade_sdk::constants::TOKEN_PROGRAM_2022;
|
||||
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();
|
||||
|
||||
@@ -5,7 +5,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
@@ -1,40 +1,31 @@
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::solana_streamer_sdk::{
|
||||
match_event, streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID,
|
||||
};
|
||||
use sol_trade_sdk::common::spl_associated_token_account::get_associated_token_address_with_program_id;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::PumpSwapParams, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
common::TradeConfig,
|
||||
solana_streamer_sdk::streaming::event_parser::{
|
||||
common::EventType, protocols::pumpswap::PumpSwapSellEvent,
|
||||
},
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
instruction::utils::pumpswap::accounts,
|
||||
solana_streamer_sdk::streaming::event_parser::{
|
||||
common::filter::EventTypeFilter, protocols::pumpswap::PumpSwapBuyEvent,
|
||||
},
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent},
|
||||
TradeTokenType,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{pubkey::Pubkey, signer::Signer};
|
||||
use spl_associated_token_account::get_associated_token_address_with_program_id;
|
||||
use solana_streamer_sdk::streaming::event_parser::{
|
||||
common::filter::EventTypeFilter, protocols::pumpswap::PumpSwapBuyEvent,
|
||||
};
|
||||
use solana_streamer_sdk::streaming::event_parser::{
|
||||
common::EventType, protocols::pumpswap::PumpSwapSellEvent,
|
||||
};
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use solana_streamer_sdk::{
|
||||
match_event, streaming::event_parser::protocols::pumpswap::parser::PUMPSWAP_PROGRAM_ID,
|
||||
};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::{common::TradeConfig, instruction::utils::raydium_amm_v4::{accounts, fetch_amm_info}, solana_streamer_sdk::{match_event, streaming::event_parser::protocols::raydium_amm_v4::RaydiumAmmV4SwapEvent}, trading::common::get_multi_token_balances, TradeTokenType};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID;
|
||||
use sol_trade_sdk::{solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent}};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::yellowstone_grpc::{
|
||||
AccountFilter, TransactionFilter,
|
||||
};
|
||||
use sol_trade_sdk::solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use sol_trade_sdk::{
|
||||
common::AnyResult,
|
||||
common::TradeConfig, instruction::utils::raydium_amm_v4::fetch_amm_info,
|
||||
trading::common::get_multi_token_balances, TradeTokenType,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
common::{spl_associated_token_account::get_associated_token_address, AnyResult},
|
||||
swqos::SwqosConfig,
|
||||
trading::{core::params::RaydiumAmmV4Params, factory::DexType},
|
||||
SolanaTrade,
|
||||
};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::parser::RAYDIUM_AMM_V4_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use solana_streamer_sdk::{
|
||||
match_event, streaming::event_parser::protocols::raydium_amm_v4::RaydiumAmmV4SwapEvent,
|
||||
};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
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"] }
|
||||
|
||||
@@ -1,37 +1,27 @@
|
||||
use sol_trade_sdk::common::spl_associated_token_account::get_associated_token_address;
|
||||
use sol_trade_sdk::common::TradeConfig;
|
||||
use sol_trade_sdk::constants::WSOL_TOKEN_ACCOUNT;
|
||||
use sol_trade_sdk::trading::core::params::RaydiumCpmmParams;
|
||||
use sol_trade_sdk::trading::factory::DexType;
|
||||
use sol_trade_sdk::TradeTokenType;
|
||||
use sol_trade_sdk::{common::AnyResult, swqos::SwqosConfig, SolanaTrade};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID;
|
||||
use solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter};
|
||||
use solana_streamer_sdk::streaming::YellowstoneGrpc;
|
||||
use solana_streamer_sdk::{
|
||||
match_event, streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use sol_trade_sdk::solana_streamer_sdk::{
|
||||
match_event, streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent,
|
||||
};
|
||||
use sol_trade_sdk::{common::AnyResult, swqos::SwqosConfig, SolanaTrade};
|
||||
use sol_trade_sdk::{
|
||||
common::TradeConfig,
|
||||
solana_streamer_sdk::streaming::yellowstone_grpc::{AccountFilter, TransactionFilter},
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
constants::WSOL_TOKEN_ACCOUNT,
|
||||
solana_streamer_sdk::streaming::event_parser::{Protocol, UnifiedEvent},
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
instruction::utils::raydium_cpmm::accounts,
|
||||
solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::parser::RAYDIUM_CPMM_PROGRAM_ID,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
solana_streamer_sdk::streaming::event_parser::common::filter::EventTypeFilter,
|
||||
trading::factory::DexType,
|
||||
};
|
||||
use sol_trade_sdk::{
|
||||
solana_streamer_sdk::streaming::event_parser::common::EventType,
|
||||
trading::core::params::RaydiumCpmmParams,
|
||||
};
|
||||
use sol_trade_sdk::{solana_streamer_sdk::streaming::YellowstoneGrpc, TradeTokenType};
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
|
||||
// Global static flag to ensure transaction is executed only once
|
||||
static ALREADY_EXECUTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
spl-associated-token-account = "7.0.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
spl-token= "8.0.0"
|
||||
spl-token-2022 = { version = "8.0.0", features = ["no-entrypoint"] }
|
||||
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"] }
|
||||
@@ -6,7 +6,8 @@ use sol_trade_sdk::{
|
||||
trading::{core::params::PumpSwapParams, factory::DexType},
|
||||
SolanaTrade, TradeTokenType,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::{pubkey::Pubkey, signer::Signer};
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
@@ -50,7 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let rpc = client.rpc.clone();
|
||||
let payer = client.payer.pubkey();
|
||||
let program_id = spl_token::ID;
|
||||
let program_id = sol_trade_sdk::constants::TOKEN_PROGRAM;
|
||||
// ❗️❗️❗️❗️ Must use the 'use seed' method to get the ATA account, otherwise the transaction will fail
|
||||
let account = get_associated_token_address_with_program_id_fast_use_seed(
|
||||
&payer,
|
||||
|
||||
@@ -5,5 +5,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
||||
@@ -3,7 +3,8 @@ use sol_trade_sdk::{
|
||||
swqos::{SwqosConfig, SwqosRegion},
|
||||
SolanaTrade,
|
||||
};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
sol-trade-sdk = { path = "../.." }
|
||||
solana-sdk = "2.3.0"
|
||||
solana-sdk = "3.0.0"
|
||||
solana-commitment-config = { version = "3.0.0", features = ["serde"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
anyhow = "1.0"
|
||||
@@ -1,5 +1,6 @@
|
||||
use sol_trade_sdk::{common::TradeConfig, swqos::SwqosConfig, SolanaTrade};
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, signature::Keypair};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use dashmap::DashMap;
|
||||
use solana_sdk::{
|
||||
address_lookup_table::state::AddressLookupTable, message::AddressLookupTableAccount,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use solana_address_lookup_table_interface::state::AddressLookupTable;
|
||||
use solana_sdk::{message::AddressLookupTableAccount, pubkey::Pubkey};
|
||||
use std::{
|
||||
error::Error,
|
||||
sync::{Arc, OnceLock},
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::instruction::utils::pumpfun::global_constants::{
|
||||
TOKEN_TOTAL_SUPPLY,
|
||||
};
|
||||
use crate::instruction::utils::pumpfun::{get_bonding_curve_pda, get_creator_vault_pda};
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
|
||||
/// Represents the global configuration account for token pricing and fees
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
|
||||
@@ -5,11 +5,10 @@ use solana_sdk::{
|
||||
instruction::{AccountMeta, Instruction},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use spl_associated_token_account::{
|
||||
get_associated_token_address_with_program_id, ID as ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use crate::common::{spl_associated_token_account::get_associated_token_address_with_program_id, spl_token::close_account};
|
||||
|
||||
const MAX_PDA_CACHE_SIZE: usize = 10000;
|
||||
const MAX_ATA_CACHE_SIZE: usize = 10000;
|
||||
const MAX_INSTRUCTION_CACHE_SIZE: usize = 10000;
|
||||
@@ -104,7 +103,7 @@ pub fn _create_associated_token_account_idempotent_fast(
|
||||
if use_seed
|
||||
&& !mint.eq(&crate::constants::WSOL_TOKEN_ACCOUNT)
|
||||
&& !mint.eq(&crate::constants::SOL_TOKEN_ACCOUNT)
|
||||
&& token_program.eq(&spl_token::ID)
|
||||
&& token_program.eq(&crate::constants::TOKEN_PROGRAM)
|
||||
{
|
||||
// Use cache to get instruction
|
||||
get_cached_instructions(cache_key, || {
|
||||
@@ -120,7 +119,7 @@ pub fn _create_associated_token_account_idempotent_fast(
|
||||
// Create Associated Token Account instruction
|
||||
// Reference implementation of spl_associated_token_account::instruction::create_associated_token_account
|
||||
vec![Instruction {
|
||||
program_id: ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
program_id: crate::constants::ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
accounts: vec![
|
||||
AccountMeta::new(*payer, true), // Payer (signer, writable)
|
||||
AccountMeta::new(associated_token_address, false), // ATA address (writable, non-signer)
|
||||
@@ -247,7 +246,7 @@ fn _get_associated_token_address_with_program_id_fast(
|
||||
let ata = if use_seed
|
||||
&& !token_mint_address.eq(&crate::constants::WSOL_TOKEN_ACCOUNT)
|
||||
&& !token_mint_address.eq(&crate::constants::SOL_TOKEN_ACCOUNT)
|
||||
&& token_program_id.eq(&spl_token::ID)
|
||||
&& token_program_id.eq(&crate::constants::TOKEN_PROGRAM)
|
||||
{
|
||||
super::seed::get_associated_token_address_with_program_id_use_seed(
|
||||
wallet_address,
|
||||
@@ -292,7 +291,7 @@ pub fn fast_init(payer: &Pubkey) {
|
||||
wsol_token_account,
|
||||
},
|
||||
|| {
|
||||
vec![spl_token::instruction::close_account(
|
||||
vec![close_account(
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
&wsol_token_account,
|
||||
&payer,
|
||||
|
||||
+5
-2
@@ -1,12 +1,15 @@
|
||||
pub mod address_lookup_cache;
|
||||
pub mod bonding_curve;
|
||||
pub mod fast_fn;
|
||||
pub mod gas_fee_strategy;
|
||||
pub mod global;
|
||||
pub mod nonce_cache;
|
||||
pub mod seed;
|
||||
pub mod spl_associated_token_account;
|
||||
pub mod spl_token;
|
||||
pub mod spl_token_2022;
|
||||
pub mod subscription_handle;
|
||||
pub mod types;
|
||||
pub mod gas_fee_strategy;
|
||||
|
||||
pub use gas_fee_strategy::*;
|
||||
pub use types::*;
|
||||
pub use gas_fee_strategy::*;
|
||||
+11
-13
@@ -1,8 +1,8 @@
|
||||
use parking_lot::Mutex;
|
||||
use solana_hash::Hash;
|
||||
use solana_nonce::state::State;
|
||||
use solana_nonce::versions::Versions;
|
||||
use solana_sdk::account_utils::StateMut;
|
||||
use solana_sdk::nonce::state::Versions;
|
||||
use solana_sdk::nonce::State;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::common::SolanaRpcClient;
|
||||
use std::str::FromStr;
|
||||
@@ -59,8 +59,8 @@ impl NonceCache {
|
||||
self.update_nonce_info_partial(nonce_account, None, Some(false));
|
||||
}
|
||||
|
||||
/// Get a copy of NonceInfo
|
||||
pub fn get_nonce_info(&self) -> NonceInfo {
|
||||
/// Get a copy of NonceInfo
|
||||
pub fn get_nonce_info(&self) -> NonceInfo {
|
||||
let nonce_info = self.nonce_info.lock();
|
||||
NonceInfo {
|
||||
nonce_account: nonce_info.nonce_account,
|
||||
@@ -72,15 +72,13 @@ impl NonceCache {
|
||||
pub fn get_durable_nonce_info() -> DurableNonceInfo {
|
||||
let nonce_info = Self::get_instance().get_nonce_info();
|
||||
let nonce_account = nonce_info.nonce_account;
|
||||
let current_nonce = if nonce_account.is_some() && nonce_info.current_nonce != Hash::default() {
|
||||
Some(nonce_info.current_nonce)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
DurableNonceInfo {
|
||||
nonce_account,
|
||||
current_nonce,
|
||||
}
|
||||
let current_nonce =
|
||||
if nonce_account.is_some() && nonce_info.current_nonce != Hash::default() {
|
||||
Some(nonce_info.current_nonce)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
DurableNonceInfo { nonce_account, current_nonce }
|
||||
}
|
||||
|
||||
/// Partially update NonceInfo, only update the passed fields
|
||||
|
||||
+13
-19
@@ -1,7 +1,7 @@
|
||||
use crate::common::SolanaRpcClient;
|
||||
use anyhow::anyhow;
|
||||
use fnv::FnvHasher;
|
||||
use solana_sdk::{instruction::Instruction, program_pack::Pack, pubkey::Pubkey};
|
||||
use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
|
||||
use solana_system_interface::instruction::create_account_with_seed;
|
||||
use std::hash::Hasher;
|
||||
use std::sync::Arc;
|
||||
@@ -34,15 +34,9 @@ pub fn start_rent_updater(client: Arc<SolanaRpcClient>) {
|
||||
|
||||
async fn fetch_rent_for_token_account(
|
||||
client: &SolanaRpcClient,
|
||||
is_2022_token: bool,
|
||||
_is_2022_token: bool,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
Ok(client
|
||||
.get_minimum_balance_for_rent_exemption(if is_2022_token {
|
||||
spl_token_2022::state::Account::LEN as usize
|
||||
} else {
|
||||
spl_token::state::Account::LEN as usize
|
||||
})
|
||||
.await?)
|
||||
Ok(client.get_minimum_balance_for_rent_exemption(165).await?)
|
||||
}
|
||||
|
||||
pub fn create_associated_token_account_use_seed(
|
||||
@@ -51,7 +45,7 @@ pub fn create_associated_token_account_use_seed(
|
||||
mint: &Pubkey,
|
||||
token_program: &Pubkey,
|
||||
) -> Result<Vec<Instruction>, anyhow::Error> {
|
||||
let is_2022_token = token_program == &spl_token_2022::id();
|
||||
let is_2022_token = token_program == &crate::constants::TOKEN_PROGRAM_2022;
|
||||
let rent =
|
||||
if is_2022_token { unsafe { SPL_TOKEN_2022_RENT } } else { unsafe { SPL_TOKEN_RENT } };
|
||||
if rent.is_none() {
|
||||
@@ -72,18 +66,14 @@ pub fn create_associated_token_account_use_seed(
|
||||
let seed = unsafe { std::str::from_utf8_unchecked(&buf) };
|
||||
let ata_like = Pubkey::create_with_seed(payer, seed, token_program)?;
|
||||
|
||||
let len = if is_2022_token {
|
||||
spl_token_2022::state::Account::LEN as u64
|
||||
} else {
|
||||
spl_token::state::Account::LEN as u64
|
||||
};
|
||||
let len = 165;
|
||||
let create_acc =
|
||||
create_account_with_seed(payer, &ata_like, owner, seed, rent.unwrap(), len, token_program);
|
||||
|
||||
let init_acc = if is_2022_token {
|
||||
spl_token_2022::instruction::initialize_account3(&token_program, &ata_like, mint, owner)?
|
||||
crate::common::spl_token_2022::initialize_account3(&token_program, &ata_like, mint, owner)?
|
||||
} else {
|
||||
spl_token::instruction::initialize_account3(&token_program, &ata_like, mint, owner)?
|
||||
crate::common::spl_token::initialize_account3(&token_program, &ata_like, mint, owner)?
|
||||
};
|
||||
|
||||
Ok(vec![create_acc, init_acc])
|
||||
@@ -106,9 +96,13 @@ pub fn get_associated_token_address_with_program_id_use_seed(
|
||||
_ => b'a' + (nibble - 10),
|
||||
};
|
||||
}
|
||||
let is_2022_token = token_program_id == &spl_token_2022::id();
|
||||
let is_2022_token = token_program_id == &crate::constants::TOKEN_PROGRAM_2022;
|
||||
let seed = unsafe { std::str::from_utf8_unchecked(&buf) };
|
||||
let token_program = if is_2022_token { &spl_token_2022::id() } else { &spl_token::id() };
|
||||
let token_program = if is_2022_token {
|
||||
&crate::constants::TOKEN_PROGRAM_2022
|
||||
} else {
|
||||
&crate::constants::TOKEN_PROGRAM
|
||||
};
|
||||
let ata_like = Pubkey::create_with_seed(wallet_address, seed, token_program)?;
|
||||
Ok(ata_like)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use solana_sdk::{
|
||||
message::{AccountMeta, Instruction},
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
use crate::constants::{ASSOCIATED_TOKEN_PROGRAM_ID, SYSTEM_PROGRAM};
|
||||
|
||||
pub fn get_associated_token_address_with_program_id(
|
||||
wallet_address: &Pubkey,
|
||||
token_mint_address: &Pubkey,
|
||||
token_program_id: &Pubkey,
|
||||
) -> Pubkey {
|
||||
Pubkey::find_program_address(
|
||||
&[&wallet_address.to_bytes(), &token_program_id.to_bytes(), &token_mint_address.to_bytes()],
|
||||
&crate::constants::ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
)
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn get_associated_token_address(
|
||||
wallet_address: &Pubkey,
|
||||
token_mint_address: &Pubkey,
|
||||
) -> Pubkey {
|
||||
get_associated_token_address_with_program_id(
|
||||
wallet_address,
|
||||
token_mint_address,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_associated_token_account_idempotent(
|
||||
funding_address: &Pubkey,
|
||||
wallet_address: &Pubkey,
|
||||
token_mint_address: &Pubkey,
|
||||
token_program_id: &Pubkey,
|
||||
) -> Instruction {
|
||||
let instruction = 1;
|
||||
let associated_account_address = get_associated_token_address_with_program_id(
|
||||
wallet_address,
|
||||
token_mint_address,
|
||||
token_program_id,
|
||||
);
|
||||
Instruction {
|
||||
program_id: ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
accounts: vec![
|
||||
AccountMeta::new(*funding_address, true),
|
||||
AccountMeta::new(associated_account_address, false),
|
||||
AccountMeta::new_readonly(*wallet_address, false),
|
||||
AccountMeta::new_readonly(*token_mint_address, false),
|
||||
AccountMeta::new_readonly(SYSTEM_PROGRAM, false),
|
||||
AccountMeta::new_readonly(*token_program_id, false),
|
||||
],
|
||||
data: vec![instruction],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use solana_program::pubkey;
|
||||
use solana_sdk::{
|
||||
message::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
pub const ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
||||
|
||||
pub fn close_account(
|
||||
token_program_id: &Pubkey,
|
||||
account_pubkey: &Pubkey,
|
||||
destination_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
signer_pubkeys: &[&Pubkey],
|
||||
) -> Result<Instruction, solana_sdk::program_error::ProgramError> {
|
||||
// CloseAccount
|
||||
let mut data = Vec::with_capacity(1);
|
||||
data.push(9);
|
||||
let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len());
|
||||
accounts.push(solana_sdk::message::AccountMeta::new(*account_pubkey, false));
|
||||
accounts.push(solana_sdk::message::AccountMeta::new(*destination_pubkey, false));
|
||||
accounts.push(solana_sdk::message::AccountMeta::new_readonly(
|
||||
*owner_pubkey,
|
||||
signer_pubkeys.is_empty(),
|
||||
));
|
||||
for signer_pubkey in signer_pubkeys.iter() {
|
||||
accounts.push(solana_sdk::message::AccountMeta::new_readonly(**signer_pubkey, true));
|
||||
}
|
||||
Ok(Instruction { program_id: *token_program_id, accounts, data })
|
||||
}
|
||||
|
||||
pub fn initialize_account3(
|
||||
token_program_id: &Pubkey,
|
||||
account_pubkey: &Pubkey,
|
||||
mint_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
// InitializeAccount3
|
||||
let mut data = Vec::with_capacity(33);
|
||||
data.push(18);
|
||||
data.extend_from_slice(owner_pubkey.as_ref());
|
||||
let accounts = vec![
|
||||
AccountMeta::new(*account_pubkey, false),
|
||||
AccountMeta::new_readonly(*mint_pubkey, false),
|
||||
];
|
||||
Ok(Instruction { program_id: *token_program_id, accounts, data })
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
use solana_program::pubkey;
|
||||
use solana_sdk::{
|
||||
message::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
pub const ID: Pubkey = pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
|
||||
|
||||
pub fn initialize_account3(
|
||||
token_program_id: &Pubkey,
|
||||
account_pubkey: &Pubkey,
|
||||
mint_pubkey: &Pubkey,
|
||||
owner_pubkey: &Pubkey,
|
||||
) -> Result<Instruction, ProgramError> {
|
||||
// InitializeAccount3
|
||||
let mut data = Vec::with_capacity(33);
|
||||
data.push(18);
|
||||
data.extend_from_slice(owner_pubkey.as_ref());
|
||||
let accounts = vec![
|
||||
AccountMeta::new(*account_pubkey, false),
|
||||
AccountMeta::new_readonly(*mint_pubkey, false),
|
||||
];
|
||||
Ok(Instruction { program_id: *token_program_id, accounts, data })
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use crate::swqos::SwqosConfig;
|
||||
use solana_sdk::commitment_config::CommitmentConfig;
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TradeConfig {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use solana_sdk::{pubkey, pubkey::Pubkey};
|
||||
|
||||
pub const SYSTEM_PROGRAM: Pubkey = solana_sdk::system_program::ID;
|
||||
pub const SYSTEM_PROGRAM: Pubkey = pubkey!("11111111111111111111111111111111");
|
||||
pub const SYSTEM_PROGRAM_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta {
|
||||
pubkey: SYSTEM_PROGRAM,
|
||||
@@ -8,7 +8,7 @@ pub const SYSTEM_PROGRAM_META: solana_sdk::instruction::AccountMeta =
|
||||
is_writable: false,
|
||||
};
|
||||
|
||||
pub const TOKEN_PROGRAM: Pubkey = spl_token::ID;
|
||||
pub const TOKEN_PROGRAM: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
||||
pub const TOKEN_PROGRAM_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta {
|
||||
pubkey: TOKEN_PROGRAM,
|
||||
@@ -16,7 +16,7 @@ pub const TOKEN_PROGRAM_META: solana_sdk::instruction::AccountMeta =
|
||||
is_writable: false,
|
||||
};
|
||||
|
||||
pub const TOKEN_PROGRAM_2022: Pubkey = spl_token_2022::ID;
|
||||
pub const TOKEN_PROGRAM_2022: Pubkey = pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
|
||||
pub const TOKEN_PROGRAM_2022_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta {
|
||||
pubkey: TOKEN_PROGRAM_2022,
|
||||
@@ -45,3 +45,6 @@ pub const USD1_TOKEN_ACCOUNT_META: solana_sdk::instruction::AccountMeta =
|
||||
pub const RENT: Pubkey = solana_sdk::sysvar::rent::id();
|
||||
pub const RENT_META: solana_sdk::instruction::AccountMeta =
|
||||
solana_sdk::instruction::AccountMeta { pubkey: RENT, is_signer: false, is_writable: false };
|
||||
|
||||
pub const ASSOCIATED_TOKEN_PROGRAM_ID: Pubkey =
|
||||
pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::{
|
||||
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::{
|
||||
@@ -18,7 +17,6 @@ use crate::{
|
||||
use anyhow::{anyhow, Result};
|
||||
use solana_sdk::instruction::AccountMeta;
|
||||
use solana_sdk::{instruction::Instruction, pubkey::Pubkey, signer::Signer};
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
/// Instruction builder for PumpFun protocol
|
||||
pub struct PumpFunInstructionBuilder;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::common::{global::GlobalAccount, SolanaRpcClient};
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use anyhow::anyhow;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@@ -216,7 +216,13 @@ pub fn get_user_volume_accumulator_pda(user: &Pubkey) -> Option<Pubkey> {
|
||||
pub async fn fetch_bonding_curve_account(
|
||||
rpc: &SolanaRpcClient,
|
||||
mint: &Pubkey,
|
||||
) -> Result<(Arc<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>, Pubkey), anyhow::Error>{
|
||||
) -> Result<
|
||||
(
|
||||
Arc<solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>,
|
||||
Pubkey,
|
||||
),
|
||||
anyhow::Error,
|
||||
> {
|
||||
let bonding_curve_pda: Pubkey =
|
||||
get_bonding_curve_pda(mint).ok_or(anyhow!("Bonding curve not found"))?;
|
||||
|
||||
@@ -225,8 +231,10 @@ pub async fn fetch_bonding_curve_account(
|
||||
return Err(anyhow!("Bonding curve not found"));
|
||||
}
|
||||
|
||||
let bonding_curve = solana_sdk::borsh1::try_from_slice_unchecked::<crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve>(&account.data[8..])
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize bonding curve account: {}", e))?;
|
||||
let bonding_curve = solana_sdk::borsh1::try_from_slice_unchecked::<
|
||||
solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve,
|
||||
>(&account.data[8..])
|
||||
.map_err(|e| anyhow::anyhow!("Failed to deserialize bonding curve account: {}", e))?;
|
||||
|
||||
Ok((Arc::new(bonding_curve), bonding_curve_pda))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use crate::{common::SolanaRpcClient, constants::TOKEN_PROGRAM};
|
||||
use crate::{
|
||||
common::{
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id, SolanaRpcClient,
|
||||
},
|
||||
constants::TOKEN_PROGRAM,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use solana_account_decoder::UiAccountEncoding;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
@@ -138,12 +143,11 @@ pub(crate) fn coin_creator_vault_authority(coin_creator: Pubkey) -> Pubkey {
|
||||
|
||||
pub(crate) fn coin_creator_vault_ata(coin_creator: Pubkey, quote_mint: Pubkey) -> Pubkey {
|
||||
let creator_vault_authority = coin_creator_vault_authority(coin_creator);
|
||||
let associated_token_creator_vault_authority =
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&creator_vault_authority,
|
||||
"e_mint,
|
||||
&TOKEN_PROGRAM,
|
||||
);
|
||||
let associated_token_creator_vault_authority = get_associated_token_address_with_program_id(
|
||||
&creator_vault_authority,
|
||||
"e_mint,
|
||||
&TOKEN_PROGRAM,
|
||||
);
|
||||
associated_token_creator_vault_authority
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
pub mod common;
|
||||
pub mod constants;
|
||||
pub mod instruction;
|
||||
pub mod protos;
|
||||
pub mod swqos;
|
||||
pub mod trading;
|
||||
pub mod utils;
|
||||
@@ -30,7 +29,6 @@ use rustls::crypto::{ring::default_provider, CryptoProvider};
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signer::Signer;
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signature::Signature};
|
||||
pub use solana_streamer_sdk;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Type of the token to buy
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GenerateAuthChallengeRequest {
|
||||
/// / Role the client is attempting to generate tokens for.
|
||||
#[prost(enumeration = "Role", tag = "1")]
|
||||
pub role: i32,
|
||||
/// / Client's 32 byte pubkey.
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub pubkey: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GenerateAuthChallengeResponse {
|
||||
#[prost(string, tag = "1")]
|
||||
pub challenge: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GenerateAuthTokensRequest {
|
||||
/// / The pre-signed challenge.
|
||||
#[prost(string, tag = "1")]
|
||||
pub challenge: ::prost::alloc::string::String,
|
||||
/// / The signing keypair's corresponding 32 byte pubkey.
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub client_pubkey: ::prost::alloc::vec::Vec<u8>,
|
||||
/// / The 64 byte signature of the challenge signed by the client's private key. The private key must correspond to
|
||||
/// the pubkey passed in the \[GenerateAuthChallenge\] method. The client is expected to sign the challenge token
|
||||
/// prepended with their pubkey. For example sign(pubkey, challenge).
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub signed_challenge: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Token {
|
||||
/// / The token.
|
||||
#[prost(string, tag = "1")]
|
||||
pub value: ::prost::alloc::string::String,
|
||||
/// / When the token will expire.
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub expires_at_utc: ::core::option::Option<::prost_types::Timestamp>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GenerateAuthTokensResponse {
|
||||
/// / The token granting access to resources.
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub access_token: ::core::option::Option<Token>,
|
||||
/// / The token used to refresh the access_token. This has a longer TTL than the access_token.
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub refresh_token: ::core::option::Option<Token>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RefreshAccessTokenRequest {
|
||||
/// / Non-expired refresh token obtained from the \[GenerateAuthTokens\] method.
|
||||
#[prost(string, tag = "1")]
|
||||
pub refresh_token: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RefreshAccessTokenResponse {
|
||||
/// / Fresh access_token.
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub access_token: ::core::option::Option<Token>,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum Role {
|
||||
Relayer = 0,
|
||||
Searcher = 1,
|
||||
Validator = 2,
|
||||
ShredstreamSubscriber = 3,
|
||||
}
|
||||
impl Role {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Relayer => "RELAYER",
|
||||
Self::Searcher => "SEARCHER",
|
||||
Self::Validator => "VALIDATOR",
|
||||
Self::ShredstreamSubscriber => "SHREDSTREAM_SUBSCRIBER",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"RELAYER" => Some(Self::Relayer),
|
||||
"SEARCHER" => Some(Self::Searcher),
|
||||
"VALIDATOR" => Some(Self::Validator),
|
||||
"SHREDSTREAM_SUBSCRIBER" => Some(Self::ShredstreamSubscriber),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod auth_service_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
/// / This service is responsible for issuing auth tokens to clients for API access.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthServiceClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl AuthServiceClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> AuthServiceClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> AuthServiceClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
AuthServiceClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// / Returns a challenge, client is expected to sign this challenge with an appropriate keypair in order to obtain access tokens.
|
||||
pub async fn generate_auth_challenge(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GenerateAuthChallengeRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GenerateAuthChallengeResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/auth.AuthService/GenerateAuthChallenge",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("auth.AuthService", "GenerateAuthChallenge"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// / Provides the client with the initial pair of auth tokens for API access.
|
||||
pub async fn generate_auth_tokens(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GenerateAuthTokensRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GenerateAuthTokensResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/auth.AuthService/GenerateAuthTokens",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("auth.AuthService", "GenerateAuthTokens"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// / Call this method with a non-expired refresh token to obtain a new access token.
|
||||
pub async fn refresh_access_token(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::RefreshAccessTokenRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::RefreshAccessTokenResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/auth.AuthService/RefreshAccessToken",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("auth.AuthService", "RefreshAccessToken"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
/// Condensed block helpful for getting data around efficiently internal to our system.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct CondensedBlock {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub header: ::core::option::Option<super::shared::Header>,
|
||||
#[prost(string, tag = "2")]
|
||||
pub previous_blockhash: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "3")]
|
||||
pub blockhash: ::prost::alloc::string::String,
|
||||
#[prost(uint64, tag = "4")]
|
||||
pub parent_slot: u64,
|
||||
#[prost(bytes = "vec", repeated, tag = "5")]
|
||||
pub versioned_transactions: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(uint64, tag = "6")]
|
||||
pub slot: u64,
|
||||
#[prost(string, tag = "7")]
|
||||
pub commitment: ::prost::alloc::string::String,
|
||||
}
|
||||
@@ -1,462 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribePacketsRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribePacketsResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub header: ::core::option::Option<super::shared::Header>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub batch: ::core::option::Option<super::packet::PacketBatch>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribeBundlesRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribeBundlesResponse {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub bundles: ::prost::alloc::vec::Vec<super::bundle::BundleUuid>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct BlockBuilderFeeInfoRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BlockBuilderFeeInfoResponse {
|
||||
#[prost(string, tag = "1")]
|
||||
pub pubkey: ::prost::alloc::string::String,
|
||||
/// commission (0-100)
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub commission: u64,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct AccountsOfInterest {
|
||||
/// use * for all accounts
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub accounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct AccountsOfInterestRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct AccountsOfInterestUpdate {
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub accounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct ProgramsOfInterestRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProgramsOfInterestUpdate {
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub programs: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
/// A series of packets with an expiration attached to them.
|
||||
/// The header contains a timestamp for when this packet was generated.
|
||||
/// The expiry is how long the packet batches have before they expire and are forwarded to the validator.
|
||||
/// This provides a more censorship resistant method to MEV than block engines receiving packets directly.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ExpiringPacketBatch {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub header: ::core::option::Option<super::shared::Header>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub batch: ::core::option::Option<super::packet::PacketBatch>,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub expiry_ms: u32,
|
||||
}
|
||||
/// Packets and heartbeats are sent over the same stream.
|
||||
/// ExpiringPacketBatches have an expiration attached to them so the block engine can track
|
||||
/// how long it has until the relayer forwards the packets to the validator.
|
||||
/// Heartbeats contain a timestamp from the system and is used as a simple and naive time-sync mechanism
|
||||
/// so the block engine has some idea on how far their clocks are apart.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PacketBatchUpdate {
|
||||
#[prost(oneof = "packet_batch_update::Msg", tags = "1, 2")]
|
||||
pub msg: ::core::option::Option<packet_batch_update::Msg>,
|
||||
}
|
||||
/// Nested message and enum types in `PacketBatchUpdate`.
|
||||
pub mod packet_batch_update {
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Msg {
|
||||
#[prost(message, tag = "1")]
|
||||
Batches(super::ExpiringPacketBatch),
|
||||
#[prost(message, tag = "2")]
|
||||
Heartbeat(super::super::shared::Heartbeat),
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct StartExpiringPacketStreamResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub heartbeat: ::core::option::Option<super::shared::Heartbeat>,
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod block_engine_validator_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
/// / Validators can connect to Block Engines to receive packets and bundles.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockEngineValidatorClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl BlockEngineValidatorClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> BlockEngineValidatorClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> BlockEngineValidatorClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
BlockEngineValidatorClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// / Validators can subscribe to the block engine to receive a stream of packets
|
||||
pub async fn subscribe_packets(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SubscribePacketsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::SubscribePacketsResponse>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineValidator/SubscribePackets",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineValidator",
|
||||
"SubscribePackets",
|
||||
),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
/// / Validators can subscribe to the block engine to receive a stream of simulated and profitable bundles
|
||||
pub async fn subscribe_bundles(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SubscribeBundlesRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::SubscribeBundlesResponse>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineValidator/SubscribeBundles",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineValidator",
|
||||
"SubscribeBundles",
|
||||
),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
/// Block builders can optionally collect fees. This returns fee information if a block builder wants to
|
||||
/// collect one.
|
||||
pub async fn get_block_builder_fee_info(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::BlockBuilderFeeInfoRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::BlockBuilderFeeInfoResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineValidator/GetBlockBuilderFeeInfo",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineValidator",
|
||||
"GetBlockBuilderFeeInfo",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod block_engine_relayer_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
/// / Relayers can forward packets to Block Engines.
|
||||
/// / Block Engines provide an AccountsOfInterest field to only send transactions that are of interest.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockEngineRelayerClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl BlockEngineRelayerClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> BlockEngineRelayerClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> BlockEngineRelayerClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
BlockEngineRelayerClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// / The block engine feeds accounts of interest (AOI) updates to the relayer periodically.
|
||||
/// / For all transactions the relayer receives, it forwards transactions to the block engine which write-lock
|
||||
/// / any of the accounts in the AOI.
|
||||
pub async fn subscribe_accounts_of_interest(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::AccountsOfInterestRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::AccountsOfInterestUpdate>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineRelayer/SubscribeAccountsOfInterest",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineRelayer",
|
||||
"SubscribeAccountsOfInterest",
|
||||
),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
pub async fn subscribe_programs_of_interest(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::ProgramsOfInterestRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::ProgramsOfInterestUpdate>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineRelayer/SubscribeProgramsOfInterest",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineRelayer",
|
||||
"SubscribeProgramsOfInterest",
|
||||
),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
/// Validators can subscribe to packets from the relayer and receive a multiplexed signal that contains a mixture
|
||||
/// of packets and heartbeats.
|
||||
/// NOTE: This is a bi-directional stream due to a bug with how Envoy handles half closed client-side streams.
|
||||
/// The issue is being tracked here: https://github.com/envoyproxy/envoy/issues/22748. In the meantime, the
|
||||
/// server will stream heartbeats to clients at some reasonable cadence.
|
||||
pub async fn start_expiring_packet_stream(
|
||||
&mut self,
|
||||
request: impl tonic::IntoStreamingRequest<Message = super::PacketBatchUpdate>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<
|
||||
tonic::codec::Streaming<super::StartExpiringPacketStreamResponse>,
|
||||
>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/block_engine.BlockEngineRelayer/StartExpiringPacketStream",
|
||||
);
|
||||
let mut req = request.into_streaming_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"block_engine.BlockEngineRelayer",
|
||||
"StartExpiringPacketStream",
|
||||
),
|
||||
);
|
||||
self.inner.streaming(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Bundle {
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub header: ::core::option::Option<super::shared::Header>,
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub packets: ::prost::alloc::vec::Vec<super::packet::Packet>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BundleUuid {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub bundle: ::core::option::Option<Bundle>,
|
||||
#[prost(string, tag = "2")]
|
||||
pub uuid: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Indicates the bundle was accepted and forwarded to a validator.
|
||||
/// NOTE: A single bundle may have multiple events emitted if forwarded to many validators.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Accepted {
|
||||
/// Slot at which bundle was forwarded.
|
||||
#[prost(uint64, tag = "1")]
|
||||
pub slot: u64,
|
||||
/// Validator identity bundle was forwarded to.
|
||||
#[prost(string, tag = "2")]
|
||||
pub validator_identity: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Indicates the bundle was dropped and therefore not forwarded to any validator.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Rejected {
|
||||
#[prost(oneof = "rejected::Reason", tags = "1, 2, 3, 4, 5")]
|
||||
pub reason: ::core::option::Option<rejected::Reason>,
|
||||
}
|
||||
/// Nested message and enum types in `Rejected`.
|
||||
pub mod rejected {
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Reason {
|
||||
#[prost(message, tag = "1")]
|
||||
StateAuctionBidRejected(super::StateAuctionBidRejected),
|
||||
#[prost(message, tag = "2")]
|
||||
WinningBatchBidRejected(super::WinningBatchBidRejected),
|
||||
#[prost(message, tag = "3")]
|
||||
SimulationFailure(super::SimulationFailure),
|
||||
#[prost(message, tag = "4")]
|
||||
InternalError(super::InternalError),
|
||||
#[prost(message, tag = "5")]
|
||||
DroppedBundle(super::DroppedBundle),
|
||||
}
|
||||
}
|
||||
/// Indicates the bundle's bid was high enough to win its state auction.
|
||||
/// However, not high enough relative to other state auction winners and therefore excluded from being forwarded.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct WinningBatchBidRejected {
|
||||
/// Auction's unique identifier.
|
||||
#[prost(string, tag = "1")]
|
||||
pub auction_id: ::prost::alloc::string::String,
|
||||
/// Bundle's simulated bid.
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub simulated_bid_lamports: u64,
|
||||
#[prost(string, optional, tag = "3")]
|
||||
pub msg: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// Indicates the bundle's bid was __not__ high enough to be included in its state auction's set of winners.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct StateAuctionBidRejected {
|
||||
/// Auction's unique identifier.
|
||||
#[prost(string, tag = "1")]
|
||||
pub auction_id: ::prost::alloc::string::String,
|
||||
/// Bundle's simulated bid.
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub simulated_bid_lamports: u64,
|
||||
#[prost(string, optional, tag = "3")]
|
||||
pub msg: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// Bundle dropped due to simulation failure.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SimulationFailure {
|
||||
/// Signature of the offending transaction.
|
||||
#[prost(string, tag = "1")]
|
||||
pub tx_signature: ::prost::alloc::string::String,
|
||||
#[prost(string, optional, tag = "2")]
|
||||
pub msg: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
/// Bundle dropped due to an internal error.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct InternalError {
|
||||
#[prost(string, tag = "1")]
|
||||
pub msg: ::prost::alloc::string::String,
|
||||
}
|
||||
/// Bundle dropped (e.g. because no leader upcoming)
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct DroppedBundle {
|
||||
#[prost(string, tag = "1")]
|
||||
pub msg: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Finalized {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Processed {
|
||||
#[prost(string, tag = "1")]
|
||||
pub validator_identity: ::prost::alloc::string::String,
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub slot: u64,
|
||||
/// / Index within the block.
|
||||
#[prost(uint64, tag = "3")]
|
||||
pub bundle_index: u64,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Dropped {
|
||||
#[prost(enumeration = "DroppedReason", tag = "1")]
|
||||
pub reason: i32,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct BundleResult {
|
||||
/// Bundle's Uuid.
|
||||
#[prost(string, tag = "1")]
|
||||
pub bundle_id: ::prost::alloc::string::String,
|
||||
#[prost(oneof = "bundle_result::Result", tags = "2, 3, 4, 5, 6")]
|
||||
pub result: ::core::option::Option<bundle_result::Result>,
|
||||
}
|
||||
/// Nested message and enum types in `BundleResult`.
|
||||
pub mod bundle_result {
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Result {
|
||||
/// Indicated accepted by the block-engine and forwarded to a jito-solana validator.
|
||||
#[prost(message, tag = "2")]
|
||||
Accepted(super::Accepted),
|
||||
/// Rejected by the block-engine.
|
||||
#[prost(message, tag = "3")]
|
||||
Rejected(super::Rejected),
|
||||
/// Reached finalized commitment level.
|
||||
#[prost(message, tag = "4")]
|
||||
Finalized(super::Finalized),
|
||||
/// Reached a processed commitment level.
|
||||
#[prost(message, tag = "5")]
|
||||
Processed(super::Processed),
|
||||
/// Was accepted and forwarded by the block-engine but never landed on-chain.
|
||||
#[prost(message, tag = "6")]
|
||||
Dropped(super::Dropped),
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum DroppedReason {
|
||||
BlockhashExpired = 0,
|
||||
/// One or more transactions in the bundle landed on-chain, invalidating the bundle.
|
||||
PartiallyProcessed = 1,
|
||||
/// This indicates bundle was processed but not finalized. This could occur during forks.
|
||||
NotFinalized = 2,
|
||||
}
|
||||
impl DroppedReason {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BlockhashExpired => "BlockhashExpired",
|
||||
Self::PartiallyProcessed => "PartiallyProcessed",
|
||||
Self::NotFinalized => "NotFinalized",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"BlockhashExpired" => Some(Self::BlockhashExpired),
|
||||
"PartiallyProcessed" => Some(Self::PartiallyProcessed),
|
||||
"NotFinalized" => Some(Self::NotFinalized),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
net::{AddrParseError, IpAddr, Ipv4Addr, SocketAddr},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use bincode::serialize;
|
||||
use solana_perf::packet::{Packet, PacketBatch, PACKET_DATA_SIZE};
|
||||
use solana_sdk::{
|
||||
packet::{Meta, PacketFlags},
|
||||
transaction::VersionedTransaction,
|
||||
};
|
||||
|
||||
use crate::protos::{
|
||||
packet::{
|
||||
Meta as ProtoMeta, Packet as ProtoPacket, PacketBatch as ProtoPacketBatch,
|
||||
PacketFlags as ProtoPacketFlags,
|
||||
},
|
||||
shared::Socket,
|
||||
};
|
||||
|
||||
/// Converts a Solana packet to a protobuf packet
|
||||
/// NOTE: the packet.data() function will filter packets marked for discard
|
||||
pub fn packet_to_proto_packet(p: &solana_perf::packet::PacketRef<'_>) -> Option<ProtoPacket> {
|
||||
Some(ProtoPacket {
|
||||
data: p.data(..)?.to_vec(),
|
||||
meta: Some(ProtoMeta {
|
||||
size: p.meta().size as u64,
|
||||
addr: p.meta().addr.to_string(),
|
||||
port: p.meta().port as u32,
|
||||
flags: Some(ProtoPacketFlags {
|
||||
discard: p.meta().discard(),
|
||||
forwarded: p.meta().forwarded(),
|
||||
repair: p.meta().repair(),
|
||||
simple_vote_tx: p.meta().is_simple_vote_tx(),
|
||||
tracer_packet: p.meta().is_perf_track_packet(),
|
||||
from_staked_node: p.meta().is_from_staked_node(),
|
||||
}),
|
||||
sender_stake: 0,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn packet_batches_to_proto_packets(
|
||||
batches: &[PacketBatch],
|
||||
) -> impl Iterator<Item = ProtoPacket> + '_ {
|
||||
batches
|
||||
.iter()
|
||||
.flat_map(|b| b.iter().filter_map(|p| packet_to_proto_packet(&p)))
|
||||
}
|
||||
|
||||
/// converts from a protobuf packet to packet
|
||||
pub fn proto_packet_to_packet(p: &ProtoPacket) -> Packet {
|
||||
let mut data = [0u8; PACKET_DATA_SIZE];
|
||||
let copy_len = min(data.len(), p.data.len());
|
||||
data[..copy_len].copy_from_slice(&p.data[..copy_len]);
|
||||
let mut packet = Packet::new(data, Meta::default());
|
||||
if let Some(meta) = &p.meta {
|
||||
packet.meta_mut().size = meta.size as usize;
|
||||
packet.meta_mut().addr = meta
|
||||
.addr
|
||||
.parse()
|
||||
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
|
||||
packet.meta_mut().port = meta.port as u16;
|
||||
if let Some(flags) = &meta.flags {
|
||||
if flags.simple_vote_tx {
|
||||
packet.meta_mut().flags.insert(PacketFlags::SIMPLE_VOTE_TX);
|
||||
}
|
||||
if flags.forwarded {
|
||||
packet.meta_mut().flags.insert(PacketFlags::FORWARDED);
|
||||
}
|
||||
if flags.tracer_packet {
|
||||
packet.meta_mut().flags.insert(PacketFlags::PERF_TRACK_PACKET);
|
||||
}
|
||||
if flags.repair {
|
||||
packet.meta_mut().flags.insert(PacketFlags::REPAIR);
|
||||
}
|
||||
if flags.discard {
|
||||
packet.meta_mut().flags.insert(PacketFlags::DISCARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
packet
|
||||
}
|
||||
|
||||
pub fn proto_packet_batch_to_packets(
|
||||
packet_batch: ProtoPacketBatch,
|
||||
) -> impl Iterator<Item = Packet> {
|
||||
packet_batch
|
||||
.packets
|
||||
.into_iter()
|
||||
.map(|proto_packet| proto_packet_to_packet(&proto_packet))
|
||||
}
|
||||
|
||||
/// Converts a protobuf packet to a VersionedTransaction
|
||||
pub fn versioned_tx_from_packet(p: &ProtoPacket) -> Option<VersionedTransaction> {
|
||||
let mut data = [0; PACKET_DATA_SIZE];
|
||||
let copy_len = min(data.len(), p.data.len());
|
||||
data[..copy_len].copy_from_slice(&p.data[..copy_len]);
|
||||
let mut packet = Packet::new(data, Default::default());
|
||||
if let Some(meta) = &p.meta {
|
||||
packet.meta_mut().size = meta.size as usize;
|
||||
}
|
||||
packet.deserialize_slice(..).ok()
|
||||
}
|
||||
|
||||
/// Coverts a VersionedTransaction to packet
|
||||
pub fn packet_from_versioned_tx(tx: VersionedTransaction) -> Packet {
|
||||
let tx_data = serialize(&tx).expect("serializes");
|
||||
let mut data = [0; PACKET_DATA_SIZE];
|
||||
let copy_len = min(tx_data.len(), data.len());
|
||||
data[..copy_len].copy_from_slice(&tx_data[..copy_len]);
|
||||
let mut packet = Packet::new(data, Default::default());
|
||||
packet.meta_mut().size = copy_len;
|
||||
packet
|
||||
}
|
||||
|
||||
/// Converts a VersionedTransaction to a protobuf packet
|
||||
pub fn proto_packet_from_versioned_tx(tx: &VersionedTransaction) -> ProtoPacket {
|
||||
let data = serialize(tx).expect("serializes");
|
||||
let size = data.len() as u64;
|
||||
ProtoPacket {
|
||||
data,
|
||||
meta: Some(ProtoMeta {
|
||||
size,
|
||||
addr: "".to_string(),
|
||||
port: 0,
|
||||
flags: None,
|
||||
sender_stake: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a GRPC Socket to stdlib SocketAddr
|
||||
impl TryFrom<&Socket> for SocketAddr {
|
||||
type Error = AddrParseError;
|
||||
|
||||
fn try_from(value: &Socket) -> Result<Self, Self::Error> {
|
||||
IpAddr::from_str(&value.ip).map(|ip| SocketAddr::new(ip, value.port as u16))
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use solana_perf::test_tx::test_tx;
|
||||
// use solana_sdk::transaction::VersionedTransaction;
|
||||
|
||||
// use crate::convert::{proto_packet_from_versioned_tx, versioned_tx_from_packet};
|
||||
|
||||
// #[test]
|
||||
// fn test_proto_to_packet() {
|
||||
// let tx_before = VersionedTransaction::from(test_tx());
|
||||
// let tx_after = versioned_tx_from_packet(&proto_packet_from_versioned_tx(&tx_before))
|
||||
// .expect("tx_after");
|
||||
|
||||
// assert_eq!(tx_before, tx_after);
|
||||
// }
|
||||
// }
|
||||
@@ -1,13 +0,0 @@
|
||||
pub mod auth;
|
||||
pub mod block;
|
||||
pub mod block_engine;
|
||||
pub mod bundle;
|
||||
pub mod packet;
|
||||
pub mod relayer;
|
||||
pub mod searcher;
|
||||
pub mod shared;
|
||||
pub mod trace_shred;
|
||||
pub mod convert;
|
||||
pub mod nextblock_grpc;
|
||||
pub mod searcher_client;
|
||||
pub mod token_authenticator;
|
||||
@@ -1,462 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitRequest {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub transaction: ::core::option::Option<TransactionMessage>,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub skip_pre_flight: bool,
|
||||
#[prost(bool, optional, tag = "3")]
|
||||
pub front_running_protection: ::core::option::Option<bool>,
|
||||
#[prost(bool, optional, tag = "8")]
|
||||
pub experimental_front_running_protection: ::core::option::Option<bool>,
|
||||
#[prost(bool, optional, tag = "9")]
|
||||
pub snipe_transaction: ::core::option::Option<bool>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitRequestEntry {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub transaction: ::core::option::Option<TransactionMessage>,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub skip_pre_flight: bool,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitBatchRequest {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub entries: ::prost::alloc::vec::Vec<PostSubmitRequestEntry>,
|
||||
#[prost(enumeration = "SubmitStrategy", tag = "2")]
|
||||
pub submit_strategy: i32,
|
||||
#[prost(bool, optional, tag = "3")]
|
||||
pub use_bundle: ::core::option::Option<bool>,
|
||||
#[prost(bool, optional, tag = "4")]
|
||||
pub front_running_protection: ::core::option::Option<bool>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitBatchResponseEntry {
|
||||
#[prost(string, tag = "1")]
|
||||
pub signature: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub error: ::prost::alloc::string::String,
|
||||
#[prost(bool, tag = "3")]
|
||||
pub submitted: bool,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitBatchResponse {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub transactions: ::prost::alloc::vec::Vec<PostSubmitBatchResponseEntry>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PostSubmitResponse {
|
||||
#[prost(string, tag = "1")]
|
||||
pub signature: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TransactionMessage {
|
||||
#[prost(string, tag = "1")]
|
||||
pub content: ::prost::alloc::string::String,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub is_cleanup: bool,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TransactionMessageV2 {
|
||||
#[prost(string, tag = "1")]
|
||||
pub content: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum SubmitStrategy {
|
||||
PUknown = 0,
|
||||
PSubmitAll = 1,
|
||||
PAbortOnFirstError = 2,
|
||||
PWaitForConfirmation = 3,
|
||||
}
|
||||
impl SubmitStrategy {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
///
|
||||
/// The values are not transformed in any way and thus are considered stable
|
||||
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::PUknown => "P_UKNOWN",
|
||||
Self::PSubmitAll => "P_SUBMIT_ALL",
|
||||
Self::PAbortOnFirstError => "P_ABORT_ON_FIRST_ERROR",
|
||||
Self::PWaitForConfirmation => "P_WAIT_FOR_CONFIRMATION",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"P_UKNOWN" => Some(Self::PUknown),
|
||||
"P_SUBMIT_ALL" => Some(Self::PSubmitAll),
|
||||
"P_ABORT_ON_FIRST_ERROR" => Some(Self::PAbortOnFirstError),
|
||||
"P_WAIT_FOR_CONFIRMATION" => Some(Self::PWaitForConfirmation),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod api_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ApiClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl ApiClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: std::convert::TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> ApiClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> ApiClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
ApiClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
pub async fn post_submit_v2(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::PostSubmitRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::PostSubmitResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static("/api.Api/PostSubmitV2");
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut().insert(GrpcMethod::new("api.Api", "PostSubmitV2"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
pub async fn post_submit_batch_v2(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::PostSubmitBatchRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::PostSubmitBatchResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/api.Api/PostSubmitBatchV2",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut().insert(GrpcMethod::new("api.Api", "PostSubmitBatchV2"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated server implementations.
|
||||
pub mod api_server {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
/// Generated trait containing gRPC methods that should be implemented for use with ApiServer.
|
||||
#[async_trait]
|
||||
pub trait Api: std::marker::Send + std::marker::Sync + 'static {
|
||||
async fn post_submit_v2(
|
||||
&self,
|
||||
request: tonic::Request<super::PostSubmitRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::PostSubmitResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
async fn post_submit_batch_v2(
|
||||
&self,
|
||||
request: tonic::Request<super::PostSubmitBatchRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::PostSubmitBatchResponse>,
|
||||
tonic::Status,
|
||||
>;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct ApiServer<T> {
|
||||
inner: Arc<T>,
|
||||
accept_compression_encodings: EnabledCompressionEncodings,
|
||||
send_compression_encodings: EnabledCompressionEncodings,
|
||||
max_decoding_message_size: Option<usize>,
|
||||
max_encoding_message_size: Option<usize>,
|
||||
}
|
||||
impl<T> ApiServer<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self::from_arc(Arc::new(inner))
|
||||
}
|
||||
pub fn from_arc(inner: Arc<T>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
accept_compression_encodings: Default::default(),
|
||||
send_compression_encodings: Default::default(),
|
||||
max_decoding_message_size: None,
|
||||
max_encoding_message_size: None,
|
||||
}
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> InterceptedService<Self, F>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
{
|
||||
InterceptedService::new(Self::new(inner), interceptor)
|
||||
}
|
||||
/// Enable decompressing requests with the given encoding.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.accept_compression_encodings.enable(encoding);
|
||||
self
|
||||
}
|
||||
/// Compress responses with the given encoding, if the client supports it.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.send_compression_encodings.enable(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.max_decoding_message_size = Some(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.max_encoding_message_size = Some(limit);
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<T, B> tonic::codegen::Service<http::Request<B>> for ApiServer<T>
|
||||
where
|
||||
T: Api,
|
||||
B: Body + std::marker::Send + 'static,
|
||||
B::Error: Into<StdError> + std::marker::Send + 'static,
|
||||
{
|
||||
type Response = http::Response<tonic::body::BoxBody>;
|
||||
type Error = std::convert::Infallible;
|
||||
type Future = BoxFuture<Self::Response, Self::Error>;
|
||||
fn poll_ready(
|
||||
&mut self,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<std::result::Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
fn call(&mut self, req: http::Request<B>) -> Self::Future {
|
||||
match req.uri().path() {
|
||||
"/api.Api/PostSubmitV2" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct PostSubmitV2Svc<T: Api>(pub Arc<T>);
|
||||
impl<T: Api> tonic::server::UnaryService<super::PostSubmitRequest>
|
||||
for PostSubmitV2Svc<T> {
|
||||
type Response = super::PostSubmitResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<super::PostSubmitRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as Api>::post_submit_v2(&inner, request).await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = PostSubmitV2Svc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
"/api.Api/PostSubmitBatchV2" => {
|
||||
#[allow(non_camel_case_types)]
|
||||
struct PostSubmitBatchV2Svc<T: Api>(pub Arc<T>);
|
||||
impl<
|
||||
T: Api,
|
||||
> tonic::server::UnaryService<super::PostSubmitBatchRequest>
|
||||
for PostSubmitBatchV2Svc<T> {
|
||||
type Response = super::PostSubmitBatchResponse;
|
||||
type Future = BoxFuture<
|
||||
tonic::Response<Self::Response>,
|
||||
tonic::Status,
|
||||
>;
|
||||
fn call(
|
||||
&mut self,
|
||||
request: tonic::Request<super::PostSubmitBatchRequest>,
|
||||
) -> Self::Future {
|
||||
let inner = Arc::clone(&self.0);
|
||||
let fut = async move {
|
||||
<T as Api>::post_submit_batch_v2(&inner, request).await
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
}
|
||||
let accept_compression_encodings = self.accept_compression_encodings;
|
||||
let send_compression_encodings = self.send_compression_encodings;
|
||||
let max_decoding_message_size = self.max_decoding_message_size;
|
||||
let max_encoding_message_size = self.max_encoding_message_size;
|
||||
let inner = self.inner.clone();
|
||||
let fut = async move {
|
||||
let method = PostSubmitBatchV2Svc(inner);
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let mut grpc = tonic::server::Grpc::new(codec)
|
||||
.apply_compression_config(
|
||||
accept_compression_encodings,
|
||||
send_compression_encodings,
|
||||
)
|
||||
.apply_max_message_size_config(
|
||||
max_decoding_message_size,
|
||||
max_encoding_message_size,
|
||||
);
|
||||
let res = grpc.unary(method, req).await;
|
||||
Ok(res)
|
||||
};
|
||||
Box::pin(fut)
|
||||
}
|
||||
_ => {
|
||||
Box::pin(async move {
|
||||
let mut response = http::Response::new(empty_body());
|
||||
let headers = response.headers_mut();
|
||||
headers
|
||||
.insert(
|
||||
tonic::Status::GRPC_STATUS,
|
||||
(tonic::Code::Unimplemented as i32).into(),
|
||||
);
|
||||
headers
|
||||
.insert(
|
||||
http::header::CONTENT_TYPE,
|
||||
tonic::metadata::GRPC_CONTENT_TYPE,
|
||||
);
|
||||
Ok(response)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Clone for ApiServer<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let inner = self.inner.clone();
|
||||
Self {
|
||||
inner,
|
||||
accept_compression_encodings: self.accept_compression_encodings,
|
||||
send_compression_encodings: self.send_compression_encodings,
|
||||
max_decoding_message_size: self.max_decoding_message_size,
|
||||
max_encoding_message_size: self.max_encoding_message_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generated gRPC service name
|
||||
pub const SERVICE_NAME: &str = "api.Api";
|
||||
impl<T> tonic::server::NamedService for ApiServer<T> {
|
||||
const NAME: &'static str = SERVICE_NAME;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct PacketBatch {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub packets: ::prost::alloc::vec::Vec<Packet>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Packet {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub data: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub meta: ::core::option::Option<Meta>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Meta {
|
||||
#[prost(uint64, tag = "1")]
|
||||
pub size: u64,
|
||||
#[prost(string, tag = "2")]
|
||||
pub addr: ::prost::alloc::string::String,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub port: u32,
|
||||
#[prost(message, optional, tag = "4")]
|
||||
pub flags: ::core::option::Option<PacketFlags>,
|
||||
#[prost(uint64, tag = "5")]
|
||||
pub sender_stake: u64,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct PacketFlags {
|
||||
#[prost(bool, tag = "1")]
|
||||
pub discard: bool,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub forwarded: bool,
|
||||
#[prost(bool, tag = "3")]
|
||||
pub repair: bool,
|
||||
#[prost(bool, tag = "4")]
|
||||
pub simple_vote_tx: bool,
|
||||
#[prost(bool, tag = "5")]
|
||||
pub tracer_packet: bool,
|
||||
#[prost(bool, tag = "6")]
|
||||
pub from_staked_node: bool,
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct GetTpuConfigsRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetTpuConfigsResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub tpu: ::core::option::Option<super::shared::Socket>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub tpu_forward: ::core::option::Option<super::shared::Socket>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribePacketsRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribePacketsResponse {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub header: ::core::option::Option<super::shared::Header>,
|
||||
#[prost(oneof = "subscribe_packets_response::Msg", tags = "2, 3")]
|
||||
pub msg: ::core::option::Option<subscribe_packets_response::Msg>,
|
||||
}
|
||||
/// Nested message and enum types in `SubscribePacketsResponse`.
|
||||
pub mod subscribe_packets_response {
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Msg {
|
||||
#[prost(message, tag = "2")]
|
||||
Heartbeat(super::super::shared::Heartbeat),
|
||||
#[prost(message, tag = "3")]
|
||||
Batch(super::super::packet::PacketBatch),
|
||||
}
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod relayer_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
/// / Relayers offer a TPU and TPU forward proxy for Solana validators.
|
||||
/// / Validators can connect and fetch the TPU configuration for the relayer and start to advertise the
|
||||
/// / relayer's information in gossip.
|
||||
/// / They can also subscribe to packets which arrived on the TPU ports at the relayer
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RelayerClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl RelayerClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> RelayerClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> RelayerClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
RelayerClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// The relayer has TPU and TPU forward sockets that validators can leverage.
|
||||
/// A validator can fetch this config and change its TPU and TPU forward port in gossip.
|
||||
pub async fn get_tpu_configs(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GetTpuConfigsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetTpuConfigsResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/relayer.Relayer/GetTpuConfigs",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("relayer.Relayer", "GetTpuConfigs"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Validators can subscribe to packets from the relayer and receive a multiplexed signal that contains a mixture
|
||||
/// of packets and heartbeats
|
||||
pub async fn subscribe_packets(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SubscribePacketsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::SubscribePacketsResponse>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/relayer.Relayer/SubscribePackets",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("relayer.Relayer", "SubscribePackets"));
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,363 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SlotList {
|
||||
#[prost(uint64, repeated, tag = "1")]
|
||||
pub slots: ::prost::alloc::vec::Vec<u64>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ConnectedLeadersResponse {
|
||||
/// Mapping of validator pubkey to leader slots for the current epoch.
|
||||
#[prost(map = "string, message", tag = "1")]
|
||||
pub connected_validators: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
SlotList,
|
||||
>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SendBundleRequest {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub bundle: ::core::option::Option<super::bundle::Bundle>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SendBundleResponse {
|
||||
/// server uuid for the bundle
|
||||
#[prost(string, tag = "1")]
|
||||
pub uuid: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct NextScheduledLeaderRequest {
|
||||
/// Defaults to the currently connected region if no region provided.
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub regions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct NextScheduledLeaderResponse {
|
||||
/// the current slot the backend is on
|
||||
#[prost(uint64, tag = "1")]
|
||||
pub current_slot: u64,
|
||||
/// the slot of the next leader
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub next_leader_slot: u64,
|
||||
/// the identity pubkey (base58) of the next leader
|
||||
#[prost(string, tag = "3")]
|
||||
pub next_leader_identity: ::prost::alloc::string::String,
|
||||
/// the block engine region of the next leader
|
||||
#[prost(string, tag = "4")]
|
||||
pub next_leader_region: ::prost::alloc::string::String,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct ConnectedLeadersRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ConnectedLeadersRegionedRequest {
|
||||
/// Defaults to the currently connected region if no region provided.
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub regions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ConnectedLeadersRegionedResponse {
|
||||
#[prost(map = "string, message", tag = "1")]
|
||||
pub connected_validators: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
ConnectedLeadersResponse,
|
||||
>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct GetTipAccountsRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetTipAccountsResponse {
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub accounts: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct SubscribeBundleResultsRequest {}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct GetRegionsRequest {}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GetRegionsResponse {
|
||||
/// The region the client is currently connected to
|
||||
#[prost(string, tag = "1")]
|
||||
pub current_region: ::prost::alloc::string::String,
|
||||
/// Regions that are online and ready for connections
|
||||
/// All regions: <https://jito-labs.gitbook.io/mev/systems/connecting/mainnet>
|
||||
#[prost(string, repeated, tag = "2")]
|
||||
pub available_regions: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
/// Generated client implementations.
|
||||
pub mod searcher_service_client {
|
||||
#![allow(
|
||||
unused_variables,
|
||||
dead_code,
|
||||
missing_docs,
|
||||
clippy::wildcard_imports,
|
||||
clippy::let_unit_value,
|
||||
)]
|
||||
use tonic::codegen::*;
|
||||
use tonic::codegen::http::Uri;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SearcherServiceClient<T> {
|
||||
inner: tonic::client::Grpc<T>,
|
||||
}
|
||||
impl SearcherServiceClient<tonic::transport::Channel> {
|
||||
/// Attempt to create a new client by connecting to a given endpoint.
|
||||
pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error>
|
||||
where
|
||||
D: TryInto<tonic::transport::Endpoint>,
|
||||
D::Error: Into<StdError>,
|
||||
{
|
||||
let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
|
||||
Ok(Self::new(conn))
|
||||
}
|
||||
}
|
||||
impl<T> SearcherServiceClient<T>
|
||||
where
|
||||
T: tonic::client::GrpcService<tonic::body::BoxBody>,
|
||||
T::Error: Into<StdError>,
|
||||
T::ResponseBody: Body<Data = Bytes> + std::marker::Send + 'static,
|
||||
<T::ResponseBody as Body>::Error: Into<StdError> + std::marker::Send,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
let inner = tonic::client::Grpc::new(inner);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_origin(inner: T, origin: Uri) -> Self {
|
||||
let inner = tonic::client::Grpc::with_origin(inner, origin);
|
||||
Self { inner }
|
||||
}
|
||||
pub fn with_interceptor<F>(
|
||||
inner: T,
|
||||
interceptor: F,
|
||||
) -> SearcherServiceClient<InterceptedService<T, F>>
|
||||
where
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
Response = http::Response<
|
||||
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
|
||||
>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<
|
||||
http::Request<tonic::body::BoxBody>,
|
||||
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
SearcherServiceClient::new(InterceptedService::new(inner, interceptor))
|
||||
}
|
||||
/// Compress requests with the given encoding.
|
||||
///
|
||||
/// This requires the server to support it otherwise it might respond with an
|
||||
/// error.
|
||||
#[must_use]
|
||||
pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.send_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Enable decompressing responses.
|
||||
#[must_use]
|
||||
pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
|
||||
self.inner = self.inner.accept_compressed(encoding);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of a decoded message.
|
||||
///
|
||||
/// Default: `4MB`
|
||||
#[must_use]
|
||||
pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_decoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Limits the maximum size of an encoded message.
|
||||
///
|
||||
/// Default: `usize::MAX`
|
||||
#[must_use]
|
||||
pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
|
||||
self.inner = self.inner.max_encoding_message_size(limit);
|
||||
self
|
||||
}
|
||||
/// Searchers can invoke this endpoint to subscribe to their respective bundle results.
|
||||
/// A success result would indicate the bundle won its state auction and was submitted to the validator.
|
||||
pub async fn subscribe_bundle_results(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SubscribeBundleResultsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<super::super::bundle::BundleResult>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/SubscribeBundleResults",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new("searcher.SearcherService", "SubscribeBundleResults"),
|
||||
);
|
||||
self.inner.server_streaming(req, path, codec).await
|
||||
}
|
||||
pub async fn send_bundle(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::SendBundleRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::SendBundleResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/SendBundle",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("searcher.SearcherService", "SendBundle"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns the next scheduled leader connected to the block engine.
|
||||
pub async fn get_next_scheduled_leader(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::NextScheduledLeaderRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::NextScheduledLeaderResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/GetNextScheduledLeader",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new("searcher.SearcherService", "GetNextScheduledLeader"),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns leader slots for connected jito validators during the current epoch. Only returns data for this region.
|
||||
pub async fn get_connected_leaders(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::ConnectedLeadersRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::ConnectedLeadersResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/GetConnectedLeaders",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new("searcher.SearcherService", "GetConnectedLeaders"),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns leader slots for connected jito validators during the current epoch.
|
||||
pub async fn get_connected_leaders_regioned(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::ConnectedLeadersRegionedRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::ConnectedLeadersRegionedResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/GetConnectedLeadersRegioned",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(
|
||||
GrpcMethod::new(
|
||||
"searcher.SearcherService",
|
||||
"GetConnectedLeadersRegioned",
|
||||
),
|
||||
);
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns the tip accounts searchers shall transfer funds to for the leader to claim.
|
||||
pub async fn get_tip_accounts(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GetTipAccountsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetTipAccountsResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/GetTipAccounts",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("searcher.SearcherService", "GetTipAccounts"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
/// Returns region the client is directly connected to, along with all available regions
|
||||
pub async fn get_regions(
|
||||
&mut self,
|
||||
request: impl tonic::IntoRequest<super::GetRegionsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<super::GetRegionsResponse>,
|
||||
tonic::Status,
|
||||
> {
|
||||
self.inner
|
||||
.ready()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tonic::Status::unknown(
|
||||
format!("Service was not ready: {}", e.into()),
|
||||
)
|
||||
})?;
|
||||
let codec = tonic::codec::ProstCodec::default();
|
||||
let path = http::uri::PathAndQuery::from_static(
|
||||
"/searcher.SearcherService/GetRegions",
|
||||
);
|
||||
let mut req = request.into_request();
|
||||
req.extensions_mut()
|
||||
.insert(GrpcMethod::new("searcher.SearcherService", "GetRegions"));
|
||||
self.inner.unary(req, path, codec).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::protos::{
|
||||
bundle::{
|
||||
Bundle, BundleResult,
|
||||
},
|
||||
convert::proto_packet_from_versioned_tx,
|
||||
searcher::{
|
||||
searcher_service_client::SearcherServiceClient, SendBundleRequest, SubscribeBundleResultsRequest,
|
||||
},
|
||||
};
|
||||
use solana_sdk::{
|
||||
signature::Signature,
|
||||
transaction::VersionedTransaction,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::Mutex;
|
||||
use tonic::{
|
||||
transport::{self, Channel, Endpoint}, Status
|
||||
};
|
||||
use yellowstone_grpc_client::ClientTlsConfig;
|
||||
|
||||
use crate::swqos::common::poll_transaction_confirmation;
|
||||
use crate::common::SolanaRpcClient;
|
||||
use crate::swqos::TradeType;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BlockEngineConnectionError {
|
||||
#[error("transport error {0}")]
|
||||
TransportError(#[from] transport::Error),
|
||||
#[error("client error {0}")]
|
||||
ClientError(#[from] Status),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum BundleRejectionError {
|
||||
#[error("bundle lost state auction, auction: {0}, tip {1} lamports")]
|
||||
StateAuctionBidRejected(String, u64),
|
||||
#[error("bundle won state auction but failed global auction, auction {0}, tip {1} lamports")]
|
||||
WinningBatchBidRejected(String, u64),
|
||||
#[error("bundle simulation failure on tx {0}, message: {1:?}")]
|
||||
SimulationFailure(String, Option<String>),
|
||||
#[error("internal error {0}")]
|
||||
InternalError(String),
|
||||
}
|
||||
|
||||
pub type BlockEngineConnectionResult<T> = Result<T, BlockEngineConnectionError>;
|
||||
|
||||
pub async fn get_searcher_client_no_auth(
|
||||
block_engine_url: &str,
|
||||
) -> BlockEngineConnectionResult<SearcherServiceClient<Channel>> {
|
||||
let searcher_channel = create_grpc_channel(block_engine_url).await?;
|
||||
let searcher_client = SearcherServiceClient::new(searcher_channel);
|
||||
Ok(searcher_client)
|
||||
}
|
||||
|
||||
pub async fn create_grpc_channel(url: &str) -> BlockEngineConnectionResult<Channel> {
|
||||
let mut endpoint = Endpoint::from_shared(url.to_string()).expect("invalid url");
|
||||
if url.starts_with("https") {
|
||||
endpoint = endpoint.tls_config(ClientTlsConfig::new().with_native_roots())?;
|
||||
}
|
||||
|
||||
endpoint = endpoint.tcp_nodelay(true);
|
||||
endpoint = endpoint.tcp_keepalive(Some(Duration::from_secs(10)));
|
||||
endpoint = endpoint.connect_timeout(Duration::from_secs(20));
|
||||
endpoint = endpoint.http2_keep_alive_interval(Duration::from_secs(10));
|
||||
|
||||
Ok(endpoint.connect().await?)
|
||||
}
|
||||
|
||||
pub async fn subscribe_bundle_results(
|
||||
searcher_client: Arc<Mutex<SearcherServiceClient<Channel>>>,
|
||||
request: impl tonic::IntoRequest<SubscribeBundleResultsRequest>,
|
||||
) -> std::result::Result<
|
||||
tonic::Response<tonic::codec::Streaming<BundleResult>>,
|
||||
tonic::Status,
|
||||
> {
|
||||
let mut searcher = searcher_client.lock().await;
|
||||
searcher.subscribe_bundle_results(request).await
|
||||
}
|
||||
|
||||
pub async fn send_bundle_with_confirmation(
|
||||
rpc: Arc<SolanaRpcClient>,
|
||||
trade_type: TradeType,
|
||||
transactions: &Vec<VersionedTransaction>,
|
||||
searcher_client: Arc<Mutex<SearcherServiceClient<Channel>>>,
|
||||
) -> Result<Vec<Signature>, anyhow::Error> {
|
||||
let start_time = Instant::now();
|
||||
let signatures = send_bundle_no_wait(transactions, searcher_client).await?;
|
||||
println!(" Jito{}提交: {:?}", trade_type, start_time.elapsed());
|
||||
|
||||
let start_time: Instant = Instant::now();
|
||||
for signature in signatures.clone() {
|
||||
match poll_transaction_confirmation(&rpc, signature).await {
|
||||
Ok(_) => continue,
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
println!(" Jito{}确认: {:?}", trade_type, start_time.elapsed());
|
||||
|
||||
Ok(signatures)
|
||||
}
|
||||
|
||||
pub async fn send_bundle_no_wait(
|
||||
transactions: &Vec<VersionedTransaction>,
|
||||
searcher_client: Arc<Mutex<SearcherServiceClient<Channel>>>,
|
||||
) -> Result<Vec<Signature>, anyhow::Error> {
|
||||
let mut packets = vec![];
|
||||
let mut signatures = vec![];
|
||||
for transaction in transactions {
|
||||
let packet = proto_packet_from_versioned_tx(transaction);
|
||||
packets.push(packet);
|
||||
signatures.push(transaction.signatures[0]);
|
||||
}
|
||||
|
||||
let mut searcher = searcher_client.lock().await;
|
||||
searcher
|
||||
.send_bundle(SendBundleRequest {
|
||||
bundle: Some(Bundle {
|
||||
header: None,
|
||||
packets,
|
||||
}),
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(signatures)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Header {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub ts: ::core::option::Option<::prost_types::Timestamp>,
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
|
||||
pub struct Heartbeat {
|
||||
#[prost(uint64, tag = "1")]
|
||||
pub count: u64,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Socket {
|
||||
#[prost(string, tag = "1")]
|
||||
pub ip: ::prost::alloc::string::String,
|
||||
#[prost(int64, tag = "2")]
|
||||
pub port: i64,
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
use std::{
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use crate::protos::auth::{
|
||||
auth_service_client::AuthServiceClient, GenerateAuthChallengeRequest,
|
||||
GenerateAuthTokensRequest, RefreshAccessTokenRequest, Role, Token,
|
||||
};
|
||||
use prost_types::Timestamp;
|
||||
use solana_metrics::datapoint_info;
|
||||
use solana_sdk::signature::{Keypair, Signer};
|
||||
use tokio::{task::JoinHandle, time::sleep};
|
||||
use tonic::{service::Interceptor, transport::Channel, Request, Status};
|
||||
|
||||
use super::searcher_client::BlockEngineConnectionResult;
|
||||
|
||||
const AUTHORIZATION_HEADER: &str = "authorization";
|
||||
const BEARER: &str = "Bearer ";
|
||||
|
||||
/// Adds the token to each requests' authorization header.
|
||||
/// Manages refreshing the token in a separate thread.
|
||||
#[derive(Clone)]
|
||||
pub struct ClientInterceptor {
|
||||
/// The token added to each request header.
|
||||
bearer_token: Arc<RwLock<String>>,
|
||||
}
|
||||
|
||||
impl ClientInterceptor {
|
||||
pub async fn new(
|
||||
mut auth_service_client: AuthServiceClient<Channel>,
|
||||
keypair: &Arc<Keypair>,
|
||||
role: Role,
|
||||
) -> BlockEngineConnectionResult<Self> {
|
||||
let (access_token, refresh_token) =
|
||||
Self::auth(&mut auth_service_client, keypair, role).await?;
|
||||
|
||||
let bearer_token = Arc::new(RwLock::new(access_token.value.clone()));
|
||||
|
||||
let _refresh_token_thread = Self::spawn_token_refresh_thread(
|
||||
auth_service_client,
|
||||
bearer_token.clone(),
|
||||
refresh_token,
|
||||
access_token.expires_at_utc.unwrap(),
|
||||
keypair.clone(),
|
||||
role,
|
||||
);
|
||||
|
||||
Ok(Self { bearer_token })
|
||||
}
|
||||
|
||||
async fn auth(
|
||||
auth_service_client: &mut AuthServiceClient<Channel>,
|
||||
keypair: &Keypair,
|
||||
role: Role,
|
||||
) -> BlockEngineConnectionResult<(Token, Token)> {
|
||||
let challenge_resp = auth_service_client
|
||||
.generate_auth_challenge(GenerateAuthChallengeRequest {
|
||||
role: role as i32,
|
||||
pubkey: keypair.pubkey().as_ref().to_vec(),
|
||||
})
|
||||
.await?
|
||||
.into_inner();
|
||||
let challenge = format!("{}-{}", keypair.pubkey(), challenge_resp.challenge);
|
||||
let signed_challenge = keypair.sign_message(challenge.as_bytes()).as_ref().to_vec();
|
||||
|
||||
let tokens = auth_service_client
|
||||
.generate_auth_tokens(GenerateAuthTokensRequest {
|
||||
challenge,
|
||||
client_pubkey: keypair.pubkey().as_ref().to_vec(),
|
||||
signed_challenge,
|
||||
})
|
||||
.await?
|
||||
.into_inner();
|
||||
|
||||
Ok((tokens.access_token.unwrap(), tokens.refresh_token.unwrap()))
|
||||
}
|
||||
|
||||
fn spawn_token_refresh_thread(
|
||||
mut auth_service_client: AuthServiceClient<Channel>,
|
||||
bearer_token: Arc<RwLock<String>>,
|
||||
refresh_token: Token,
|
||||
access_token_expiration: Timestamp,
|
||||
keypair: Arc<Keypair>,
|
||||
role: Role,
|
||||
) -> JoinHandle<BlockEngineConnectionResult<()>> {
|
||||
tokio::spawn(async move {
|
||||
let mut refresh_token = refresh_token;
|
||||
let mut access_token_expiration = access_token_expiration;
|
||||
|
||||
loop {
|
||||
let access_token_ttl = SystemTime::try_from(access_token_expiration.clone())
|
||||
.unwrap()
|
||||
.duration_since(SystemTime::now())
|
||||
.unwrap_or_else(|_| Duration::from_secs(0));
|
||||
let refresh_token_ttl =
|
||||
SystemTime::try_from(refresh_token.expires_at_utc.as_ref().unwrap().clone())
|
||||
.unwrap()
|
||||
.duration_since(SystemTime::now())
|
||||
.unwrap_or_else(|_| Duration::from_secs(0));
|
||||
|
||||
let does_access_token_expire_soon = access_token_ttl < Duration::from_secs(5 * 60);
|
||||
let does_refresh_token_expire_soon =
|
||||
refresh_token_ttl < Duration::from_secs(5 * 60);
|
||||
|
||||
match (
|
||||
does_refresh_token_expire_soon,
|
||||
does_access_token_expire_soon,
|
||||
) {
|
||||
// re-run entire auth workflow is refresh token expiring soon
|
||||
(true, _) => {
|
||||
let is_error = {
|
||||
if let Ok((new_access_token, new_refresh_token)) =
|
||||
Self::auth(&mut auth_service_client, &keypair, role).await
|
||||
{
|
||||
*bearer_token.write().unwrap() = new_access_token.value.clone();
|
||||
access_token_expiration = new_access_token.expires_at_utc.unwrap();
|
||||
refresh_token = new_refresh_token;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
datapoint_info!("searcher-full-auth", ("is_error", is_error, bool));
|
||||
}
|
||||
// re-up the access token if it expires soon
|
||||
(_, true) => {
|
||||
let is_error = {
|
||||
if let Ok(refresh_resp) = auth_service_client
|
||||
.refresh_access_token(RefreshAccessTokenRequest {
|
||||
refresh_token: refresh_token.value.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
let access_token = refresh_resp.into_inner().access_token.unwrap();
|
||||
*bearer_token.write().unwrap() = access_token.value.clone();
|
||||
access_token_expiration = access_token.expires_at_utc.unwrap();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
datapoint_info!("searcher-refresh-auth", ("is_error", is_error, bool));
|
||||
}
|
||||
_ => {
|
||||
sleep(Duration::from_secs(60)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Interceptor for ClientInterceptor {
|
||||
fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
|
||||
let l_token = self.bearer_token.read().unwrap();
|
||||
if !l_token.is_empty() {
|
||||
request.metadata_mut().insert(
|
||||
AUTHORIZATION_HEADER,
|
||||
format!("{BEARER}{l_token}").parse().unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// This file is @generated by prost-build.
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TraceShred {
|
||||
/// source region, one of: <https://jito-labs.gitbook.io/mev/systems/connecting/mainnet>
|
||||
#[prost(string, tag = "1")]
|
||||
pub region: ::prost::alloc::string::String,
|
||||
/// timestamp of creation
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub created_at: ::core::option::Option<::prost_types::Timestamp>,
|
||||
/// monotonically increases, resets upon service restart
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub seq_num: u32,
|
||||
}
|
||||
+2
-1
@@ -12,7 +12,8 @@ pub mod astralane;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use solana_sdk::{commitment_config::CommitmentConfig, transaction::VersionedTransaction};
|
||||
use solana_commitment_config::CommitmentConfig;
|
||||
use solana_sdk::transaction::VersionedTransaction;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use solana_client::rpc_config::RpcSendTransactionConfig;
|
||||
use solana_sdk::{commitment_config::CommitmentLevel, transaction::VersionedTransaction};
|
||||
use solana_commitment_config::CommitmentLevel;
|
||||
use solana_sdk::transaction::VersionedTransaction;
|
||||
use solana_transaction_status::UiTransactionEncoding;
|
||||
|
||||
use crate::swqos::SwqosClientTrait;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use dashmap::DashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use smallvec::SmallVec;
|
||||
use solana_sdk::{compute_budget::ComputeBudgetInstruction, instruction::Instruction};
|
||||
use solana_sdk::instruction::Instruction;
|
||||
use solana_compute_budget_interface::ComputeBudgetInstruction;
|
||||
|
||||
/// Cache key containing all parameters for compute budget instructions
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
|
||||
use solana_system_interface::instruction::transfer;
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
use crate::common::SolanaRpcClient;
|
||||
use crate::common::{
|
||||
fast_fn::get_associated_token_address_with_program_id_fast, spl_token::close_account,
|
||||
SolanaRpcClient,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
|
||||
/// Get the balances of two tokens in the pool
|
||||
@@ -35,7 +36,11 @@ pub async fn get_token_balance(
|
||||
payer: &Pubkey,
|
||||
mint: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let ata = get_associated_token_address(payer, mint);
|
||||
let ata = crate::common::fast_fn::get_associated_token_address_with_program_id_fast(
|
||||
payer,
|
||||
mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
let balance = rpc.get_token_account_balance(&ata).await?;
|
||||
let balance_u64 =
|
||||
balance.amount.parse::<u64>().map_err(|_| anyhow!("Failed to parse token balance"))?;
|
||||
@@ -102,7 +107,11 @@ pub async fn close_token_account(
|
||||
mint: &Pubkey,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
// Get associated token account address
|
||||
let ata = get_associated_token_address(&payer.pubkey(), mint);
|
||||
let ata = get_associated_token_address_with_program_id_fast(
|
||||
&payer.pubkey(),
|
||||
mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
|
||||
// Check if account exists
|
||||
let account_exists = rpc.get_account(&ata).await.is_ok();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::common::fast_fn::create_associated_token_account_idempotent_fast;
|
||||
use crate::common::{
|
||||
fast_fn::create_associated_token_account_idempotent_fast, spl_token::close_account,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use solana_sdk::{instruction::Instruction, pubkey::Pubkey};
|
||||
use solana_sdk::{instruction::Instruction, message::AccountMeta, pubkey::Pubkey};
|
||||
use solana_system_interface::instruction::transfer;
|
||||
use spl_token::instruction::close_account;
|
||||
|
||||
#[inline]
|
||||
pub fn handle_wsol(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 3]> {
|
||||
@@ -22,8 +23,12 @@ pub fn handle_wsol(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 3]>
|
||||
));
|
||||
insts.extend([
|
||||
transfer(&payer, &wsol_token_account, amount_in),
|
||||
spl_token::instruction::sync_native(&crate::constants::TOKEN_PROGRAM, &wsol_token_account)
|
||||
.unwrap(),
|
||||
// sync_native
|
||||
Instruction {
|
||||
program_id: crate::constants::TOKEN_PROGRAM,
|
||||
accounts: vec![AccountMeta::new(wsol_token_account, false)],
|
||||
data: vec![17],
|
||||
},
|
||||
]);
|
||||
|
||||
insts
|
||||
|
||||
+20
-19
@@ -1,9 +1,10 @@
|
||||
use super::traits::ProtocolParams;
|
||||
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::solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use solana_streamer_sdk::streaming::event_parser::common::EventType;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::BonkTradeEvent;
|
||||
use crate::swqos::{SwqosClient, TradeType};
|
||||
use crate::trading::common::get_multi_token_balances;
|
||||
use crate::trading::MiddlewareManager;
|
||||
@@ -15,7 +16,6 @@ use solana_streamer_sdk::streaming::event_parser::protocols::pumpswap::{
|
||||
};
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_amm_v4::types::AmmInfo;
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_cpmm::RaydiumCpmmSwapEvent;
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Swap parameters
|
||||
@@ -121,7 +121,11 @@ impl PumpFunParams {
|
||||
complete: account.0.complete,
|
||||
creator: account.0.creator,
|
||||
};
|
||||
let associated_bonding_curve = get_associated_token_address(&bonding_curve.account, mint);
|
||||
let associated_bonding_curve = get_associated_token_address_with_program_id(
|
||||
&bonding_curve.account,
|
||||
mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
let creator_vault =
|
||||
crate::instruction::utils::pumpfun::get_creator_vault_pda(&bonding_curve.creator);
|
||||
Ok(Self {
|
||||
@@ -244,18 +248,16 @@ impl PumpSwapParams {
|
||||
let coin_creator_vault_authority =
|
||||
crate::instruction::utils::pumpswap::coin_creator_vault_authority(creator);
|
||||
|
||||
let base_token_program_ata =
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.base_mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
let quote_token_program_ata =
|
||||
spl_associated_token_account::get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.quote_mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
let base_token_program_ata = get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.base_mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
let quote_token_program_ata = get_associated_token_address_with_program_id(
|
||||
&pool_address,
|
||||
&pool_data.quote_mint,
|
||||
&crate::constants::TOKEN_PROGRAM,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
pool: pool_address.clone(),
|
||||
@@ -303,7 +305,6 @@ pub struct BonkParams {
|
||||
pub base_vault: Pubkey,
|
||||
pub quote_vault: Pubkey,
|
||||
/// Token program ID
|
||||
/// Specifies the program used by the token, usually spl_token::ID or spl_token_2022::ID
|
||||
pub mint_token_program: Pubkey,
|
||||
pub platform_config: Pubkey,
|
||||
pub platform_associated_account: Pubkey,
|
||||
@@ -473,9 +474,9 @@ pub struct RaydiumCpmmParams {
|
||||
pub base_vault: Pubkey,
|
||||
/// Quote token vault address
|
||||
pub quote_vault: Pubkey,
|
||||
/// Base token program ID (usually spl_token::ID or spl_token_2022::ID)
|
||||
/// Base token program ID
|
||||
pub base_token_program: Pubkey,
|
||||
/// Quote token program ID (usually spl_token::ID or spl_token_2022::ID)
|
||||
/// Quote token program ID
|
||||
pub quote_token_program: Pubkey,
|
||||
/// Observation state account
|
||||
pub observation_state: Pubkey,
|
||||
|
||||
Reference in New Issue
Block a user