jsir 1.2.9 → 1.3.1

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