Skip to main content

simulator_api/
mints.rs

1//! The major-mint universe shared by the reroute reports: tables of stables and priced majors,
2//! plus the helpers that classify, price, and label mints against them.
3
4use solana_address::{Address, address};
5use spl_token_interface::native_mint;
6
7/// A mint: its address, display symbol, and token decimals.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Mint {
10    pub address: Address,
11    pub symbol: &'static str,
12    pub decimals: u32,
13}
14
15const fn mint(address: Address, symbol: &'static str, decimals: u32) -> Mint {
16    Mint {
17        address,
18        symbol,
19        decimals,
20    }
21}
22
23pub const STABLES: [Mint; 8] = [
24    mint(
25        address!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
26        "USDC",
27        6,
28    ),
29    mint(
30        address!("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"),
31        "USDT",
32        6,
33    ),
34    mint(
35        address!("2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"),
36        "PYUSD",
37        6,
38    ),
39    mint(
40        address!("USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA"),
41        "USDS",
42        6,
43    ),
44    mint(
45        address!("DEkqHyPN7GMRJ5cArtQFAWefqbZb33Hyf6s5iCwjEonT"),
46        "USDe",
47        9,
48    ),
49    mint(
50        address!("9zNQRsGLjNKwCUU5Gq5LR8beUCPzQMVMqKAi3SSZh54u"),
51        "FDUSD",
52        6,
53    ),
54    mint(
55        address!("A1KLoBrKBde8Ty9qtNQUtq3C2ortoC3u7twggz7sEto6"),
56        "USDY",
57        6,
58    ),
59    mint(
60        address!("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB"),
61        "USD1",
62        6,
63    ),
64];
65
66/// Non-stable majors priced from in-range quote medians (vs USDC/USDT/SOL).
67pub const PRICED_MAJORS: [Mint; 10] = [
68    mint(
69        address!("HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr"),
70        "EURC",
71        6,
72    ),
73    mint(
74        address!("cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij"),
75        "cbBTC",
76        8,
77    ),
78    mint(
79        address!("3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh"),
80        "WBTC",
81        8,
82    ),
83    mint(
84        address!("7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"),
85        "WETH",
86        8,
87    ),
88    mint(
89        address!("J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn"),
90        "jitoSOL",
91        9,
92    ),
93    mint(
94        address!("mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So"),
95        "mSOL",
96        9,
97    ),
98    mint(
99        address!("bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"),
100        "bSOL",
101        9,
102    ),
103    mint(
104        address!("jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v"),
105        "jupSOL",
106        9,
107    ),
108    mint(
109        address!("5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm"),
110        "INF",
111        9,
112    ),
113    mint(
114        address!("BNso1VUJnh4zcfpZa6986Ea66P6TCp59hvtNJ8b1X85"),
115        "bnSOL",
116        9,
117    ),
118];
119
120/// Wrapped SOL, the mint every native-SOL leg trades as.
121pub const SOL: Address = native_mint::id();
122pub const USDC: Address = STABLES[0].address;
123pub const USDT: Address = STABLES[1].address;
124
125fn find_in(table: &'static [Mint], mint: &Address) -> Option<&'static Mint> {
126    table.iter().find(|entry| entry.address == *mint)
127}
128
129pub fn stable_decimals(mint: &Address) -> Option<u32> {
130    find_in(&STABLES, mint).map(|entry| entry.decimals)
131}
132
133pub fn priced_decimals(mint: &Address) -> Option<u32> {
134    find_in(&PRICED_MAJORS, mint).map(|entry| entry.decimals)
135}
136
137/// Whether the mint is one the tables name, and so can anchor another mint's price.
138pub fn is_major(mint: &Address) -> bool {
139    *mint == SOL || stable_decimals(mint).is_some() || priced_decimals(mint).is_some()
140}
141
142/// One major against a longtail mint is not a majors pair: the longtail side is what the
143/// router misprices.
144pub fn trades_only_majors(input_mint: &Address, output_mint: &Address) -> bool {
145    is_major(input_mint) && is_major(output_mint)
146}
147
148/// `None` outside the tables, which displays as a shortened address rather than a guess.
149pub fn symbol(mint: &Address) -> Option<&'static str> {
150    if *mint == SOL {
151        return Some("SOL");
152    }
153    find_in(&STABLES, mint)
154        .or_else(|| find_in(&PRICED_MAJORS, mint))
155        .map(|entry| entry.symbol)
156}
157
158/// Leg mints are resolved before a swap ever becomes reroutable (`resolve_swap` only emits a
159/// `Reroutable` swap once both mints parse), so a leg mint that still fails to parse here is
160/// corrupt wire data or an upstream bug, not a legitimate mint-less state. Panicking loudly beats
161/// defaulting: a silent default would bucket that bug's output as an ordinary exotic leg instead
162/// of surfacing it.
163pub fn parse_mint(raw: &str) -> Address {
164    raw.parse()
165        .expect("leg mints are resolved before a swap becomes reroutable")
166}
167
168/// The token's symbol, or a shortened address
169pub fn short_mint(mint: &str) -> String {
170    if let Some(symbol) = mint.parse().ok().as_ref().and_then(symbol) {
171        return symbol.to_string();
172    }
173    match mint.len() > 12 {
174        true => format!("{}..{}", &mint[..6], &mint[mint.len() - 4..]),
175        false => mint.to_string(),
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    /// A duplicate reads as a missing mint, quietly widening what counts as exotic.
184    #[test]
185    fn the_major_universe_is_nineteen_distinct_mints() {
186        let mut all: Vec<Address> = STABLES
187            .iter()
188            .chain(PRICED_MAJORS.iter())
189            .map(|entry| entry.address)
190            .chain([SOL])
191            .collect();
192        assert_eq!(all.len(), 19);
193        all.sort();
194        all.dedup();
195        assert_eq!(all.len(), 19, "a mint is listed twice");
196        assert!(all.iter().all(is_major));
197        // Every major displays as a symbol, so an address in the output means an uncommon token.
198        assert!(all.iter().all(|mint| symbol(mint).is_some()));
199
200        let mut symbols: Vec<&str> = all.iter().filter_map(symbol).collect();
201        symbols.sort_unstable();
202        symbols.dedup();
203        assert_eq!(symbols.len(), 19, "two mints share a symbol");
204    }
205
206    #[test]
207    fn short_mint_prefers_the_symbol_and_shortens_the_rest() {
208        assert_eq!(
209            short_mint("So11111111111111111111111111111111111111112"),
210            "SOL"
211        );
212        assert_eq!(
213            short_mint("DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263"),
214            "DezXAZ..B263"
215        );
216        assert_eq!(short_mint("BONK"), "BONK");
217    }
218}