crypto-bot
Version:
multi-function tools for blockchain automation
907 lines (743 loc) • 33.1 kB
JavaScript
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 []
}
},
}