UNPKG

crypto-bot

Version:

multi-function tools for blockchain automation

907 lines (743 loc) 33.1 kB
const { ethers } = require("ethers"); const { Contract, Provider } = require('ethcall') const fs = require('fs'); const { tokenTable } = require('./utils/tableheader'); const cliProgress = require('cli-progress'); const colors = require('ansi-colors'); const { logger } = require('./utils/logger'); const botType = require('./core/type'); const tools = require("./utils/tools"); const crypto = require("./utils/crypto"); const tokenJsonFile = "./lib/ABI/ERC20.json"; const BOTtokenABI = JSON.parse(fs.readFileSync(tokenJsonFile)); module.exports = { listToken: async function (Network, Option) { if(Option == undefined){ Option = 1 } let tokenlist = await this.readTokenData(Network); logger.verbose(`*********************************************************************************************`); logger.verbose("Token Configuration Analyser..."); logger.verbose(` ${Network.name}: ${tokenlist.length} token in configuration`); if(Option == 1){ for(let i = 0 ; i < tokenlist.length ; i++) { //tokenTable.addRow({index: i, symbol: tokenlist[i].symbol, name: tokenlist[i].name, decimals: tokenlist[i].decimals}) logger.info(` (${tokenlist[i].name}) ${tokenlist[i].symbol} `); } //tokenTable.printTable() } logger.verbose("Token Configuration Analyser Completed..."); logger.verbose(`*********************************************************************************************`); return }, displayToken: async function (Network, Option) { if(Option == undefined){ Option = 0 } let tokenlist = await this.readTokenData(Network); if(tokenlist.length > 0 ) { tokenTable.title=`${Network.name}: ${tokenlist.length} token in configuration` const _display = tokenTable; for(let i = 0 ; i < tokenlist.length ; i++) { _display.addRow({index: i, symbol: tokenlist[i].symbol, name: tokenlist[i].name, decimals: tokenlist[i].decimals, address: tokenlist[i].address}) } _display.printTable() } return }, getTokenData: async function (Network, _tokenlist) { logger.verbose(`Request ${_tokenlist.length} token data for network ${Network.name}`); let knownToken = await this.readTokenData(Network); logger.verbose(` ${Network.name}: ${knownToken.length} token in configuration`); // BATCH OF MAX 100 logger.verbose(` ${Network.name}: ${knownToken.length} token in configuration`); for (let i = 0; i < _tokenlist.length; i++) { let newtoken = _tokenlist[i] if(!this.tokenDataExist(newtoken)){ const token0 = 0//await getTokenName.token0(); const getToken0Info = new ethers.Contract(token0, BOTtokenABI); const token0name = await getToken0Info.name(); const token0symbol = await getToken0Info.symbol(); const token0decimals = await getToken0Info.decimals(); const token0totalsypply = await getToken0Info.totalSupply(); logger.info("(" + token0symbol + ") " + token0name + " : " + token0 + " Total Supply: " + token0totalsypply + " (" + token0decimals + ")"); } } }, tokenDataExist: async function (_token, _chainId) { let configToken = await this.readTokenData(Network); for(let i = 0 ; i < configToken.length ; i++){ if(configToken[i].address == _token.address) return _token; } return false; }, addTokenData: async function (Network, TokenList) { if( TokenList == undefined){ logger.error(` Error... cannot add undefined token list to master file.`) return false; } logger.verbose(` Adding ${TokenList.length} token data for ${Network.name} `); const _currentData = new Object(await this.readTokenData(Network)); let _total = _currentData.length + TokenList.length; for(let k = 0 ; k < TokenList.length ; k++) { const TokenObject = new Object(TokenList[k]) _currentData.push(TokenObject) } if(_currentData.length != _total ){ logger.error(` Error... record number mistach...`) return false; } const _res = await this.writeTokenData(Network, _currentData) return _res }, readTokenData: async function (Network) { logger.verbose(` Reading token data for ${Network.name} `); const _data = await tools.readFile(Network, botType.TOKEN_TYPE, null) return _data }, writeTokenData: async function (Network, TokenArray) { logger.verbose(` Saving ${TokenArray.length} token information for ${Network.name} `); const _result = await tools.writeFile(Network, botType.TOKEN_TYPE, null, TokenArray) if(_result) { return true; } else { logger.error(`error while saving record saved. `) return false; } }, updateTokenFromPairList: async function(Network, pairlist, Exchange) { const _valid = await this.validateTokenList(Network) let force = true if( !_valid) { if( !force ){ logger.warn(' Validation of network token list failed. Use (--force to clean) ') } else{ let fix = await this.fixTokenList(Network); if(!fix){ logger.error(' Error while trying to fix network token list... ') } } } logger.info(` Extracting token from ${pairlist.length} pairs data from (${Network.chainId}) ${Exchange.name}...`); const _sanitizedData = await this.removeDuplicatePairList(Network, pairlist); logger.verbose(` ${_sanitizedData.length} unique token found in pairs data.`); const _tokenlist = await this.findNewTokenData(Network, _sanitizedData); logger.verbose(` Fetching data for ${_tokenlist.length} new token...`) logger.verbose(` Number of new token found: ${_tokenlist.length}`); const _newTokenDataList = new Array(); // create a new progress bar instance and use shades_classic theme const title = `${Exchange.name} | Extracting token data |` const bar1 = new cliProgress.SingleBar({format: title + colors.green('{bar}') + '| {percentage}% | {value}/{total} Token'}, cliProgress.Presets.shades_classic); // NEW TOKEN GENERATED const _splitTokenList = crypto.splitMultiCall(_tokenlist, botType.MULTI_CALL_TOKEN_FULL); let _totalToken = _tokenlist.length if(_totalToken > 0) { bar1.start(_totalToken, 1); let _countProgress = 0; for(let j = 0 ; j < _splitTokenList.length ; j++) { let fetchTokenList = await this.multiCallGetTokenData(Network, _splitTokenList[j], BOTtokenABI) // OPTION //fetchTokenList = await this.multiCallGetTokenDataNoSupply(Network, _splitTokenList[j], BOTtokenABI) if( !(fetchTokenList.length == _splitTokenList[j].length) ){ logger.error(` Received data for ${fetchTokenList.length} new token. Expected data for: ${_splitTokenList[j].length}`) } else{ logger.verbose(` Received data for ${fetchTokenList.length} new token...`) for(let i = 0; i < fetchTokenList.length ; i++){ _newTokenDataList.push(fetchTokenList[i]) logger.info(`Name: ${fetchTokenList[i].name} (${fetchTokenList[i].symbol}) decimals: ${fetchTokenList[i].decimals} total supply: ${fetchTokenList[i].totalsupply}`) } } _countProgress = _countProgress + _splitTokenList[j].length; bar1.update(_countProgress); } bar1.update(_countProgress); bar1.stop(); } if(_newTokenDataList.length > 0) { logger.info(` ${_newTokenDataList.length} new token found...`) await this.addTokenData( Network, _newTokenDataList ) } else{ logger.verbose(` No new token found token...`) } return }, call3: async function(rpc, _newtokenList, _tokenABI, chainID) { const tokenName = new Array(); const tokenSymbol = new Array(); const tokenDecimals = new Array(); const tokenTotalSupply = new Array(); const tokenAddress = new Array(); const newTokenData = new Array(); const ethcallProvider = new Provider(); const provider = new ethers.providers.JsonRpcProvider(rpc); await ethcallProvider.init(provider); let totalCount = _newtokenList.length; const callarray = new Array(); for(let index = 0 ; index < totalCount ; index++){ const TokenContract = new Contract( _newtokenList[index].address, _tokenABI ); tokenName[index] = TokenContract.name(); tokenSymbol[index] = TokenContract.symbol(); tokenDecimals[index] = TokenContract.decimals(); tokenTotalSupply[index] = TokenContract.totalSupply(); tokenAddress[index] = _newtokenList[index].address callarray.push(tokenName[index]); callarray.push(tokenSymbol[index]); callarray.push(tokenDecimals[index]); callarray.push(tokenTotalSupply[index]); } logger.debug(`Calling multi provider with ${callarray.length} contract call `); let data = undefined try{ data = await ethcallProvider.all(callarray); } catch (e){ let _fakeName = e.reason let _fakeSymbol = e.code if(e.code == "BUFFER_OVERRUN") { logger.error(`BUFFER OVERRUN error encountered. reason: ${e.reason}`) } if (e.code == "NUMERIC_FAULT") { logger.error(`NUMERIC_FAULT error encountered reason: ${e.reason}`) } if ( (e.code == "CALL_EXCEPTION") ) { if (e.reason == "missing revert data in call exception") { logger.error(`Error when call multi-calling contract. Invalid ABI?: ${e.reason}`) } else if (e.reason == "undefined"){ logger.error(`Bug in Crypto BOT caused data not received: ${e.reason}`) } else { logger.error(`Unknown CALL EXCEPTION error encountered reason: ${e.reason}`) } } else { logger.error(`UNKNOWN error encountered: ${e.code}`) console.log(e) _fakeSymbol = "UNKNOWN" } // look for error const patch = false; if( patch ){ // patchinig deatch contract on FTM for now for(let i = 0 ; i < (callarray.length/4) ; i++) { let _address = tokenAddress[i] const newToken = Object({ "address": _address, "name": _fakeName, "symbol": _fakeSymbol, "decimals": 0, "totalsupply":"NOSUPPLY", "chainId": chainID, "logoURI": "", "status": "death" }) newTokenData.push(newToken); } return newTokenData } //logger.error(`${e}`); } if( !(data == undefined) ){ let resultLength = data.length; logger.debug(`Number of pairs in result: ${resultLength}`); if (resultLength != callarray.length) { logger.error(`Error in the result data count | received: ${resultLength} vs expected: ${callarray.length} `); } let count = 0; let index_pointer = 0; let _name = undefined; let _symbol = undefined; let _decimals = undefined; let _totalsupply = undefined; let _address = undefined; for(let i = 0 ; i < data.length ; i++) { if(count == 0){ _name = data[i] count++ } else if(count == 1){ _symbol = data[i] count++ } else if(count == 2){ _decimals = data[i] count++ } else if(count == 3){ _totalsupply = data[i] _address = tokenAddress[index_pointer] const newToken = Object({ "address": _address, "name": _name, "symbol": _symbol, "decimals": _decimals, "totalsupply":_totalsupply, "chainId": chainID, "logoURI": "", "status": "new" }) newTokenData.push(newToken); index_pointer++; count = 0; } logger.debug(`data received: ${data[i]}`) } } return newTokenData }, multiCallGetTokenDataNoSupply: async function(Network, _tokenList, _tokenABI) { logger.debug(`multiCallGetTokenData ChainID: ${Network.chainId} - ${Network.name} - ${Network.https[0]}` ); let provider = Network.https[0]; const newTokenData = new Array(); let result4 = new Array(); try { result4 = await this.call4(provider, _tokenList, _tokenABI, Network.chainId ); } catch (e) { console.log(e) let _fakeName = e.reason let _fakeSymbol = e.code if(e.code == "BUFFER_OVERRUN") { logger.warn(`BUFFER OVERRUN error encountered. reason: ${e.reason}`) } else if (e.code == "NUMERIC_FAULT") { logger.warn(`NUMERIC_FAULT error encountered reason: ${e.reason}`) } else if (e.code == "CALL_EXCEPTION") { logger.warn(`CALL EXCEPTION error encountered reason: ${e.reason}`) } else { logger.error(`UNKNOWN error encountered: ${e.code}`) _fakeSymbol = "UNKNOWN" } //console.log(e.length) //console.log(e.offset) // look for error const patch = false; if( patch ){ // patchinig deatch contract on FTM for now for(let i = 0 ; i < _tokenList.length ; i++) { let tokenAddress = _tokenList[i] let _address = tokenAddress.address const newToken = Object({ "address": _address, "name": _fakeName, "symbol": _fakeSymbol, "decimals": 0, "totalsupply":"NOSUPPLY", "chainId": Network.chainId, "logoURI": "", "status": "death" }) newTokenData.push(newToken); } return newTokenData } logger.error(`${e}`); // look for error logger.error(_tokenList); } logger.debug("Done multiCallGetTokenData...") return result4 }, call4: async function(rpc, _newtokenList, _tokenABI, chainID) { const tokenName = new Array(); const tokenAddress = new Array(); const newTokenData = new Array(); const ethcallProvider = new Provider(); const provider = new ethers.providers.JsonRpcProvider(rpc); await ethcallProvider.init(provider); let totalCount = _newtokenList.length; const callarray = new Array(); for(let index = 0 ; index < totalCount ; index++){ const TokenContract = new Contract( _newtokenList[index].address, _tokenABI ); tokenName[index] = TokenContract.name(); //tokenSymbol[index] = TokenContract.symbol(); //tokenDecimals[index] = TokenContract.decimals(); //tokenTotalSupply[index] = TokenContract.totalSupply(); tokenAddress[index] = _newtokenList[index].address callarray.push(tokenName[index]); //callarray.push(tokenSymbol[index]); //callarray.push(tokenDecimals[index]); //callarray.push(tokenTotalSupply[index]); } logger.debug(`Calling multi provider with ${callarray.length} contract call `); let data = undefined try { data = await ethcallProvider.all(callarray); } catch (e) { //console.log(e.error) //console.log(e.reason) let _fakeName = e.reason let _fakeSymbol = e.code if(e.code == "BUFFER_OVERRUN") { logger.warn(`BUFFER OVERRUN error encountered. reason: ${e.reason}`) } else if (e.code == "NUMERIC_FAULT") { logger.warn(`NUMERIC_FAULT error encountered reason: ${e.reason}`) } else if (e.code == "CALL_EXCEPTION") { logger.warn(`CALL EXCEPTION error encountered reason: ${e.reason}`) } else { logger.error(`UNKNOWN error encountered: ${e.code}`) _fakeSymbol = "UNKNOWN" } //console.log(e.length) //console.log(e.offset) // look for error const patch = false; if( patch ){ // patchinig deatch contract on FTM for now for(let i = 0 ; i < callarray.length ; i++) { let _address = tokenAddress[i] const newToken = Object({ "address": _address, "name": _fakeName, "symbol": _fakeSymbol, "decimals": 0, "totalsupply":"NOSUPPLY", "chainId": chainID, "logoURI": "", "status": "death" }) newTokenData.push(newToken); } return newTokenData } logger.error(`${e}`); } let resultLength = data.length; //console.log(data) //process.exit() logger.info(`Number of pairs in result: ${resultLength}`); if (resultLength != callarray.length) { logger.error(`Error in the result data count | received: ${resultLength} vs expected: ${callarray.length} `); } let count = 0; let index_pointer = 0; let _name = undefined; let _symbol = undefined; let _decimals = undefined; let _address = undefined; for(let i = 0 ; i < data.length ; i++) { if(count == 0){ _name = data[i] count++ } else if(count == 1){ _symbol = data[i] count++ } else if(count == 2){ _decimals = data[i] //_totalsupply = data[i] _address = tokenAddress[index_pointer] const newToken = Object({ "address": _address, "name": _name, "symbol": _symbol, "decimals": _decimals, "totalsupply":"NOSUPPLY", "chainId": chainID, "logoURI": "", "status": "death" }) newTokenData.push(newToken); index_pointer++; count = 0; } logger.debug(`data received: ${data[i]}`) } return newTokenData }, validateTokenList: async function(Network){ logger.verbose(`Validation of token for ${Network.chainId} ${Network.name}`); let _validationList = await this.readTokenData(Network) if(_validationList.length > 0){ logger.verbose(` starting validation of ${_validationList.length} token`); } else{ logger.warn('No token found... nothing to validate.') return true } // sorting list by address _validationList = _validationList.sort(function(a, b){return a.address - b.address}); //console.log(_validationList) // loop and count duplicate let _init = true; let _previous = undefined; let _duplicate = 0; let _validCount = 0; _validationList.forEach(_token => { if(_init){ _previous = _token; _init = false; _validCount++ } else { if(_token.address == _previous.address){ //console.log(_token) logger.error(` Duplicate token address found. (${_token.name}) || (${_previous.name})`); _duplicate++; } else{ _validCount++ } } _previous = _token; }); // validate result if(_duplicate > 0){ logger.warn(` Found ${_duplicate} record in token list.`) return false } logger.verbose(`Validation of ${_validCount} token succesfull`); return true }, fixTokenList: async function(Network){ logger.verbose(`Crypto BOT Token Fix running for (${Network.chainId}) ${Network.name}`); let _validationList = await this.readTokenData(Network) const _newTokenList = new Array(); const _newAddressList = new Array(); if(_validationList.length > 0){ logger.verbose(` starting repair of ${_validationList.length} token found.`); } else{ logger.warn('No token found... nothing to validate.') return true } // loop and count duplicate let _duplicate = 0; let _validCount = 0; let _init = true; for(let i = 0 ; i < _validationList.length ; i++){ let token = new Object(_validationList[i]); if(_init){ _init = false; _newTokenList.push(token); _newAddressList.push(token.address); _validCount++ } else{ if(_newAddressList.indexOf(token.address) == -1){ _newTokenList.push(token); _newAddressList.push(token.address); _validCount++ } else{ _duplicate++ } } } if( (_duplicate + _validCount) != _validationList.length){ logger.error(`Error while loading data to fix... Duplicate: ${_duplicate} Valid: ${_validCount} Expected: ${_validationList.length}`) return false } // sorting list by address _validationList = _validationList.sort(function(a, b){return a.address - b.address}); //console.log(_validationList) // validate result if(_duplicate > 0){ logger.info(` Fixing ${_duplicate} error in token list... `) const res = await tools.writeFile(Network, botType.TOKEN_TYPE, null, _newTokenList) logger.verbose(` Successfully saved ${_validCount} token for ${Network.name}`); return res } else{ logger.warn('No problem found found... nothing to fix.') } return true }, removeDuplicatePairList: async function(Network, _PairList){ logger.verbose(`Removing duplicate from pairlist... returning a clean`); let _validationList = _PairList; if(_validationList.length > 0){ logger.verbose(` starting validation of ${_validationList.length} token`); } else{ logger.warn('No token found... nothing to validate.') return _PairList } // sorting list by address //_validationList.sort(function(a, b){return a.address - b.address}); // loop and count duplicate let _duplicate = 0; let dynamicTokenList = new Array() let _Rebuild = new Array() let token0found = -1 let token1found = -1; //_validationList.forEach(_pairlist => { for(let i = 0 ; i < _validationList.length ; i++){ let _pairlist = _validationList[i] //console.log(_pairlist) token0found = dynamicTokenList.indexOf(_pairlist.token0) token1found = dynamicTokenList.indexOf(_pairlist.token1) if(token0found == - 1){ //console.log(_pairlist) const token0Data = new Object({ address: _pairlist.token0, chainId: Network.chainId, status: 'new', verified: 'false' }) dynamicTokenList.push(_pairlist.token0) _Rebuild.push(token0Data) } if(token1found == - 1){ const token1Data = new Object({ address: _pairlist.token1, chainId: Network.chainId, status: 'new', verified: 'false' }) dynamicTokenList.push(_pairlist.token1) _Rebuild.push(token1Data) } } _Rebuild.sort(function(a, b){return a.address - b.address}); _duplicate = _validationList.length - _Rebuild.length // validate result if(_duplicate > 0){ logger.verbose(` found ${_duplicate} duplicate record in pairs list.`) } logger.verbose(`Removal of duplicate token succesfull`); return _Rebuild }, findNewTokenData: async function(Network, TokenList){ logger.verbose(`Finding new token... returning unknown token only...`); let _validationList = TokenList; if(_validationList.length > 0){ logger.verbose(` validation of ${_validationList.length} token`); } else{ logger.warn('No data provided... nothing to validate.') return TokenList } const _tokenlist = await this.readTokenData(Network); if( !(_tokenlist.length > 0) ){ logger.warn(`No token loaded from configuration...`) return TokenList } let dynamicTokenList = new Array() //_tokenlist.forEach(_data => { for(let i = 0; i < _tokenlist.length ; i++){ let _data = new Object(_tokenlist[i]) dynamicTokenList.push(_data.address); } // loop and count duplicate let _duplicate = 0; let _Rebuild = new Array() let tokenfound = -1 //_validationList.forEach(_pairlist => { for(let i = 0 ; i < _validationList.length ; i++){ let _token = new Object(_validationList[i]) //console.log(_token) tokenfound = dynamicTokenList.indexOf(_token.address) if(tokenfound == - 1){ const tokenData = new Object({ address: _token.address, chainId: Network.chainId, status: 'new', verified: 'false' }) _Rebuild.push(tokenData) } } _Rebuild.sort(function(a, b){return a.address - b.address}); _duplicate = _validationList.length - _Rebuild.length // validate result if(_duplicate > 0){ logger.verbose(` ${_duplicate} duplicate token in list...`) } logger.verbose(`Finding new token as completed successfull.`); return _Rebuild }, multiCallGetTokenData: async function(Network, _newtokenList, _tokenABI) { const tokenName = new Array(); const tokenSymbol = new Array(); const tokenDecimals = new Array(); const tokenTotalSupply = new Array(); const tokenAddress = new Array(); const newTokenData = new Array(); let totalCount = _newtokenList.length; const callarray = new Array(); for(let index = 0 ; index < totalCount ; index++){ const TokenContract = new Contract( _newtokenList[index].address, _tokenABI ); tokenName[index] = TokenContract.name(); tokenSymbol[index] = TokenContract.symbol(); tokenDecimals[index] = TokenContract.decimals(); tokenTotalSupply[index] = TokenContract.totalSupply(); tokenAddress[index] = _newtokenList[index].address callarray.push(tokenName[index]); callarray.push(tokenSymbol[index]); callarray.push(tokenDecimals[index]); callarray.push(tokenTotalSupply[index]); } logger.debug(`Calling multi provider with ${callarray.length} contract call `); const data = await crypto.cryptocall(Network, callarray, botType.TOKEN_TYPE) //logger.error(`${e}`); if( !(data == undefined) ){ let resultLength = data.length; logger.debug(`Number of pairs in result: ${resultLength}`); if (resultLength != callarray.length) { logger.error(`Error in the result data count | received: ${resultLength} vs expected: ${callarray.length} `); } let count = 0; let index_pointer = 0; let _name = undefined; let _symbol = undefined; let _decimals = undefined; let _totalsupply = undefined; let _address = undefined; for(let i = 0 ; i < data.length ; i++) { if(count == 0){ _name = data[i] count++ } else if(count == 1){ _symbol = data[i] count++ } else if(count == 2){ _decimals = data[i] count++ } else if(count == 3){ _totalsupply = data[i] _address = tokenAddress[index_pointer] const newToken = Object({ "address": _address, "name": _name, "symbol": _symbol, "decimals": _decimals, "totalsupply":_totalsupply, "chainId": Network.chainId, "logoURI": "", "status": "new" }) newTokenData.push(newToken); index_pointer++; count = 0; } logger.debug(`data received: ${data[i]}`) } return newTokenData } else{ return [] } }, }