import axios from "axios"; import * as bluebird from "bluebird"; import * as fs from "fs"; import * as path from "path"; import * as chalk from 'chalk'; import * as config from "../config"; import { ActionInterface, CheckStepInterface } from "../generic/interface"; import { getChainAssetsPath } from "../generic/repo-structure"; import { Binance } from "../generic/blockchains"; import { readDirSync } from "../generic/filesystem"; import { readJsonFile } from "../generic/json"; import { getChainAssetLogoPath, getChainDenylistPath } from "../generic/repo-structure"; const binanceChain = "binance"; const binanceUrlTokenAssets = config.binanceUrlTokenAssets; let cachedAssets = []; async function retrieveBep2AssetList(): Promise { console.log(`Retrieving token asset infos from: ${binanceUrlTokenAssets}`); const { assetInfoList } = await axios.get(binanceUrlTokenAssets).then(r => r.data); console.log(`Retrieved ${assetInfoList.length} token asset infos`); return assetInfoList } async function retrieveAssets(): Promise { // cache results because of rate limit, used more than once if (cachedAssets.length == 0) { console.log(`Retrieving token infos`); const bep2assets = await axios.get(`${config.binanceDexURL}/v1/tokens?limit=1000`); const bep8assets = await axios.get(`${config.binanceDexURL}/v1/mini/tokens?limit=1000`); cachedAssets = bep2assets.data.concat(bep8assets.data); } console.log(`Using ${cachedAssets.length} assets`); return cachedAssets; } export async function retrieveAssetSymbols(): Promise { const assets = await retrieveAssets(); const symbols = assets.map(({ symbol }) => symbol); return symbols; } function fetchImage(url) { return axios.get(url, { responseType: "stream" }) .then(r => r.data) .catch(err => { throw `Error fetchImage: ${url} ${err.message}`; }); } /// Return: array with images to fetch; {asset, assetImg} export function findImagesToFetch(assetInfoList: unknown[], denylist: string[]): unknown[] { const toFetch: unknown[] = []; console.log(`Checking for asset images to be fetched`); assetInfoList.forEach(({asset, assetImg}) => { process.stdout.write(`.${asset} `); if (assetImg) { if (denylist.indexOf(asset) != -1) { console.log(); console.log(`${asset} is denylisted`); } else { const imagePath = getChainAssetLogoPath(binanceChain, asset); if (!fs.existsSync(imagePath)) { console.log(chalk.red(`Missing image: ${asset}`)); toFetch.push({asset, assetImg}); } } } }); console.log(); console.log(`${toFetch.length} asset image(s) to be fetched`); return toFetch; } async function fetchMissingImages(toFetch: unknown[]): Promise { console.log(`Attempting to fetch ${toFetch.length} asset image(s)`); const fetchedAssets: string[] = []; await bluebird.each(toFetch, async ({ asset, assetImg }) => { if (assetImg) { const imagePath = getChainAssetLogoPath(binanceChain, asset); fs.mkdir(path.dirname(imagePath), err => { if (err && err.code != `EEXIST`) throw err; }); await fetchImage(assetImg).then(buffer => { buffer.pipe(fs.createWriteStream(imagePath)); fetchedAssets.push(asset) console.log(`Fetched image ${asset} ${imagePath} from ${assetImg}`) }); } }); console.log(); return fetchedAssets; } export class BinanceAction implements ActionInterface { getName(): string { return "Binance chain"; } getSanityChecks(): CheckStepInterface[] { return [ { getName: () => { return "Binance chain; assets must exist on chain"}, check: async () => { const errors = []; const tokenSymbols = await retrieveAssetSymbols(); const assets = readDirSync(getChainAssetsPath(Binance)); assets.forEach(asset => { if (!(tokenSymbols.indexOf(asset) >= 0)) { errors.push(`Asset ${asset} missing on chain`); } }); console.log(` ${assets.length} assets checked.`); return [errors, []]; } }, ]; } async update(): Promise { // retrieve missing token images; BEP2 (bep8 not supported) const bep2InfoList = await retrieveBep2AssetList(); const denylist: string[] = readJsonFile(getChainDenylistPath(binanceChain)) as string[]; const toFetch = findImagesToFetch(bep2InfoList, denylist); const fetchedAssets = await fetchMissingImages(toFetch); if (fetchedAssets.length > 0) { console.log(`Fetched ${fetchedAssets.length} asset(s):`); fetchedAssets.forEach(asset => console.log(` ${asset}`)); } } }