feat: Pump.fun mayhem_mode, Solana 3.1.12, TradeConfig builder alignment

- PumpFunParams::from_trade/from_dev_trade: add mayhem_mode Option; infer Mayhem
  via is_mayhem_fee_recipient when None (fixes fee recipient / NotAuthorized 6000
  when AMM protocol fee pubkey is used).
- Add is_mayhem_fee_recipient and is_amm_fee_recipient helpers in pumpfun utils.
- Bump solana-* crates to 3.1.12; align tonic/prost; use solana-message for
  AddressLookupTableAccount; add solana-system-interface 3.0.
- Update examples and latency script for new PumpFunParams signature.
- SWQoS and transaction builder adjustments for dependency changes.

Made-with: Cursor
This commit is contained in:
0xfnzero
2026-04-11 18:43:18 +08:00
parent 8f2f99f3d9
commit 4b451af5ff
24 changed files with 272 additions and 77 deletions
+10 -10
View File
@@ -51,7 +51,7 @@ pub mod server_client {
}
impl<T> ServerClient<T>
where
T: tonic::client::GrpcService<tonic::body::Body>,
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,
@@ -72,13 +72,13 @@ pub mod server_client {
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<
http::Request<tonic::body::Body>,
http::Request<tonic::body::BoxBody>,
Response = http::Response<
<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody,
<T as tonic::client::GrpcService<tonic::body::BoxBody>>::ResponseBody,
>,
>,
<T as tonic::codegen::Service<
http::Request<tonic::body::Body>,
http::Request<tonic::body::BoxBody>,
>>::Error: Into<StdError> + std::marker::Send + std::marker::Sync,
{
ServerClient::new(InterceptedService::new(inner, interceptor))
@@ -126,7 +126,7 @@ pub mod server_client {
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic_prost::ProstCodec::default();
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/serverpb.Server/SendTransaction",
);
@@ -147,7 +147,7 @@ pub mod server_client {
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic_prost::ProstCodec::default();
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/serverpb.Server/GetHealth",
);
@@ -244,7 +244,7 @@ pub mod server_server {
B: Body + std::marker::Send + 'static,
B::Error: Into<StdError> + std::marker::Send + 'static,
{
type Response = http::Response<tonic::body::Body>;
type Response = http::Response<tonic::body::BoxBody>;
type Error = std::convert::Infallible;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(
@@ -283,7 +283,7 @@ pub mod server_server {
let inner = self.inner.clone();
let fut = async move {
let method = SendTransactionSvc(inner);
let codec = tonic_prost::ProstCodec::default();
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
@@ -326,7 +326,7 @@ pub mod server_server {
let inner = self.inner.clone();
let fut = async move {
let method = GetHealthSvc(inner);
let codec = tonic_prost::ProstCodec::default();
let codec = tonic::codec::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
@@ -344,7 +344,7 @@ pub mod server_server {
_ => {
Box::pin(async move {
let mut response = http::Response::new(
tonic::body::Body::default(),
tonic::body::BoxBody::default(),
);
let headers = response.headers_mut();
headers
+61 -2
View File
@@ -8,7 +8,6 @@ use quinn::{
use rand::seq::IndexedRandom as _;
use solana_client::rpc_client::SerializableTransaction;
use solana_sdk::{signature::Keypair, transaction::VersionedTransaction};
use solana_tls_utils::{new_dummy_x509_certificate, SkipServerVerification};
use std::time::Instant;
use std::{
net::{SocketAddr, ToSocketAddrs as _},
@@ -25,6 +24,66 @@ use crate::{
swqos::{SwqosType, TradeType},
};
// Skip server verification implementation
#[derive(Debug)]
struct SkipServerVerification;
impl SkipServerVerification {
fn new() -> Arc<Self> {
Arc::new(Self)
}
}
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
vec![rustls::SignatureScheme::ECDSA_NISTP256_SHA256]
}
}
// Generate dummy self-signed certificate using rcgen
fn generate_self_signed_cert(_keypair: &Keypair) -> Result<(rustls::pki_types::CertificateDer<'static>, rustls::pki_types::PrivateKeyDer<'static>)> {
// Generate a new key pair for the certificate
let key_pair = rcgen::KeyPair::generate()?;
let params = rcgen::CertificateParams::default();
let cert = params.self_signed(&key_pair)?;
let cert_der = rustls::pki_types::CertificateDer::from(cert.der().to_vec());
let key_der = rustls::pki_types::PrivateKeyDer::try_from(key_pair.serialize_der())
.map_err(|e| anyhow::anyhow!("Failed to create private key: {:?}", e))?;
Ok((cert_der, key_der))
}
const ALPN_TPU_PROTOCOL_ID: &[u8] = b"solana-tpu";
const SOYAS_SERVER: &str = "soyas-landing";
const KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(25);
@@ -43,7 +102,7 @@ impl SoyasClient {
pub async fn new(rpc_url: String, endpoint_string: String, api_key: String) -> Result<Self> {
let rpc_client = SolanaRpcClient::new(rpc_url);
let keypair = Keypair::from_base58_string(&api_key);
let (cert, key) = new_dummy_x509_certificate(&keypair);
let (cert, key) = generate_self_signed_cert(&keypair)?;
let mut crypto = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(SkipServerVerification::new())
+61 -2
View File
@@ -7,7 +7,6 @@ use quinn::{
};
use rand::seq::IndexedRandom as _;
use solana_sdk::{signature::Keypair, transaction::VersionedTransaction};
use solana_tls_utils::{new_dummy_x509_certificate, SkipServerVerification};
use std::time::Instant;
use std::{
net::{SocketAddr, ToSocketAddrs as _},
@@ -26,6 +25,66 @@ use crate::{
swqos::{SwqosType, TradeType},
};
// Skip server verification implementation
#[derive(Debug)]
struct SkipServerVerification;
impl SkipServerVerification {
fn new() -> Arc<Self> {
Arc::new(Self)
}
}
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
vec![rustls::SignatureScheme::ECDSA_NISTP256_SHA256]
}
}
// Generate dummy self-signed certificate using rcgen
fn generate_self_signed_cert(_keypair: &Keypair) -> Result<(rustls::pki_types::CertificateDer<'static>, rustls::pki_types::PrivateKeyDer<'static>)> {
// Generate a new key pair for the certificate
let key_pair = rcgen::KeyPair::generate()?;
let params = rcgen::CertificateParams::default();
let cert = params.self_signed(&key_pair)?;
let cert_der = rustls::pki_types::CertificateDer::from(cert.der().to_vec());
let key_der = rustls::pki_types::PrivateKeyDer::try_from(key_pair.serialize_der())
.map_err(|e| anyhow::anyhow!("Failed to create private key: {:?}", e))?;
Ok((cert_der, key_der))
}
const ALPN_TPU_PROTOCOL_ID: &[u8] = b"solana-tpu";
/// Fallback SNI when endpoint is IP or cannot extract host (keeps legacy behavior).
const SPEED_SERVER_FALLBACK: &str = "speed-landing";
@@ -66,7 +125,7 @@ impl SpeedlandingClient {
let rpc_client = SolanaRpcClient::new(rpc_url);
let server_name = Self::server_name_from_endpoint(&endpoint_string);
let keypair = Keypair::from_base58_string(&api_key);
let (cert, key) = new_dummy_x509_certificate(&keypair);
let (cert, key) = generate_self_signed_cert(&keypair)?;
let mut crypto = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(SkipServerVerification::new())