trustwallet-assets/script/generic/subgraph.ts
Adam R 88f26b09d1
[Internal] Retrieve trading pairs from PancakeSwap (#5355)
* Retrieve trading pairs from PancakeSwap.

* Retrieve trading pairs from Uniswap.

* Add doc link

* Lint fix

* Include decimals

* Move binance tokenlist generation to BinanceAction.

* Generate smartChain tokenlist, from base and pancakeswap pairs.

* Fix array types in tokenlists.ts

* Common writeToFile method, sort.

* Type fixes

* Revert tokenlist.json to master

* Include pairs with allowlisted coins only.

* Move assetID functions to common asset.ts

* Use common assetID functions.

* Use dynamic generation time.

* Keep constant generation time; Version in tokenlist.

* Count additions

* Update tokenlist version and timestamp if needed before save.

* No counting is needed in addXxxIfNeeded() functions.

* Tokenlist timestamp: take over if previous

* Update ethereum tokenlist with pairs from Uniswap.

* Increase query limit to 400 pairs.

* iDecimals always number, add decimal fields to Eth also.

* Include only pairs with primary tokens, add pair info to primary token only.

* Update base tokenlist (BSC) to current full list, in order not to remove any coins.

* Update base tokenlist (ETH) to current full list, in order not to remove any coins.

* Move out pair checks into common code.

* Add checks for volume and txCount.

* Assets tokenlist: Adjust token names for added tokens.

* Move parameters to central config.ts

* Nicer query string, compact

* Prevent change if subgraph API fails

* Reduce max limit in Pancakeswap query, with 400 often times out.

* Stricter error handling

* Reduce code duplication.

* Minor comment

* Display number of original tokens

* Lint fix

Co-authored-by: Catenocrypt <catenocrypt@users.noreply.github.com>
2021-01-29 07:45:43 +01:00

105 lines
3.9 KiB
TypeScript

// Interfacing with TheGraph subgraph APIs
import axios from "axios";
import { getChainAssetLogoPath } from "../generic/repo-structure";
import { isPathExistsSync } from "../generic/filesystem";
export interface TokenInfo {
id: string;
symbol: string;
name: string;
decimals: number;
}
export interface PairInfo {
id: string;
reserveUSD: number;
volumeUSD: number;
txCount: number;
token0: TokenInfo;
token1: TokenInfo;
}
export async function getTradingPairs(apiUrl: string, subgraphQuery: string): Promise<unknown[]> {
// compact the query string
const compactQuery = subgraphQuery.replace(/(?:\r\n|\r|\n)/g, ' ').replace(/\s[\s]+/g, ' ');
const postData = '{"operationName":"pairs", "variables":{}, "query":"' + compactQuery + '"}';
console.log(`Retrieving trading pair infos from: ${apiUrl}`);
try {
const result = await axios.post(apiUrl, postData).then(r => r.data);
if (!result || !result.data || !result.data.pairs) {
throw `Unexpected result: ${result}`;
}
const pairs = result.data.pairs;
console.log(`Retrieved ${pairs.length} trading pair infos`);
return pairs;
} catch (err) {
console.log("Exception from graph api:", err, apiUrl, JSON.stringify(postData));
throw err;
}
}
function checkBSCTokenExists(id: string, chainName: string, tokenAllowlist: string[]): boolean {
const logoPath = getChainAssetLogoPath(chainName, id);
if (!isPathExistsSync(logoPath)) {
return false;
}
if (tokenAllowlist.find(t => (id.toLowerCase() === t.toLowerCase())) === undefined) {
//console.log(`Token not found in allowlist, ${id}`);
return false;
}
return true;
}
function isTokenPrimary(token: TokenInfo, primaryTokens: string[]): boolean {
return (primaryTokens.find(t => (t === token.symbol.toUpperCase())) != undefined);
}
// check which token of the pair is the primary token, 1st, or 2nd, or 0 for none
export function primaryTokenIndex(pair: PairInfo, primaryTokens: string[]): number {
if (isTokenPrimary(pair.token0, primaryTokens)) {
return 1;
}
if (isTokenPrimary(pair.token1, primaryTokens)) {
return 2;
}
return 0;
}
// Verify a trading pair, whether we support the tokens, has enough liquidity, etc.
export function checkTradingPair(pair: PairInfo, chainName: string,
minLiquidity: number, minVol24: number, minTxCount24: number,
tokenAllowlist: string[], primaryTokens: string[]
): boolean {
if (!pair.id || !pair.reserveUSD || !pair.volumeUSD || !pair.txCount || !pair.token0 || !pair.token1) {
return false;
}
if (pair.reserveUSD < minLiquidity) {
console.log("pair with low liquidity:", pair.token0.symbol, "--", pair.token1.symbol, " ", Math.round(pair.reserveUSD));
return false;
}
if (pair.volumeUSD < minVol24) {
console.log("pair with low volume:", pair.token0.symbol, "--", pair.token1.symbol, " ", Math.round(pair.volumeUSD));
return false;
}
if (pair.txCount < minTxCount24) {
console.log("pair with low tx count:", pair.token0.symbol, "--", pair.token1.symbol, " ", pair.txCount);
return false;
}
if (!checkBSCTokenExists(pair.token0.id, chainName, tokenAllowlist)) {
console.log("pair with unsupported 1st coin:", pair.token0.symbol, "--", pair.token1.symbol);
return false;
}
if (!checkBSCTokenExists(pair.token1.id, chainName, tokenAllowlist)) {
console.log("pair with unsupported 2nd coin:", pair.token0.symbol, "--", pair.token1.symbol);
return false;
}
if (primaryTokenIndex(pair, primaryTokens) == 0) {
console.log("pair with no primary coin:", pair.token0.symbol, "--", pair.token1.symbol);
return false;
}
//console.log("pair:", pair.token0.symbol, "--", pair.token1.symbol, " ", pair.reserveUSD);
return true;
}