const logger = require('../model/logger') var remote_config_db = require("../model/db/remote_config_db"); var collect_coins_db = require("../model/db/collect_coins_db"); var withdraw_db = require("../model/db/withdraw_db"); var moralis = require("../model/moralis_sdk"); var utils = require("../model/utils"); const axios = require('axios'); var { account_config } = require('../config/config.js'); const { max } = require('moment'); // 拿到飞书写入的 token const feishu_write_table_token_url = 'https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal' const feishu_write_table_data_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/values_batch_update' const feishu_insert_table_url = 'https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg/insert_dimension_range' const reportTime = '02:00:00' //########################################### 出入金数据统计 ######################################## const http_request_get = async (data) => { var host = account_config.STATISTICS_URL // host = 'https://api.denetme.net/denet/wallet/stat/getMoneyStat?date=' var path = data var url = host + path logger.log('http_request_get', url) return new Promise(response => { axios.get(url) .then(res => { logger.log('res=>', res.status, res.data); if (res.data.code == 0) { response(res.data) } else { response({ code: 0, msg: err.toString(), data: { canNotWithdrawUSD: '0', canWithdrawUSD: '0', incomeUSDTotal: '0', incomeUSDFee: '0' } }) } }).catch(err => { logger.error('http_request_get', err.toString(), url.toString()); response({ code: -1, msg: err.toString(), data: { canNotWithdrawUSD: '0', canWithdrawUSD: '0', incomeUSDTotal: '0', incomeUSDFee: '0' } }) }); }) } async function computeAddressPrice(total_in_coins) { //计算总的价格 for (key of total_in_coins.keys()) { var item = total_in_coins.get(key) var amount = item.amount var usdPrice = item.usdPrice if (key == '0x0000000000000000000000000000000000000000') { item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** 18) * parseFloat(usdPrice) } else { var decimals = 18 try { decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', item.chain, key.toLowerCase()) logger.info('REDIS_ERC20_CONTRACT_DECIMALS', key.toLowerCase(), decimals) } catch (error) { decimals = 18 } item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** decimals) * parseFloat(usdPrice) } } } function getBscEnv() { var bsc_env switch (process.env.NODE_ENV) { case 'dev': case 'test': // bsc_env = 'bsc_testnet' bsc_env = 'bsc_mainnet' break case 'prd': bsc_env = 'bsc_mainnet' break default: bsc_env = 'bsc_mainnet' break } return bsc_env; } async function filterCollectCoinsLists(collect_ret, filterTypt) { const total_in_coins = new Map(); var total_gas_fee = 0 for (let index = 0; index < collect_ret.results.length; index++) { const element = collect_ret.results[index]; if (element.chain == null || element.chain == filterTypt) { var before_gas_fee = element.before_gas_fee ? BigInt(element.before_gas_fee) : element.chain == 'czz' ? BigInt(21000 * 2000000000) : BigInt(21000 * 5000000000) total_gas_fee = BigInt(element.total_gas_fee) + BigInt(before_gas_fee) + BigInt(total_gas_fee) if (element.transfers) { var opts = JSON.parse(element.transfers) for (let index = 0; index < opts.length; index++) { const transfers = opts[index]; if (transfers.chain == filterTypt) { var address = transfers.contractAddress ? transfers.contractAddress : '0x0000000000000000000000000000000000000000' if (total_in_coins.get(address) != null) { var ins = total_in_coins.get(address) ins.amount = BigInt(ins.amount) + BigInt(transfers.amount) total_in_coins.set(address, ins) } else { total_in_coins.set(address, { amount: BigInt(transfers.amount), //总入金 usdPrice: transfers.usdPrice, chain: transfers.chain, }) } } } } } } //计算总的价格 await computeAddressPrice(total_in_coins) var bsc_env = getBscEnv() //获取 total gas try { if (total_in_coins.size > 0) { switch (filterTypt) { case 'bsc_testnet': case 'bsc_mainnet': var price = await moralis.getAllTotkenPrice({ chain: bsc_env }) if (typeof price === 'string') { price = JSON.parse(price) } var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) total_gas_fee = parseFloat(total_gas_fee) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice) logger.info('new-total_gas_fee ', total_gas_fee, bnbPriceItem) break case 'czz': var price = await moralis.getAllTotkenPrice({ chain: 'czz' }) if (typeof price === 'string') { price = JSON.parse(price) } var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) total_gas_fee = parseFloat(total_gas_fee) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice) logger.info('new-total_gas_fee czz', total_gas_fee, czzPriceItem) break } } } catch (error) { logger.error('total_gas_fee', error) } if (!total_in_coins.get('0x0000000000000000000000000000000000000000')) { total_in_coins.set('0x0000000000000000000000000000000000000000', { amount: 0, usdPrice: 0, chain: filterTypt, totalUsdPrice: 0 }) } return { map: total_in_coins, totalGasFee: total_gas_fee //总入金所消耗的 gas fee } } function sumBalance(map) { var balances = 0; for (key of map.keys()) { balances += map.get(key).totalUsdPrice } return balances } async function getSLGas() { var maps = new Map() var collect_ret = await collect_coins_db.query_collect_total_fee(null, null); for (let index = 0; index < collect_ret.results.length; index++) { var element = collect_ret.results[index] try { if (element.prestore_gas_fee && typeof element.prestore_gas_fee === 'string') { var pre_gas_obj = JSON.parse(element.prestore_gas_fee) var bgf = element.before_gas_fee if (!bgf && pre_gas_obj.amount) { bgf = pre_gas_obj.amount } // logger.info('getSLGas item', bgf, pre_gas_obj, element) if (maps.get(pre_gas_obj.chain)) { maps.get(pre_gas_obj.chain).sl_gas_fee = BigInt(maps.get(pre_gas_obj.chain).sl_gas_fee) + (BigInt(pre_gas_obj.amount) - BigInt(bgf)) } else { maps.set(pre_gas_obj.chain, { sl_gas_fee: BigInt(pre_gas_obj.amount) - BigInt(bgf) }) } } } catch (error) { logger.error('getSLGas error', error.toString()) } } var bsc_env = getBscEnv() logger.info('getSLGas', maps) var bsc_sl var czz_sl for (key of maps.keys()) { var item = maps.get(key) switch (key) { case "bsc_mainnet": case "bsc_testnet": var price = await moralis.getAllTotkenPrice({ chain: bsc_env }) if (typeof price === 'string') { price = JSON.parse(price) } var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) bsc_sl = parseFloat(item.sl_gas_fee) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice) break case "czz": var price = await moralis.getAllTotkenPrice({ chain: 'czz' }) if (typeof price === 'string') { price = JSON.parse(price) } var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) czz_sl = parseFloat(item.sl_gas_fee) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice) break } } return { bsc: bsc_sl, czz: czz_sl, total: bsc_sl + czz_sl } } async function getCollectCoinsOutInfo(startTime, endTime) { var collect_ret = await collect_coins_db.query_collect_total_fee(startTime, endTime); logger.info('getCollectCoinsOutInfo query_collect_total_fee', startTime, endTime) var bsc_env = getBscEnv() var bsc_envnet = await filterCollectCoinsLists(collect_ret, bsc_env) logger.info('getCollectCoinsOutInfo bsc_env', bsc_env, bsc_envnet) var czz = await filterCollectCoinsLists(collect_ret, 'czz') logger.info('getCollectCoinsOutInfo czz', czz) logger.info('getCollectCoinsOutInfo total ', bsc_envnet.totalGasFee, czz.totalGasFee) return { bsc: bsc_envnet.map, czz: czz.map, totalGasFee: parseFloat(bsc_envnet.totalGasFee) + parseFloat(czz.totalGasFee), //总归集消耗的 gas fee totalInFee: sumBalance(bsc_envnet.map) + sumBalance(czz.map),//总入金美元 } } async function filterWithdrawTotalOutFee(chain_id, filterItem) { const withdraw_out_map = new Map(); var price = await moralis.getAllTotkenPrice({ chain: chain_id + "" }) if (typeof price === 'string') { price = JSON.parse(price) } for (let index = 0; index < filterItem.length; index++) { const element = filterItem[index]; if (element.chain_id == chain_id) { var address = element.type == 'native' ? '0x0000000000000000000000000000000000000000' : element.contract_address if (withdraw_out_map.get(address) != null) { var item = withdraw_out_map.get(address) item.totalAmount = BigInt(element.amount) + BigInt(item.totalAmount) } else { var priceItem = moralis.findTokenPriceItem(address, price) var decimals = 18 if (key == '0x0000000000000000000000000000000000000000') { item.totalUsdPrice = parseFloat(amount) / parseFloat(10 ** 18) * parseFloat(usdPrice) } else { try { decimals = await redis.readAppendRedis('REDIS_ERC20_CONTRACT_DECIMALS', chain_id + "", address.toLowerCase()) logger.info('REDIS_ERC20_CONTRACT_DECIMALS', key.toLowerCase(), decimals) } catch (error) { decimals = 18 } } withdraw_out_map.set(address, { totalAmount: BigInt(element.amount), //出金数量 usdPrice: priceItem.usdPrice, chain: element.chain_id, decimals: decimals, }) } } } logger.info('filterWithdrawTotalOutFee', withdraw_out_map, chain_id) return withdraw_out_map } async function getWithdrawOutInfo(startTime, endTime) { if (startTime && endTime) { startTime = new Date(startTime).getTime() endTime = new Date(endTime).getTime() } var withdraw_ret = await withdraw_db.getWidthdrawTotalFee(startTime, endTime) const withdraw_gas_map = new Map(); for (let index = 0; index < withdraw_ret.length; index++) { const element = withdraw_ret[index]; if (element.gas_price && element.gas_limit) var total_gas_fee2 = (BigInt(element.gas_price) * BigInt(element.gas_limit)) if (withdraw_gas_map.get(element.chain_id) != null) { withdraw_gas_map.get(element.chain_id).total_gas_fee = BigInt(withdraw_gas_map.get(element.chain_id).total_gas_fee) + BigInt(total_gas_fee2) } else { withdraw_gas_map.set(element.chain_id, { total_gas_fee: BigInt(total_gas_fee2), total_withdraw: null } ) } } var bsc_env = getBscEnv() //计算总消耗的 gas var keys = withdraw_gas_map.keys(); var total_gas_fee = 0 for (key of keys) { // console.log(key, withdraw_gas_map.get(key)); // map.get(key)可得value值。 var value = withdraw_gas_map.get(key).total_gas_fee //获取币价 try { if (key == 2019) { var price = await moralis.getAllTotkenPrice({ chain: 'czz' }) if (typeof price === 'string') { price = JSON.parse(price) } var czzPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) total_gas_fee += parseFloat(value) / parseFloat(10 ** 18) * parseFloat(czzPriceItem.usdPrice) logger.info('new-total_gas_fee czz', total_gas_fee, czzPriceItem) } else { var price = await moralis.getAllTotkenPrice({ chain: bsc_env }) if (typeof price === 'string') { price = JSON.parse(price) } var bnbPriceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price) total_gas_fee += parseFloat(value) / parseFloat(10 ** 18) * parseFloat(bnbPriceItem.usdPrice) logger.info('new-total_gas_fee bsc', total_gas_fee, bnbPriceItem) } } catch (error) { logger.error('total_gas_fee', error) } } var withdrawout = 0 if (!startTime && !endTime) { for (key of withdraw_gas_map.keys()) { try { var outs = await filterWithdrawTotalOutFee(key, withdraw_ret) withdraw_gas_map.get(key).total_withdraw = outs // logger.error('withdraw_gas_map.get(key).total_withdraw outs', outs) if (outs) { for (ckey of outs.keys()) { var item = outs.get(ckey); withdrawout = parseFloat(withdrawout) + (parseFloat(item.totalAmount) / parseFloat(10 ** item.decimals) * parseFloat(item.usdPrice)) } } } catch (error) { logger.error('filterWithdrawTotalOutFee error', error.toString()) } } } return { outmap: withdraw_gas_map, totalGasFee: total_gas_fee, totalWithdraw: withdrawout }; } async function updateBalance(obj, type) { var price if (type == 'bsc') { var bsc_env = getBscEnv() price = await moralis.getAllTotkenPrice({ chain: bsc_env }) } else if (type == 'czz') { price = await moralis.getAllTotkenPrice({ chain: 'czz' }) } if (typeof price === 'string') { price = JSON.parse(price) } var priceItem = moralis.findTokenPriceItem('0x0000000000000000000000000000000000000000', price, price) if (!obj.native.balance) { obj.native.balance = '0' } if (obj.native) { obj.native.usdPrice = parseFloat(obj.native.balance) / parseFloat(10 ** 18) * priceItem.usdPrice logger.info('findTokenPriceItem 0x0000000000000000000000000000000000000000 ', obj, priceItem, type) } var tokenTotal = 0 if (obj.other && Array.isArray(obj.other)) { for (let index = 0; index < obj.other.length; index++) { const element = obj.other[index]; priceItem = moralis.findTokenPriceItem(element.token_address, price) logger.info('findTokenPriceItem element ', priceItem, element.token_address) if (priceItem) { if (!element.decimals || element.decimals == 0) element.decimals = 18 element.usdPrice = parseFloat(element.balance) / parseFloat(10 ** element.decimals) * priceItem.usdPrice tokenTotal += element.usdPrice } } } return { nativeTotal: obj.native.usdPrice, tokenTotal: tokenTotal, total: parseFloat(obj.native.usdPrice) + parseFloat(tokenTotal) } } async function getAllBalance() { var bsc_env = getBscEnv() var company = await moralis.queryCompanyInfoFromId(0) logger.info('getAllBalance company', company) var bsc_balance = await moralis.getAccountAllCoins({ chain: bsc_env, address: company.user_address }) logger.info('getAccountAllCoins bsc_balance', bsc_balance) var bscb = await updateBalance(bsc_balance, 'bsc') logger.info('getAccountAllCoins updateBalance bscb', bscb) var czz_balance = await moralis.getAccountAllCoins({ chain: 'czz', address: company.user_address }) logger.info('getAccountAllCoins czz_balance', czz_balance) var czzb = await updateBalance(czz_balance, 'czz') logger.info('getAccountAllCoins updateBalance czzb', czzb) return { bsc: bscb, czz: czzb, totalBalance: bscb.total + czzb.total } } /** * 获取时间段总支出的 gas fee * @param {*} startTime * @param {*} endTime */ async function getStatisticsInfo() { // //今日 var startTime = utils.getLastDay('YYYY-MM-DD') + " 00:00:00" var endTime = utils.getLastDay('YYYY-MM-DD') + " 23:59:59" logger.info('getTotalOutGasFee', startTime, endTime) //归集 var collectCoinsOut = await getCollectCoinsOutInfo(startTime, endTime) logger.info('getCollectCoinsOutInfo collectCoinsOut', collectCoinsOut) //提币 var withdrawOut = await getWithdrawOutInfo(startTime, endTime) logger.info('getWithdrawOutInfo withdrawOut ', withdrawOut) var data = await http_request_get(utils.getLastDay('YYYYMMDD')) //历史,总的 //归集 var totalCollectCoinsOut = await getCollectCoinsOutInfo(null, null) logger.info('totalCollectCoinsOut ', totalCollectCoinsOut) //提币 var totalWithdrawOut = await getWithdrawOutInfo(null, null) logger.info('totalWithdrawOut ', totalWithdrawOut) //获取当前账户总余额 var curBalances = await getAllBalance() logger.info('getAllBalance ', curBalances) //获取散落 gas var totalSLGas = await getSLGas() logger.info('getSLGas ret', totalSLGas) return { updateTime: utils.getLastDay('YYYY-MM-DD'), todayTotalProfit: parseFloat(data.data.incomeUSDTotal) - parseFloat(collectCoinsOut.totalGasFee + withdrawOut.totalGasFee), todayTotalOutGasFee: collectCoinsOut.totalGasFee + withdrawOut.totalGasFee, //今日总支出的 gas fee canNotWithdrawUSD: parseFloat(data.data.canNotWithdrawUSD), //不可提现余额 canWithdrawUSD: parseFloat(data.data.canWithdrawUSD), //可提现余额 todayIncomeUSDTotal: parseFloat(data.data.incomeUSDTotal), //今日总收入 todayIncomeUSDFee: parseFloat(data.data.incomeUSDFee), //今日固定收入 totalOutGasFee: totalCollectCoinsOut.totalGasFee + totalWithdrawOut.totalGasFee, //总支出 gas fee totalWithdrawGasFee: totalWithdrawOut.totalGasFee, //总提币 gas fee totalCollectCoinsGasFee: totalCollectCoinsOut.totalGasFee, //总归集 gas fee totalInFee: totalCollectCoinsOut.totalInFee, //总入金 totalNativeInFee: { bsc: totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice, czz: totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice }, //总 native 入金 totalOutFee: totalWithdrawOut.totalWithdraw, //总出金 totalBalances: curBalances.totalBalance, //总余额 ylGasBalance: { //预留 gas 费余额 native 总余额 - 总入金 bsc: curBalances.bsc.nativeTotal - totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice, czz: curBalances.czz.nativeTotal - totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice, total: (curBalances.bsc.nativeTotal - totalCollectCoinsOut.bsc.get('0x0000000000000000000000000000000000000000').totalUsdPrice) + (curBalances.czz.nativeTotal - totalCollectCoinsOut.czz.get('0x0000000000000000000000000000000000000000').totalUsdPrice) }, slGasBalance: totalSLGas, //散落 gas 费余额 充值 0.5 gas - 使用 0.3 gas= 散落 0.2gas } } function timeoutFunc(config, func) { config.runNow && func() let nowTime = new Date(utils.getCurrentDate()).getTime() // nowTime = new Date().getTime() let timePoints = config.time.split(':').map(i => parseInt(i)) let recent = new Date(utils.getCurrentDate()).setHours(...timePoints) recent >= nowTime || (recent += 24 * 3600000) console.log('timeoutFunc-------------------', nowTime, timePoints, recent) setTimeout(() => { func() setInterval(func, config.interval * 3600000) }, recent - nowTime) } const getFeishuToken = async (params) => { return new Promise(resolve => { axios.post(feishu_write_table_token_url, { app_id: "cli_a223f015abbad00e", app_secret: "DMCF6tBwIpeOQPnWrFUMYd6tmjb53C4n" }, { timeout: 1 * 60 * 1000, headers: { 'Content-Type': "application/json; charset=utf-8" } }) .then(res => { logger.log('getFeishuToken res=>', res.status, res.data); resolve(res.data) }).catch(err => { logger.error('getFeishuToken error ', JSON.stringify(err)); resolve(JSON.stringify(err)) }); }) } function writeTable(app_token, data) { logger.info('writeTable', data) var body = { 'valueRanges': [ { 'range': '0pRQpu!A2:C2', 'values': [ [data.totalCollectCoinsGasFee, //归集总 gas data.totalWithdrawGasFee, //提币总 gas data.totalOutGasFee], //总支出 gas ] }, { 'range': '1ygrMB!A2:B2', 'values': [ [ data.totalInFee, //总入金 data.totalOutFee,//总出金 ], ] }, { 'range': 'BMjMDr!A3:J3', 'values': [ [ data.updateTime, //更新时间 data.todayTotalProfit,//今日总利润 data.todayIncomeUSDTotal,//今日总收入 data.todayIncomeUSDFee,//今日固定手续费收入 data.todayTotalOutGasFee,//今日总 gas 支出 data.totalBalances, //总余额 data.canNotWithdrawUSD, //不可提现余额 data.canWithdrawUSD,//可提现余额 data.ylGasBalance.total,//预留 gas data.slGasBalance.total,//散落 gas ], ] } ] } return new Promise(resolve => { axios.post(feishu_write_table_data_url, body, { timeout: 1 * 60 * 1000, headers: { 'Content-Type': "application/json; charset=utf-8", 'Authorization': 'Bearer ' + app_token } }) .then(res => { logger.log('writeTable res=>', res.status, res.data); resolve(res.data) }).catch(err => { logger.error('writeTable error ', JSON.stringify(err)); resolve(JSON.stringify(err)) }); }) } async function insertTableRows(app_token) { var body = { dimension: { sheetId: 'BMjMDr', majorDimension: 'ROWS', startIndex: 2, endIndex: 3, }, inheritStyle: 'AFTER' } return new Promise(resolve => { axios.post(feishu_insert_table_url, JSON.stringify(body), { timeout: 1 * 60 * 1000, headers: { 'Content-Type': "application/json; charset=utf-8", 'Authorization': 'Bearer ' + app_token } }) .then(res => { console.log('res=>', res.status, res.data); resolve(res.data) }).catch(err => { logger.error('error ', JSON.stringify(err)); resolve(JSON.stringify(err)) }); }) } async function exec(data) { var app = await getFeishuToken() await insertTableRows(app.app_access_token) await writeTable(app.app_access_token, data) } async function report2FeishuTable() { try { logger.error('数据统计 start') logger.info('report2FeishuTable') var data = await getStatisticsInfo(); logger.info('getStatisticsInfo', data) var ret = await exec(data) logger.error('数据统计 end','https://st94nif1cq.feishu.cn/sheets/shtcnp6zbrsep1Sz3Cvk7NXRpDg?sheet=BMjMDr',JSON.stringify(data),JSON.stringify(ret)) } catch (error) { logger.error('report2FeishuTable', error.toString()) } } timeoutFunc({ interval: 1, //间隔天数,间隔为整数 runNow: false, //是否立即运行 time: reportTime //执行的时间点 时在0~23之间 }, func => { if (process.env.NODE_ENV == 'prd') { report2FeishuTable() } }) // logger.info('getAllBalance ', getAllBalance()) module.exports = { getStatisticsInfo }