sdk.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. var router = require('koa-router')();
  2. var moralis = require('../model/moralis_sdk.js')
  3. var utils = require('../model/utils.js');
  4. var { reids_token_config, account_config } = require('../config/config.js');
  5. const logger = require('../model/logger.js');
  6. router.prefix('/sdk');
  7. const redis = require("../model/db/redis_db") //导入 db.js
  8. const withdraw_db = require("../model/db/withdraw_db") //导入 db.js
  9. const report = require("../model/report") //导入 db.js
  10. const BigNumber = require('bignumber.js')
  11. /**
  12. * 获取代币价格
  13. * @param {*} ctx
  14. */
  15. async function getAllTotkenPrice(ctx) {
  16. console.log('getTotkenPrice in:')
  17. var ret = await moralis.getAllTotkenPrice()
  18. console.log('getTotkenPrice result:', ret)
  19. if (ret)
  20. ctx.body = utils.toJson(0, ret, null);
  21. else ctx.body = utils.toJson(-1, null, "redis read error.");
  22. }
  23. /**
  24. * 获取交易记录
  25. * @param {*} ctx
  26. */
  27. async function getTransfers(ctx) {
  28. const obj = ctx.request.body;
  29. console.log("getTransfers body", obj);
  30. if (!obj.chain)//默认 bsc 币安链
  31. obj.chain = 'bsc_mainnet'
  32. var temp_obj = { ...obj }
  33. var index = 0
  34. // for (let index = 0; index < 30; index++) {
  35. await moralis.getTokenTransfers(obj).then((result) => {
  36. logger.log('getTransfers response', 'index=' + index, result)
  37. ctx.body = result;
  38. if (result) {
  39. //提交归集任务 native 能获取到 gas 、token 无法获取到 gas 费
  40. try {
  41. if (temp_obj.address && moralis.isTransferSucceed(result)) {
  42. var log_obj = { ...obj }
  43. log_obj.results = result
  44. log_obj.type = report.REPORT_TYPE.transfer_record
  45. //埋点日志上报-入金检查
  46. report.logReport(log_obj)
  47. var json_obj = JSON.parse(result);
  48. //缓存当前交易的 gas 费用
  49. var tr = moralis.getTransferRecordGasFree('native', json_obj, temp_obj.address)
  50. logger.log('getTransferRecordGasFree:', tr, temp_obj.address)
  51. if (tr && tr.totalGasFree > 0) {
  52. logger.log('getTransferRecordGasFree redis_set LAST_TOTAL_BNB_FREE:', tr.totalGasFree.toString())
  53. logger.log('getTransferRecordGasFree redis_set LAST_TOTAL_TOKEN_FREE:', (parseInt(tr.totalGasFree) * parseInt(account_config.TOKEN_GAS_LIMIT)).toString())
  54. redis.redis_set(reids_token_config.LAST_TOTAL_BNB_FREE, tr.totalGasFree.toString());
  55. redis.redis_set(reids_token_config.LAST_TOTAL_TOKEN_FREE, (parseInt(tr.gas_price) * parseInt(account_config.TOKEN_GAS_LIMIT)).toString());
  56. }
  57. if (json_obj.data.total > 0) {
  58. //提交归集任务
  59. if (temp_obj.address) {
  60. logger.log('pushCollectConisObj>>>', temp_obj.address)
  61. redis.redis_push(reids_token_config.COLLECT_CONIS_QUEUE_KEY, JSON.stringify(temp_obj))
  62. }
  63. }
  64. }
  65. } catch (error) {
  66. console.error('pushCollectConisObj error=', error)
  67. }
  68. }
  69. })
  70. // }
  71. }
  72. /**
  73. * 获取交易记录
  74. * @param {*} ctx
  75. */
  76. async function getTransfersV2(ctx) {
  77. const obj = ctx.request.body;
  78. console.log("getTransfers body", obj);
  79. if (!obj.chain)//默认 bsc 币安链
  80. obj.chain = 'bsc_mainnet'
  81. var temp_obj = { ...obj }
  82. var index = 0
  83. await moralis.getTokenTransfersV2(obj).then((result) => {
  84. logger.log('getTokenTransfersV2 response', 'index=' + index, result)
  85. ctx.body = result;
  86. if (result) {
  87. //提交归集任务 native 能获取到 gas 、token 无法获取到 gas 费
  88. try {
  89. if (temp_obj.address && moralis.isTransferSucceed(result)) {
  90. var log_obj = { ...obj }
  91. log_obj.results = result
  92. log_obj.type = report.REPORT_TYPE.transfer_record
  93. //埋点日志上报-入金检查
  94. report.logReport(log_obj)
  95. var json_obj = JSON.parse(result);
  96. //缓存当前交易的 gas 费用
  97. var tr = moralis.getTransferRecordGasFree('native', json_obj, temp_obj.address)
  98. logger.log('getTransferRecordGasFree:', tr, temp_obj.address)
  99. if (tr && tr.totalGasFree > 0) {
  100. logger.log('getTransferRecordGasFree redis_set LAST_TOTAL_BNB_FREE:', tr.totalGasFree.toString())
  101. logger.log('getTransferRecordGasFree redis_set LAST_TOTAL_TOKEN_FREE:', (parseInt(tr.totalGasFree) * parseInt(account_config.TOKEN_GAS_LIMIT)).toString())
  102. redis.redis_set(reids_token_config.LAST_TOTAL_BNB_FREE, tr.totalGasFree.toString());
  103. redis.redis_set(reids_token_config.LAST_TOTAL_TOKEN_FREE, (parseInt(tr.gas_price) * parseInt(account_config.TOKEN_GAS_LIMIT)).toString());
  104. }
  105. if (json_obj.data.total > 0) {
  106. //提交归集任务
  107. if (temp_obj.address) {
  108. logger.log('pushCollectConisObj>>>', temp_obj.address)
  109. redis.redis_push(reids_token_config.COLLECT_CONIS_QUEUE_KEY, JSON.stringify(temp_obj))
  110. }
  111. }
  112. }
  113. } catch (error) {
  114. console.error('pushCollectConisObj error=', error)
  115. }
  116. }
  117. })
  118. }
  119. async function getAllTokenWithdrawInfoLists(ctx) {
  120. if (ctx.request == null || ctx.request.body == null) {
  121. ctx.body = utils.toJson(-1, null, "request error. ");
  122. return
  123. }
  124. ctx.body = await moralis.getAllTokenWithdrawInfoLists(ctx);
  125. }
  126. async function collect_conis_task() {
  127. logger.log("collect_conis_task start")
  128. while (true) {
  129. var start_time = utils.getTimestamp()
  130. var exec_obj = await redis.redis_pop(reids_token_config.COLLECT_CONIS_QUEUE_KEY)
  131. if (!exec_obj) {
  132. await utils.sleep(30000)
  133. logger.log("没有归集任务")
  134. continue
  135. }
  136. try {
  137. exec_obj = JSON.parse(exec_obj)
  138. logger.log('collect_conis_task exec item>>>>', exec_obj);
  139. if (exec_obj.address == await redis.readRedis(reids_token_config.LAST_COLLECT_PUBLIC_KEY) && utils.getTimestamp() - await redis.readRedis(reids_token_config.LAST_COLLECT_TIME) < 60 * 2 * 1000) {
  140. logger.log('间隔不足 1 分钟', exec_obj);
  141. continue
  142. }
  143. redis.redis_set(reids_token_config.LAST_COLLECT_TIME, utils.getTimestamp())
  144. redis.redis_set(reids_token_config.LAST_COLLECT_PUBLIC_KEY, exec_obj.address)
  145. //开始收集用户地址里面的币到归集地址
  146. var ret = await moralis.collectCoins(exec_obj)
  147. logger.log('collect_conis_task ret =', exec_obj, ret)
  148. try {
  149. var ret_obj = JSON.parse(ret)
  150. if (ret_obj.code == 0) {
  151. logger.log('触发归集 delay collect_conis_task ret =', exec_obj, ret)
  152. //间隔 10s 归集,避免提交任务过多
  153. await utils.sleep(10000)
  154. }
  155. } catch (error) { }
  156. } catch (error) {
  157. logger.error('collect_conis_task error', error.toString());
  158. redis.redis_set(reids_token_config.LAST_COLLECT_TIME, 0)
  159. redis.redis_set(reids_token_config.LAST_COLLECT_PUBLIC_KEY, 0)
  160. }
  161. logger.log("collect_conis_task cost-time", utils.getTimestamp() - start_time, exec_obj)
  162. }
  163. }
  164. async function withdraw_task() {
  165. logger.log("withdraw_task start")
  166. let last_time = 0
  167. let last_hash = ''
  168. while (true) {
  169. var exec_obj = await redis.redis_pop(reids_token_config.WITHDRAW_QUEUE_KEY)
  170. if (!exec_obj) {
  171. await utils.sleep(60000)
  172. logger.log("没有出金任务")
  173. continue
  174. }
  175. try {
  176. exec_obj = JSON.parse(exec_obj)
  177. } catch (error) {
  178. logger.error('item parse error', error);
  179. break
  180. }
  181. var temp_obj = { ...exec_obj }
  182. if (utils.getTimestamp() - last_time < 60000) {
  183. //有可能上一个区块还未更新,这里做一个尝试限制
  184. //Error: Failed to make "eth_sendRawTransaction" request with networkConnector: "already known"
  185. //通过 交易 hash 获取块。last_hash
  186. if (last_hash) {
  187. var options = {
  188. transaction_hash: last_hash,
  189. chain: temp_obj.chain,
  190. endTime: '2099-01-01'
  191. }
  192. var tryCount = 10;
  193. do {
  194. try {
  195. //通过获取上一个交易记录来进行确认
  196. var transaction = await moralis.getTokenTransfers(options);
  197. logger.log('withdraw_task exectransaction', transaction, options, tryCount);
  198. transaction = JSON.parse(transaction)
  199. if (transaction.code == 0) {
  200. if (transaction.data.result.length <= 0) {
  201. logger.log('等待10s');
  202. await utils.sleep(10000)
  203. } else {
  204. logger.log('等待5s');
  205. await utils.sleep(5000)
  206. break
  207. }
  208. } else {
  209. break
  210. }
  211. tryCount -= 1
  212. } catch (error) {
  213. logger.error('withdraw_task exectransaction', error.toString());
  214. }
  215. } while (tryCount >= 0);
  216. }
  217. }
  218. //如果失败重试一次
  219. var tryCount = 1;
  220. for (let index = 0; index < 1 + tryCount; index++) {
  221. try {
  222. var result = await withdraw_({ ...temp_obj })
  223. last_time = utils.getTimestamp()
  224. logger.log('withdraw_task withdraw_ =', result, last_time)
  225. if (result && moralis.isTransferSucceed(result)) {
  226. var obj = JSON.parse(result)
  227. var nonce = obj.data.nonce
  228. var curGasPrice = BigNumber(obj.data.gasPrice.hex).toNumber()
  229. var curGasLimit = BigNumber(obj.data.gasLimit.hex).toNumber()
  230. var value = BigNumber(obj.data.value.hex).toNumber()
  231. var hash = obj.data.hash
  232. last_hash = hash
  233. var update_obj = {}
  234. update_obj.withdraw_status = 2
  235. update_obj.withdraw_hash = hash
  236. update_obj.nonce = nonce
  237. update_obj.gas_price = curGasPrice.toString()
  238. update_obj.gas_limit = curGasLimit.toString()
  239. update_obj.value = value.toString()
  240. update_obj.errorMsg = ''
  241. await withdraw_db.update_withdraw_task(exec_obj.withdraw_id, update_obj)
  242. break
  243. } else {
  244. logger.error('withdraw_task withdraw_ error=', result, JSON.stringify(temp_obj))
  245. if (index < 1 + tryCount && result.includes('eth_sendRawTransaction')) {
  246. logger.error('try withdraw_:', JSON.stringify(temp_obj), index)
  247. await utils.sleep(3000)
  248. continue
  249. }
  250. var update_obj = {}
  251. update_obj.withdraw_status = 3
  252. if (typeof result === 'string') {
  253. try {
  254. result = JSON.parse(result)
  255. update_obj.errorMsg = result.errMsg
  256. } catch (error) {
  257. logger.error('withdraw_task=', result)
  258. }
  259. }
  260. await withdraw_db.update_withdraw_task(exec_obj.withdraw_id, update_obj)
  261. break
  262. }
  263. } catch (error) {
  264. var update_obj = {}
  265. update_obj.withdraw_status = 3
  266. update_obj.errorMsg = error.toString()
  267. await withdraw_db.update_withdraw_task(exec_obj.withdraw_id, update_obj)
  268. logger.error('withdraw_task error=', error.toString())
  269. break
  270. }
  271. }
  272. }
  273. logger.log("withdraw_task end")
  274. }
  275. /**
  276. * 队列版本
  277. * @param {*} ctx
  278. * @returns
  279. */
  280. async function withdrawV3(ctx) {
  281. logger.log('withdrawV3')
  282. if (ctx.request == null || ctx.request.body == null) {
  283. ctx.body = utils.toJson(-1, null, "request error. ");
  284. return
  285. }
  286. const obj = ctx.request.body;
  287. // for (let index = 0; index < 10; index++) {
  288. var log_obj = { ...obj }
  289. logger.log('withdrawV3', log_obj)
  290. var obj_ = decrypt_withdraw_content(log_obj.content)
  291. obj_.withdraw_id = obj_.withdrawId;
  292. // obj_.withdraw_id = utils.getTimestamp().toString();
  293. // var obj_ = log_obj
  294. if (obj_.withdraw_id) {
  295. var isExist = await withdraw_db.withdraw_id_exist(obj_.withdraw_id)
  296. if (isExist) {
  297. logger.error('withdraw_id_exist', obj_.withdraw_id + ' is already in the queue.')
  298. ctx.body = utils.toJson(-2, null, obj_.withdraw_id + ' is already in the queue.')
  299. return
  300. }
  301. redis.redis_push(reids_token_config.WITHDRAW_QUEUE_KEY, JSON.stringify(obj_))
  302. var info = await moralis.queryCompanyInfoFromId(0);
  303. obj_.user_address = info.user_address
  304. await withdraw_db.create_withdraw_task(obj_)
  305. // withdraw_task()
  306. ctx.body = utils.toJson(0, obj_.withdraw_id, null)
  307. } else {
  308. return utils.toJson(-2, null, ' withdraw_id not empty.')
  309. }
  310. // }
  311. }
  312. async function withdrawV3Test(ctx) {
  313. logger.log('withdrawV3Test')
  314. if (ctx.request == null || ctx.request.body == null) {
  315. ctx.body = utils.toJson(-1, null, "request error. ");
  316. return
  317. }
  318. const obj = ctx.request.body;
  319. // for (let index = 0; index < 10; index++) {
  320. var log_obj = { ...obj }
  321. logger.log('withdrawV3', log_obj)
  322. var obj_ = decrypt_withdraw_content(log_obj.content)
  323. obj_.withdraw_id = obj_.withdrawId;
  324. obj_.withdraw_id = utils.getTimestamp().toString();
  325. // var obj_ = log_obj
  326. if (obj_.withdraw_id) {
  327. var isExist = await withdraw_db.withdraw_id_exist(obj_.withdraw_id)
  328. if (isExist) {
  329. logger.error('withdraw_id_exist', obj_.withdraw_id + ' is already in the queue.')
  330. ctx.body = utils.toJson(-2, null, obj_.withdraw_id + ' is already in the queue.')
  331. return
  332. }
  333. redis.redis_push(reids_token_config.WITHDRAW_QUEUE_KEY, JSON.stringify(obj_))
  334. var info = await moralis.queryCompanyInfoFromId(0);
  335. obj_.user_address = info.user_address
  336. await withdraw_db.create_withdraw_task(obj_)
  337. ctx.body = utils.toJson(0, obj_.withdraw_id, null)
  338. } else {
  339. return utils.toJson(-2, null, ' withdraw_id not empty.')
  340. }
  341. // }
  342. }
  343. function decrypt_withdraw_content(content) {
  344. // const encryptText = utils.encrypt(log_obj);
  345. const encryptText = content;
  346. logger.log("加密", encryptText);
  347. let decryptObj = utils.decrypt(encryptText);
  348. try {
  349. logger.log("解密 before", decryptObj);
  350. decryptObj = JSON.parse(decryptObj);
  351. console.log("解密 json parse", decryptObj);
  352. } catch (error) {
  353. logger.error("json error:", error);
  354. decryptObj = null;
  355. }
  356. return decryptObj;
  357. }
  358. /**
  359. *
  360. * @param {鉴权版本} ctx
  361. */
  362. async function withdrawV2(ctx) {
  363. if (ctx.request == null || ctx.request.body == null) {
  364. ctx.body = utils.toJson(-1, null, "request error. ");
  365. return
  366. }
  367. const obj = ctx.request.body;
  368. var log_obj = { ...obj }
  369. // const encryptText = utils.encrypt(log_obj);
  370. const encryptText = log_obj.content;
  371. logger.log("加密", encryptText);
  372. let decryptObj = utils.decrypt(encryptText);
  373. try {
  374. logger.log("解密 before", decryptObj);
  375. decryptObj = JSON.parse(decryptObj);
  376. // console.log("解密 json parse", decryptObj);
  377. await withdraw_(decryptObj).then(result => {
  378. ctx.body = result;
  379. })
  380. } catch (error) {
  381. logger.error("json error:", error);
  382. ctx.body = utils.toJson(-1, null, error.toString());
  383. }
  384. }
  385. async function withdraw_(obj) {
  386. console.log("withdraw_", obj);
  387. var log_obj = { ...obj }
  388. var info = await moralis.queryCompanyInfoFromId(0);
  389. // log_obj.company_address_total_balance_before = await moralis.queryCollectBalance(info.user_address, obj.chain)
  390. log_obj.company_public_key = info.user_address
  391. logger.log('withdraw log', log_obj);
  392. return new Promise((resolve) => {
  393. moralis.withdraw(obj).then((result) => {
  394. if (moralis.isTransferSucceed(result)) {
  395. //提币日志上报
  396. log_obj.results = result
  397. log_obj.type = report.REPORT_TYPE.withdraw
  398. //缓存当前交易的 gas 费用
  399. if (result && log_obj.contractAddress) {
  400. var tr = moralis.getTransferGasFree('token', result)
  401. log_obj.withdrawTotalGasFee = tr.totalGasFree.toString()
  402. } else {
  403. var tr = moralis.getTransferGasFree('native', result)
  404. log_obj.withdrawTotalGasFee = tr.totalGasFree.toString()
  405. }
  406. // log_obj.receiver_address_total_balance_after = await queryCollectBalance(info.user_address, utils.getChainName(obj.chain))
  407. //日志上报
  408. report.logReport(log_obj)
  409. }
  410. resolve(result)
  411. });
  412. })
  413. }
  414. //出金
  415. async function withdraw(ctx) {
  416. if (ctx.request == null || ctx.request.body == null) {
  417. ctx.body = utils.toJson(-1, null, "request error. ");
  418. return
  419. }
  420. const obj = ctx.request.body;
  421. await withdraw_(obj).then(result => {
  422. ctx.body = result;
  423. })
  424. }
  425. /**
  426. * 查询出金状态
  427. * @param {*} ctx
  428. */
  429. async function getWithdrawStatus(ctx) {
  430. if (ctx.request == null || ctx.request.body == null) {
  431. ctx.body = utils.toJson(-1, null, "request error. ");
  432. return
  433. }
  434. const obj = ctx.request.body;
  435. var info = await withdraw_db.queryWithdrawInfoFromWithdrawId(obj.withdrawId)
  436. logger.log('getWithdrawStatus info', JSON.stringify(info))
  437. if (info) {
  438. if (info.withdraw_status != 3) {
  439. ctx.body = utils.toJson(0, {
  440. withdrawId: info.withdraw_id,
  441. withdrawStatus: info.withdraw_status,
  442. withdrawHash: info.withdraw_hash,
  443. chainId: info.chain_id,
  444. transferTimestamp: info.update_time,
  445. }, null)
  446. } else {
  447. ctx.body = utils.toJson(0, {
  448. withdrawId: info.withdraw_id,
  449. withdrawStatus: info.withdraw_status,
  450. withdrawHash: info.withdraw_hash,
  451. chainId: info.chain_id,
  452. transferTimestamp: info.update_time,
  453. errorMsg: info.errorMsg
  454. }, null)
  455. }
  456. } else {
  457. ctx.body = utils.toJson(-1, null, obj.withdraw_id + ' id does not exist.')
  458. }
  459. }
  460. //获取交易记录
  461. router.post('/getTransfers', getTransfers)
  462. router.post('/getTransfersV2', getTransfersV2)
  463. // 获取所有代币价格
  464. router.post('/getAllTotkenPrice', getAllTotkenPrice)
  465. // router.post('/transfer', transfer)
  466. //提现
  467. router.post('/withdraw', withdraw);
  468. //提现鉴权-body 加密
  469. router.post('/withdrawV2', withdrawV2);
  470. //队列的形式
  471. router.post('/withdrawV3', withdrawV3);
  472. if (process.env.NODE_ENV == 'dev')
  473. router.post('/withdrawV3Test', withdrawV3Test);
  474. //查询出金服务
  475. router.post('/getWithdrawStatus', getWithdrawStatus);
  476. //获取所有地址的所要消耗的最低提取费
  477. router.post('/getAllTokenWithdrawInfoLists', getAllTokenWithdrawInfoLists)
  478. // 定时任务 提币+归集
  479. withdraw_task();
  480. collect_conis_task();
  481. module.exports = router