use solana_address::{Address, address};
use spl_token_interface::native_mint;
#[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,
),
];
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,
),
];
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)
}
pub fn is_major(mint: &Address) -> bool {
*mint == SOL || stable_decimals(mint).is_some() || priced_decimals(mint).is_some()
}
pub fn trades_only_majors(input_mint: &Address, output_mint: &Address) -> bool {
is_major(input_mint) && is_major(output_mint)
}
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)
}
pub fn parse_mint(raw: &str) -> Address {
raw.parse()
.expect("leg mints are resolved before a swap becomes reroutable")
}
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::*;
#[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));
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");
}
}