import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { combineLatest, Observable } from 'rxjs'; import { map, timeout } from 'rxjs/operators'; import { environment } from '../../environments/environment'; export type Price = { usd: number }; export interface UsdPrice { [tokenAddress: string]: Price; } @Injectable({ providedIn: 'root' }) export class CoinGeckoService { private endpoint = 'https://api.coingecko.com/api/v3'; private ethUsdPriceEndpoint = `${ this.endpoint }/simple/price?ids=ethereum&vs_currencies=usd`; constructor( private httpClient: HttpClient, ) { } public getUSDPrices(tokenAddresses: string[]): Observable<UsdPrice> { const ethAddr = environment.ETH_ADDRESS.toLowerCase(); const ethIndex = tokenAddresses.indexOf(ethAddr); if (ethIndex === -1) { const url = this.endpoint + `/simple/token_price/ethereum?contract_addresses=${ tokenAddresses.join(',') }&vs_currencies=usd`; return this.httpClient.get<UsdPrice>(url).pipe( timeout(1000) ); } if (tokenAddresses.length === 1) { return this.getEthUsdPrice(); } const _tokenAddresses = tokenAddresses.filter((_, i) => i !== ethIndex); const url = this.endpoint + `/simple/token_price/ethereum?contract_addresses=${ _tokenAddresses.join(',') }&vs_currencies=usd`; const tokensReq$ = this.httpClient.get<UsdPrice>(url).pipe( timeout(1000) ); const ethReq$ = this.getEthUsdPrice(); return combineLatest([tokensReq$, ethReq$]).pipe( map(([tokensPrices, ethPrice]) => { tokensPrices[ethAddr] = ethPrice[ethAddr]; return tokensPrices; }) ); } public getEthUsdPrice(): Observable<UsdPrice> { return this.httpClient.get(this.ethUsdPriceEndpoint).pipe( timeout(1000), map((price) => { const priceObj = {}; const ethAddr = environment.ETH_ADDRESS.toLowerCase(); priceObj[ethAddr] = price['ethereum']; return priceObj; }) ); } public getUSDPrice(tokenAddress: string): Observable<number | never> { const addr = tokenAddress.toLowerCase(); return this.getUSDPrices([addr]).pipe( map((price: UsdPrice) => price[addr].usd) ); } }