1use solana_address::{Address, address};
5use spl_token_interface::native_mint;
6
7#[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
66pub 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
120pub 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
137pub fn is_major(mint: &Address) -> bool {
139 *mint == SOL || stable_decimals(mint).is_some() || priced_decimals(mint).is_some()
140}
141
142pub fn trades_only_majors(input_mint: &Address, output_mint: &Address) -> bool {
145 is_major(input_mint) && is_major(output_mint)
146}
147
148pub 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
158pub fn parse_mint(raw: &str) -> Address {
164 raw.parse()
165 .expect("leg mints are resolved before a swap becomes reroutable")
166}
167
168pub 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 #[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 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}