slotstrike 1.0.0

Low-latency Solana slotstrike runtime for event-driven token execution
Documentation
use std::sync::Arc;

use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{program_error::ProgramError, pubkey::Pubkey};

use crate::MAX_RETRIES;

const MARKET_STATE_LAYOUT_V3_LEN: usize = 388;
const OWN_ADDRESS_START: usize = 13;
const BASE_VAULT_START: usize = 117;
const QUOTE_VAULT_START: usize = 165;
const EVENT_QUEUE_START: usize = 253;
const BIDS_START: usize = 285;
const ASKS_START: usize = 317;

#[derive(Debug, Clone)]
pub struct Market {
    pub program_id: Pubkey,
    pub state: MarketStateLayoutV3,
}

#[derive(Debug, Clone)]
pub struct MarketStateLayoutV3 {
    pub own_address: Pubkey,
    pub base_vault: Pubkey,
    pub quote_vault: Pubkey,
    pub event_queue: Pubkey,
    pub bids: Pubkey,
    pub asks: Pubkey,
}

impl MarketStateLayoutV3 {
    fn read_pubkey(bytes: &[u8], start: usize) -> Option<Pubkey> {
        let end = start.checked_add(32)?;
        let key_bytes: [u8; 32] = bytes.get(start..end)?.try_into().ok()?;
        Some(Pubkey::new_from_array(key_bytes))
    }

    pub fn decode(bytes: &[u8]) -> Option<Self> {
        if bytes.len() != MARKET_STATE_LAYOUT_V3_LEN {
            return None;
        }

        Some(Self {
            own_address: Self::read_pubkey(bytes, OWN_ADDRESS_START)?,
            base_vault: Self::read_pubkey(bytes, BASE_VAULT_START)?,
            quote_vault: Self::read_pubkey(bytes, QUOTE_VAULT_START)?,
            event_queue: Self::read_pubkey(bytes, EVENT_QUEUE_START)?,
            bids: Self::read_pubkey(bytes, BIDS_START)?,
            asks: Self::read_pubkey(bytes, ASKS_START)?,
        })
    }
}

pub async fn get_market_accounts(rpc: &Arc<RpcClient>, market_id: &Pubkey) -> Option<Market> {
    let mut attempts = 0_usize;

    loop {
        let market_account_info = rpc
            .get_account_with_commitment(market_id, CommitmentConfig::confirmed())
            .await;

        match market_account_info {
            Ok(response) => {
                let account = response.value?;
                let state = MarketStateLayoutV3::decode(&account.data)?;
                return Some(Market {
                    program_id: account.owner,
                    state,
                });
            }
            Err(error) => {
                log::debug!("Error getting market accounts: {}", error);
                if attempts >= MAX_RETRIES {
                    return None;
                }
            }
        }

        tokio::time::sleep(tokio::time::Duration::from_millis(1_000)).await;
        attempts = attempts.saturating_add(1);
    }
}

pub fn get_associated_authority(
    program_id: &Pubkey,
    market_id: &Pubkey,
) -> Result<(Pubkey, u64), ProgramError> {
    let market_bytes = market_id.to_bytes();
    let mut nonce = 0_u64;

    while nonce < 100_u64 {
        let nonce_bytes = nonce.to_le_bytes();
        let seeds_with_nonce: [&[u8]; 3] = [&market_bytes, &nonce_bytes, &[0_u8; 7]];

        if let Some((pubkey, _)) = Pubkey::try_find_program_address(&seeds_with_nonce, program_id) {
            return Ok((pubkey, nonce));
        }

        nonce = nonce.saturating_add(1);
    }

    Err(ProgramError::Custom(1))
}