token_list.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. use std::collections::HashMap;
  2. use serde_json::Value;
  3. use crate::{
  4. types::DrkTokenId,
  5. util::{generate_id, NetworkName},
  6. Error, Result,
  7. };
  8. #[derive(Debug, Clone)]
  9. pub struct TokenList {
  10. tokens: Vec<Value>,
  11. }
  12. impl TokenList {
  13. pub fn new(data: &[u8]) -> Result<Self> {
  14. let tokenlist: Value = serde_json::from_slice(data)?;
  15. let tokens = tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  16. Ok(Self { tokens })
  17. }
  18. pub fn get_symbols(&self) -> Result<Vec<String>> {
  19. let mut symbols: Vec<String> = Vec::new();
  20. for item in self.tokens.iter() {
  21. let symbol = item["symbol"].as_str().unwrap();
  22. symbols.push(symbol.to_string());
  23. }
  24. Ok(symbols)
  25. }
  26. pub fn search_id(&self, symbol: &str) -> Result<Option<String>> {
  27. for item in self.tokens.iter() {
  28. if item["symbol"] == symbol.to_uppercase() {
  29. let address = item["address"].clone();
  30. let address = address.as_str().ok_or(Error::TokenParseError)?;
  31. return Ok(Some(address.to_string()))
  32. }
  33. }
  34. Ok(None)
  35. }
  36. pub fn search_decimal(&self, symbol: &str) -> Result<Option<usize>> {
  37. for item in self.tokens.iter() {
  38. if item["symbol"] == symbol.to_uppercase() {
  39. let decimals = item["decimals"].clone();
  40. let decimals = decimals.as_u64().ok_or(Error::TokenParseError)?;
  41. let decimals = decimals as usize;
  42. return Ok(Some(decimals))
  43. }
  44. }
  45. Ok(None)
  46. }
  47. }
  48. #[derive(Debug, Clone)]
  49. pub struct DrkTokenList {
  50. pub tokens: HashMap<NetworkName, HashMap<String, DrkTokenId>>,
  51. }
  52. impl DrkTokenList {
  53. pub fn new(sol_list: &TokenList, eth_list: &TokenList, btc_list: &TokenList) -> Result<Self> {
  54. let sol_symbols = sol_list.get_symbols()?;
  55. let eth_symbols = eth_list.get_symbols()?;
  56. let btc_symbols = btc_list.get_symbols()?;
  57. let sol_tokens: HashMap<String, DrkTokenId> = sol_symbols
  58. .iter()
  59. .filter_map(|symbol| {
  60. Self::generate_hash_pair(sol_list, &NetworkName::Solana, symbol).ok()
  61. })
  62. .collect();
  63. let eth_tokens: HashMap<String, DrkTokenId> = eth_symbols
  64. .iter()
  65. .filter_map(|symbol| {
  66. Self::generate_hash_pair(eth_list, &NetworkName::Ethereum, symbol).ok()
  67. })
  68. .collect();
  69. let btc_tokens: HashMap<String, DrkTokenId> = btc_symbols
  70. .iter()
  71. .filter_map(|symbol| {
  72. Self::generate_hash_pair(btc_list, &NetworkName::Bitcoin, symbol).ok()
  73. })
  74. .collect();
  75. let tokens: HashMap<NetworkName, HashMap<String, DrkTokenId>> = HashMap::from([
  76. (NetworkName::Solana, sol_tokens),
  77. (NetworkName::Ethereum, eth_tokens),
  78. (NetworkName::Bitcoin, btc_tokens),
  79. ]);
  80. Ok(Self { tokens })
  81. }
  82. fn generate_hash_pair(
  83. token_list: &TokenList,
  84. network_name: &NetworkName,
  85. symbol: &str,
  86. ) -> Result<(String, DrkTokenId)> {
  87. if let Some(token_id) = &token_list.search_id(symbol)? {
  88. return Ok((symbol.to_string(), generate_id(token_id, network_name)?))
  89. };
  90. Err(Error::NotSupportedToken)
  91. }
  92. pub fn symbol_from_id(&self, id: &DrkTokenId) -> Result<Option<(NetworkName, String)>> {
  93. for (network, tokens) in self.tokens.iter() {
  94. for (key, val) in tokens.iter() {
  95. if val == id {
  96. return Ok(Some((network.clone(), key.clone())))
  97. }
  98. }
  99. }
  100. Ok(None)
  101. }
  102. }
  103. #[cfg(test)]
  104. mod tests {
  105. use super::*;
  106. use crate::{
  107. util::{DrkTokenList, TokenList},
  108. Result,
  109. };
  110. fn _get_sol_tokens() -> Result<TokenList> {
  111. let file_contents = include_bytes!("../../testdata/solanatokenlisttest.json");
  112. let sol_tokenlist: Value = serde_json::from_slice(file_contents)?;
  113. let tokens = sol_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  114. let sol_tokenlist = TokenList { tokens };
  115. Ok(sol_tokenlist)
  116. }
  117. fn _get_eth_tokens() -> Result<TokenList> {
  118. let file_contents = include_bytes!("../../testdata/erc20tokenlisttest.json");
  119. let eth_tokenlist: Value = serde_json::from_slice(file_contents)?;
  120. let tokens = eth_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  121. let eth_tokenlist = TokenList { tokens };
  122. Ok(eth_tokenlist)
  123. }
  124. fn _get_btc_tokens() -> Result<TokenList> {
  125. let file_contents = include_bytes!("../../token/bitcoin_token_list.json");
  126. let btc_tokenlist: Value = serde_json::from_slice(file_contents)?;
  127. let tokens = btc_tokenlist["tokens"].as_array().ok_or(Error::TokenParseError)?.clone();
  128. let btc_tokenlist = TokenList { tokens };
  129. Ok(btc_tokenlist)
  130. }
  131. #[test]
  132. pub fn test_get_symbols() -> Result<()> {
  133. let tokens = _get_sol_tokens()?;
  134. let symbols = tokens.get_symbols()?;
  135. assert_eq!(symbols.len(), 5);
  136. assert_eq!("MILLI", symbols[0]);
  137. assert_eq!("ZI", symbols[1]);
  138. assert_eq!("SOLA", symbols[2]);
  139. assert_eq!("SOL", symbols[3]);
  140. assert_eq!("USDC", symbols[4]);
  141. Ok(())
  142. }
  143. #[test]
  144. pub fn test_get_id_from_symbols() -> Result<()> {
  145. let tokens = _get_sol_tokens()?;
  146. let symbol = &tokens.get_symbols()?[3];
  147. let id = tokens.search_id(symbol)?;
  148. assert!(id.is_some());
  149. assert_eq!(id.unwrap(), "So11111111111111111111111111111111111111112");
  150. Ok(())
  151. }
  152. #[test]
  153. pub fn test_hashmap() -> Result<()> {
  154. let sol_tokens = _get_sol_tokens()?;
  155. let sol_tokens2 = _get_sol_tokens()?;
  156. let eth_tokens = _get_eth_tokens()?;
  157. let eth_tokens2 = _get_eth_tokens()?;
  158. let btc_tokens = _get_btc_tokens()?;
  159. let btc_tokens2 = _get_btc_tokens()?;
  160. let drk_token = DrkTokenList::new(&sol_tokens, &eth_tokens, &btc_tokens)?;
  161. assert_eq!(drk_token.tokens[&NetworkName::Solana].len(), 5);
  162. assert_eq!(drk_token.tokens[&NetworkName::Ethereum].len(), 3);
  163. assert_eq!(drk_token.tokens[&NetworkName::Bitcoin].len(), 1);
  164. assert_eq!(
  165. drk_token.tokens[&NetworkName::Solana]["SOL"],
  166. generate_id(&sol_tokens2.search_id("SOL")?.unwrap(), &NetworkName::Solana)?
  167. );
  168. assert_eq!(
  169. drk_token.tokens[&NetworkName::Bitcoin]["BTC"],
  170. generate_id(&btc_tokens2.search_id("BTC")?.unwrap(), &NetworkName::Bitcoin)?
  171. );
  172. assert_eq!(
  173. drk_token.tokens[&NetworkName::Ethereum]["WBTC"],
  174. generate_id(&eth_tokens2.search_id("WBTC")?.unwrap(), &NetworkName::Ethereum)?
  175. );
  176. Ok(())
  177. }
  178. }