/* import moralis */ const Moralis = require("moralis/node"); // const Moralis = require("../Moralis_JS_SDK/node"); var utils = require('./utils.js'); // var config = require('../config/config.js')(db_config, // moralis_config) var { moralis_config, reids_token_config, account_config } = require('../config/config.js') const redis = require("./db/redis_db") //导入 db.js const mysql = require("./db/mysql_db") const account_mysql = require("../model/db/account_info_db") //导入 db.js const logger = require('./logger') const report = require("./report") //导入 db.js const BigNumber = require('bignumber.js') const collect_coins_db = require('./db/collect_coins_db') const czz = require('./http_withdraw') var remote_config_db = require("../model/db/remote_config_db"); /* Moralis init code */ var serverUrl = moralis_config.SERVER_URL; var appId = moralis_config.APP_ID; var masterKey = moralis_config.MASTER_KEY; var moralisSecret = moralis_config.MORALIS_SECRET; // 内部异常 const ERROR_CODE_001 = -1; const SUCCEED_CODE = 0; // 归集队列 var collectCoinsArrays = []; // 是否执行归集 var isExecCollect = false; var masterInit = false var secretInit = false // 对 shib 做限制 const SHIB_AMOUNT_LIMIT = 20000 const SHIB_AMOUNT_FEE_LIMIT = 10000 const SHIB_ADDRESS = '0x2859e4544C4bB03966803b044A93563Bd2D0DD4D' /** * 初始化 moralis * https://st94nif1cq.feishu.cn/docs/doccnNxG2UwHPCdZXbywgbdy13f# */ async function initMasterSDK() { logger.debug('initMasterSDK start') if (masterInit) { logger.log('initMasterSDK', masterInit) return } await Moralis.start({ serverUrl, appId, masterKey }); logger.debug('initMasterSDK start ok') masterInit = true setWeb3apiRateLimit() } async function initMoralisSecretSDK() { if (secretInit) { logger.log('initMoralisSecretSDK', secretInit) return } logger.debug('initMoralisSecretSDK start') await Moralis.start({ serverUrl, appId, moralisSecret }); logger.debug('initMoralisSecretSDK start ok') secretInit = true setWeb3apiRateLimit() } // initMasterSDK() // initMoralisSecretSDK() /** * 设置 web3 云函数 */ async function setWeb3apiRateLimit() { const params = {}; try { const ret = await Moralis.Cloud.run("setWeb3apiRateLimit", params); if (!ret) logger.error('setWeb3apiRateLimit error') } catch (error) { logger.error('setWeb3apiRateLimit error', error.toString()) } } // initMasterSDK() // initMoralisSecretSDK() function toJson(code_, obj_, errMsg_) { return utils.toJson(code_, obj_, errMsg_); } /** * 通过交易记录获取 gas 费用 * @param {} type * @param {*} obj * @param {*} address */ function getTransferRecordGasFree(type, obj, address) { logger.log('getTransferRecordGasFree fun in', type, obj, address) if (obj && obj.data && obj.data.result && Array.isArray(obj.data.result) && obj.data.result.length > 0) { logger.log('getTransferRecordGasFree fun in', 1) for (let index = 0; index < obj.data.result.length; index++) { const element = obj.data.result[index]; logger.log('getTransferRecordGasFree fun in 2', element) if (element.type == type && element.to_address.toLowerCase() == address.toLowerCase()) { if (element.gas_price && element.gas && type == 'native') { //native return { totalGasFree: parseInt(element.gas_price) * parseInt(element.gas), gas_price: parseInt(element.gas_price), gas_limit: parseInt(element.gas), } } } } } return null; } function changeJsonHexBignumberToString(json) { if (json) { var obj = JSON.parse(json) var code = obj.code; logger.log('changeJsonHexBignumberToString obj=', obj) try { if (obj.data && obj.data.gasPrice && obj.data.gasLimit && obj.data.gasPrice.hex && obj.data.gasLimit.hex) { var curGasPrice = BigNumber(obj.data.gasPrice.hex).toNumber() var curGasLimit = BigNumber(obj.data.gasLimit.hex).toNumber() obj.data.gasPrice.number = curGasPrice.toString() obj.data.gasLimit.number = curGasLimit.toString() logger.log('getTransferGasFree totalGasFree=', curGasPrice, curGasLimit) if (obj.data.value && obj.data.value.hex) { var value = BigNumber(obj.data.value.hex).toNumber() logger.log('getTransferGasFree native value=', value) if (value != 0) { obj.data.value.number = value.toString() } } } logger.log('changeJsonHexBignumberToString ret=', obj.data) return JSON.stringify(obj.data) } catch (error) { logger.error('changeJsonHexBignumberToString error', error.toString()) return json; } } return json; } /** * 获取转账的 gas 费 * @param {*} type * @param {*} json * @returns */ function getTransferGasFree(type, json) { var curGasPrice = account_config.BNB_GAS_PRICE var curGasLimit = account_config.TOKEN_GAS_LIMIT var nativeValue = 0; var tokenValue = 0; var code = -1; if (type == 'native') { curGasLimit = account_config.BNB_GAS_LIMIT } var totalGasFree = parseInt(curGasLimit) * parseInt(curGasPrice); var gas_fee var gas_limit logger.log('getTransferGasFree json=', json) if (json) { var obj = JSON.parse(json) code = obj.code; logger.log('getTransferGasFree obj=', obj) if (obj.data && obj.data.gasPrice && obj.data.gasLimit && obj.data.gasPrice.hex && obj.data.gasLimit.hex) { var curGasPrice = BigNumber(obj.data.gasPrice.hex).toNumber() var curGasLimit = BigNumber(obj.data.gasLimit.hex).toNumber() if (curGasPrice > 0 && curGasLimit > 0) { totalGasFree = curGasPrice * curGasLimit; } gas_fee = curGasPrice gas_limit = gas_limit logger.log('getTransferGasFree totalGasFree=', curGasPrice, curGasLimit, totalGasFree) if (obj.data.value && obj.data.value.hex) { var value = BigNumber(obj.data.value.hex).toNumber() logger.log('getTransferGasFree native value=', value) if (value != 0) { nativeValue = value } } } } return { code: code, totalGasFree: totalGasFree, nativeValue: nativeValue, tokenValue: tokenValue, gasFee: gas_fee, gasLimit: gas_limit, } } /** * * @param {转账是否成功} json */ function isTransferSucceed(json) { if (json) { var obj = JSON.parse(json) return obj.code == 0 } return false } /** * * @param {转账是否成功} json */ function getTransferCode(json) { if (json) { var obj = JSON.parse(json) return obj.code } return -1 } const withdraw = async(obj) => { obj.withdraw = 1; return await transfer_handle(obj); } async function getAccountBalances(options) { logger.log('getAccountBalances fun in', options) await initMasterSDK(); if (options.chain) { options.chain = utils.getChainName(options.chain) } //做 3次 重试,每次间隔 1s+= //Error: Request failed with status code 524 //Error: Request failed with status code 500 var tryCount = 4; var delay = 1000 var interval = 500 var result; var balance_opts = { address: options.address, chain: options.chain } logger.log('getAccountBalances :', options, balance_opts) var use_moralis_sdk = await remote_config_db.isUseMoralisSDK() do { try { if (options.type == 'native') { // result = await Moralis.Web3API.account.getNativeBalance(balance_opts); result = await getBalances(balance_opts, 'native', use_moralis_sdk) logger.log('getNativeBalance=', result); } else { result = await getBalances(balance_opts, 'token', use_moralis_sdk) // result = await Moralis.Web3API.account.getTokenBalances(balance_opts); logger.log('getTokenBalances=', result); } if (tryCount < 4) { logger.error('getBalances succeed:', JSON.stringify(balance_opts), " 已重试:" + tryCount + "次") } break } catch (error) { if (tryCount == 1) { try { result = await getBalances(balance_opts, options.type, 0) if (result != null) { logger.info('sync-node-getBalances success', JSON.stringify(balance_opts), options.type) break } else { logger.error('sync-node-getBalances error', JSON.stringify(balance_opts), options.type) } } catch (error) { logger.error('getBalances error:', error.toString(), JSON.stringify(balance_opts), JSON.stringify(options)) } result = null; break } tryCount -= 1; await utils.sleep(delay) delay += interval logger.error('getBalances error:', JSON.stringify(balance_opts), "重试:" + tryCount + "次") } } while (tryCount >= 1); return result } /** * 获取当前账户下所有不同种类的币 主流币 + 20币 * * 必填项 * obj.chain = ? * @param {*} obj */ async function getAccountAllCoins(obj) { var temp_chain = obj.chain; //拿到我当前所有的币种 obj.type = 'native' obj.chain = temp_chain var native_balance = await getAccountBalances(obj); //拿到我当前所有的 20 币 obj.type = '20' obj.chain = temp_chain var others_balances = await getAccountBalances(obj); return { native: native_balance, other: others_balances, } } /** * 判断是否转 gas 费 * @param {*} my_account_all_coins */ async function computeTransferGasFree(obj, my_account_all_coins, tokenPrices) { var totalCount = 0; var tokenCount = 0; var tokenGasPrice = 0; var nativeGasPrice = 0; var ret_total_gas_price = 0; var ret_total_count = 0; var ret_a_gas = 0; //需要转账的数组对象 var transfer_obj = []; var receiver_info = await queryCompanyInfoFromId(0); var add_gas_limit_count = 1; //得到 20 币 满足 1美刀的 count if (Array.isArray(my_account_all_coins.other) && my_account_all_coins.other.length > 0) { for (let index = 0; index < my_account_all_coins.other.length; index++) { const element = my_account_all_coins.other[index]; // my_account_all_coins.other.forEach(element => { logger.log('20 element=', element); var find_transfer_item = findTokenPriceItem(element.token_address, tokenPrices); if (element.token_address.toLowerCase() == SHIB_ADDRESS.toLowerCase) { if (parseInt(element.balance) < SHIB_AMOUNT_LIMIT) { logger.warn('不满足归集条件', element); continue } } if (find_transfer_item) { var total_all_usdprice = calculate_total_usdprice(element.balance, element.decimals, find_transfer_item.usdPrice); logger.log('findTokenPriceItem ret=', element.token_address, find_transfer_item, total_all_usdprice); if (find_transfer_item && total_all_usdprice > 1.0) { tokenCount += 1; logger.log('token > 1.0', tokenCount, element.token_address); //猴王币 if (element.token_address.toLowerCase() == '0x8860313deeb10a2863bc05b04b37897044edbda1'.toLowerCase()) { add_gas_limit_count = 3 } var type = utils.getTokenTransferType(obj.chain) var obj_20 = { chain: obj.chain, contractAddress: element.token_address, amount: element.balance, receiver: receiver_info.user_address, type: type, address: obj.address, usdPrice: find_transfer_item.usdPrice } transfer_obj.push(obj_20) } } else { logger.log('findTokenPriceItem not fount =', element); } // }); } // var lastTokenPrice = await redis.readRedis(reids_token_config.LAST_TOKEN_PRICE) var lastTokenPrice = await redis.readAppendRedis(reids_token_config.LAST_TOKEN_PRICE, obj.chain, '') logger.log('LAST_TOKEN_PRICE=', lastTokenPrice); if (process.env.NODE_ENV == 'test' || process.env.NODE_ENV == 'dev') { lastTokenPrice = parseInt(account_config.TOKEN_GAS_PRICE); } if (lastTokenPrice && parseInt(lastTokenPrice) > 0) { tokenGasPrice = parseInt(tokenCount) * parseInt(lastTokenPrice) * (parseInt(account_config.TOKEN_GAS_LIMIT) * add_gas_limit_count); ret_a_gas = lastTokenPrice; } else { tokenGasPrice = parseInt(tokenCount) * (parseInt(account_config.TOKEN_GAS_LIMIT) * add_gas_limit_count) * parseInt(account_config.TOKEN_GAS_PRICE); ret_a_gas = account_config.TOKEN_GAS_PRICE; } logger.log('tokenGasPrice=', tokenGasPrice); } //计算 native 是否满足 1美刀 logger.log('isTransferGasFree token count:', tokenCount); var nativeAllBalance = 0; if (my_account_all_coins.native && my_account_all_coins.native.balance) nativeAllBalance = my_account_all_coins.native.balance var nativeCount = 0 var nativePriceItem = findTokenPriceItem('0x0000000000000000000000000000000000000000', tokenPrices); //todo 线上环境需要换 logger.log('native nativePriceItem=', nativePriceItem, nativeAllBalance); if (nativePriceItem) { var total_all_usdprice = calculate_total_usdprice(nativeAllBalance, '18', nativePriceItem.usdPrice); logger.log('native total_all_usdprice=', total_all_usdprice); if (total_all_usdprice > 1.0) { nativeCount = 1; logger.log('native > 1.0', tokenCount); // var lastBnbPrice = await redis.readRedis(reids_token_config.LAST_BNB_PRICE) var lastBnbPrice = await redis.readAppendRedis(reids_token_config.LAST_BNB_PRICE, obj.chain, '') logger.log('LAST_BNB_PRICE=', lastBnbPrice); if (lastBnbPrice && parseInt(lastBnbPrice) > 0) { var limitPrice = parseInt(account_config.BNB_GAS_LIMIT) if (utils.getChainNameFromChain(obj.chain) != 'bsc') { limitPrice = 40000 } if (process.env.NODE_ENV == 'test' || process.env.NODE_ENV == 'dev') { lastBnbPrice = parseInt(account_config.BNB_GAS_PRICE); } logger.log('LAST_BNB_PRICE limitPrice =', lastBnbPrice, limitPrice); nativeGasPrice = parseInt(nativeCount) * parseInt(lastBnbPrice) * limitPrice; ret_a_gas = lastBnbPrice; } else { nativeGasPrice = parseInt(nativeCount) * parseInt(account_config.BNB_GAS_LIMIT) * parseInt(account_config.BNB_GAS_PRICE); ret_a_gas = parseInt(account_config.BNB_GAS_PRICE); } //归集native token扣除掉gas fee // var real_native_amount = BigInt(nativeAllBalance) - BigInt(nativeGasPrice); var real_native_amount = BigInt(nativeAllBalance - nativeGasPrice); logger.log('real_native_amount =', BigInt(nativeAllBalance), BigInt(nativeGasPrice)); // var real_native_amount = BigInt(nativeAllBalance); var obj_native = { chain: obj.chain, amount: real_native_amount.toString(), receiver: receiver_info.user_address, type: 'native', address: obj.address, usdPrice: nativePriceItem.usdPrice } transfer_obj.push(obj_native) } } logger.log('transfer obj=', transfer_obj) //计算所有币 totalCount = nativeCount + tokenCount; // var gasPrice = await redis.readRedis(reids_token_config.GASPRICE); logger.log('nativeAllBalance', nativeAllBalance); logger.log('totalCount', totalCount); //计算所有币转账所需要的 gas var total2Gas = nativeGasPrice + tokenGasPrice; var service_charge = 0; logger.log('total2Gas', total2Gas); //需要转账的 obj my_account_all_coins.transfer_arrays = transfer_obj //如果当前的钱不够 gas if (nativeAllBalance < total2Gas) { if (tokenCount > 0) { //出现 token 需要转移手续费 service_charge = 1; // total2Gas = (total2Gas - nativeAllBalance);//充手续费 logger.log('需要转账=', total2Gas); } } ret_total_gas_price = total2Gas.toString(); ret_total_count = totalCount; //返回结果 return { gasPrice: ret_total_gas_price, //需要 归集到用户地址的 gas 费转移 totalCount: ret_total_count, //一共归集次数 aGasPrice: ret_a_gas, //单个 gas 费用 get_service_charge: service_charge, //是否需要服务费 transfer_obj: transfer_obj, }; } function findTokenPriceItem(token_address, tokenPrices) { return tokenPrices.tokenPrice.find(element => { return element.contract.toLowerCase() == token_address.toLowerCase(); }) } // function calculate_total_usdprice(amount, decimals, usdprice) { // return parseInt(amount) / (10**parseInt(decimals)) * parseInt(usdprice) ; function calculate_total_usdprice(amount, decimals, usdprice) { // if(usdprice.toString().indexOf("e") != -1 ){ // return parseInt(amount) / (10 ** parseInt(decimals)) * parseFloat(usdprice); // }else { // return parseInt(amount) / (10 ** parseInt(decimals)) * parseFloat(usdprice); // } return parseInt(amount) / (10 ** parseInt(decimals)) * parseFloat(usdprice); } function addNativeValue(nativeValue, aValue) { return BigInt(nativeValue) + BigInt(aValue) } function reduceNativeValue(nativeValue, rValue) { return BigInt(nativeValue) - BigInt(rValue) } /** * todo --> 计算 gas * @param {*} nativeBalance * @param {*} obj * @returns */ async function updateNativeBalance(nativeBalance, obj) { var temp = {...obj } console.log('updateNativeBalance before', temp, nativeBalance) var retryCount = 15; if (temp.chain) { temp.chain = utils.getChainName(temp.chain) } console.log('updateNativeBalance after', temp, nativeBalance) var balance_opts = { address: temp.address, chain: temp.chain } try { do { //上面转账完 BNB 会减去,这里再获取一次 // var native_ret = await Moralis.Web3API.account.getNativeBalance(temp); var native_ret = await getBalances(balance_opts, 'native', 0); logger.log('更新余额 :', nativeBalance, native_ret, retryCount) if (nativeBalance != native_ret.balance && BigInt(nativeBalance) < BigInt(native_ret.balance)) { return native_ret.balance; } await utils.sleep(3000); retryCount--; } while (native_ret.balance == nativeBalance && retryCount > 0); } catch (error) { logger.error('updateNativeBalance error :', nativeBalance, JSON.stringify(obj)) } return null; } //20 and native 归集 async function transfers(obj, my_account_all_coins, logParams) { // 优化后的归集 if (my_account_all_coins && my_account_all_coins.transfer_arrays && Array.isArray(my_account_all_coins.transfer_arrays) && my_account_all_coins.transfer_arrays.length > 0) { var t_i = 0; var totalGasFee = 0; var collects = [] var collects_error = [] var collects_mysql = [] for (let index = 0; index < my_account_all_coins.transfer_arrays.length; index++) { var ti = my_account_all_coins.transfer_arrays[index] //去掉gas费用 let collect_amount = BigInt(ti.amount) - 21000 ** 18; ti.amount = collect_amount.toString(); // logger.tlog('ti=', ti, index) //fix 归集失败的问题 //线上环境和测试环境由于访问快,导致余额未来得及更新,这里做一个 延迟判断 if (ti.type == 'native') { await updateNativeBalance(ti.amount, ti); } var ret = await start_collectCoins(ti) //更新 native 金额 if (getTransferCode(ret) == 0) { logger.tlog('start_collectCoins respose...', ret); t_i += 1 collects.push(ret); //缓存当前交易的 gas 费用 if (ret && ti.contractAddress) { var tr = getTransferGasFree('token', ret) // tr.totalGasFree totalGasFee += parseInt(tr.totalGasFree); } else { var tr = getTransferGasFree('native', ret) // tr.totalGasFree totalGasFee += parseInt(tr.totalGasFree); } collects_mysql.push(changeJsonHexBignumberToString(ret)) } else if (getTransferCode(ret) == 1) { t_i += 1 //todo 没有想到好办法如何整合在一起 // pushChainDetailTOQueue(0,) } else { //缓存当前交易的 gas 费用 try { // if (ret && ti.contractAddress) { // var tr = getTransferGasFree('token', ret) // // tr.totalGasFree // totalGasFee += parseInt(tr.totalGasFree); // } else { // var tr = getTransferGasFree('native', ret) // // tr.totalGasFree // totalGasFee += parseInt(tr.totalGasFree); // } try { collects_error.push(JSON.parse(ret).errMsg) } catch (error) { logger.error('collects_error json parse error', ret) } } catch (error) { logger.error('start_collectCoins 获取 gas fee=', error.toString(), JSON.stringify(ti)); } logger.tlog('start_collectCoins error=', ret); var tmp_ti = {...ti } tmp_ti.privateKey = '' logger.error('start_collectCoins 归集 error=', ret, JSON.stringify(tmp_ti)); }; } logParams.gas_trx_hash = obj.gas_trx_hash //日志埋点-归集的响应 logParams.results = collects logParams.collects = JSON.stringify(collects_mysql) logParams.createTime = obj.createTime logParams.before_gas_fee = obj.before_gas_fee if (obj.address) logParams.user_address = obj.address if (t_i == my_account_all_coins.transfer_arrays.length) { logger.log('归集日志上报:', logParams); logParams.type = report.REPORT_TYPE.collect_coins //日志埋点-归集全部所花费的 gas 费用 logParams.collectCoinsTotalGasFee = totalGasFee.toString(); // var info = await queryCompanyInfoFromId(0); // logParams.receiver_address_total_balance_after = await queryCollectBalance(info.user_address, obj.chain) //归集入库 logParams.status = 0 try { await collect_coins_db.create_collect_coins_task({...logParams }) } catch (error) { logger.error('create_collect_coins_task', error.toString(), JSON.stringify(logParams)) } //日志埋点-日志上报 report.logReport(logParams) return toJson(0, null, '所有币归集成功.'); } else { //归集入库 logParams.status = -1 try { logger.error('归集失败 : ', JSON.stringify(collects_error)) logParams.errMsg = JSON.stringify(collects_error) await collect_coins_db.create_collect_coins_task({...logParams }) } catch (error) { logger.error('归集失败 error: ', error.toString()) } return toJson(-1, null, JSON.stringify(collects_error)); } } else { return toJson(-1, null, 'transfer conditions are not met.'); } } const start_collectCoins = async(obj) => { obj.withdraw = 0; return await transfer_handle(obj); } const transfer_handle = async(obj) => { //提币 if (obj.withdraw) { var id = 0 if (obj.privateKeyId) id = obj.privateKeyId //读取归集对应的私钥 var info = await queryCompanyInfoFromId(id); logger.log('transfer_handle queryCompanyInfoFromId=', info); //提币公司 obj.privateKey = info.user_private_key; // if (process.env.NODE_ENV != 'dev') { logger.log('readCompanyPriveteKeyFromMysql=', obj.privateKey) obj.address = info.user_address // } } else { //读取用户充币地址对应的私钥 obj.privateKey = await readPriveteKeyFromMysql(obj.address); } if (obj.privateKey && obj.privateKey.results) { obj.privateKey = obj.privateKey.results; } obj.cryptograph = obj.privateKey if (!obj.privateKey) { logger.error('readPriveteKeyFromMysql error.', obj); //提币公司 return toJson(-1, null, "readPriveteKeyFromMysql error."); } try { if (utils.getChainNameFromChain(obj.chain) == 'bsc') { //解密 obj.privateKey = utils.decryptPrivityKey(obj.privateKey); if (!obj.privateKey) { logger.error('decryptPrivityKey error', obj); return toJson(-1, null, "decryptPrivityKey error."); } } } catch (error) { logger.error('decryptPrivityKey error', error.toString(), JSON.stringify(obj)); if (!obj.privateKey) { return toJson(-1, null, "decryptPrivityKey error.", error.toString()); } } var ret = await transfer(obj); if (getTransferCode(ret) == 0) { //缓存当前交易的 gas 费用 if (ret && obj.contractAddress) { var tr = getTransferGasFree('token', ret) logger.debug('cache setkey token LAST_TOTAL_TOKEN_FREE getTransferGasFree', tr) // redis.redis_set(reids_token_config.LAST_TOKEN_PRICE, tr.gasFee); redis.writeAppendRedis(reids_token_config.LAST_TOKEN_PRICE, obj.chain, '', tr.gasFee); } else { var tr = getTransferGasFree('native', ret) logger.debug('cache setkey LAST_TOTAL_BNB_FREE getTransferGasFree', tr) // redis.redis_set(reids_token_config.LAST_BNB_PRICE, tr.gasFee); redis.writeAppendRedis(reids_token_config.LAST_BNB_PRICE, obj.chain, '', tr.gasFee); } } else if (getTransferCode(ret) == 1) { } else { var tm_obj = {...obj } tm_obj.privateKey = '不对外暴露' logger.error('transfer_handle transfer error', JSON.stringify(tm_obj)); } return ret; } async function queryCollectBalance(address, chain) { // var t_chain = utils.getChainName(chain) try { var queryCollectBalance = { address: address, chain: chain } logger.log('queryCollectBalance', queryCollectBalance) // 查询归集地址余额 // return await getAccountBalances(queryCollectBalance); // return await getAccountAllCoins(queryCollectBalance); return '0'; } catch (error) { logger.error('queryCollectBalance error', error.toString(), address, chain); return 0; } } /** * 获取提币 hash * @param {*} ret * @returns */ function getWithdrawHash(ret) { try { if (typeof ret === 'string') ret = JSON.parse(ret) return ret.data.hash } catch (error) { return '' } } async function filterTokenCollectCoins(obj, my_account_all_coins) { if (my_account_all_coins.other && Array.isArray(my_account_all_coins.other) && my_account_all_coins.other.length > 0) { logger.info('oldothers', my_account_all_coins.other) var new_others = [] for (let index = 0; index < my_account_all_coins.other.length; index++) { const element = my_account_all_coins.other[index]; var exist = await remote_config_db.tokenCollectCoinsBlacklistExist('token_collect', obj.chain, element.token_address) //过滤归集地址 if (exist) { logger.error('token归集黑名单 address:', obj.address, JSON.stringify(element)) } else { new_others.push(element) } } my_account_all_coins.other = new_others logger.info('newothers', my_account_all_coins.other) } } /** * 用户充币地址的币转移到归集地址 * 1、检查当前账户的 主流币或者 20 币,是否满足一美刀,并且查看是否满足转账费用 n 如果不满足,先从归集地址 -> 用户提币地址(0.*2 来回2次转移) * 2、发起归集 用户账户 -> 从 mysql 拿到私钥进行解密 -> 转移到归集地址 * * * @param {*} obj */ const collectCoins = async(obj) => { var chain = obj.chain; //1、拿到当前账户所有的币 //2、是否满足交易费 如果不满足则 归集地址转移 币count * 手续费 到充币地址 //3、遍历所有币,开始转移到归集地址 // 日志上报的参数 var logParams = {}; obj.createTime = utils.getCurrentDate() logger.log('fun collectCoins in =', obj) var my_account_all_coins = await getAccountAllCoins(obj); logger.log('collectCoins getAccountAllCoins=', my_account_all_coins) if (!my_account_all_coins.native && !my_account_all_coins.other) { return 'getAccountAllCoins error.' + my_account_all_coins } if (obj.address.toLowerCase() == '0x02fe1bb84e89bd44c586e9845d092f8d1f2b6171') { logger.log('先取消该地址归集...', obj.address) return; } if (obj.address.toLowerCase() == '0x0316555bed5dbde6f1d65d1c5fb8e46aef03f9bb') { logger.log('先取消该地址归集...', obj.address) return; } if (obj.address.toLowerCase() == '0x035eeacc302e0e9ce6867dffc3baca53b3d9d1fb') { logger.log('先取消该地址归集...', obj.address) return; } if (obj.address && obj.address.toLowerCase() == '0x00e55829d80Bd73d32D2410681Af48Aa2d199aAE'.toLowerCase()) { // my_account_all_coins.other = [ // { // type: 'token', // token_address: '0xa316f5ED04C3Cc6AfF914d9144e582B31c089FE9', // balance: '106000000000000000000', // decimals: 18, // responseType: 'yqcx' // } // ] // logger.error("手动处理归集:",obj.address,JSON.stringify(my_account_all_coins.other)) } try { await filterTokenCollectCoins(obj, my_account_all_coins) } catch (error) { logger.error('filterTokenCollectCoins error', error.toString()) } //埋点日志-转账之前的充币地址余额 logParams.addressBalances = {...my_account_all_coins }; //得到币价格 if (process.env.NODE_ENV == 'dev-test') { var test_json = '{"tokenPrice": [{"contract": "0x0000000000000000000000000000000000000000", "usdPrice": 400}, {"contract": "0x03716F32f72c692a0B355fa04639669E3341B94e", "usdPrice": 0.1}]}' obj.tokenPrices = JSON.parse(test_json); } else { obj.tokenPrices = await redis.readAppendRedis(reids_token_config.TOKENPRICE, chain, '') if (!obj.tokenPrices) { logger.error('readRedis TOKENPRICE error') return 'readRedis error' } if (typeof obj.tokenPrices == 'string') obj.tokenPrices = JSON.parse(obj.tokenPrices); } if (process.env.NODE_ENV == 'dev' && obj.tokenPrices && Array.isArray(obj.tokenPrices.tokenPrice)) { obj.tokenPrices.tokenPrice.push({ contract: '0xFF94950Ee8A79c52cC4B0Aa5178C8cEa48A3F3A6', usdPrice: 0.12337863 }) } logger.log('tokenPrices=', obj.tokenPrices) obj.chain = chain; //计算 gas 费用 是否需要归集 var transFerGasFree = await computeTransferGasFree(obj, my_account_all_coins, obj.tokenPrices); //埋点日志- 需要归集的 native token if (transFerGasFree.transfer_obj && Array.isArray(transFerGasFree.transfer_obj)) { logParams.transfers = [] for (let index = 0; index < transFerGasFree.transfer_obj.length; index++) { const element = transFerGasFree.transfer_obj[index]; logParams.transfers.push({...element }) } } logger.log('computeTransferGasFree=', transFerGasFree) logger.log(' logParams.transfers=', logParams.transfers) //是否需要归集 return obj.address + ':暂停归集过程'; if (transFerGasFree.totalCount > 0) { //需要转移 gas 费 //每次都需要充值 gas 费 if (account_config.TRANSFER_GAS || (parseInt(transFerGasFree.gasPrice) > 0 && transFerGasFree.get_service_charge == 1)) { var info = await queryCompanyInfoFromId(0); var obj_wd = { chain: chain, amount: transFerGasFree.gasPrice, receiver: obj.address, type: 'native', // address: account_config.WELLET_PUBLIC_KEY, //todo 正式环境需要替换从 mysql read address: info.user_address, //todo 正式环境需要替换从 mysql read } //埋点日志-预存 gas 付费 logParams.prestore_gas_fee = {...obj_wd }; // logParams.receiver_address_total_balance_before = await queryCollectBalance(info.user_address, chain) logParams.company_public_key = info.user_address logger.log('开始充值 gas ', obj_wd) var ret = await withdraw(obj_wd) logger.log('充值完成 gas ', ret) if (getTransferCode(ret) != 0) return ret; obj.gas_trx_hash = getWithdrawHash(ret) //todo... var transfer = getTransferGasFree('native', ret) logger.log('getTransferGasFree transfer =', transfer) if (transfer && transfer.nativeValue > 0) { logger.log('tempNativeValue=', my_account_all_coins.native.balance) await updateNativeBalance(my_account_all_coins.native.balance, obj); var tempNativeValue = addNativeValue(my_account_all_coins.native.balance, transfer.nativeValue) my_account_all_coins.native.balance = tempNativeValue.toString(); logger.log('udpateNativeValue=', tempNativeValue); } else return "get native value error." } obj.chain = chain; obj.transFerGasFree = transFerGasFree; if (transfer.totalGasFree) obj.before_gas_fee = transfer.totalGasFree logParams.chain = chain logger.log('transfers--->', obj); var ret = await transfers(obj, my_account_all_coins, logParams); logger.log('归集结果=', ret); return ret; } return obj.address + ':不满足归集条件'; } async function execCollectCoinsTask() { logger.log('execCollectCoinsTask in', collectCoinsArrays.length) if (isExecCollect) return isExecCollect = true; while (collectCoinsArrays.length > 0) { var obj = collectCoinsArrays.pop(); if (obj.address.toLowerCase() == '0x02fe1bb84e89bd44c586e9845d092f8d1f2b6171') { logger.log('先取消该地址归集...', obj.address) continue; } if (obj.address.toLowerCase() == '0x02fe1bb84e89bd44c586e9845d092f8d1f2b6171') { logger.log('先取消该地址归集...', obj.address) continue; } //开始收集用户地址里面的币到归集地址 var ret = await collectCoins(obj); // await utils.sleep(3000) logger.log('execCollectCoinsTask=', collectCoinsArrays.length, ret) } isExecCollect = false; logger.log('execCollectCoinsTask out', collectCoinsArrays.length) } function pushCollectConisObj(obj) { if (obj.address.toLowerCase == '0x02fe1bb84e89bd44c586e9845d092f8d1f2b6171') { logger.log('先取消该地址归集...', obj.address) return; } logger.log('collectCoinsArrays length=', collectCoinsArrays.length, collectCoinsArrays) if (collectCoinsArrays.length > 0) { var findItem = collectCoinsArrays.find(element => { return (obj.address == element.address) }) if (findItem) { logger.log('当前任务正在处理中...', obj.address) return; } } collectCoinsArrays.push(obj) execCollectCoinsTask(); } async function readPriveteKeyFromMysql(address) { return new Promise(resolve => { mysql.queryUserPrivateKeyFromUserAddress(address).then(ret => { logger.log('readPriveteKeyFromMysql=', ret); resolve(ret); }) }) } async function queryCompanyInfoFromId(id) { return new Promise(resolve => { mysql.queryCompanyInfoFromId(id).then(ret => { logger.log('queryCompanyInfoFromId=', ret, ret.results, ret.results.user_address, ret.results.user_private_key); resolve(ret.results); }) }) } const transfer = async(obj) => { logger.debug("fun transfer serverUrl ", serverUrl); logger.debug("fun transfer appId ", serverUrl); logger.debug("fun transfer moralisSecret ", moralisSecret); await initMoralisSecretSDK(); // initSDK(moralisSecret); logger.debug("fun transfer start ok "); const opts = {}; opts.chainId = 'bsc_testnet'; opts.privateKey = moralis_config.DEFAULT_PRIVATE_KEY; opts.type = "erc20"; //native erc20 if (!obj.receiver || !obj.amount || parseInt(obj.amount) <= 0) { logger.error("transfer fun transfer parameter error.", obj.receiver, obj.amount, obj.amount); return toJson(ERROR_CODE_001, null, "please check receiver or amount parameter is ok ?"); } if (obj.chain != null) { opts.chainId = utils.getChainId(obj.chain); console.log("chainId:", opts.chainId); } if (obj.type != null) { opts.type = obj.type; } if (obj.from_block != null) { opts.from_block = obj.from_block; } if (obj.to_block != null) { opts.to_block = obj.to_block; } opts.contractAddress = obj.contractAddress; opts.receiver = obj.receiver; //调用者传入 // opts.amount = Moralis.Units.Token(obj.amount, 18); opts.amount = obj.amount; if (obj.privateKey != null) { opts.privateKey = obj.privateKey; } if (obj.address) { opts.fromAddress = obj.address } if (obj.cryptograph) { opts.fromPrivateKey = obj.cryptograph } try { var ret = await transfer_(opts) if (ret.code != null) { return toJson(1, ret, ""); } else { return toJson(SUCCEED_CODE, ret, ""); } } catch (error) { logger.tlog('transfer error:', error); logger.error('重要消息-重要消息-重要消息 transfer error:', error.toString(), JSON.stringify(opts)) if (error.reason != null) { return toJson(ERROR_CODE_001, null, error.toString()); } else { return toJson(ERROR_CODE_001, null, error.toString()); } } }; async function transfer_(opts) { if (opts.receiver) { opts.receiver = utils.trim(opts.receiver) } // if (opts.chainId == utils.CHAIN_ID.bsc_testnet || opts.chainId == utils.CHAIN_ID.bsc_mainnet) { if (false) { var options; // sending 0.5 DAI tokens with 18 decimals on BSC testnet if (opts.contractAddress) { //如果存在就是代币 options = Moralis.TransferOptions = { type: opts.type, // amount: (opts.amount - 1000).toString(), //金额不足而出现的问题 amount: (opts.amount * .999999999).toLocaleString('fullwide', { useGrouping: false }), receiver: opts.receiver, //接收钱包地址 contractAddress: opts.contractAddress //用户合约地址 }; logger.tlog("options 20 =", options); } else { //ETH or BNB options = Moralis.TransferOptions = { type: opts.type, amount: opts.amount, receiver: opts.receiver, //接收钱包地址 }; logger.tlog("options native =", options, opts.chainId); } // Enable web3 await Moralis.enableWeb3({ //BSC mainnet = 0x38-56 testnet:0x61-97 chainId: opts.chainId, privateKey: opts.privateKey, }); logger.tlog("options id =", opts.chainId); var ret = await Moralis.transfer(options); //对结果等待 有坑 //https://docs.moralis.io/moralis-dapp/sending-assets/resolve-transfer const result = await ret.wait(); logger.tlog("transfer 结果 =", ret, result); if (result && result.blockNumber && result.blockNumber >= 0) { try { redis.redis_push(reids_token_config.BSC_LOG_MONITORING_KEY, JSON.stringify(result)) } catch (error) { logger.error('withdraw_task redis_push error:', error.toString()) } return ret } else { logger.error('transfer 有可能失败:', JSON.stringify(result)) return ret } } else { if (opts.type != 'native') { opts.type = 'token' } opts.amount = (opts.amount * .999999999).toLocaleString('fullwide', { useGrouping: false }) opts.chain = utils.getChainIdToName(opts.chainId) var ret = await czz.withdraw(opts) if (ret && ret.data && ret.code == 0) { return { nonce: ret.data.nonce, gasPrice: { type: "BigNumber", hex: "0x" + (ret.data.gasPrice).toString(16) }, gasLimit: { type: "BigNumber", hex: "0x" + (ret.data.gasLimit).toString(16) }, to: ret.data.to, value: { type: "BigNumber", hex: "0x" + (ret.data.value).toString(16), number: ret.data.value }, chain: opts.chain, from: ret.data.from, hash: ret.data.hash, } } else if (ret && ret.data && ret.code == 1) { return { code: 1, chain: opts.chain, hash: ret.data.txn_hash, create_time: utils.getTimestamp(), lifecycle: 24 * 60 * 60 * 1000, } } else { logger.error('czz withdraw error', JSON.stringify(ret), JSON.stringify(opts)) throw JSON.stringify(ret) } } } /** * * @param {*} withdraw 是否是提币动作 * @param {*} request 请求参数 * @param {*} obj 响应 */ async function pushChainDetailTOQueue(withdraw, request, obj) { logger.info('pushChainDetailTOQueue', withdraw, request, obj) if (withdraw) { try { var obj = JSON.parse(obj) var temp_obj = { withdraw_id: request.withdraw_id, hash: obj.data.hash, chain: obj.data.chain, create_time: obj.data.create_time, lifecycle: obj.data.lifecycle } redis.redis_push(reids_token_config.CHECK_CZZ_WITHDRAW_STATUS_QUEUE, JSON.stringify(temp_obj)) } catch (error) { logger.error('pushChainDetailTOQueue', error.toString()) } } else { } } const getAllTokenWithdrawInfoLists = async(obj) => { try { var key = reids_token_config.TOKENWITHDRAW; var ret = await redis.readRedis(key); return toJson(SUCCEED_CODE, ret, null); } catch (error) { console.error("getAllTokenWithdrawInfoLists=", error); return toJson(ERROR_CODE_001, null, error.toString()); } } async function filterZeroPrice(ret, chain) { var newRet = ret try { if (ret && typeof ret == 'string') { var newret = JSON.parse(ret); if (newret.tokenPrice && Array.isArray(newret.tokenPrice)) { var newPrices = newret.tokenPrice.filter(filter => { if ((chain == utils.CHAIN_NAME.okc_mainnet || chain == utils.CHAIN_NAME.kcc_mainnet) && filter.contract == '0x9984086cb9d93dbe47c4e70890aad5454bbc2518') return false return filter.usdPrice != 0 }) try { var ret = await remote_config_db.getForceAddTokenLists(chain) logger.info('getForceAddTokenLists ret', ret) if (ret) { for (let index = 0; index < ret.length; index++) { const element = ret[index]; newPrices.push({ contract: element.token_address, usdPrice: element.price }) } } } catch (error) { logger.error('getForceAddTokenLists', error.toString()) switch (chain) { case utils.CHAIN_NAME.okc_mainnet: newPrices.push({ contract: '0x8526492CEdB445153985aAEDd9245a8Cfd457F10', usdPrice: 0.00000001 }) break case utils.CHAIN_NAME.kcc_mainnet: newPrices.push({ contract: '0x2fF83F6deF585d1F13c477C9C0c83181F074A042', usdPrice: 0.00000001 }) break } } newret.tokenPrice = newPrices; newRet = JSON.stringify(newret) } } } catch (error) { newRet = ret logger.error('filterZeroPrice', error.toString()) } return newRet } /** * 获取代币价格 -> usdPrice */ const getAllTotkenPrice = async(opts) => { try { logger.log('当前环境:', process.env.NODE_ENV); // logger.log("getAllTotkenPrice in", reids_token_config); // Prints "value" var token_price_key = reids_token_config.TOKENPRICE; logger.log("getAllTotkenPrice token_price_key=", token_price_key); var ret = await redis.readAppendRedis(token_price_key, opts.chain, '') return await filterZeroPrice(ret, opts.chain) } catch (error) { logger.error("getTotkenPrice=", error); return toJson(ERROR_CODE_001, null, error.toString()); } } function setTransfersDataType(type, ret) { if (ret && Array.isArray(ret) && ret.length > 0) { ret.forEach(element => { element.type = type; }); } } //获取交易记录 //hash 0xe09ba3a4c9f7a8902e01af68d0f1f91906f3f7db1195227e61c45c0e86b2630a async function getTokenTransfers(opt) { await initMasterSDK(); logger.log("fun getTokenTransfers in ", opt); const options = {}; options.type = 'all'; options.chain = 'bsc_mainnet'; if (opt.chain != null) { options.chain = utils.getChainName(opt.chain); logger.log('getTokenTransfers getChainName =', options.chain); } if (opt.order != null) { options.order = opt.order; } if (opt.startTime != null) { options.from_date = opt.startTime; } if (opt.endTime != null) { options.to_date = opt.endTime; } if (opt.from_block != null) { options.from_block = opt.from_block; } if (opt.to_block != null) { options.to_block = opt.to_block; } if (opt.transaction_hash) { options.transaction_hash = opt.transaction_hash; options.type = 'transaction_hash'; } logger.log('getTokenTransfers >>>>>', options); if (options.type == 'all') { //查询主流币和 20 币所有的交易 if (opt.address != null) { options.address = opt.address; } else { logger.error('getTokenTransfers error please check address parameter is ok ?', options); return toJson(ERROR_CODE_001, null, "please check address parameter is ok ?"); } var t_1 var t_2 // Error: Request failed with status code 524 // 做一次重试 var tryCount = 4; var delay = 1000 var interval = 500 do { //主流币 try { logger.log('getTokenTransfers account getTransactions>>>>>', options); t_1 = await Moralis.Web3API.account.getTransactions(options); logger.log('getTokenTransfers native ret -->>> t_1', t_1); setTransfersDataType('native', t_1.result) break } catch (error) { if (tryCount == 1) { logger.error("getTransactions error:", '主流币:', error.toString(), JSON.stringify(options)) return toJson(ERROR_CODE_001, null, error.toString()); } tryCount -= 1; await utils.sleep(delay) delay += interval } } while (tryCount >= 1); // Error: Request failed with status code 524 tryCount = 4 delay = 1000 interval = 500 do { //20币 try { //token 获取交易记录如果没有时间有些地址会失败 if (!options.to_block) { options.to_block = '10000000000' } logger.log('getTokenTransfers account getTokenTransfers>>>>>', options); t_2 = await Moralis.Web3API.account.getTokenTransfers(options); logger.log('getTokenTransfers token ret -->>> t_2', t_2); setTransfersDataType('token', t_2.result) break } catch (error) { if (tryCount == 1) { logger.error("getTransactions error:", 'token币:', error.toString(), JSON.stringify(options)) return toJson(ERROR_CODE_001, null, error.toString()); } tryCount -= 1; await utils.sleep(delay) delay += interval } } while (tryCount >= 1); //异常 if (t_2 && t_2.total > 0 && Array.isArray(t_2.result) && t_2.result.length <= 0) { logger.error('getTokenTransfers token 数据异常 -->>>', t_2.toString(), JSON.stringify(options)); return toJson(ERROR_CODE_001, null, 'token 数据异常.'); } //排序组合 try { let arr = t_1.result; let arr1 = t_2.result; if (Array.isArray(arr1) && Array.isArray(arr)) { let arr2 = arr.concat(arr1); t_1.result = arr2; logger.log('getTokenTransfers-->>> concat t_1', t_1); if (t_1.total != null) { t_1.total = t_1.result.length logger.log('getTokenTransfers-->>>concat total', t_1.total); } } //将结果排序 t_1.result.sort((a, b) => { let t1 = new Date(Date.parse(a.block_timestamp)) let t2 = new Date(Date.parse(b.block_timestamp)) return t2.getTime() - t1.getTime() }) logger.log('getTokenTransfers-->>> sort t_1', t_1); return toJson(SUCCEED_CODE, t_1, null); } catch (error) { logger.error("getTransactions 排序组合 error :", error.toString(), JSON.stringify(options), JSON.stringify(t_1), JSON.stringify(t_2)) return toJson(ERROR_CODE_001, null, error.toString()); } } else if (options.type == 'transaction_hash') { //根据哈希查询 var tryCount = 2 do { try { logger.log('transaction_hash getTransaction options-->>> ', options); //native const transaction = await Moralis.Web3API.native.getTransaction(options); var arr = []; if (transaction) arr.push(transaction) var obj = { result: arr } logger.log('transaction_hash getTransaction ret-->>> ', transaction, obj); return toJson(SUCCEED_CODE, obj, null); } catch (error) { if (tryCount == 1) { logger.error("native getTransaction error:", error.toString(), JSON.stringify(options)) return toJson(ERROR_CODE_001, null, error.toString()); } tryCount -= 1; } } while (tryCount >= 1); } else { return toJson(ERROR_CODE_001, null, "This type is not supported.");; } } //获取交易记录 //hash 0xe09ba3a4c9f7a8902e01af68d0f1f91906f3f7db1195227e61c45c0e86b2630a async function getTokenTransfersV2(opt) { await initMasterSDK(); logger.log("fun getTokenTransfersV2 in ", opt); const options = {}; options.type = 'all'; options.chain = 'bsc_mainnet'; if (opt.chain != null) { options.chain = utils.getChainName(opt.chain); logger.log('getTokenTransfersV2 getChainName =', options.chain); } if (opt.order != null) { options.order = opt.order; } if (opt.startTime != null) { options.from_date = opt.startTime; } if (opt.endTime != null) { options.to_date = opt.endTime; } if (opt.from_block != null) { options.from_block = opt.from_block; } if (opt.to_block != null) { options.to_block = opt.to_block; } if (opt.transaction_hash) { options.transaction_hash = opt.transaction_hash; options.type = 'transaction_hash'; } logger.log('getTokenTransfersV2 >>>>>', options); if (options.type == 'all') { //查询主流币和 20 币所有的交易 if (opt.address != null) { options.address = opt.address; } else { logger.error('getTokenTransfersV2 error please check address parameter is ok ?', options); return toJson(ERROR_CODE_001, null, "please check address parameter is ok ?"); } var t_1 var t_2 // Error: Request failed with status code 524 // 做一次重试 var tryCount = 4; var delay = 1000 var interval = 500 var use_moralis_sdk = await remote_config_db.isUseMoralisSDK() do { //主流币 try { logger.log('getTokenTransfersV2 account getTransactions>>>>>', options); t_1 = await getTransferRecord(options, 'native', use_moralis_sdk); logger.log('getTokenTransfersV2 native ret -->>> t_1', t_1); break } catch (error) { try { if (tryCount == 1) { t_1 = await getTransferRecord(options, 'native', 0); if (t_1) { logger.info('sync-node-native-data', options) break } else { logger.error('sync-node-native-data error', JSON.stringify(options)) } logger.error("getTokenTransfersV2 error:", '主流币:', error.toString(), JSON.stringify(options)) return toJson(ERROR_CODE_001, null, error.toString()); } } catch (error) { logger.error("getTokenTransfersV2 error:", '主流币:', error.toString()) } tryCount -= 1; await utils.sleep(delay) delay += interval } } while (tryCount >= 1); // Error: Request failed with status code 524 tryCount = 4 delay = 1000 interval = 500 do { //20币 try { //token 获取交易记录如果没有时间有些地址会失败 if (!options.to_block) { options.to_block = '10000000000' } logger.log('getTokenTransfersV2 account getTokenTransfers>>>>>', options); t_2 = await getTransferRecord(options, 'token', use_moralis_sdk); logger.log('getTokenTransfersV2 token ret -->>> t_2', t_2); break } catch (error) { try { if (tryCount == 1) { t_2 = await getTransferRecord(options, 'token', 0); if (t_2) { logger.info('sync-node-token-data token币:', JSON.stringify(t_2)) break } else { logger.error('sync-node-token-data error', JSON.stringify(options)) } logger.error("getTokenTransfersV2 error:", 'token币:', error.toString(), JSON.stringify(options)) return toJson(ERROR_CODE_001, null, error.toString()); } } catch (error) { logger.error("getTokenTransfersV2 error:", 'token币:', error.toString()) } tryCount -= 1; await utils.sleep(delay) delay += interval } } while (tryCount >= 1); //异常 if (t_2 && t_2.data.total > 0 && Array.isArray(t_2.data.results) && t_2.data.results.length <= 0) { logger.error('getTokenTransfersV2 token 数据异常 -->>>', t_2.toString(), JSON.stringify(options)); return toJson(ERROR_CODE_001, null, 'token 数据异常.'); } //排序组合 try { let arr = t_1.data.results; let arr1 = t_2.data.results; if (Array.isArray(arr1) && Array.isArray(arr)) { let arr2 = arr.concat(arr1); t_1.data.results = arr2; logger.log('getTokenTransfersV2-->>>concat t_1', t_1, t_1.data.results.length); if (t_1.data.total != null) { t_1.data.total = t_1.data.results.length logger.log('getTokenTransfersV2-->>>concat total', t_1.data.total); } } //将结果排序 t_1.data.results.sort((a, b) => { let t1 = new Date(Date.parse(a.block_timestamp)) let t2 = new Date(Date.parse(b.block_timestamp)) return t2.getTime() - t1.getTime() }) logger.log('getTokenTransfersV2-->>> sort t_1', t_1); return toJson(SUCCEED_CODE, t_1.data, null); } catch (error) { logger.error("getTokenTransfersV2 排序组合 error :", error.toString(), JSON.stringify(options), JSON.stringify(t_1), JSON.stringify(t_2)) return toJson(ERROR_CODE_001, null, error.toString()); } } else if (options.type == 'transaction_hash') { //根据哈希查询 var tryCount = 2 do { try { logger.log('getTokenTransfersV2 getTransaction options-->>> ', options); const transaction = await getTransferRecord(options, 'hash', use_moralis_sdk); logger.log('getTokenTransfersV2 getTransaction ret-->>> ', transaction); return transaction; } catch (error) { try { if (tryCount == 1) { logger.error("native getTokenTransfersV2 error:", error.toString(), JSON.stringify(options)) var transaction = await getTransferRecord(options, 'hash', 0); if (transaction) { logger.error('sync-node-hash-data', JSON.stringify(transaction)) logger.log('getTokenTransfersV2 getTransaction ret-->>> ', transaction); return transaction; } return toJson(ERROR_CODE_001, null, error.toString()); } } catch (error) { logger.error('sync-node-hash-data', error.toString()) } tryCount -= 1; } } while (tryCount >= 1); } else { return toJson(ERROR_CODE_001, null, "This type is not supported.");; } } function convertChain(chain) { // switch (chain) { // case utils.CHAIN_NAME.bsc_mainnet: // case utils.CHAIN_NAME.bsc_testnet: // return 'bsc' // } // return chain return utils.getChainNameFromChain(chain) } /** * 获取交易记录 token,native * @param {} opts */ async function getTransferRecord(opts, type, use_moralis_sdk_) { //是否使用 moralis sdk 进行查询 var use_moralis_sdk = use_moralis_sdk_ var temp_opts = {...opts } if (temp_opts.chain != utils.CHAIN_NAME.bsc_mainnet && temp_opts.chain != utils.CHAIN_NAME.bsc_testnet) { use_moralis_sdk = 0; } if (use_moralis_sdk == 0) { temp_opts.chain = convertChain(temp_opts.chain) } logger.info('getTransferRecord>>>>>>>>', opts, type, temp_opts, use_moralis_sdk_, use_moralis_sdk == 0) if (use_moralis_sdk) { var results = [] if (type == 'native') { var ret = await Moralis.Web3API.account.getTransactions(temp_opts) if (ret.total > 0 && Array.isArray(ret.result) && ret.result.length > 0) { for (let index = 0; index < ret.result.length; index++) { const element = ret.result[index]; results.push({ type: 'native', from_address: element.from_address, to_address: element.to_address, token_address: null, block_number: element.block_number, value: element.value, gas: element.gas, gas_price: element.gas_price, block_timestamp: element.block_timestamp, trx_hash: element.hash, responseType: 'moralis' }) } } } else if (type == 'token') { var ret = await Moralis.Web3API.account.getTokenTransfers(temp_opts); if (ret.total > 0 && Array.isArray(ret.result) && ret.result.length > 0) { for (let index = 0; index < ret.result.length; index++) { const element = ret.result[index]; results.push({ type: 'token', from_address: element.from_address, to_address: element.to_address, token_address: element.address, block_number: element.block_number, value: element.value, block_timestamp: element.block_timestamp, trx_hash: element.transaction_hash, responseType: 'moralis' }) } } } else if (type == 'hash') { var ret = await Moralis.Web3API.native.getTransaction(temp_opts); if (ret) results.push({ type: 'hash', from_address: ret.from_address, to_address: ret.to_address, token_address: null, block_number: ret.block_number, value: ret.value, gas: ret.gas, gas_price: ret.gas_price, block_timestamp: ret.block_timestamp, trx_hash: ret.hash, responseType: 'moralis' }) } return { code: 0, data: { total: results.length, results: results }, errMsg: null, } } else { //使用自建节点缓存 mysql 查询 if (type == 'native') { temp_opts.type = 'native' return await account_mysql.getAccountTransactions(temp_opts) } else if (type == 'token') { temp_opts.type = 'token' return await account_mysql.getAccountTransactions(temp_opts) } else if (type == 'hash') { temp_opts.type = 'hash' return await account_mysql.getAccountTransactions(temp_opts) } } } /** * 获取 token or native 余额 * @param {*} opts */ async function getBalances(opts, type, use_moralis_sdk_) { logger.info('getBalances>>>>>>>>', opts, type) //是否使用 moralis sdk 进行查询 var use_moralis_sdk = use_moralis_sdk_ var temp_opts = {...opts } if (temp_opts.chain != utils.CHAIN_NAME.bsc_mainnet && temp_opts.chain != utils.CHAIN_NAME.bsc_testnet) { use_moralis_sdk = 0; } else if (use_moralis_sdk == 0) { temp_opts.chain = convertChain(temp_opts.chain) } if (use_moralis_sdk) { if (type == 'native') { return await Moralis.Web3API.account.getNativeBalance(temp_opts); } else if (type == 'token') { return await Moralis.Web3API.account.getTokenBalances(temp_opts); } } else { if (type == 'native') { temp_opts.type = 'native' var ret = await account_mysql.getAccountBalances(temp_opts) if (ret && Array.isArray(ret.data.results) && ret.data.results.length > 0) { return ret.data.results[0] } else return { "type": "native", "balance": "", "responseType": "yqcx" } } else if (type == 'token') { temp_opts.type = 'token' var ret = await account_mysql.getAccountBalances(temp_opts) return ret.data.results } } } module.exports = { transfer, getTokenTransfers, getTokenTransfersV2, toJson, getAllTokenWithdrawInfoLists, getAllTotkenPrice, withdraw, getTransferCode, pushChainDetailTOQueue, collectCoins, isTransferSucceed, getTransferRecordGasFree, pushCollectConisObj, getTransferGasFree, queryCollectBalance, queryCompanyInfoFromId, getAccountAllCoins, findTokenPriceItem, }