jsir 1.2.8 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ethWeb.js DELETED
@@ -1,1239 +0,0 @@
1
- let {BigNumber, getLibDataDir, trim, toBigNum, getConfig, objDataFile, infoStr, errorStr, batchAsync,
2
- eFn, removeFirst, randomInt, splitArray, sleep, vl, cacheFn, isError, bMin, getOr, errorStack} = require('./util')
3
- const abiDecoder = require('abi-decoder');
4
- let fs = require('fs')
5
- let contractMapPath = getLibDataDir() + "/contractMap.json"
6
- let erc20Abi = require('./source/erc20Abi')
7
- let createKeccakHash = require('keccak')
8
- let tokenMapCache = {}
9
- const util = require('ethereumjs-util')
10
- const Tx = require('ethereumjs-tx');
11
- const ethers = require("ethers")
12
- let { Interface } = require('@ethersproject/abi')
13
- const batchCallAbi = [{"inputs":[{"internalType":"bool","name":"atom","type":"bool"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct main.Call[]","name":"calls","type":"tuple[]"}],"name":"aggregate","outputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"bytes[]","name":"returnData","type":"bytes[]"},{"internalType":"bool[]","name":"results","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"}]
14
- let Web3Contract = require('web3-eth-contract');
15
-
16
- let batchCallAddressMap = {
17
- 1: '0xf375451681f567bEB7AeAD101F7944C2574718Eb',
18
- 128: '0x0817dC05EAb0A0C8aDBafDbE35883B5e5DD8a299',
19
- 137: '0xa36e94d707ff68629F01f0886c4d790459b90fB4',
20
- 66: '0x5a8138bF21BdCf8424A999069F24f6F7A065F6A0',
21
- 56: '0x3C23d715f13797CDF93E54a054ACa2d3A98DD18C',
22
- 250: '0x8D334d8fd2da9D610Ec69eef7a44fA6a2AcE1fD9',
23
- 1313161554: '0x4633F95925951F856544DD7ADaE262Bb487c3422'
24
- }
25
-
26
- async function _ethBatchQuery(calls = [], web3) {
27
- web3 = web3 || global.web3;
28
- let chainId = global.chainId || await web3.eth.getChainId()
29
- let batchCallAddress = batchCallAddressMap[chainId]
30
- if (!batchCallAddress) {
31
- throw 'batchCallAddress not found on chainId ' + chainId
32
- }
33
- if (calls.length === 0) {
34
- return []
35
- }
36
- const itfs = calls.map(call => newAbiInterface(call.abi))
37
- const calldata = calls.map((call, i) => [call.address.toLowerCase(), itfs[i].encodeFunctionData(call.name || call.method, call.params || call.args || [])])
38
- const { returnData } = await newContract(web3, batchCallAbi, batchCallAddress).methods.aggregate(false, calldata).call()
39
- let pros = []
40
- for(let i = 0; i<returnData.length; i++) {
41
- let data = returnData[i]
42
- let result
43
- let error
44
- try {
45
- result = itfs[i].decodeFunctionResult(calls[i].name || calls[i].method, data)
46
- } catch (e) {
47
- $log(new Error(errorStack(e)).stack)
48
- error = e
49
- }
50
- pros.push(new Promise(async resolve => {
51
- if (calls[i].callback) {
52
- try {
53
- await calls[i].callback(error, result)
54
- } catch (e) {
55
- $log(new Error(errorStack(e)).stack)
56
- resolve(result || error);
57
- throw e
58
- }
59
- }
60
- resolve(result || error);
61
- }))
62
- }
63
- return await Promise.all(pros);
64
- }
65
-
66
- async function ethBatchQuery(calls = [], web3, batchNum = 49, asyncNum = 33, useWebReq = false) {
67
- web3 = web3 || global.web3;
68
- let chainId = global.chainId || await web3.eth.getChainId()
69
- if (batchCallAddressMap[chainId] && !useWebReq) {
70
- return await ethBatchQuery_(calls, web3, batchNum, asyncNum)
71
- } else {
72
- let transResult = result => {
73
- if (!result) {
74
- return [null]
75
- }
76
- if (result.constructor.name !== 'Result') {
77
- return [result]
78
- } else {
79
- let temp = []
80
- for (let key of Object.keys(result)) {
81
- if (/^\d+$/.test(key)) {
82
- temp.push(result[key])
83
- }
84
- }
85
- return temp;
86
- }
87
- }
88
- let newCalls = []
89
- for (let call of calls) {
90
- let item = {
91
- method: newContract(web3, call.abi, call.address)
92
- .methods[call.name || call.method](...(call.args || call.params || []))
93
- .call
94
- }
95
- if (call.callback) {
96
- item.callback = async (error, result) => {
97
- await call.callback(error, transResult(result))
98
- }
99
- }
100
- newCalls.push(item)
101
- }
102
- let results = await web3BatchReq(newCalls, batchNum, asyncNum, web3)
103
- let newResults = []
104
- for (let result of results) {
105
- if (isError(result)) {
106
- newResults.push(result)
107
- continue
108
- }
109
- newResults.push(transResult(result))
110
- }
111
- return newResults
112
- }
113
- }
114
-
115
- async function ethBatchQuery_(calls = [], web3, batchNum, asyncNum) {
116
- if (calls.length <= 0) {
117
- return []
118
- }
119
- if (batchNum) {
120
- let callBatchs = splitArray(calls, batchNum)
121
- return (await batchAsync(callBatchs.map(item => () => _ethBatchQuery(item, web3)), asyncNum))
122
- .reduce((a,b) => a.concat(b))
123
- }
124
- return _ethBatchQuery(calls, web3)
125
- }
126
-
127
- // avoid memory leak
128
- function newAbiInterface(abi) {
129
- if (!global.$abiInterfaces) {
130
- global.$abiInterfaces = {}
131
- }
132
- let key = JSON.stringify(abi)
133
- if (!global.$abiInterfaces[key]) {
134
- global.$abiInterfaces[key] = new Interface(abi)
135
- }
136
-
137
- return global.$abiInterfaces[key]
138
- }
139
-
140
- function initRootWallet(mnemonic) {
141
- let wallets = {}
142
- return {
143
- getWallet(i) {
144
- i = String(i)
145
- if (!wallets[i]) {
146
- if (/^\d+$/.test(i)) {
147
- wallets[i] = ethers.Wallet.fromMnemonic(mnemonic, `m/44\'/60\'/0\'/0/${i}`)
148
- } else {
149
- wallets[i] = new ethers.Wallet(i);
150
- }
151
- }
152
- return wallets[i]
153
- },
154
- getAddress(i){
155
- return this.getWallet(i).address
156
- },
157
- getPrivateKey(i){
158
- return this.getWallet(i).privateKey
159
- }
160
- }
161
- }
162
-
163
- async function erc20Query(address, method, ...args) {
164
- try {
165
- const contract = newContract(web3, erc20Abi, address);
166
- return await contract.methods[method](...args).call()
167
- } catch (e) {
168
- $log(new Error(errorStack(e)).stack)
169
- return null;
170
- }
171
- }
172
-
173
- async function initSender(web3, wallet, froms, resetNum) {
174
- let senderQueue = {}
175
- let senderBlock = {}
176
- let senderNonce = {}
177
- let senderResetNum = Number(resetNum)
178
-
179
- for(let aIndex of froms) {
180
- senderNonce[aIndex] = Number(await web3.eth.getTransactionCount(walletGet(wallet, aIndex).address)) -1
181
- }
182
-
183
- let sender = async (txObject) => {
184
- let aIndex = froms[randomInt(froms.length) - 1]
185
- let adInfo = walletGet(wallet, aIndex)
186
-
187
- if (!(txObject.gasLimit || txObject.gas)) {
188
- try {
189
- let gas = await web3.eth.estimateGas({
190
- from: adInfo.address,
191
- to: txObject.to,
192
- value: txObject.value,
193
- data: txObject.data
194
- })
195
- txObject.gasLimit = gas + 10000;
196
- } catch (e) {
197
- return [Promise.reject(e), {}, aIndex]
198
- }
199
- }
200
-
201
- if (senderBlock[aIndex]) {
202
- return []
203
- }
204
-
205
- if (!senderQueue[aIndex]) {
206
- senderQueue[aIndex] = []
207
- }
208
- senderNonce[aIndex] ++
209
- txObject.nonce = senderNonce[aIndex]
210
- let signedTx = txnSign(txObject, adInfo.privateKey)
211
-
212
- let result = web3.eth.sendSignedTransaction(signedTx.rawTransaction).then(resp => {
213
- removeFirst(senderQueue[aIndex], result)
214
- return Promise.resolve(resp)
215
- }).catch(e => {
216
- removeFirst(senderQueue[aIndex], result)
217
- return Promise.reject(e)
218
- })
219
- senderQueue[aIndex].push(result)
220
-
221
- let nonce = senderNonce[aIndex]
222
- if (nonce > 0 && senderResetNum > 0 && nonce%senderResetNum === 0) {
223
- senderBlock[aIndex] = true
224
- let queueFin = async () => {
225
- try {
226
- senderNonce[aIndex] = Number(await web3.eth.getTransactionCount(adInfo.address)) -1
227
- } catch (e) {}
228
- senderBlock[aIndex] = false
229
- }
230
- Promise.all(senderQueue[aIndex]).then(queueFin).catch(queueFin)
231
- }
232
- return [result, signedTx, aIndex]
233
- }
234
- sender.web3 = web3
235
- return sender
236
- }
237
-
238
- async function initSenderWithKeys(web3, privateKeys, resetNum) {
239
- let senderQueue = {}
240
- let senderBlock = {}
241
- let senderNonce = {}
242
- let senderResetNum = Number(resetNum)
243
-
244
- let keyMap = {}
245
- for(let key of privateKeys) {
246
- let adds = privateToAddress(key)
247
- senderNonce[adds] = Number(await web3.eth.getTransactionCount(adds)) -1
248
- keyMap[key] = adds
249
- }
250
-
251
- let sender = async (txObject) => {
252
- let key = privateKeys[randomInt(privateKeys.length) - 1]
253
- let adds = keyMap[key]
254
-
255
- if (!(txObject.gasLimit || txObject.gas)) {
256
- try {
257
- let gas = await web3.eth.estimateGas({
258
- from: adds,
259
- to: txObject.to,
260
- value: txObject.value,
261
- data: txObject.data
262
- })
263
- txObject.gasLimit = gas + 10000;
264
- } catch (e) {
265
- return [Promise.reject(e), {}, adds]
266
- }
267
- }
268
-
269
- if (senderBlock[adds]) {
270
- return []
271
- }
272
-
273
- if (!senderQueue[adds]) {
274
- senderQueue[adds] = []
275
- }
276
- senderNonce[adds] ++
277
- txObject.nonce = senderNonce[adds]
278
- let signedTx = txnSign(txObject, key)
279
-
280
- let result = web3.eth.sendSignedTransaction(signedTx.rawTransaction).then(resp => {
281
- removeFirst(senderQueue[adds], result)
282
- return Promise.resolve(resp)
283
- }).catch(e => {
284
- removeFirst(senderQueue[adds], result)
285
- return Promise.reject(e)
286
- })
287
- senderQueue[adds].push(result)
288
-
289
- let nonce = senderNonce[adds]
290
- if (nonce > 0 && senderResetNum > 0 && nonce%senderResetNum === 0) {
291
- senderBlock[adds] = true
292
- let queueFin = async () => {
293
- try {
294
- senderNonce[adds] = Number(await web3.eth.getTransactionCount(adds)) - 1
295
- } catch (e) {}
296
- senderBlock[adds] = false
297
- }
298
- Promise.all(senderQueue[adds]).then(queueFin).catch(queueFin)
299
- }
300
- return [result, signedTx, adds]
301
- }
302
- sender.web3 = web3
303
- return sender
304
- }
305
-
306
- function privateToAddress(privateKey) {
307
- return util.bufferToHex(util.privateToAddress(util.toBuffer('0x' + privateKey.replace(/^0x/, ''))))
308
- }
309
-
310
- function txnSign(txObject, privateKey) {
311
- let params = {
312
- nonce: web3.utils.toHex(txObject.nonce),
313
- gasPrice: web3.utils.toHex(txObject.gasPrice),
314
- gasLimit: web3.utils.toHex(txObject.gasLimit || txObject.gas),
315
- to: txObject.to,
316
- value: web3.utils.toHex(txObject.value),
317
- data: txObject.data
318
- }
319
- if (global.chainId) {
320
- params.chainId = web3.utils.toHex(global.chainId)
321
- }
322
- let tx = new Tx(params);
323
- tx.sign(util.toBuffer(privateKey));
324
- let serializedTx = tx.serialize();
325
-
326
- let transactionHash = "0x" + tx.hash().toString("hex")
327
- let rawTransaction = '0x' + serializedTx.toString('hex')
328
- return {
329
- transactionHash,
330
- rawTransaction,
331
- hash: transactionHash,
332
- raw: rawTransaction
333
- }
334
- }
335
-
336
- function walletGet(wallet, i) {
337
- i = Number(i)
338
- return {
339
- address: wallet.getAddress(i),
340
- privateKey: wallet.getPrivateKey(i),
341
- }
342
- }
343
-
344
- async function sendTxn(privateKey, toAddress, value, contract, method, ...args) {
345
- let ADDRESS = util.bufferToHex(util.privateToAddress(util.toBuffer('0x' + privateKey.replace(/^0x/, ''))))
346
- let signTxn = await web3.eth.accounts.signTransaction({
347
- from: ADDRESS,
348
- to: toAddress,
349
- value,
350
- gas: 1000000,
351
- gasPrice: Number(await web3.eth.getGasPrice()) + (Number(getConfig("gasAddGwei") || 0) * 1000 * 1000 * 1000),
352
- data: contract.methods[method](...args).encodeABI()
353
- }, privateKey)
354
-
355
- console.log(`begin send txn ${signTxn.transactionHash}`)
356
- try {
357
- await web3.eth.sendSignedTransaction(signTxn.rawTransaction)
358
- console.info(`发送成功:${signTxn.transactionHash}`)
359
- } catch (e) {
360
- console.error(`发送失败:${signTxn.transactionHash}`)
361
- console.log(String(e))
362
- }
363
- }
364
-
365
- async function gasPrice() {
366
- return web3.utils.fromWei(await web3.eth.getGasPrice(), 'gwei')
367
- }
368
-
369
- // avoid memory leak
370
- function newContract(web3, abi, address) {
371
- if (!global.$contracts) {
372
- global.$contracts = {}
373
- }
374
- if (address) {
375
- address = address.toLowerCase()
376
- }
377
- let key = [String(chainId), JSON.stringify(abi), address].filter(i => i).join("-")
378
-
379
- if (!global.$contracts[key]) {
380
- global.$contracts[key] = new web3.eth.Contract(abi, address);
381
- }
382
-
383
- return global.$contracts[key]
384
- }
385
-
386
- // avoid memory leak
387
- function newWeb3Contract(abi, address) {
388
- if (!global.$web3Contracts) {
389
- global.$web3Contracts = {}
390
- }
391
- if (address) {
392
- address = address.toLowerCase()
393
- }
394
- let key = [JSON.stringify(abi), address].filter(i => i).join("-")
395
-
396
- if (!global.$web3Contracts[key]) {
397
- global.$web3Contracts[key] = new Web3Contract(abi, address);
398
- }
399
-
400
- return global.$web3Contracts[key]
401
- }
402
-
403
- async function tokenMap(address, web3) {
404
- if (!address) {
405
- return {name: '本币', decimals: 18, symbol: '本币'}
406
- }
407
- address = '0x' + address.toLowerCase().replace(/^0x/, '')
408
- let chainMap = await getTokenMap([address], web3)
409
-
410
- if (chainMap[address]) {
411
- let decimals = chainMap[address].decimals
412
- if (Number.isNaN(Number(String(decimals)))) {
413
- throw new Error(`invalid token decimals ${address} ${decimals}`)
414
- }
415
- }
416
-
417
- return chainMap[address]
418
- }
419
-
420
- async function getLastBlockSecTime() {
421
- let blockNum = await web3.eth.getBlockNumber();
422
- let blockInfo = await web3.eth.getBlock(blockNum)
423
- return blockInfo.timestamp
424
- }
425
-
426
- async function getPrice(token0Addr, token1Addr, factory, web3) {
427
- web3 = web3 || global.web3;
428
- return await getPriceByAmt(token0Addr, token1Addr, 1, factory, web3)
429
- }
430
-
431
- async function getPriceNoFee(token0Addr, token1Addr, factory, web3) {
432
- web3 = web3 || global.web3;
433
- return await getPriceByAmtNoFee(token0Addr, token1Addr, 1, factory, web3)
434
- }
435
-
436
- //返回买入baseAmt个a所需要的b数
437
- //包含千三手续费
438
- async function getPriceByAmt(token0Addr, token1Addr, token0Amt, factory, web3) {
439
- web3 = web3 || global.web3;
440
- token0Amt = toBigNum(token0Amt)
441
- let result = await getPoolAmt(token0Addr, token1Addr, factory, web3)
442
- return getPairAmt(result[0], result[1], token0Amt)
443
- }
444
-
445
- //返回买入baseAmt个a所需要的b数
446
- //不包含千三手续费
447
- async function getPriceByAmtNoFee(token0Addr, token1Addr, token0Amt, factory, web3) {
448
- web3 = web3 || global.web3;
449
- token0Amt = toBigNum(token0Amt)
450
- let result = await getPoolAmt(token0Addr, token1Addr, factory, web3)
451
- return getPairAmtNoFee(result[0], result[1], token0Amt)
452
- }
453
-
454
- // 获得池子中两种币的数量
455
- async function getPoolAmt(token0Addr, token1Addr, factory, web3) {
456
- web3 = web3 || global.web3;
457
- let tokenOInfo = await tokenMap(token0Addr, web3)
458
- let token1Info = await tokenMap(token1Addr, web3)
459
-
460
- let pair = await ethQuery1(web3, factory, require('./source/uniFact'), 'getPair', token0Addr, token1Addr)
461
- if (!pair) {
462
- return null;
463
- }
464
- let pairToken0 = await ethQuery1(web3, pair, require('./source/uniPair'), "token0")
465
-
466
- let result = await ethQuery1(web3, pair, require('./source/uniPair'), "getReserves")
467
-
468
- let num0;
469
- let num1;
470
- if (pairToken0.toLowerCase() === token0Addr.toLowerCase()) {
471
- num0 = exDcmNum(result[0], -parseInt(tokenOInfo['decimals']))
472
- num1 = exDcmNum(result[1], -parseInt(token1Info['decimals']))
473
- } else {
474
- num1 = exDcmNum(result[0], -parseInt(token1Info['decimals']))
475
- num0= exDcmNum(result[1], -parseInt(tokenOInfo['decimals']))
476
- }
477
-
478
- return [num0, num1]
479
- }
480
-
481
- //返回买入baseAmt个a所需要的b数
482
- //包含千三手续费
483
- function getPairAmt(a, b, baseAmt) {
484
- return toBigNum(getPairAmtNoFee(a,b,baseAmt)).abs().multipliedBy(Number(baseAmt) > 0 ? 1.003:0.997).toFixed(9).toString()
485
- }
486
-
487
- //返回买入baseAmt个a所需要的b数
488
- //不包含千三手续费
489
- function getPairAmtNoFee(a, b, baseAmt) {
490
- a = toBigNum(String(a))
491
- b = toBigNum(String(b))
492
- baseAmt = toBigNum(String(baseAmt))
493
- let out = a.multipliedBy(b).dividedBy(a.minus(baseAmt)).minus(b)
494
-
495
- if (a.minus(baseAmt).comparedTo(0) < 0 || b.plus(out).comparedTo(0) < 0) {
496
- throw "exceed pool"
497
- }
498
-
499
- return out.abs().toFixed(9).toString()
500
- }
501
-
502
- let tokenMapLoaded = {}
503
- let tokenMapLoadSize = {}
504
- async function saveTokenMap(web3) {
505
- web3 = web3 || global.web3;
506
- let chainId = global.chainId || await web3.getChainId()
507
- let chainMap = getOr(tokenMapCache, chainId, {})
508
- let tokenMapFile = `tokenMap_${chainId}`
509
- if (!tokenMapLoaded[chainId]) {
510
- let temp = objDataFile(tokenMapFile)
511
- tokenMapLoadSize[chainId] = Object.keys(temp).length
512
- Object.assign(chainMap, temp)
513
- tokenMapLoaded[chainId] = true
514
- }
515
- if (Object.keys(chainMap).length > tokenMapLoadSize[chainId]) {
516
- await objDataFile(tokenMapFile, () => chainMap);
517
- }
518
- }
519
- async function getTokenMap(tokens, web3, batchNum, asyncNum) {
520
- web3 = web3 || global.web3;
521
- let chainId = global.chainId || await web3.getChainId()
522
- let chainMap = getOr(tokenMapCache, chainId, {})
523
- if (!tokenMapLoaded[chainId]) {
524
- let tokenMapFile = `tokenMap_${chainId}`
525
- let temp = objDataFile(tokenMapFile)
526
- tokenMapLoadSize[chainId] = Object.keys(temp).length
527
- Object.assign(chainMap, temp)
528
- tokenMapLoaded[chainId] = true
529
- }
530
-
531
- tokens = tokens || []
532
- tokens = tokens.map(token => '0x' + token.toLowerCase().replace(/^0x/, ''))
533
- tokens = [...new Set(tokens)]
534
- tokens = tokens.filter(token => !chainMap[token])
535
- if (tokens.length <= 0) {
536
- return chainMap
537
- }
538
- let calls = []
539
- for (let token of tokens) {
540
- calls.push({
541
- abi: erc20Abi,
542
- address: token,
543
- name: 'name'
544
- })
545
- calls.push({
546
- abi: erc20Abi,
547
- address: token,
548
- name: 'symbol'
549
- })
550
- calls.push({
551
- abi: erc20Abi,
552
- address: token,
553
- name: 'decimals'
554
- })
555
- }
556
- let results = await ethBatchQuery(calls, web3, batchNum, asyncNum)
557
- for(let i = 0; i<results.length; i+=3) {
558
- let token = calls[i].address
559
- try {
560
- if (isError(results[i]) || isError(results[i + 1]) || isError(results[i + 2])) {
561
- $log(`${token} erc20 info error`)
562
- continue
563
- }
564
- chainMap[token] = {
565
- name: results[i] ? results[i][0] : null,
566
- symbol: results[i + 1] ? results[i + 1][0] : null,
567
- decimals: results[i + 2] ? results[i + 2][0] : null,
568
- }
569
- } catch (e) {
570
- $log(`${token} erc20 info error`)
571
- $log(new Error(errorStack(e)).stack)
572
- }
573
- }
574
- return chainMap
575
- }
576
-
577
- async function getReserves(pairs, factory, fmt, web3) {
578
- web3 = web3 || global.web3;
579
-
580
- if (!Array.isArray(pairs[0])) {
581
- pairs = [pairs]
582
- }
583
-
584
- let calls = []
585
- let uniFact = require('./source/uniFact')
586
- for(let i = 0; i < pairs.length; i++) {
587
- let pair = pairs[i]
588
- calls.push({
589
- abi: uniFact,
590
- address: factory,
591
- name: 'getPair',
592
- params: pair
593
- })
594
- }
595
- let results = await ethBatchQuery(calls, web3)
596
- let pairMap = {}
597
- for(let i = 0; i<results.length; i++) {
598
- let call = calls[i]
599
- if (results[i]) {
600
- pairMap[results[i][0]] = pairMap[call.params.join('-')] = {
601
- pairAddress: results[i][0]
602
- }
603
-
604
- }
605
- }
606
-
607
- let uniPair = require('./source/uniPair')
608
- calls = []
609
- for(let i = 0; i<pairs.length; i++) {
610
- let pair = pairs[i]
611
- let pairInfo = pairMap[pair.join('-')]
612
- if (!pairInfo) {
613
- continue
614
- }
615
- calls.push({
616
- abi: uniPair,
617
- address: pairInfo.pairAddress,
618
- name: 'token0'
619
- })
620
- calls.push({
621
- abi: uniPair,
622
- address: pairInfo.pairAddress,
623
- name: 'getReserves'
624
- })
625
- }
626
- results = await ethBatchQuery(calls, web3)
627
- for(let i = 0; i < results.length; i+=2) {
628
- let call = calls[i]
629
- pairMap[call.address].token0 = results[i][0]
630
- pairMap[call.address].reserves = results[i + 1].map(i => i.toString()).slice(0, 2)
631
- }
632
-
633
- let tokenMap
634
- if (fmt) {
635
- let tempMap = {}
636
- for (let pair of pairs) {
637
- tempMap[pair[0]] = true;
638
- tempMap[pair[1]] = true;
639
- }
640
- tokenMap = await getTokenMap(Object.keys(tempMap), web3)
641
- }
642
-
643
- let rs = []
644
- for(let i =0; i<pairs.length; i++) {
645
- let pair = pairs[i]
646
- let pairInfo = pairMap[pair.join('-')]
647
- if (!pairInfo) {
648
- rs.push([])
649
- continue
650
- }
651
- let item
652
- if (pairInfo.token0.toLowerCase() === pair[0].toLowerCase()) {
653
- item = pairInfo.reserves
654
- } else {
655
- item = pairInfo.reserves.reverse()
656
- }
657
- rs.push(item)
658
- if (fmt) {
659
- let tokenOInfo = tokenMap[pair[0].toLowerCase()]
660
- let token1Info = tokenMap[pair[1].toLowerCase()]
661
- item[0] = exDcmNum(item[0], -tokenOInfo['decimals']).toString()
662
- item[1] = exDcmNum(item[1], -token1Info['decimals']).toString()
663
- }
664
- }
665
- return rs.length === 1 ? rs[0]:rs
666
- }
667
-
668
- async function fmtErc20Amt(token, amount, direction = 1, web3) {
669
- web3 = web3 || global.web3;
670
- let decimals = (await tokenMap(token, web3)).decimals;
671
- if (direction >= 0) {
672
- return exDcmNum(amount, direction * decimals).toFixed(0)
673
- } else {
674
- return exDcmNum(amount, direction * decimals).toString()
675
- }
676
- }
677
-
678
- async function getTokenBal(address, token, fmt, web3) {
679
- web3 = web3 || global.web3;
680
- let bal
681
- if (token) {
682
- bal = await ethRead(token, erc20Abi, 'balanceOf', [address])
683
- } else {
684
- bal = await web3.eth.getBalance(address)
685
- }
686
- if (fmt) {
687
- return fmtErc20Amt(token, bal, -1, web3)
688
- }
689
- return bal
690
- }
691
-
692
- function swapExactFor(amount, reserves, fee) {
693
- fee = fee || 0
694
- if (!Array.isArray(reserves[0])) {
695
- reserves = [reserves]
696
- }
697
- for(let i = 0; i<reserves.length; i++) {
698
- let r = reserves[i]
699
- let a = toBigNum(r[0])
700
- let b = toBigNum(r[1])
701
- amount = b.minus(a.multipliedBy(b).dividedBy(a.plus(toBigNum(amount))))
702
- amount = amount.multipliedBy(1 - Number(fee))
703
- }
704
- return amount.toString()
705
- }
706
-
707
- function fastSwapExactFor(amount, reserves, fee) {
708
- fee = fee || 0
709
- if (!Array.isArray(reserves[0])) {
710
- reserves = [reserves]
711
- }
712
- for(let i = 0; i<reserves.length; i++) {
713
- let r = reserves[i]
714
- let a = Number(r[0])
715
- let b = Number(r[1])
716
- amount = b - (a*b/(a + Number(amount)))
717
- amount = amount * (1 - Number(fee))
718
- }
719
- return amount
720
- }
721
-
722
- function swapForExact(amount, reserves, fee) {
723
- fee = fee || 0
724
- if (!Array.isArray(reserves[0])) {
725
- reserves = [reserves]
726
- }
727
- for(let i = reserves.length - 1; i>=0; i--) {
728
- let r = reserves[i]
729
- let a = toBigNum(r[0])
730
- let b = toBigNum(r[1])
731
- amount = a.multipliedBy(b).div(b.minus(toBigNum(amount))).minus(a)
732
- amount = amount.multipliedBy(1 + Number(fee))
733
- }
734
- return amount.toString()
735
- }
736
-
737
- function fastSwapForExact(amount, reserves, fee) {
738
- fee = fee || 0
739
- if (!Array.isArray(reserves[0])) {
740
- reserves = [reserves]
741
- }
742
- for(let i = reserves.length - 1; i>=0; i--) {
743
- let r = reserves[i]
744
- let a = Number(r[0])
745
- let b = Number(r[1])
746
- amount = (a * b / (b - Number(amount))) - a
747
- amount = amount * (1 + Number(fee))
748
- }
749
- return amount
750
- }
751
-
752
- function encodeEthAddress(address) {
753
- if (address.length !== 42) throw new TypeError('Bad address')
754
- address = address.slice(2).toLowerCase()
755
- let checksum = createKeccakHash('keccak256')
756
- .update(address)
757
- .digest()
758
-
759
- let ret = '0x'
760
- for (let i = 0; i < 20; ++i) {
761
- let byte = checksum[i]
762
- let ha = address.charAt(i * 2)
763
- let hb = address.charAt(i * 2 + 1)
764
- ret += (byte & 0xf0) >= 0x80 ? ha.toUpperCase() : ha
765
- ret += (byte & 0x0f) >= 0x08 ? hb.toUpperCase() : hb
766
- }
767
-
768
- return ret
769
- }
770
-
771
- async function ethQuery(web3, address, method, ...args) {
772
- try {
773
- const contract = newContract(web3, (await getContractJson(address)).abi, address);
774
- return await contract.methods[method](...args).call()
775
- } catch (e) {
776
- $log(new Error(errorStack(e)).stack)
777
- return null;
778
- }
779
- }
780
-
781
- async function ethQuery1(web3, address, abi, method, ...args) {
782
- try {
783
- const contract = newContract(web3, abi, address);
784
- return await contract.methods[method](...args).call()
785
- } catch (e) {
786
- $log(new Error(errorStack(e)).stack)
787
- return null
788
- }
789
- }
790
-
791
- async function ethRead(address, abi, method, args, web3) {
792
- web3 = web3 || global.web3
793
- try {
794
- const contract = newContract(web3, abi, address);
795
- return await contract.methods[method](...args).call()
796
- } catch (e) {
797
- $log(new Error(errorStack(e)).stack)
798
- return null
799
- }
800
- }
801
-
802
- function createEthWrite(config = {}) {
803
- return async (sender, address, opt) => {
804
- return await ethWrite(sender, address, Object.assign({}, config, opt))
805
- }
806
- }
807
-
808
- async function ethWrite(sender, address,
809
- {data, abi, method, args, gasPrice, gasPriceAdd, gasLimit,
810
- value, waitConfirm, sleepSec, msg, onError} = {}) {
811
- if (!address) {
812
- throw 'address require for ethWrite'
813
- }
814
- let web3 = sender.web3 || global.web3
815
-
816
- let txObject = {
817
- gasPrice: gasPrice ? gasPrice * 1000000000 : await cacheFn('getGasPrice', async () => await web3.eth.getGasPrice(), 1000 * 5),
818
- gasLimit: gasLimit,
819
- to: address,
820
- value
821
- }
822
- if (gasPriceAdd && !gasPrice) {
823
- txObject.gasPrice = Number(txObject.gasPrice) + (gasPriceAdd * 1000000000)
824
- }
825
- if (method) {
826
- txObject.data = newContract(web3, abi, address).methods[method](...args).encodeABI()
827
- }
828
- if (data) {
829
- txObject.data = data
830
- }
831
-
832
- let [result, signedTx] = await sender(txObject)
833
- if (!signedTx) {
834
- console.log("No address to used")
835
- return {}
836
- }
837
- msg = trim(msg)
838
- msg = msg ? msg + ' ':''
839
- console.log(`${msg}send ${signedTx.hash} ${txObject.gasPrice/1000000000}g`)
840
- result = result.then(resp => {
841
- console.log(infoStr(`${msg}${signedTx.hash} 交易成功`))
842
- return true
843
- }).catch(async e => {
844
- console.log(errorStr(`${msg}${signedTx.hash} 交易失败`))
845
- if (onError) {
846
- return await onError(e)
847
- } else {
848
- console.error(String(e))
849
- $log(new Error(errorStack(e)).stack)
850
- return false
851
- }
852
- })
853
- if (waitConfirm) {
854
- await result
855
- }
856
- if (sleepSec) {
857
- await sleep(sleepSec * 1000)
858
- }
859
- return {
860
- promise: result
861
- }
862
- }
863
-
864
- function parseInputData(abi, input) {
865
- abiDecoder.addABI(abi);
866
- return abiDecoder.decodeMethod(input);
867
- }
868
-
869
- async function forEachBlock(web3, num, fn) {
870
- let blockNum = await web3.eth.getBlockNumber();
871
- while (num > 0 && blockNum > 0) {
872
- let blockInfo = await web3.eth.getBlock(blockNum)
873
-
874
- await fn(blockInfo)
875
-
876
- blockNum --
877
- num --
878
- }
879
- }
880
-
881
- async function web3BatchReq(items, batchNum = 49, asyncNum = 33, web3) {
882
- web3 = web3 || global.web3
883
- if (items.length <= 0) {
884
- return []
885
- }
886
- if (batchNum) {
887
- let callBatchs = splitArray(items, batchNum)
888
- return (await batchAsync(callBatchs.map(item => () => _web3BatchReq(item, web3)), asyncNum))
889
- .reduce((a,b) => a.concat(b))
890
- }
891
- return _web3BatchReq(items, web3)
892
- }
893
-
894
- async function _web3BatchReq(items, web3) {
895
- let batcher = new web3.BatchRequest();
896
- let proms = []
897
- items.forEach(item => {
898
- proms.push(
899
- new Promise(resolve => {
900
- batcher.add((item.method || item.name).request(...(item.args || item.params || []), async (error, result) => {
901
- if (item.callback) {
902
- try {
903
- await item.callback(error, result)
904
- } catch (e) {
905
- $log(new Error(errorStack(e)).stack)
906
- resolve(result || error);
907
- throw e
908
- }
909
- }
910
- if (error) {
911
- $log(new Error(errorStack(error)).stack)
912
- }
913
- resolve(result || error);
914
- }));
915
- }))
916
- })
917
- batcher.execute();
918
- return await Promise.all(proms)
919
- }
920
-
921
- async function forEachBlockBatch(getFromAndTo, fn, web3) {
922
- web3 = web3 || global.web3;
923
- let arr = getFromAndTo
924
- if (!Array.isArray(getFromAndTo)) {
925
- let blockNum = await web3.eth.getBlockNumber();
926
- arr = await getFromAndTo(blockNum);
927
- }
928
- let [from, to] = arr;
929
-
930
- let items = []
931
- for(let i = from; i<= to; i++) {
932
- items.push({
933
- method: web3.eth.getBlock,
934
- args: [i],
935
- callback: async (...args) => await fn(...args)
936
- })
937
- }
938
- await web3BatchReq(items)
939
- }
940
-
941
- async function forEachBlockAsync(web3, num, fn) {
942
- let blockNum = await web3.eth.getBlockNumber();
943
- let items = []
944
- while (num > 0 && blockNum > 0) {
945
- items.push(eFn(async (blockNum) => {
946
- let blockInfo = await web3.eth.getBlock(blockNum)
947
- await fn(blockInfo)
948
- }, blockNum))
949
- blockNum --
950
- num --
951
- }
952
- await Promise.all(items);
953
- }
954
-
955
- async function forEachEvent(con, eventName, getFromAndTo, callback, filter = {}, web3) {
956
- web3 = web3 || global.web3;
957
- let arr = getFromAndTo
958
- if (!Array.isArray(getFromAndTo)) {
959
- let blockNum = await web3.eth.getBlockNumber();
960
- arr = await getFromAndTo(blockNum);
961
- }
962
- let [from, to] = arr;
963
-
964
- let proms = [];
965
- for(let i = from; i<= to; i+=5000) {
966
- let prom = new Promise(async resolve => {
967
- await con.getPastEvents(eventName, {
968
- filter,
969
- fromBlock: i,
970
- toBlock: (i+5000) > to ? to : i+5000
971
- }, async function(error, events){
972
- try {
973
- await callback(error, events)
974
- } catch (e) {
975
- resolve()
976
- throw e
977
- }
978
- resolve()
979
- })
980
- })
981
-
982
- if (i % 33 === 0) {
983
- await prom;
984
- }
985
- proms.push(prom)
986
- }
987
- await Promise.all(proms)
988
- }
989
-
990
-
991
- let newSender = async (item) => {
992
- if (String(item).startsWith('0x')) {
993
- return await initSenderWithKeys(web3, [item])
994
- } else {
995
- return await initSender(web3, global.$wallet, [item])
996
- }
997
- }
998
-
999
- async function batchTranfer(ethTranserWrite, token, froms, tos, min, max) {
1000
- let decimals = 18
1001
- let symbol
1002
- if (token) {
1003
- let info = await tokenMap(token)
1004
- decimals = info.decimals
1005
- symbol = info.symbol
1006
- }
1007
- if (!vl(max)) {
1008
- max = min
1009
- }
1010
-
1011
- min = Number(exDcmNum(min, decimals).toFixed(0))
1012
- max = Number(exDcmNum(max, decimals).toFixed(0))
1013
-
1014
- let pros = []
1015
- for(let from of froms) {
1016
- let sender = await newSender(from)
1017
- for(let to of tos) {
1018
- let fromAddress = String(from).startsWith('0x') ? privateToAddress(from):global.$wallet.getAddress(from);
1019
- let toAddress = String(to).startsWith('0x') ? to:global.$wallet.getAddress(to);
1020
-
1021
- if (token) {
1022
- let fb = await ethRead(token, erc20Abi, 'balanceOf', [fromAddress])
1023
- let tb = await ethRead(token, erc20Abi, 'balanceOf', [toAddress])
1024
-
1025
- if (Number(tb) >= min) {
1026
- continue
1027
- }
1028
- let transferAmt = BigNumber(bMin(max, tb)).toFixed(0)
1029
- if (Number(fb) < Number(transferAmt)) {
1030
- continue
1031
- }
1032
- let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
1033
- pros.push(await ethTranserWrite(sender, token, {
1034
- abi: erc20Abi,
1035
- method: 'transfer',
1036
- args: [toAddress, transferAmt],
1037
- msg: from + '转' + fmtTransferAmt + symbol +'给' + to
1038
- }))
1039
- } else {
1040
- let fb = await web3.eth.getBalance(fromAddress);
1041
- let tb = await web3.eth.getBalance(toAddress);
1042
-
1043
- if (Number(tb) >= min) {
1044
- continue
1045
- }
1046
- let transferAmt = BigNumber(bMin(max, tb)).toFixed(0)
1047
- if (Number(fb) < Number(transferAmt)) {
1048
- continue
1049
- }
1050
- let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
1051
- pros.push(await ethTranserWrite(sender, toAddress, {
1052
- value: transferAmt,
1053
- msg: from + '转' + fmtTransferAmt + '本币给' + to
1054
- }))
1055
- }
1056
- }
1057
- }
1058
- await Promise.all(pros.map(i => i.promise))
1059
- }
1060
-
1061
- async function batchCollect(ethTranserWrite, token, froms, to, remain) {
1062
- let decimals = 18
1063
- let symbol
1064
- if (token) {
1065
- let info = await tokenMap(token)
1066
- decimals = info.decimals
1067
- symbol = info.symbol
1068
- }
1069
-
1070
- remain = Number(exDcmNum(remain, decimals).toFixed(0))
1071
- let pros = []
1072
- for(let from of froms) {
1073
- let sender = await newSender(from)
1074
-
1075
- let fromAddress = String(from).startsWith('0x') ? privateToAddress(from):global.$wallet.getAddress(from);
1076
- let toAddress = String(to).startsWith('0x') ? to:global.$wallet.getAddress(to);
1077
-
1078
- if (token) {
1079
- let fb = await ethRead(token, erc20Abi, 'balanceOf', [fromAddress])
1080
-
1081
- if (Number(fb) <= remain) {
1082
- continue
1083
- }
1084
- let transferAmt = BigNumber(bMin(fb, remain)).toFixed(0)
1085
- let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
1086
- pros.push(await ethTranserWrite(sender, token, {
1087
- abi: erc20Abi,
1088
- method: 'transfer',
1089
- args: [toAddress, transferAmt],
1090
- msg: from + '转' + fmtTransferAmt + symbol +'给' + to
1091
- }))
1092
- } else {
1093
- let fb = await web3.eth.getBalance(fromAddress);
1094
-
1095
- if (Number(fb) <= remain) {
1096
- continue
1097
- }
1098
- let transferAmt = BigNumber(bMin(fb, remain)).toFixed(0)
1099
- let fmtTransferAmt = exDcmNum(transferAmt, -decimals).toString()
1100
- pros.push(await ethTranserWrite(sender, toAddress, {
1101
- value: transferAmt,
1102
- msg: from + '转' + fmtTransferAmt + '本币给' + to
1103
- }))
1104
- }
1105
- }
1106
- await Promise.all(pros.map(i => i.promise))
1107
- }
1108
-
1109
- function coinNum(num, decimals) {
1110
- return parseFloat(String(new BigNumber(num).dividedBy(new BigNumber(`10e+${(decimals || 18) - 1}`))))
1111
- }
1112
-
1113
- function exDcm(num, decimals, fixed) {
1114
- return new BigNumber(num).multipliedBy(new BigNumber(`1e${(decimals || 18)}`)).toFixed(fixed || 9).toString()
1115
- }
1116
-
1117
- function exDcmNum(num, decimals) {
1118
- return new BigNumber(num).multipliedBy(new BigNumber(`1e${(decimals || 18)}`))
1119
- }
1120
-
1121
- async function getContractJson(address) {
1122
- if (fs.existsSync(address)) {
1123
- return JSON.parse(String(fs.readFileSync(address)))
1124
- }
1125
- if (fs.existsSync(contractMapPath)) {
1126
- let obj = JSON.parse(String(fs.readFileSync(contractMapPath)))
1127
- if (obj[address]) {
1128
- return JSON.parse(String(fs.readFileSync(obj[address]['abiPath'])))
1129
- }
1130
- }
1131
- }
1132
-
1133
- async function transferToken(ethTranserWrite, sender, address, token, amt, msg) {
1134
- if (token) {
1135
- await ethTranserWrite(sender, token, {
1136
- abi: erc20Abi,
1137
- method: 'transfer',
1138
- args: [address, amt],
1139
- msg
1140
- })
1141
- } else {
1142
- await ethTranserWrite(sender, address, {
1143
- value: amt,
1144
- msg
1145
- })
1146
- }
1147
- }
1148
-
1149
- async function tokenApprove(ethWrite, sender, senderAddress, erc20, spender, opt) {
1150
- if (!erc20) {
1151
- return
1152
- }
1153
- let {amt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', msg} = opt
1154
- let bal = await ethRead(erc20, erc20Abi, 'allowance', [senderAddress, spender])
1155
- if (bal <Number(amt) ) {
1156
- return await ethWrite(sender, erc20, {
1157
- abi: erc20Abi,
1158
- method: 'approve',
1159
- args: [spender, amt],
1160
- msg
1161
- })
1162
- }
1163
-
1164
- return {
1165
- promise: true
1166
- }
1167
- }
1168
-
1169
- function txnInputReplacer(input, callback) {
1170
- let item = [input.substring(0, 10)]
1171
- input = input.substring(10, input.length)
1172
-
1173
- let str = ''
1174
- while (input.length > 0) {
1175
- str = input.substring(0, 64);
1176
- input = input.substring(64, input.length);
1177
- if (callback) {
1178
- str = callback(str)
1179
- }
1180
- item.push(str)
1181
- }
1182
- return item.join('')
1183
- }
1184
-
1185
- module.exports = {
1186
- getContractJson,
1187
- coinNum,
1188
- forEachBlock,
1189
- parseInputData,
1190
- exDcm,
1191
- ethQuery,
1192
- ethQuery1,
1193
- exDcmNum,
1194
- encodeEthAddress,
1195
- gasPrice,
1196
- tokenMap,
1197
- getPrice,
1198
- getPriceNoFee,
1199
- getPriceByAmt,
1200
- getPriceByAmtNoFee,
1201
- getPairAmt,
1202
- getPairAmtNoFee,
1203
- getPoolAmt,
1204
- getLastBlockSecTime,
1205
- forEachBlockAsync,
1206
- newContract,
1207
- sendTxn,
1208
- walletGet,
1209
- txnSign,
1210
- initSender,
1211
- privateToAddress,
1212
- initSenderWithKeys,
1213
- erc20Query,
1214
- initRootWallet,
1215
- ethBatchQuery,
1216
- newAbiInterface,
1217
- newWeb3Contract,
1218
- getTokenMap,
1219
- getReserves,
1220
- swapExactFor,
1221
- swapForExact,
1222
- ethRead,
1223
- ethWrite,
1224
- fmtErc20Amt,
1225
- createEthWrite,
1226
- forEachBlockBatch,
1227
- forEachEvent,
1228
- batchTranfer,
1229
- batchCollect,
1230
- web3BatchReq,
1231
- fastSwapExactFor,
1232
- fastSwapForExact,
1233
- getTokenBal,
1234
- transferToken,
1235
- tokenApprove,
1236
- txnInputReplacer,
1237
- abiDecoder,
1238
- saveTokenMap
1239
- }