@mimicry/kaleidoscope
Version:
Kaleidoscope is an NPM package that conveniently aggregates responses from multiple NFT data providers.
219 lines (196 loc) • 7.07 kB
text/typescript
import { Chain, Timeframe } from '../../../../../enums';
import { ContractPointer, NFTCollectionMetadata, NFTCollectionSales, Value } from '../../../../../types';
import { RestfulProvider } from '../../RestfulProvider';
import { NftCollectionDataProvider } from '../NftCollectionDataProvider';
import { numberToValue } from '../../../../../utils/numberToValue';
import { ticksToIOCHLV } from '../../../../../utils/charts/ticksToIOCHLV';
import { batchCandleJSON, IOHLCV } from 'candlestick-convert';
import { chainToBlockchainExplorerHost } from '../../../../../utils/crossChainSupport';
// Docs: https://www.coingecko.com/en/api/documentation
// Pro Docs: https://apiguide.coingecko.com/exclusive-endpoints/for-paid-plan-subscribers
export class CoinGeckoProNonFungible extends RestfulProvider
implements NftCollectionDataProvider {
constructor(_config: any) {
const apiHost = 'https://pro-api.coingecko.com/api/v3/';
super(_config, apiHost);
}
// {host}/nfts/{asset_platform_id}/contract/{contract_address}/market_chart
async getFloorChart(
_contract: ContractPointer,
_timeframe?: Timeframe
): Promise<any> {
const [ _days, _newTimeframe ] = this._getTimeParams(_timeframe);
const host = this.getApiHost();
const chain = this.getBlockchain(_contract.chain);
const uri = `${host}nfts/${chain}/contract/${_contract.address}/market_chart`;
const options = {
searchParams: {
days: _days,
},
headers: {
Accept: 'application/json',
'x-cg-pro-api-key': this.getApiKey(),
},
};
// Get the ticks and convert them to candles
const json: any = await this.gotJson(uri, options);
let iochlv: IOHLCV[] = ticksToIOCHLV(json.floor_price_native);
// 5 minutes is the default timeframe for <=14 days of data
let baseTimeframe: number = 60 * 5;
if (_days === 'max') {
// 1d is the default timeframe for >14 days of data
// We have 1d volume to match to 1d candles
iochlv = this._injectVolumeIntoIOCHLV(iochlv, json.h24_volume_native); // add volume to 1d candles
baseTimeframe = 60 * 60 * 24;
}
// Convert the candles to the requested timeframe
iochlv = batchCandleJSON(iochlv, baseTimeframe, _newTimeframe);
const currencyInfo = this.getCurrencyInfoFromChain(_contract.chain);
const combinedResponse = {
currencyInfo: currencyInfo,
iochlv: iochlv,
// ticks: json,
};
return combinedResponse;
}
private _getTimeParams(_timeframe?: Timeframe): [number | string, number] {
switch (_timeframe) {
case Timeframe.FIVE_MIN:
return [14, 60 * 5];
case Timeframe.FIFTEEN_MIN:
return [14, 60 * 15];
case Timeframe.THIRTY_MIN:
return [14, 60 * 15];
case Timeframe.ONE_HOUR:
return [14, 60 * 60];
case undefined:
case Timeframe.ONE_DAY:
return ["max", 60 * 60 * 24];
case Timeframe.ONE_WEEK:
return ["max", 60 * 60 * 24 * 7];
default:
throw new Error('Invalid timeframe.');
}
}
private _injectVolumeIntoIOCHLV(
_iochlv: IOHLCV[],
_volume: [number, number][]
): IOHLCV[] {
// Create a volume map for quick lookup
const volumeMap = new Map(_volume.map(([time, volume]) => [time, volume]));
// Add volume to the matching IOHLCV data
return _iochlv.map(ohlcv => {
const volume = volumeMap.get(ohlcv.time) || 0;
return {
...ohlcv,
volume: volume
};
});
}
// {host}/nfts/{chain}/contract/{contract_address}
async getFloor(_contract: ContractPointer): Promise<Value> {
const host = this.getApiHost();
const chain = this.getBlockchain(_contract.chain);
const uri = `${host}nfts/${chain}/contract/${_contract.address}`;
const options = {
headers: {
Accept: 'application/json',
'x-cg-pro-api-key': this.getApiKey(),
},
};
const json: any = await this.gotJson(uri, options);
const currencyInfo = this.getCurrencyInfoFromChain(_contract.chain);
return numberToValue(
Number(json.floor_price.native_currency),
currencyInfo
);
}
async getHistoricSales(_contract: ContractPointer): Promise<NFTCollectionSales> {
throw new Error('Method not implemented.');
}
async getMarketCap(_contract: ContractPointer): Promise<Value> {
throw new Error('Method not implemented.');
}
// /nfts/{asset_platform_id}/contract/{contract_address}
async getMetadata(_contract: ContractPointer): Promise<any> {
const host = this.getApiHost();
const chain = this.getBlockchain(_contract.chain);
const uri = `${host}nfts/${chain}/contract/${_contract.address}`;
const options = {
headers: {
Accept: 'application/json',
'x-cg-pro-api-key': this.getApiKey(),
},
};
const json: any = await this.gotJson(uri, options);
const currencyInfo = this.getCurrencyInfoFromChain(_contract.chain);
const metadata: NFTCollectionMetadata = {
contract: _contract,
symbol: json.symbol,
name: json.name,
description: json.description,
collectionSize: Number(json.total_supply),
ownerCount: Number(json.number_of_unique_addresses),
images: {
thumbnail: json.image.small,
},
urls: {
explorer: `${chainToBlockchainExplorerHost(_contract.chain)}/address/${
_contract.address
}`,
website: json.links.homepage,
discord: json.links.discord,
twitter: json.links.twitter,
},
stats: {
currencyInfo: currencyInfo,
floor: {
h24: numberToValue(Number(json.floor_price.native_currency), currencyInfo)
.amount,
h24Change: numberToValue(
Number(json.floor_price_24h_percentage_change.native_currency),
currencyInfo
).amount.decimal,
},
volume: {
h24: numberToValue(Number(json.volume_24h.native_currency), currencyInfo)
.amount,
h24Change: numberToValue(
Number(json.volume_24h_percentage_change.native_currency),
currencyInfo
).amount.decimal,
},
},
};
return metadata;
}
getBlockchain(_chain?: Chain): string {
switch (_chain) {
case undefined:
case Chain.ETHEREUM:
return 'ethereum';
case Chain.BSC:
return 'binance-smart-chain';
case Chain.POLYGON:
return 'polygon-pos';
case Chain.ARBITRUM:
return 'arbitrum-one';
case Chain.SOLANA:
throw new Error(
'Solana is supported by CoinGecko, but not yet implemented.'
);
// return 'solana';
case Chain.OPTIMISM:
return 'optimistic-ethereum';
case Chain.AVALANCHE:
return 'avalanche';
case Chain.KLAYTN:
return 'klay-token';
default:
throw new Error(`${_chain} is not supported by ${this.getName()}.`);
}
}
getName(): string {
return 'CoinGecko Pro';
}
}