simulator-api 0.24.0

Wire-protocol types for the Solana simulator backtest WebSocket API
Documentation
//! The major-mint universe shared by the reroute reports: tables of stables and priced majors,
//! plus the helpers that classify, price, and label mints against them.

use solana_address::{Address, address};
use spl_token_interface::native_mint;

/// A mint: its address, display symbol, and token decimals.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Mint {
    pub address: Address,
    pub symbol: &'static str,
    pub decimals: u32,
}

const fn mint(address: Address, symbol: &'static str, decimals: u32) -> Mint {
    Mint {
        address,
        symbol,
        decimals,
    }
}

pub const STABLES: [Mint; 8] = [
    mint(
        address!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
        "USDC",
        6,
    ),
    mint(
        address!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"),
        "USDT",
        6,
    ),
    mint(
        address!("2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"),
        "PYUSD",
        6,
    ),
    mint(
        address!("USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA"),
        "USDS",
        6,
    ),
    mint(
        address!("DEkqHyPN7GMRJ5cArtQFAWefqbZb33Hyf6s5iCwjEonT"),
        "USDe",
        9,
    ),
    mint(
        address!("9zNQRsGLjNKwCUU5Gq5LR8beUCPzQMVMqKAi3SSZh54u"),
        "FDUSD",
        6,
    ),
    mint(
        address!("A1KLoBrKBde8Ty9qtNQUtq3C2ortoC3u7twggz7sEto6"),
        "USDY",
        6,
    ),
    mint(
        address!("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"),
        "USD1",
        6,
    ),
];

/// Non-stable majors priced from in-range quote medians (vs USDC/USDT/SOL).
pub const PRICED_MAJORS: [Mint; 10] = [
    mint(
        address!("HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr"),
        "EURC",
        6,
    ),
    mint(
        address!("cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij"),
        "cbBTC",
        8,
    ),
    mint(
        address!("3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh"),
        "WBTC",
        8,
    ),
    mint(
        address!("7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"),
        "WETH",
        8,
    ),
    mint(
        address!("J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn"),
        "jitoSOL",
        9,
    ),
    mint(
        address!("mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So"),
        "mSOL",
        9,
    ),
    mint(
        address!("bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"),
        "bSOL",
        9,
    ),
    mint(
        address!("jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v"),
        "jupSOL",
        9,
    ),
    mint(
        address!("5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm"),
        "INF",
        9,
    ),
    mint(
        address!("BNso1VUJnh4zcfpZa6986Ea66P6TCp59hvtNJ8b1X85"),
        "bnSOL",
        9,
    ),
];

/// Wrapped SOL, the mint every native-SOL leg trades as.
pub const SOL: Address = native_mint::id();
pub const USDC: Address = STABLES[0].address;
pub const USDT: Address = STABLES[1].address;

fn find_in(table: &'static [Mint], mint: &Address) -> Option<&'static Mint> {
    table.iter().find(|entry| entry.address == *mint)
}

pub fn stable_decimals(mint: &Address) -> Option<u32> {
    find_in(&STABLES, mint).map(|entry| entry.decimals)
}

pub fn priced_decimals(mint: &Address) -> Option<u32> {
    find_in(&PRICED_MAJORS, mint).map(|entry| entry.decimals)
}

/// Whether the mint is one the tables name, and so can anchor another mint's price.
pub fn is_major(mint: &Address) -> bool {
    *mint == SOL || stable_decimals(mint).is_some() || priced_decimals(mint).is_some()
}

/// One major against a longtail mint is not a majors pair: the longtail side is what the
/// router misprices.
pub fn trades_only_majors(input_mint: &Address, output_mint: &Address) -> bool {
    is_major(input_mint) && is_major(output_mint)
}

/// `None` outside the tables, which displays as a shortened address rather than a guess.
pub fn symbol(mint: &Address) -> Option<&'static str> {
    if *mint == SOL {
        return Some("SOL");
    }
    find_in(&STABLES, mint)
        .or_else(|| find_in(&PRICED_MAJORS, mint))
        .map(|entry| entry.symbol)
}

/// Leg mints are resolved before a swap ever becomes reroutable (`resolve_swap` only emits a
/// `Reroutable` swap once both mints parse), so a leg mint that still fails to parse here is
/// corrupt wire data or an upstream bug, not a legitimate mint-less state. Panicking loudly beats
/// defaulting: a silent default would bucket that bug's output as an ordinary exotic leg instead
/// of surfacing it.
pub fn parse_mint(raw: &str) -> Address {
    raw.parse()
        .expect("leg mints are resolved before a swap becomes reroutable")
}

/// The token's symbol, or a shortened address
pub fn short_mint(mint: &str) -> String {
    if let Some(symbol) = mint.parse().ok().as_ref().and_then(symbol) {
        return symbol.to_string();
    }
    match mint.len() > 12 {
        true => format!("{}..{}", &mint[..6], &mint[mint.len() - 4..]),
        false => mint.to_string(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    /// A duplicate reads as a missing mint, quietly widening what counts as exotic.
    #[test]
    fn the_major_universe_is_nineteen_distinct_mints() {
        let mut all: Vec<Address> = STABLES
            .iter()
            .chain(PRICED_MAJORS.iter())
            .map(|entry| entry.address)
            .chain([SOL])
            .collect();
        assert_eq!(all.len(), 19);
        all.sort();
        all.dedup();
        assert_eq!(all.len(), 19, "a mint is listed twice");
        assert!(all.iter().all(is_major));
        // Every major displays as a symbol, so an address in the output means an uncommon token.
        assert!(all.iter().all(|mint| symbol(mint).is_some()));

        let mut symbols: Vec<&str> = all.iter().filter_map(symbol).collect();
        symbols.sort_unstable();
        symbols.dedup();
        assert_eq!(symbols.len(), 19, "two mints share a symbol");
    }

    #[test]
    fn short_mint_prefers_the_symbol_and_shortens_the_rest() {
        assert_eq!(
            short_mint("So11111111111111111111111111111111111111112"),
            "SOL"
        );
        assert_eq!(
            short_mint("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"),
            "DezXAZ..B263"
        );
        assert_eq!(short_mint("BONK"), "BONK");
    }
}