import { ITokenBase } from '@dequanto/models/IToken'; import { TResultAsync } from '@dequanto/models/TResult'; import { $config } from '@dequanto/utils/$config'; import { $date } from '@dequanto/utils/$date'; import { IOracle, IOracleResult, ISwapOptions } from '../IOracle'; import { Web3ClientFactory } from '@dequanto/clients/Web3ClientFactory'; import { $block } from '@dequanto/utils/$block'; import { $require } from '@dequanto/utils/$require'; import { TPCoingecko } from '@dequanto/tokens/TokenProviders/TPCoingecko'; import { $http } from '@dequanto/utils/$http'; export class CoingeckoOracle implements IOracle { private config = $config.get<{ key: string, root: string, rateLimit: string }>('oracles.coingecko'); async getPrice(token: ITokenBase, opts?: ISwapOptions): TResultAsync { $require.notNull(this.config?.root, `Coingecko api root path not found in config`); opts ??= {}; let coingeckoProvider = new TPCoingecko(); let coingeckoToken = await coingeckoProvider.find(token); if (coingeckoToken == null) { return { error: new Error(`NOT_FOUND: Token ${token.symbol} not found`) } } let date = opts.date; if (date == null && opts.block) { let platform = token.platform ?? 'eth'; let client = Web3ClientFactory.get(platform); let block = await client.getBlock(opts.block); date = $block.getDate(block); } let priceInfo: { price: number, date: Date }; if (date) { priceInfo = await this.getHistorical(coingeckoToken.id, date); } else { priceInfo = await this.getCurrent(coingeckoToken.id); } return { result: { quote: { symbol: 'USD' }, price: priceInfo.price, date: priceInfo.date } }; } private async getCurrent (tokenId: string) { let path = `/simple/price`; let query = { ids: tokenId, vs_currencies:'usd', include_market_cap: 'false', include_24hr_vol: 'false', include_24hr_change:'false', include_last_updated_at:'true', precision:'full' }; let resp = await this.fetch(path, query); let data = resp[tokenId]; return { price: data.usd, date: new Date(data.last_updated_at * 1000), }; } private async getHistorical (tokenId: string, date: Date) { let dateFormat = 'dd-MM-yyyy'; let path = `/coins/${tokenId}/history`; let query = { date: $date.format(date, dateFormat), localization: 'false' } let data = await this.fetch(path, query); let price = data.market_data.current_price.usd; return { price, date: date } } private async fetch (path: string, query?: { [key: string]: string }) { let headers = {}; if (this.config.key != null) { headers['x-cg-pro-api-key'] = this.config.key; } let resp = await $http.get({ url: `${this.config.root}${path}`, params: query, headers }); return resp.data; } } interface ICoingeckoHistoryPrice { id: string symbol: string name: string image: { thumb } market_data: { current_price: { usd: number } } } interface ICoingeckoCurrentPrice { [id: string]: { usd: number last_updated_at: number } }