@mimicry/kaleidoscope
Version:
Kaleidoscope is an NPM package that conveniently aggregates responses from multiple NFT data providers.
180 lines (166 loc) • 5.96 kB
text/typescript
import {
ContractPointer,
NFTCollectionMetadata,
NFTCollectionSales,
Value,
} from '../../../../../types';
import { RestfulProvider } from '../../RestfulProvider';
import { NftCollectionDataProvider } from '../NftCollectionDataProvider';
import { numberToValue } from '../../../../../utils/numberToValue';
import { chainToBlockchainExplorerHost } from '../../../../../utils/crossChainSupport/chainToBlockchainExplorerHost';
import { Chain, Timeframe } from '../../../../../enums';
// Docs: https://docs.reservoir.tools/
export class Reservoir extends RestfulProvider
implements NftCollectionDataProvider {
constructor(_config: any) {
const apiHost = 'https://api.reservoir.tools/';
super(_config, apiHost);
}
// @see https://docs.reservoir.tools/reference/getstatsv2
// https://api.reservoir.tools/stats/v2
async getFloor(_contract: ContractPointer): Promise<Value> {
const host = this.getHost(_contract.chain);
const uri = `${host}stats/v2/`;
const options = {
searchParams: {
collection: _contract.address,
},
headers: {
Accept: 'application/json',
'x-api-key': this.getApiKey(),
},
};
// Reservoir doesn't use the last lowest sale price for floor, but rather
// the average of the top bid and floor ask.
const json: any = await this.gotJson(uri, options);
const ask = Number(json.stats.market.floorAsk.price.amount.decimal);
const bid = Number(json.stats.market.topBid.price.amount.decimal);
const floor: number = (ask + bid) / 2;
const currencyInfo = this.getCurrencyInfoFromChain(_contract.chain);
return numberToValue(floor, currencyInfo);
}
async getFloorChart(
_contract: ContractPointer,
_timeframe?: Timeframe
): Promise<any> {
throw new Error('Method not implemented.');
}
async getHistoricSales(
_contract: ContractPointer
): Promise<NFTCollectionSales> {
throw new Error('Method not implemented.');
}
async getMarketCap(_contract: ContractPointer): Promise<Value> {
throw new Error('Method not implemented.');
}
// https://docs.reservoir.tools/reference/getcollectionsv5
// https://api.reservoir.tools/collections/v5?id={contractAddress}
async getMetadata(_contract: ContractPointer): Promise<any> {
const host = this.getHost(_contract.chain);
const uri = `${host}collections/v5`;
const options = {
searchParams: {
id: _contract.address,
},
headers: {
Accept: 'application/json',
'x-api-key': this.getApiKey(),
},
};
const json: any = await this.gotJson(uri, options);
const collection = json.collections[0];
const currencyInfo = this.getCurrencyInfoFromChain(_contract.chain);
const metadata: NFTCollectionMetadata = {
contract: _contract,
name: collection.name,
description: collection.description,
createdAt: collection.createdAt,
openseaVerificationStatus:
collection.openseaVerificationStatus === 'verified' ? true : false,
openseaSlug: collection.slug,
collectionSize: Number(collection.tokenCount),
onSaleCount: Number(collection.onSaleCount),
ownerCount: Number(collection.ownerCount),
contractType: collection.contractKind,
images: {
thumbnail: collection.image,
banner: collection.banner,
samples: collection.sampleImages,
},
urls: {
explorer: `${chainToBlockchainExplorerHost(_contract.chain)}/address/${
_contract.address
}`,
website: collection.externalUrl,
discord: collection.discordUrl,
twitter: `https://twitter.com/${collection.twitterUsername}`,
},
stats: {
currencyInfo: currencyInfo,
floor: {
h24: numberToValue(Number(collection.floorSale['1day']), currencyInfo)
.amount,
h24Change: numberToValue(
Number(collection.floorSaleChange['1day']),
currencyInfo
).amount.decimal,
d7: numberToValue(Number(collection.floorSale['7day']), currencyInfo)
.amount,
d7Change: numberToValue(
Number(collection.floorSaleChange['7day']),
currencyInfo
).amount.decimal,
d30: numberToValue(
Number(collection.floorSale['30day']),
currencyInfo
).amount,
d30Change: numberToValue(
Number(collection.floorSaleChange['30day']),
currencyInfo
).amount.decimal,
},
volume: {
h24: numberToValue(Number(collection.volume['1day']), currencyInfo)
.amount,
h24Change: numberToValue(
Number(collection.volumeChange['1day']),
currencyInfo
).amount.decimal,
d7: numberToValue(Number(collection.volume['7day']), currencyInfo)
.amount,
d7Change: numberToValue(
Number(collection.volumeChange['7day']),
currencyInfo
).amount.decimal,
d30: numberToValue(Number(collection.volume['30day']), currencyInfo)
.amount,
d30Change: numberToValue(
Number(collection.volumeChange['30day']),
currencyInfo
).amount.decimal,
},
},
};
return metadata;
}
getHost(_chain?: Chain): string {
switch (_chain) {
case undefined:
case Chain.ETHEREUM:
return this.getApiHost();
case Chain.POLYGON:
return 'https://api-polygon.reservoir.tools/';
case Chain.BSC:
return 'https://api-bsc.reservoir.tools/';
case Chain.ARBITRUM:
return 'https://api-arbitrum.reservoir.tools/';
case Chain.OPTIMISM:
return 'https://api-optimism.reservoir.tools/';
default:
throw new Error(`${_chain} is not supported by ${this.getName()}.`);
}
}
getName(): string {
return 'Reservoir';
}
}