diff --git a/core/bin/zksync_api/src/fee_ticker/ticker_api/coingecko.rs b/core/bin/zksync_api/src/fee_ticker/ticker_api/coingecko.rs index 3b7b2d45b..ea7b1b3f0 100644 --- a/core/bin/zksync_api/src/fee_ticker/ticker_api/coingecko.rs +++ b/core/bin/zksync_api/src/fee_ticker/ticker_api/coingecko.rs @@ -6,10 +6,12 @@ use num::rational::Ratio; use num::BigUint; use reqwest::Url; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::fs::File; use std::str::FromStr; use std::time::Instant; -use zksync_types::{Address, Token, TokenPrice}; +use std::{collections::HashMap, path::Path}; +use zksync_types::Token; +use zksync_types::{network::Network, Address, TokenPrice}; use zksync_utils::{remove_prefix, UnsignedRatioSerializeAsDecimal}; #[derive(Debug, Clone)] @@ -17,10 +19,56 @@ pub struct CoinGeckoAPI { base_url: Url, client: reqwest::Client, token_ids: HashMap, + tnet_to_mnet_address_mapping: HashMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TokenInfo { + /// Address (prefixed with 0x) + pub address: Address, + /// Powers of 10 in 1.0 token (18 for default RBTC-like tokens) + pub decimals: u8, + /// Token symbol + pub symbol: String, + pub name: String, +} + +impl TokenInfo { + pub fn from_json(reader: R) -> anyhow::Result> { + let tokens: Vec = serde_json::from_reader(reader)?; + Ok(tokens) + } } impl CoinGeckoAPI { pub async fn new(client: reqwest::Client, base_url: Url) -> anyhow::Result { + // If network is testner + let zksync_config = zksync_config::ZkSyncConfig::from_env(); + let network = zksync_config.chain.eth.network; + + // Get tokens from etc/tokens/testnet.json and etc/tokens/mainnet.json + let address_mapping = if network == Network::Testnet { + let testnet_tokens_file = Path::new("etc/tokens/testnet.json"); + let mainnet_tokens_file = Path::new("etc/tokens/mainnet.json"); + + let testnet_tokens_file = File::open(testnet_tokens_file)?; + let mainnet_tokens_file = File::open(mainnet_tokens_file)?; + + let testnet_tokens: Vec = TokenInfo::from_json(testnet_tokens_file)?; + let mainnet_tokens: Vec = TokenInfo::from_json(mainnet_tokens_file)?; + + let mut address_mapping = HashMap::new(); + + for (testnet_token, mainnet_token) in testnet_tokens.iter().zip(mainnet_tokens.iter()) { + address_mapping.insert(testnet_token.address, mainnet_token.address); + } + + address_mapping + } else { + HashMap::new() + }; + // create a testnet to mainnet address mapping + let token_list_url = base_url .join("api/v3/coins/list?include_platform=true") .expect("failed to join URL path"); @@ -50,6 +98,7 @@ impl CoinGeckoAPI { base_url, client, token_ids, + tnet_to_mnet_address_mapping: address_mapping, }) } } @@ -57,11 +106,25 @@ impl CoinGeckoAPI { #[async_trait] impl TokenPriceAPI for CoinGeckoAPI { async fn get_price(&self, token: &Token) -> Result { + let token_address = { + let zksync_config = zksync_config::ZkSyncConfig::from_env(); + let network = zksync_config.chain.eth.network; + + if network == Network::Testnet { + self.tnet_to_mnet_address_mapping + .get(&token.address) + .copied() + .unwrap_or(token.address) + } else { + token.address + } + }; + let start = Instant::now(); - let token_id = self.token_ids.get(&token.address).ok_or_else(|| { + let token_id = self.token_ids.get(&token_address).ok_or_else(|| { PriceError::token_not_found(format!( "Token '{}, {:?}' is not listed on CoinGecko", - token.symbol, token.address + token.symbol, token_address )) })?; diff --git a/etc/tokens/testnet.json b/etc/tokens/testnet.json index 6ccbcd0e9..54953060f 100644 --- a/etc/tokens/testnet.json +++ b/etc/tokens/testnet.json @@ -2,13 +2,13 @@ { "address": "0x19f64674D8a5b4e652319F5e239EFd3bc969a1FE", "decimals": 18, - "symbol": "RIF", + "symbol": "TRIF", "name": "RSK Infrastructure Framework" }, { "address": "0xC3De9f38581F83e281F260D0ddBAac0E102Ff9F8", "decimals": 18, - "symbol": "rDOC", + "symbol": "RDOC", "name": "RIF Dollar on Chain" } ]