use crate::adapters::common::coins;
pub(crate) use coins::{asset_name, resolve_ticker};
const ASSET_ALIASES: &[(&str, &str)] = &[
("BTC", "XBT"),
("DOGE", "XDG"),
("LUNC", "LUNA"),
];
const QUOTE_ASSETS: &[&str] = &[
"USDT", "USDC", "CHF", "AUD", "USD", "EUR", "GBP", "JPY", "CAD", "XBT", "ETH", "DAI",
];
pub(crate) fn kraken_asset(ticker: &str) -> String {
let upper = ticker.trim().to_uppercase();
ASSET_ALIASES
.iter()
.find(|(common, _)| *common == upper)
.map(|(_, kraken)| (*kraken).to_string())
.unwrap_or(upper)
}
pub(crate) fn from_kraken_asset(code: &str) -> String {
let trimmed = match code.len() {
4 if code.starts_with(['X', 'Z']) => &code[1..],
_ => code,
};
ASSET_ALIASES
.iter()
.find(|(_, kraken)| *kraken == trimmed)
.map(|(common, _)| (*common).to_string())
.unwrap_or_else(|| trimmed.to_string())
}
pub(crate) fn pair(base_id: &str, vs_currency: &str) -> String {
format!(
"{}{}",
kraken_asset(&resolve_ticker(base_id)),
kraken_asset(vs_currency)
)
}
pub(crate) fn split_pair(symbol: &str) -> Option<(&str, &str)> {
coins::split_on_quote(symbol, QUOTE_ASSETS)
}
pub(crate) fn parse_market(symbol: &str) -> Option<String> {
let symbol = symbol.trim();
if let Some((base, quote)) = symbol
.split_once(['-', '/', '_'])
.filter(|(b, q)| !b.is_empty() && !q.is_empty())
{
return Some(pair(base, quote));
}
let upper = symbol.to_uppercase();
let (base, quote) = split_pair(&upper)?;
Some(format!("{}{}", kraken_asset(base), kraken_asset(quote)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kraken_keeps_its_own_codes_for_btc_and_doge() {
assert_eq!(kraken_asset("BTC"), "XBT");
assert_eq!(kraken_asset("doge"), "XDG");
assert_eq!(kraken_asset("ETH"), "ETH");
assert_eq!(kraken_asset("usd"), "USD");
}
#[test]
fn legacy_class_prefixes_are_stripped_on_the_way_back() {
assert_eq!(from_kraken_asset("XXBT"), "BTC");
assert_eq!(from_kraken_asset("ZUSD"), "USD");
assert_eq!(from_kraken_asset("XETH"), "ETH");
assert_eq!(from_kraken_asset("SOL"), "SOL");
assert_eq!(from_kraken_asset("ADA"), "ADA");
assert_eq!(from_kraken_asset("AAVE"), "AAVE");
}
#[test]
fn pairs_use_kraken_codes_on_both_sides() {
assert_eq!(pair("bitcoin", "usd"), "XBTUSD");
assert_eq!(pair("dogecoin", "eur"), "XDGEUR");
assert_eq!(pair("SOL", "USD"), "SOLUSD");
}
#[test]
fn separated_and_concatenated_spellings_both_parse() {
assert_eq!(parse_market("BTC-USD").as_deref(), Some("XBTUSD"));
assert_eq!(parse_market("bitcoin/eur").as_deref(), Some("XBTEUR"));
assert_eq!(parse_market("SOLUSD").as_deref(), Some("SOLUSD"));
assert_eq!(parse_market("XBTUSD").as_deref(), Some("XBTUSD"));
assert_eq!(parse_market("BTC"), None);
}
#[test]
fn quote_assets_split_longest_first() {
assert_eq!(split_pair("XBTUSDT"), Some(("XBT", "USDT")));
assert_eq!(split_pair("SOLUSD"), Some(("SOL", "USD")));
assert_eq!(split_pair("USD"), None);
}
}