2020-10-19 22:51:47 +00:00
import {
allChains ,
getChainAssetsList ,
getChainAssetsPath ,
2021-06-25 09:12:11 +00:00
getChainAssetInfoPath ,
getChainInfoPath ,
getChainCoinInfoPath
2020-10-19 22:51:47 +00:00
} from "./repo-structure" ;
2021-03-01 14:46:10 +00:00
import { isPathExistsSync } from "./filesystem" ;
2020-10-19 22:51:47 +00:00
import { arrayDiff } from "./types" ;
2021-03-01 14:46:10 +00:00
import { isValidJSON , readJsonFile , writeJsonFile } from "../generic/json" ;
2020-10-19 22:51:47 +00:00
import { ActionInterface , CheckStepInterface } from "../generic/interface" ;
2020-12-07 10:56:44 +00:00
import { CoinType } from "@trustwallet/wallet-core" ;
2021-03-17 10:51:03 +00:00
import { isValidStatusValue } from "../generic/status-values" ;
2021-03-17 23:27:21 +00:00
import { isValidTagValues } from "../generic/tag-values" ;
2020-10-19 22:51:47 +00:00
import * as bluebird from "bluebird" ;
2021-06-25 09:12:11 +00:00
const requiredKeysCoin = [ "name" , "type" , "symbol" , "decimals" , "description" , "website" , "explorer" , "status" ] ;
const requiredKeysToken = [ . . . requiredKeysCoin , "id" ] ;
2020-10-19 22:51:47 +00:00
2021-06-24 08:58:38 +00:00
// Supported keys in links, and their mandatory prefix
const linksKeys = {
//"explorer": "",
"github" : "https://github.com/" ,
"whitepaper" : "" ,
"twitter" : "https://twitter.com/" ,
"telegram" : "https://t.me/" ,
"telegram_news" : "https://t.me/" , // read-only announcement channel
"medium" : "" , // url contains 'medium.com'
"discord" : "https://discord.com/" ,
2021-06-26 20:02:27 +00:00
"reddit" : "https://reddit.com/" ,
2021-06-24 08:58:38 +00:00
"facebook" : "https://facebook.com/" ,
"youtube" : "https://youtube.com/" ,
"coinmarketcap" : "https://coinmarketcap.com/" ,
"coingecko" : "https://coingecko.com/" ,
"blog" : "" , // blog, other than medium
"forum" : "" , // community site
"docs" : "" ,
"source_code" : "" // other than github
} ;
const linksKeysString = Object . keys ( linksKeys ) . reduce ( function ( agg , item ) { return agg + item + "," ; } , '' ) ;
const linksMediumContains = 'medium.com' ;
2021-06-25 09:12:11 +00:00
function isAssetInfoHasAllKeys ( info : unknown , path : string , isCoin : boolean ) : [ boolean , string ] {
2020-10-19 22:51:47 +00:00
const infoKeys = Object . keys ( info ) ;
2021-06-25 09:12:11 +00:00
const requiredKeys = isCoin ? requiredKeysCoin : requiredKeysToken ;
2020-10-19 22:51:47 +00:00
const hasAllKeys = requiredKeys . every ( k = > Object . prototype . hasOwnProperty . call ( info , k ) ) ;
2021-02-04 16:47:14 +00:00
return [ hasAllKeys , ` Info at path ' ${ path } ' missing next key(s): ${ arrayDiff ( requiredKeys , infoKeys ) } ` ] ;
}
2020-10-19 22:51:47 +00:00
2021-03-01 14:46:10 +00:00
// return error, warning, and fixed into if applicable
2021-06-25 09:12:11 +00:00
function isAssetInfoValid ( info : unknown , path : string , address : string , chain : string , isCoin : boolean , checkOnly : boolean ) : [ string , string , unknown ? ] {
2021-12-10 13:58:39 +00:00
let fixedInfo : unknown | null = null ;
const isKeys1CorrectType =
2021-02-04 16:47:14 +00:00
typeof info [ 'name' ] === "string" && info [ 'name' ] !== "" &&
typeof info [ 'type' ] === "string" && info [ 'type' ] !== "" &&
typeof info [ 'symbol' ] === "string" && info [ 'symbol' ] !== "" &&
2021-02-08 10:38:26 +00:00
typeof info [ 'decimals' ] === "number" && //(info['description'] === "-" || info['decimals'] !== 0) &&
2021-06-25 09:12:11 +00:00
typeof info [ 'status' ] === "string" && info [ 'status' ] !== ""
2021-02-05 10:59:21 +00:00
;
2021-02-04 16:47:14 +00:00
if ( ! isKeys1CorrectType ) {
2021-06-25 09:12:11 +00:00
return [ ` Field missing or invalid; name ' ${ info [ 'name' ] } ' type ' ${ info [ 'type' ] } ' symbol ' ${ info [ 'symbol' ] } ' decimals ' ${ info [ 'decimals' ] } ' ${ path } ` , "" , fixedInfo ] ;
}
if ( ! isCoin ) {
const isIdKeyCorrectType = typeof info [ 'id' ] === "string" && info [ 'id' ] !== "" ;
if ( ! isIdKeyCorrectType ) {
return [ ` Field 'id' missing or invalid, ' ${ info [ 'id' ] } ' ${ path } ` , "" , fixedInfo ] ;
}
2021-02-04 16:47:14 +00:00
}
2021-02-05 10:59:21 +00:00
2021-03-01 14:46:10 +00:00
// type
2021-07-04 05:35:54 +00:00
if ( ! isCoin ) { // token
2021-12-10 13:58:39 +00:00
if ( chainFromAssetType ( info [ 'type' ] . toUpperCase ( ) ) !== chain ) {
2021-07-04 05:35:54 +00:00
return [ ` Incorrect value for type ' ${ info [ 'type' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
2021-06-25 09:12:11 +00:00
}
2021-07-04 05:35:54 +00:00
if ( info [ 'type' ] !== info [ 'type' ] . toUpperCase ( ) ) {
// type is correct value, but casing is wrong, fix
if ( checkOnly ) {
return [ ` Type should be ALLCAPS ' ${ info [ 'type' ] . toUpperCase ( ) } ' instead of ' ${ info [ 'type' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
}
// fix
if ( ! fixedInfo ) { fixedInfo = info ; }
fixedInfo [ 'type' ] = info [ 'type' ] . toUpperCase ( ) ;
}
} else { // coin
2021-12-10 13:58:39 +00:00
const expectedType = 'coin' ;
2021-07-04 05:35:54 +00:00
if ( info [ 'type' ] !== expectedType ) {
if ( checkOnly ) {
return [ ` Incorrect value for type ' ${ info [ 'type' ] } ', expected ' ${ expectedType } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
}
// fix
if ( ! fixedInfo ) { fixedInfo = info ; }
fixedInfo [ 'type' ] = expectedType ;
2021-03-01 14:46:10 +00:00
}
2021-02-05 10:59:21 +00:00
}
2021-12-10 13:58:39 +00:00
2021-06-25 09:12:11 +00:00
if ( ! isCoin ) {
// id, should match address
if ( info [ 'id' ] != address ) {
2021-12-10 13:58:39 +00:00
if ( checkOnly ) {
2021-06-25 09:12:11 +00:00
if ( info [ 'id' ] . toUpperCase ( ) != address . toUpperCase ( ) ) {
return [ ` Incorrect value for id ' ${ info [ 'id' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
}
// is is correct value, but casing is wrong
return [ ` Wrong casing for id ' ${ info [ 'id' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
2021-12-10 13:58:39 +00:00
}
// fix
if ( ! fixedInfo ) { fixedInfo = info ; }
2021-06-25 09:12:11 +00:00
fixedInfo [ 'id' ] = address ;
2021-03-12 18:01:29 +00:00
}
}
2021-08-12 07:50:33 +00:00
// extra checks on decimals
if ( info [ 'decimals' ] > 30 || info [ 'decimals' ] < 0 ) {
return [ ` Incorrect value for decimals ' ${ info [ 'decimals' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
}
if ( info [ 'type' ] === 'BEP2' && info [ 'decimals' ] != 8 ) {
return [ ` Incorrect value for decimals, BEP2 tokens have 8 decimals. ' ${ info [ 'decimals' ] } ' ' ${ chain } ' ${ path } ` , "" , fixedInfo ] ;
}
2021-03-17 10:51:03 +00:00
// status
if ( ! isValidStatusValue ( info [ 'status' ] ) ) {
2021-03-17 23:27:21 +00:00
return [ ` Invalid value for status field, ' ${ info [ 'status' ] } ' ` , "" , fixedInfo ] ;
}
// tags
if ( info [ 'tags' ] ) {
if ( ! isValidTagValues ( info [ 'tags' ] ) ) {
return [ ` Invalid tags, ' ${ info [ 'tags' ] } ' ` , "" , fixedInfo ] ;
}
2021-03-17 10:51:03 +00:00
}
2021-12-10 13:58:39 +00:00
const isKeys2CorrectType =
2021-02-04 16:47:14 +00:00
typeof info [ 'description' ] === "string" && info [ 'description' ] !== "" &&
// website should be set (exception description='-' marks empty infos)
typeof info [ 'website' ] === "string" && ( info [ 'description' ] === "-" || info [ 'website' ] !== "" ) &&
typeof info [ 'explorer' ] === "string" && info [ 'explorer' ] != "" ;
if ( ! isKeys2CorrectType ) {
2021-03-01 14:46:10 +00:00
return [ ` Check keys2 ' ${ info [ 'description' ] } ' ' ${ info [ 'website' ] } ' ' ${ info [ 'explorer' ] } ' ${ path } ` , "" , fixedInfo ] ;
2021-02-04 16:47:14 +00:00
}
if ( info [ 'description' ] . length > 500 ) {
const msg = ` Description too long, ${ info [ 'description' ] . length } , ${ path } ` ;
2021-03-01 14:46:10 +00:00
return [ msg , "" , fixedInfo ] ;
2021-02-04 16:47:14 +00:00
}
2021-03-01 14:46:10 +00:00
return [ "" , "" , fixedInfo ] ;
2020-10-19 22:51:47 +00:00
}
2021-06-24 08:58:38 +00:00
// return error, warning
function isInfoLinksValid ( links : unknown , path : string , address : string , chain : string ) : [ string , string ] {
if ( ! Array . isArray ( links ) ) {
return [ ` Links must be an array ' ${ JSON . stringify ( links ) } ' ' ${ path } ' ' ${ address } ' ' ${ chain } ' ` , "" ] ;
}
for ( let idx = 0 ; idx < links . length ; idx ++ ) {
const f = links [ idx ] ;
const fname = f [ 'name' ] ;
if ( ! fname ) {
return [ ` Field name missing ' ${ JSON . stringify ( f ) } ' ` , "" ] ;
}
const furl = f [ 'url' ] ;
if ( ! fname ) {
return [ ` Field url missing ' ${ JSON . stringify ( f ) } ' ` , "" ] ;
}
// Check there are no other fields
for ( const f2 in f ) {
if ( f2 !== 'name' && f2 !== 'url' ) {
return [ ` Invalid field ' ${ f2 } ' in links ' ${ JSON . stringify ( f ) } ', path ${ path } ` , "" ] ;
}
}
if ( ! Object . prototype . hasOwnProperty . call ( linksKeys , fname ) ) {
return [ ` Not supported field in links ' ${ fname } '. Supported keys: ${ linksKeysString } ` , "" ] ;
}
2021-06-26 20:02:27 +00:00
const prefix = linksKeys [ fname ] ;
2021-06-24 08:58:38 +00:00
if ( prefix ) {
if ( ! furl . startsWith ( prefix ) ) {
return [ ` Links field ' ${ fname } ': ' ${ furl } ' must start with ' ${ prefix } '. Supported keys: ${ linksKeysString } ` , "" ] ;
}
}
if ( ! furl . startsWith ( 'https://' ) ) {
return [ ` Links field ' ${ fname } ': ' ${ furl } ' must start with 'https://'. Supported keys: ${ linksKeysString } ` , "" ] ;
}
// special handling for medium
if ( fname === 'medium' ) {
if ( ! furl . includes ( linksMediumContains ) ) {
return [ ` Links field ' ${ fname } ': ' ${ furl } ' must include ' ${ linksMediumContains } '. Supported keys: ${ linksKeysString } ` , "" ] ;
}
}
}
return [ "" , "" ] ;
}
2021-02-08 10:23:34 +00:00
export function chainFromAssetType ( type : string ) : string {
2021-03-01 14:46:10 +00:00
switch ( type ) {
2021-02-08 10:23:34 +00:00
case "ERC20" : return "ethereum" ;
case "BEP2" : return "binance" ;
case "BEP20" : return "smartchain" ;
case "ETC20" : return "classic" ;
case "TRC10" :
case "TRC20" :
return "tron" ;
case "WAN20" : return "wanchain" ;
case "TRC21" : return "tomochain" ;
case "TT20" : return "thundertoken" ;
case "SPL" : return "solana" ;
2021-05-20 12:33:44 +00:00
case "EOS" : return "eos" ;
2021-02-08 10:23:34 +00:00
case "GO20" : return "gochain" ;
case "KAVA" : return "kava" ;
case "NEP5" : return "neo" ;
case "NRC20" : return "nuls" ;
case "VET" : return "vechain" ;
2021-02-09 22:43:12 +00:00
case "ONTOLOGY" : return "ontology" ;
2021-05-20 12:33:44 +00:00
case "THETA" : return "theta" ;
case "TOMO" : return "tomochain" ;
case "XDAI" : return "xdai" ;
2021-06-03 14:21:12 +00:00
case "WAVES" : return "waves" ;
2021-07-14 07:20:58 +00:00
case "POA" : return "poa" ;
2021-07-27 19:33:56 +00:00
case "POLYGON" : return "polygon" ;
2021-08-26 07:33:24 +00:00
case "OPTIMISM" : return "optimism" ;
2021-09-02 05:42:58 +00:00
case "AVALANCHE" : return "avalanchec" ;
case "ARBITRUM" : return "arbitrum" ;
case "FANTOM" : return "fantom" ;
2021-09-17 10:55:44 +00:00
case "TERRA" : return "terra" ;
2021-10-12 07:18:05 +00:00
case "RONIN" : return "ronin" ;
2021-12-07 15:48:28 +00:00
case "CELO" : return "celo" ;
2021-03-01 14:46:10 +00:00
default : return "" ;
2021-02-08 10:23:34 +00:00
}
}
2021-02-01 11:12:30 +00:00
export function explorerUrl ( chain : string , contract : string ) : string {
2020-10-19 23:22:00 +00:00
if ( contract ) {
switch ( chain . toLowerCase ( ) ) {
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . ethereum ) . toLowerCase ( ) :
2020-10-19 23:22:00 +00:00
return ` https://etherscan.io/token/ ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . tron ) . toLowerCase ( ) :
2020-10-19 23:22:00 +00:00
if ( contract . startsWith ( "10" ) ) {
// trc10
return ` https://tronscan.io/#/token/ ${ contract } ` ;
}
// trc20
return ` https://tronscan.io/#/token20/ ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . binance ) . toLowerCase ( ) :
2020-10-19 23:22:00 +00:00
return ` https://explorer.binance.org/asset/ ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . smartchain ) . toLowerCase ( ) :
2021-02-04 16:47:14 +00:00
case "smartchain" :
2020-10-19 23:22:00 +00:00
return ` https://bscscan.com/token/ ${ contract } ` ;
2021-05-20 12:33:44 +00:00
case CoinType . name ( CoinType . eos ) . toLowerCase ( ) :
return ` https://bloks.io/account/ ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . neo ) . toLowerCase ( ) :
2020-10-22 14:41:12 +00:00
return ` https://neo.tokenview.com/en/token/0x ${ contract } ` ;
2020-10-19 23:22:00 +00:00
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . nuls ) . toLowerCase ( ) :
2020-10-19 23:22:00 +00:00
return ` https://nulscan.io/token/info?contractAddress= ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . wanchain ) . toLowerCase ( ) :
2020-10-19 23:22:00 +00:00
return ` https://www.wanscan.org/token/ ${ contract } ` ;
2020-12-07 10:56:44 +00:00
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . solana ) . toLowerCase ( ) :
2020-12-07 10:56:44 +00:00
return ` https://explorer.solana.com/address/ ${ contract } ` ;
2021-02-03 22:42:15 +00:00
case CoinType . name ( CoinType . tomochain ) . toLowerCase ( ) :
return ` https://scan.tomochain.com/address/ ${ contract } ` ;
case CoinType . name ( CoinType . kava ) . toLowerCase ( ) :
return "https://www.mintscan.io/kava" ;
case CoinType . name ( CoinType . ontology ) . toLowerCase ( ) :
return "https://explorer.ont.io" ;
case CoinType . name ( CoinType . gochain ) . toLowerCase ( ) :
2021-05-10 13:08:10 +00:00
return ` https://explorer.gochain.io/addr/ ${ contract } ` ;
2021-02-04 16:47:14 +00:00
2021-05-20 12:33:44 +00:00
case CoinType . name ( CoinType . theta ) . toLowerCase ( ) :
return 'https://explorer.thetatoken.org/' ;
2021-02-04 16:47:14 +00:00
case CoinType . name ( CoinType . thundertoken ) . toLowerCase ( ) :
case "thundertoken" :
2021-05-10 13:08:10 +00:00
return ` https://viewblock.io/thundercore/address/ ${ contract } ` ;
2021-02-08 10:08:45 +00:00
case CoinType . name ( CoinType . classic ) . toLowerCase ( ) :
case "classic" :
2021-05-10 13:08:10 +00:00
return ` https://blockscout.com/etc/mainnet/tokens/ ${ contract } ` ;
case CoinType . name ( CoinType . vechain ) . toLowerCase ( ) :
case "vechain" :
return ` https://explore.vechain.org/accounts/ ${ contract } ` ;
2021-05-20 12:33:44 +00:00
2021-06-03 14:21:12 +00:00
case CoinType . name ( CoinType . waves ) . toLowerCase ( ) :
return ` https://wavesexplorer.com/assets/ ${ contract } ` ;
2021-05-20 12:33:44 +00:00
case "xdai" :
return ` https://blockscout.com/xdai/mainnet/tokens/ ${ contract } ` ;
2021-07-14 07:20:58 +00:00
case CoinType . name ( CoinType . poa ) . toLowerCase ( ) :
2021-09-02 05:42:58 +00:00
case "poa" :
2021-07-14 07:20:58 +00:00
return ` https://blockscout.com/poa/core/tokens/ ${ contract } ` ;
2021-07-26 09:59:22 +00:00
case CoinType . name ( CoinType . polygon ) . toLowerCase ( ) :
2021-09-02 05:42:58 +00:00
case "polygon" :
2021-08-26 07:33:24 +00:00
return ` https://polygonscan.com/token/ ${ contract } ` ;
2021-09-02 05:42:58 +00:00
case "optimism" :
2021-08-26 07:33:24 +00:00
return ` https://optimistic.etherscan.io/address/ ${ contract } ` ;
2021-09-02 05:42:58 +00:00
case "avalanchec" :
2021-12-10 13:58:39 +00:00
return ` https://snowtrace.io/address/ ${ contract } `
2021-09-02 05:42:58 +00:00
case "arbitrum" :
return ` https://arbiscan.io/token/ ${ contract } `
case "fantom" :
return ` https://ftmscan.com/token/ ${ contract } `
2021-09-17 10:55:44 +00:00
case "terra" :
return ` https://finder.terra.money/columbus-4/ ${ contract } `
2021-10-12 07:18:05 +00:00
case "ronin" :
return ` https://explorer.roninchain.com/token/ronin: ${ contract } `
2021-12-07 15:48:28 +00:00
case "celo" :
return ` https://explorer.bitquery.io/celo_rc1/token/ ${ contract } ` ;
}
2020-10-19 23:22:00 +00:00
}
return "" ;
}
2020-10-22 14:41:12 +00:00
function explorerUrlAlternatives ( chain : string , contract : string , name : string ) : string [ ] {
const altUrls : string [ ] = [ ] ;
if ( name ) {
const nameNorm = name . toLowerCase ( ) . replace ( ' ' , '' ) . replace ( ')' , '' ) . replace ( '(' , '' ) ;
2020-12-07 10:56:44 +00:00
if ( chain . toLowerCase ( ) == CoinType . name ( CoinType . ethereum ) ) {
2020-10-22 14:41:12 +00:00
altUrls . push ( ` https://etherscan.io/token/ ${ nameNorm } ` ) ;
}
altUrls . push ( ` https://explorer. ${ nameNorm } .io ` ) ;
altUrls . push ( ` https://scan. ${ nameNorm } .io ` ) ;
}
return altUrls ;
}
2021-03-01 14:46:10 +00:00
// Check the an assets's info.json; for errors/warning. Also does fixes in certain cases
2021-06-25 09:12:11 +00:00
function isAssetInfoOK ( chain : string , isCoin : boolean , address : string , errors : string [ ] , warnings : string [ ] , checkOnly : boolean ) : void {
const assetInfoPath = isCoin ? getChainCoinInfoPath ( chain ) : getChainAssetInfoPath ( chain , address ) ;
2020-10-19 22:51:47 +00:00
if ( ! isPathExistsSync ( assetInfoPath ) ) {
2020-10-19 23:22:00 +00:00
// Info file doesn't exist, no need to check
return ;
2020-10-19 22:51:47 +00:00
}
if ( ! isValidJSON ( assetInfoPath ) ) {
console . log ( ` JSON at path: ' ${ assetInfoPath } ' is invalid ` ) ;
2020-10-19 23:22:00 +00:00
errors . push ( ` JSON at path: ' ${ assetInfoPath } ' is invalid ` ) ;
return ;
2020-10-19 22:51:47 +00:00
}
2021-03-01 14:46:10 +00:00
let info : unknown = readJsonFile ( assetInfoPath ) ;
2021-12-10 13:58:39 +00:00
let fixedInfo : unknown | null = null ;
2021-03-01 14:46:10 +00:00
2021-06-25 09:12:11 +00:00
const [ hasAllKeys , msg1 ] = isAssetInfoHasAllKeys ( info , assetInfoPath , isCoin ) ;
2020-10-19 22:51:47 +00:00
if ( ! hasAllKeys ) {
2021-02-04 16:47:14 +00:00
console . log ( msg1 ) ;
errors . push ( msg1 ) ;
2020-10-19 22:51:47 +00:00
}
2021-06-25 09:12:11 +00:00
const [ err2 , warn2 , fixedInfo2 ] = isAssetInfoValid ( info , assetInfoPath , address , chain , isCoin , checkOnly ) ;
2021-02-04 16:47:14 +00:00
if ( err2 ) {
errors . push ( err2 ) ;
}
if ( warn2 ) {
warnings . push ( warn2 ) ;
2021-02-03 22:42:15 +00:00
}
2021-03-01 14:46:10 +00:00
if ( fixedInfo2 && ! checkOnly ) {
info = fixedInfo2 ;
fixedInfo = fixedInfo2 ;
}
2021-02-03 22:42:15 +00:00
2021-06-24 08:58:38 +00:00
if ( Object . prototype . hasOwnProperty . call ( info , 'links' ) && info [ 'links' ] ) {
const [ err3 , warn3 ] = isInfoLinksValid ( info [ 'links' ] , assetInfoPath , address , chain ) ;
if ( err3 ) {
errors . push ( err3 ) ;
}
if ( warn3 ) {
warnings . push ( warn3 ) ;
}
}
2021-06-28 15:49:10 +00:00
// Fields moved to links section:
[ 'socials' , 'source_code' , 'whitepaper' , 'white_paper' ] . forEach ( f = > {
if ( Object . prototype . hasOwnProperty . call ( info , f ) ) {
errors . push ( ` Field ${ f } is no longer used, use 'links' section instead. ( ${ chain } ${ address } ) ` ) ;
2021-06-24 08:58:38 +00:00
}
2021-06-28 15:49:10 +00:00
} ) ;
2021-06-24 08:58:38 +00:00
2021-06-25 09:12:11 +00:00
if ( ! isCoin ) {
const explorerExpected = explorerUrl ( chain , address ) ;
const hasExplorer = Object . prototype . hasOwnProperty . call ( info , 'explorer' ) ;
const explorerActual = info [ 'explorer' ] || '' ;
const explorerActualLower = explorerActual . toLowerCase ( ) ;
const explorerExpectedLower = explorerExpected . toLowerCase ( ) ;
if ( checkOnly ) {
if ( ! hasExplorer ) {
errors . push ( ` Missing explorer key ` ) ;
} else {
if ( explorerActualLower !== explorerExpectedLower && explorerExpected ) {
// doesn't match, check for alternatives
const explorersAlt = explorerUrlAlternatives ( chain , address , info [ 'name' ] ) ;
if ( explorersAlt && explorersAlt . length > 0 ) {
let matchCount = 0 ;
2021-12-10 13:58:39 +00:00
explorersAlt . forEach ( exp = > { if ( exp . toLowerCase ( ) == explorerActualLower ) { ++ matchCount ; } } ) ;
2021-06-25 09:12:11 +00:00
if ( matchCount == 0 ) {
2021-11-29 09:08:24 +00:00
// none matches, this is error
errors . push ( ` Incorrect explorer, ${ explorerActual } instead of ${ explorerExpected } ( ${ explorersAlt . join ( ', ' ) } ) ` ) ;
2021-05-10 13:08:10 +00:00
}
2020-10-27 15:09:28 +00:00
}
2020-10-22 14:41:12 +00:00
}
}
2021-06-25 09:12:11 +00:00
} else {
// fix: simply replace with expected (case-only deviation is accepted)
if ( explorerActualLower !== explorerExpectedLower ) {
if ( ! fixedInfo ) { fixedInfo = info ; }
fixedInfo [ 'explorer' ] = explorerExpected ;
}
2021-05-10 13:08:10 +00:00
}
2020-10-19 23:22:00 +00:00
}
2021-03-01 14:46:10 +00:00
if ( fixedInfo && ! checkOnly ) {
writeJsonFile ( assetInfoPath , fixedInfo ) ;
console . log ( ` Done fixes to info.json, ${ assetInfoPath } ` ) ;
2021-12-10 13:58:39 +00:00
}
2020-10-19 22:51:47 +00:00
}
export class AssetInfos implements ActionInterface {
getName ( ) : string { return "Asset Infos" ; }
2021-12-10 13:58:39 +00:00
2020-10-19 22:51:47 +00:00
getSanityChecks ( ) : CheckStepInterface [ ] {
const steps : CheckStepInterface [ ] = [ ] ;
2021-06-25 09:12:11 +00:00
// tokens info.json's
2020-10-19 22:51:47 +00:00
allChains . forEach ( chain = > {
if ( isPathExistsSync ( getChainAssetsPath ( chain ) ) ) {
steps . push (
{
2021-12-10 13:58:39 +00:00
getName : ( ) = > { return ` Token info.json's for chain ${ chain } ` ; } ,
2020-10-19 22:51:47 +00:00
check : async ( ) = > {
const errors : string [ ] = [ ] ;
2020-10-19 23:22:00 +00:00
const warnings : string [ ] = [ ] ;
2020-10-19 22:51:47 +00:00
const assetsList = getChainAssetsList ( chain ) ;
//console.log(` Found ${assetsList.length} assets for chain ${chain}`);
await bluebird . each ( assetsList , async ( address ) = > {
2021-06-25 09:12:11 +00:00
isAssetInfoOK ( chain , false , address , errors , warnings , true ) ;
2020-10-19 22:51:47 +00:00
} ) ;
2020-10-19 23:22:00 +00:00
return [ errors , warnings ] ;
2021-12-10 13:58:39 +00:00
}
2020-10-19 22:51:47 +00:00
}
) ;
}
} ) ;
2021-06-25 09:12:11 +00:00
// coin info.json
steps . push (
{
2021-12-10 13:58:39 +00:00
getName : ( ) = > { return ` Coin info.json's ` ; } ,
2021-06-25 09:12:11 +00:00
check : async ( ) = > {
const errors : string [ ] = [ ] ;
const warnings : string [ ] = [ ] ;
allChains . forEach ( chain = > {
if ( isPathExistsSync ( getChainInfoPath ( chain ) ) ) {
isAssetInfoOK ( chain , true , '../info' , errors , warnings , true ) ;
}
} ) ;
return [ errors , warnings ] ;
}
}
) ;
2020-10-19 22:51:47 +00:00
return steps ;
}
2021-03-01 14:46:10 +00:00
async consistencyFix ( ) : Promise < void > {
bluebird . each ( allChains , async chain = > {
// only if there is no assets subfolder
if ( isPathExistsSync ( getChainAssetsPath ( chain ) ) ) {
const errors : string [ ] = [ ] ;
const warnings : string [ ] = [ ] ;
const assetsList = getChainAssetsList ( chain ) ;
await bluebird . each ( assetsList , async ( address ) = > {
2021-06-25 09:12:11 +00:00
isAssetInfoOK ( chain , false , address , errors , warnings , false ) ;
2021-03-01 14:46:10 +00:00
} ) ;
}
2021-06-25 09:12:11 +00:00
if ( isPathExistsSync ( getChainInfoPath ( chain ) ) ) {
const errors : string [ ] = [ ] ;
const warnings : string [ ] = [ ] ;
isAssetInfoOK ( chain , true , '[COIN]' , errors , warnings , false ) ;
}
2021-03-01 14:46:10 +00:00
} ) ;
}
2020-10-19 22:51:47 +00:00
}