token_list.rs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. use fxhash::FxHashMap;
  2. use serde_json::Value;
  3. use crate::{
  4. crypto::{token_id::generate_id2, types::DrkTokenId},
  5. util::NetworkName,
  6. Error, Result,
  7. };
  8. pub const ETH_NATIVE_TOKEN_ID: &str = "0x0000000000000000000000000000000000000000";
  9. #[derive(Debug, Clone)]
  10. pub struct TokenList {
  11. tokens: Vec<Value>,
  12. }
  13. impl TokenList {
  14. pub fn new(data: &[u8]) -> Result<Self> {
  15. let tokenlist: Value = serde_json::from_slice(data)?;
  16. let tokens = tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  17. Ok(Self { tokens })
  18. }
  19. pub fn get_symbols(&self) -> Result<Vec<String>> {
  20. let mut symbols: Vec<String> = Vec::new();
  21. for item in self.tokens.iter() {
  22. let symbol = item["symbol"].as_str().unwrap();
  23. symbols.push(symbol.to_string());
  24. }
  25. Ok(symbols)
  26. }
  27. pub fn search_id(&self, symbol: &str) -> Result<Option<String>> {
  28. for item in self.tokens.iter() {
  29. if item["symbol"] == symbol.to_uppercase() {
  30. let address = item["address"].clone();
  31. let address = address.as_str().ok_or(Error::TokenParseError)?;
  32. return Ok(Some(address.to_string()))
  33. }
  34. }
  35. Ok(None)
  36. }
  37. pub fn search_decimal(&self, symbol: &str) -> Result<Option<usize>> {
  38. for item in self.tokens.iter() {
  39. if item["symbol"] == symbol.to_uppercase() {
  40. let decimals = item["decimals"].clone();
  41. let decimals = decimals.as_u64().ok_or(Error::TokenParseError)?;
  42. let decimals = decimals as usize;
  43. return Ok(Some(decimals))
  44. }
  45. }
  46. Ok(None)
  47. }
  48. }
  49. #[derive(Debug, Clone)]
  50. pub struct DrkTokenList {
  51. pub tokens: FxHashMap<NetworkName, FxHashMap<String, DrkTokenId>>,
  52. }
  53. impl DrkTokenList {
  54. pub fn new(sol_list: &TokenList, eth_list: &TokenList, btc_list: &TokenList) -> Result<Self> {
  55. let sol_symbols = sol_list.get_symbols()?;
  56. let eth_symbols = eth_list.get_symbols()?;
  57. let btc_symbols = btc_list.get_symbols()?;
  58. let sol_tokens: FxHashMap<String, DrkTokenId> = sol_symbols
  59. .iter()
  60. .filter_map(|symbol| {
  61. Self::generate_hash_pair(sol_list, &NetworkName::Solana, symbol).ok()
  62. })
  63. .collect();
  64. let eth_tokens: FxHashMap<String, DrkTokenId> = eth_symbols
  65. .iter()
  66. .filter_map(|symbol| {
  67. Self::generate_hash_pair(eth_list, &NetworkName::Ethereum, symbol).ok()
  68. })
  69. .collect();
  70. let btc_tokens: FxHashMap<String, DrkTokenId> = btc_symbols
  71. .iter()
  72. .filter_map(|symbol| {
  73. Self::generate_hash_pair(btc_list, &NetworkName::Bitcoin, symbol).ok()
  74. })
  75. .collect();
  76. let mut tokens: FxHashMap<NetworkName, FxHashMap<String, DrkTokenId>> =
  77. FxHashMap::default();
  78. tokens.insert(NetworkName::Solana, sol_tokens);
  79. tokens.insert(NetworkName::Ethereum, eth_tokens);
  80. tokens.insert(NetworkName::Bitcoin, btc_tokens);
  81. Ok(Self { tokens })
  82. }
  83. fn generate_hash_pair(
  84. token_list: &TokenList,
  85. network_name: &NetworkName,
  86. symbol: &str,
  87. ) -> Result<(String, DrkTokenId)> {
  88. if let Some(token_id) = &token_list.search_id(symbol)? {
  89. return Ok((symbol.to_string(), generate_id2(token_id, network_name)?))
  90. };
  91. Err(Error::NotSupportedToken)
  92. }
  93. pub fn symbol_from_id(&self, id: &DrkTokenId) -> Result<Option<(NetworkName, String)>> {
  94. for (network, tokens) in self.tokens.iter() {
  95. for (key, val) in tokens.iter() {
  96. if val == id {
  97. return Ok(Some((network.clone(), key.clone())))
  98. }
  99. }
  100. }
  101. Ok(None)
  102. }
  103. }
  104. pub fn assign_id(
  105. network: &NetworkName,
  106. token: &str,
  107. sol_tokenlist: &TokenList,
  108. eth_tokenlist: &TokenList,
  109. btc_tokenlist: &TokenList,
  110. ) -> Result<String> {
  111. match network {
  112. NetworkName::Solana => {
  113. // (== 44) can represent a Solana base58 token mint address
  114. if token.len() == 44 {
  115. Ok(token.to_string())
  116. } else {
  117. let tok_lower = token.to_lowercase();
  118. symbol_to_id(&tok_lower, sol_tokenlist)
  119. }
  120. }
  121. NetworkName::Bitcoin => {
  122. if token.len() == 34 {
  123. Ok(token.to_string())
  124. } else {
  125. let tok_lower = token.to_lowercase();
  126. symbol_to_id(&tok_lower, btc_tokenlist)
  127. }
  128. }
  129. NetworkName::Ethereum => {
  130. // (== 42) can represent a erc20 token mint address
  131. if token.len() == 42 {
  132. Ok(token.to_string())
  133. } else if token == "eth" {
  134. Ok(ETH_NATIVE_TOKEN_ID.to_string())
  135. } else {
  136. let tok_lower = token.to_lowercase();
  137. symbol_to_id(&tok_lower, eth_tokenlist)
  138. }
  139. }
  140. _ => Err(Error::NotSupportedNetwork),
  141. }
  142. }
  143. pub fn symbol_to_id(token: &str, tokenlist: &TokenList) -> Result<String> {
  144. let vec: Vec<char> = token.chars().collect();
  145. let mut counter = 0;
  146. for c in vec {
  147. if c.is_alphabetic() {
  148. counter += 1;
  149. }
  150. }
  151. if counter == token.len() {
  152. if let Some(id) = tokenlist.search_id(token)? {
  153. Ok(id)
  154. } else {
  155. Err(Error::TokenParseError)
  156. }
  157. } else {
  158. Ok(token.to_string())
  159. }
  160. }
  161. #[cfg(test)]
  162. mod tests {
  163. use super::*;
  164. fn _get_sol_tokens() -> Result<TokenList> {
  165. let file_contents = include_bytes!("../../tests/data/solanatokenlisttest.json");
  166. let sol_tokenlist: Value = serde_json::from_slice(file_contents)?;
  167. let tokens = sol_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  168. let sol_tokenlist = TokenList { tokens };
  169. Ok(sol_tokenlist)
  170. }
  171. fn _get_eth_tokens() -> Result<TokenList> {
  172. let file_contents = include_bytes!("../../tests/data/erc20tokenlisttest.json");
  173. let eth_tokenlist: Value = serde_json::from_slice(file_contents)?;
  174. let tokens = eth_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  175. let eth_tokenlist = TokenList { tokens };
  176. Ok(eth_tokenlist)
  177. }
  178. fn _get_btc_tokens() -> Result<TokenList> {
  179. let file_contents = include_bytes!("../../contrib/token/bitcoin_token_list.json");
  180. let btc_tokenlist: Value = serde_json::from_slice(file_contents)?;
  181. let tokens = btc_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  182. let btc_tokenlist = TokenList { tokens };
  183. Ok(btc_tokenlist)
  184. }
  185. #[test]
  186. pub fn test_get_symbols() -> Result<()> {
  187. let tokens = _get_sol_tokens()?;
  188. let symbols = tokens.get_symbols()?;
  189. assert_eq!(symbols.len(), 5);
  190. assert_eq!("MILLI", symbols[0]);
  191. assert_eq!("ZI", symbols[1]);
  192. assert_eq!("SOLA", symbols[2]);
  193. assert_eq!("SOL", symbols[3]);
  194. assert_eq!("USDC", symbols[4]);
  195. Ok(())
  196. }
  197. #[test]
  198. pub fn test_get_id_from_symbols() -> Result<()> {
  199. let tokens = _get_sol_tokens()?;
  200. let symbol = &tokens.get_symbols()?[3];
  201. let id = tokens.search_id(symbol)?;
  202. assert!(id.is_some());
  203. assert_eq!(id.unwrap(), "So11111111111111111111111111111111111111112");
  204. Ok(())
  205. }
  206. #[test]
  207. pub fn test_hashmap() -> Result<()> {
  208. let sol_tokens = _get_sol_tokens()?;
  209. let sol_tokens2 = _get_sol_tokens()?;
  210. let eth_tokens = _get_eth_tokens()?;
  211. let eth_tokens2 = _get_eth_tokens()?;
  212. let btc_tokens = _get_btc_tokens()?;
  213. let btc_tokens2 = _get_btc_tokens()?;
  214. let drk_token = DrkTokenList::new(&sol_tokens, &eth_tokens, &btc_tokens)?;
  215. assert_eq!(drk_token.tokens[&NetworkName::Solana].len(), 5);
  216. assert_eq!(drk_token.tokens[&NetworkName::Ethereum].len(), 3);
  217. assert_eq!(drk_token.tokens[&NetworkName::Bitcoin].len(), 1);
  218. assert_eq!(
  219. drk_token.tokens[&NetworkName::Solana]["SOL"],
  220. generate_id2(&sol_tokens2.search_id("SOL")?.unwrap(), &NetworkName::Solana)?
  221. );
  222. assert_eq!(
  223. drk_token.tokens[&NetworkName::Bitcoin]["BTC"],
  224. generate_id2(&btc_tokens2.search_id("BTC")?.unwrap(), &NetworkName::Bitcoin)?
  225. );
  226. assert_eq!(
  227. drk_token.tokens[&NetworkName::Ethereum]["WBTC"],
  228. generate_id2(&eth_tokens2.search_id("WBTC")?.unwrap(), &NetworkName::Ethereum)?
  229. );
  230. Ok(())
  231. }
  232. }