@pioneer-platform/eth-network 8.6.0 → 8.7.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/CHANGELOG.md +25 -0
- package/package.json +19 -21
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/constant.d.ts +0 -208
- package/lib/constant.js +0 -554
- package/lib/constant.js.map +0 -1
- package/lib/etherscan-api.d.ts +0 -58
- package/lib/etherscan-api.js +0 -104
- package/lib/etherscan-api.js.map +0 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1701
- package/lib/index.js.map +0 -1
- package/lib/types/client-types.d.ts +0 -41
- package/lib/types/client-types.js +0 -9
- package/lib/types/client-types.js.map +0 -1
- package/lib/types/etherscan-api-types.d.ts +0 -55
- package/lib/types/etherscan-api-types.js +0 -3
- package/lib/types/etherscan-api-types.js.map +0 -1
- package/lib/types/index.d.ts +0 -2
- package/lib/types/index.js +0 -19
- package/lib/types/index.js.map +0 -1
- package/lib/utils.d.ts +0 -135
- package/lib/utils.js +0 -471
- package/lib/utils.js.map +0 -1
package/lib/index.js
DELETED
@@ -1,1701 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
/*
|
3
|
-
ETH Network tools
|
4
|
-
|
5
|
-
|
6
|
-
Goals:
|
7
|
-
|
8
|
-
*
|
9
|
-
|
10
|
-
|
11
|
-
*/
|
12
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
13
|
-
if (k2 === undefined) k2 = k;
|
14
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
15
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
16
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
17
|
-
}
|
18
|
-
Object.defineProperty(o, k2, desc);
|
19
|
-
}) : (function(o, m, k, k2) {
|
20
|
-
if (k2 === undefined) k2 = k;
|
21
|
-
o[k2] = m[k];
|
22
|
-
}));
|
23
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
24
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
25
|
-
}) : function(o, v) {
|
26
|
-
o["default"] = v;
|
27
|
-
});
|
28
|
-
var __importStar = (this && this.__importStar) || (function () {
|
29
|
-
var ownKeys = function(o) {
|
30
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
31
|
-
var ar = [];
|
32
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
33
|
-
return ar;
|
34
|
-
};
|
35
|
-
return ownKeys(o);
|
36
|
-
};
|
37
|
-
return function (mod) {
|
38
|
-
if (mod && mod.__esModule) return mod;
|
39
|
-
var result = {};
|
40
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
41
|
-
__setModuleDefault(result, mod);
|
42
|
-
return result;
|
43
|
-
};
|
44
|
-
})();
|
45
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
46
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
47
|
-
};
|
48
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
49
|
-
const TAG = " | eth-network | ";
|
50
|
-
let Web3 = require('web3');
|
51
|
-
// @ts-ignore
|
52
|
-
const ethers = __importStar(require("ethers"));
|
53
|
-
// @ts-ignore
|
54
|
-
const BigNumber = require('bignumber.js');
|
55
|
-
//
|
56
|
-
const Axios = require('axios');
|
57
|
-
const https = require('https');
|
58
|
-
const axios = Axios.create({
|
59
|
-
httpsAgent: new https.Agent({
|
60
|
-
rejectUnauthorized: false
|
61
|
-
})
|
62
|
-
});
|
63
|
-
const request = require("request-promise");
|
64
|
-
//blockbook
|
65
|
-
let blockbook = require("@pioneer-platform/blockbook");
|
66
|
-
const Web3Utils = require('web3-utils');
|
67
|
-
const providers_1 = require("@ethersproject/providers");
|
68
|
-
const abi_1 = require("@ethersproject/abi");
|
69
|
-
const utils_1 = require("./utils");
|
70
|
-
const xchain_util_1 = require("@xchainjs/xchain-util");
|
71
|
-
const etherscanAPI = __importStar(require("./etherscan-api"));
|
72
|
-
const log = require('@pioneer-platform/loggerdog')();
|
73
|
-
let ETHPLORER_API_KEY = process.env['ETHPLORER_API_KEY'] || 'freekey';
|
74
|
-
const nodes_1 = __importDefault(require("@pioneer-platform/nodes"));
|
75
|
-
let wait = require('wait-promise');
|
76
|
-
let sleep = wait.sleep;
|
77
|
-
//
|
78
|
-
let web3;
|
79
|
-
let web3Base;
|
80
|
-
let ETHERSCAN;
|
81
|
-
let ETHPLORER;
|
82
|
-
let PROVIDER;
|
83
|
-
let NODE_URL;
|
84
|
-
//TODO precision module
|
85
|
-
let BASE = 1000000000000000000;
|
86
|
-
// Remove deprecated import
|
87
|
-
// import { Interface } from '@ethersproject/abi'
|
88
|
-
const constant_1 = require("./constant"); // Replace with your actual module file path
|
89
|
-
const BASE_NODE = 'https://base.llamarpc.com';
|
90
|
-
const NODES = [];
|
91
|
-
module.exports = {
|
92
|
-
init: function (settings) {
|
93
|
-
//blockbook.init()
|
94
|
-
//log.debug("node: ",process.env['PARITY_ARCHIVE_NODE'])
|
95
|
-
//load
|
96
|
-
// @ts-ignore
|
97
|
-
let web3nodes = nodes_1.default.getWeb3Nodes();
|
98
|
-
for (let i = 0; i < web3nodes.length; i++) {
|
99
|
-
let node = web3nodes[i];
|
100
|
-
if (!node.networkId)
|
101
|
-
throw Error('missing networkId');
|
102
|
-
if (!node.service)
|
103
|
-
throw Error('missing networkId');
|
104
|
-
NODES.push(node);
|
105
|
-
}
|
106
|
-
if (!settings) {
|
107
|
-
//use default
|
108
|
-
web3 = new Web3(process.env['PARITY_ARCHIVE_NODE']);
|
109
|
-
web3Base = new Web3(process.env['BASE_NODE'] || 'https://base.llamarpc.com');
|
110
|
-
ETHERSCAN = new providers_1.EtherscanProvider('mainnet', process.env['ETHERSCAN_API_KEY']);
|
111
|
-
PROVIDER = new ethers.providers.InfuraProvider('mainnet', process.env['INFURA_API_KEY']);
|
112
|
-
NODE_URL = process.env['PARITY_ARCHIVE_NODE'];
|
113
|
-
}
|
114
|
-
else if (settings.testnet) {
|
115
|
-
if (!process.env['INFURA_TESTNET_ROPSTEN'])
|
116
|
-
throw Error("Missing INFURA_TESTNET_ROPSTEN");
|
117
|
-
if (!process.env['ETHERSCAN_API_KEY'])
|
118
|
-
throw Error("Missing ETHERSCAN_API_KEY");
|
119
|
-
//if testnet
|
120
|
-
web3 = new Web3(process.env['INFURA_TESTNET_ROPSTEN']);
|
121
|
-
NODE_URL = process.env['INFURA_TESTNET_ROPSTEN'];
|
122
|
-
ETHERSCAN = new providers_1.EtherscanProvider('ropsten', process.env['ETHERSCAN_API_KEY']);
|
123
|
-
PROVIDER = new ethers.providers.InfuraProvider('ropsten', process.env['INFURA_API_KEY']);
|
124
|
-
}
|
125
|
-
else if (settings.network) {
|
126
|
-
//force a network setting
|
127
|
-
//TODO
|
128
|
-
}
|
129
|
-
else {
|
130
|
-
//TODO if custom
|
131
|
-
web3 = new Web3(process.env['PARITY_ARCHIVE_NODE']);
|
132
|
-
}
|
133
|
-
},
|
134
|
-
getNodes: function () {
|
135
|
-
return NODES;
|
136
|
-
},
|
137
|
-
addNode: function (node) {
|
138
|
-
if (!node.networkId)
|
139
|
-
throw Error('missing networkId');
|
140
|
-
if (!node.service)
|
141
|
-
throw Error('missing networkId');
|
142
|
-
return NODES.push(node);
|
143
|
-
},
|
144
|
-
addNodes: function (nodes) {
|
145
|
-
for (let i = 0; i < nodes.length; i++) {
|
146
|
-
let node = nodes[i];
|
147
|
-
if (!node.networkId)
|
148
|
-
throw Error('missing networkId');
|
149
|
-
if (!node.service)
|
150
|
-
throw Error('missing networkId');
|
151
|
-
return NODES.push(node);
|
152
|
-
}
|
153
|
-
},
|
154
|
-
decodeTx: function (tx) {
|
155
|
-
return decode_tx(tx);
|
156
|
-
},
|
157
|
-
getInfo: function () {
|
158
|
-
return check_online_status();
|
159
|
-
},
|
160
|
-
getAllowance: function (token, spender, sender) {
|
161
|
-
return get_allowance(token, spender, sender);
|
162
|
-
},
|
163
|
-
getNonce: function (address) {
|
164
|
-
return web3.eth.getTransactionCount(address, 'pending');
|
165
|
-
},
|
166
|
-
getTxCount: function (address, options) {
|
167
|
-
return get_tx_count(address, options);
|
168
|
-
},
|
169
|
-
getFees: function (params) {
|
170
|
-
return get_fees(params);
|
171
|
-
},
|
172
|
-
estimateFee: async function (asset, params) {
|
173
|
-
if (!params)
|
174
|
-
throw Error("params required");
|
175
|
-
if (!params.asset && !asset)
|
176
|
-
throw Error("Asset or params.asset required");
|
177
|
-
//create asset locally with correct types
|
178
|
-
let assetForEstimate = asset ? asset : params.asset;
|
179
|
-
const { average: averageGP, fast: fastGP, fastest: fastestGP } = (0, utils_1.getDefaultGasPrices)();
|
180
|
-
let assetAddress;
|
181
|
-
// @ts-ignore
|
182
|
-
if (assetForEstimate && (0, xchain_util_1.assetToString)(assetForEstimate) !== (0, xchain_util_1.assetToString)(xchain_util_1.AssetETH)) {
|
183
|
-
// @ts-ignore
|
184
|
-
assetAddress = (0, utils_1.getTokenAddress)(assetForEstimate);
|
185
|
-
}
|
186
|
-
let gasLimit;
|
187
|
-
if (assetAddress && assetAddress !== utils_1.ETHAddress) {
|
188
|
-
if (!params.sender || !params.recipient)
|
189
|
-
throw Error("missing params! need sender and recipient on token tx!");
|
190
|
-
let contract = new ethers.Contract(assetAddress, constant_1.ERC20ABI, PROVIDER);
|
191
|
-
gasLimit = params.data
|
192
|
-
? await contract.estimateGas.transfer(params.recipient, params.data, { from: params.sender })
|
193
|
-
: await contract.estimateGas.transfer(params.recipient, 1, { from: params.sender }); // amount = 1 as a dummy value
|
194
|
-
}
|
195
|
-
else {
|
196
|
-
// ETH transfer
|
197
|
-
if (!params.sender || !params.recipient)
|
198
|
-
throw Error("missing params! need sender and recipient");
|
199
|
-
const gasEstimate = await PROVIDER.estimateGas({
|
200
|
-
from: params.sender,
|
201
|
-
to: params.recipient,
|
202
|
-
value: ethers.utils.parseEther('0.000001') // using dummy ETH amount for gas calculation
|
203
|
-
});
|
204
|
-
gasLimit = gasEstimate.add(0); // Add extra gas as buffer
|
205
|
-
console.log("ETH gasEstimate: ", gasEstimate.toString());
|
206
|
-
console.log("ETH gasEstimate + buffer: ", gasLimit.toString());
|
207
|
-
}
|
208
|
-
return {
|
209
|
-
gasPrices: {
|
210
|
-
average: averageGP,
|
211
|
-
fast: fastGP,
|
212
|
-
fastest: fastestGP
|
213
|
-
},
|
214
|
-
gasLimit,
|
215
|
-
fees: {
|
216
|
-
type: 'byte',
|
217
|
-
average: (0, utils_1.getFee)({ gasPrice: averageGP, gasLimit }),
|
218
|
-
fast: (0, utils_1.getFee)({ gasPrice: fastGP, gasLimit }),
|
219
|
-
fastest: (0, utils_1.getFee)({ gasPrice: fastestGP, gasLimit })
|
220
|
-
}
|
221
|
-
};
|
222
|
-
},
|
223
|
-
getMemoEncoded: function (params) {
|
224
|
-
return get_memo_data(params);
|
225
|
-
},
|
226
|
-
getStreamInfo: function (streamId) {
|
227
|
-
return get_stream(streamId);
|
228
|
-
},
|
229
|
-
getSymbolFromContract: function (contract) {
|
230
|
-
return get_symbol_from_contract(contract);
|
231
|
-
},
|
232
|
-
getTransferData: function (toAddress, amount, contract) {
|
233
|
-
return get_token_transfer_data(toAddress, amount, contract);
|
234
|
-
},
|
235
|
-
getPoolPositions: function (address) {
|
236
|
-
return get_pool_positions(address);
|
237
|
-
},
|
238
|
-
getAllPioneers: function () {
|
239
|
-
return get_all_pioneers();
|
240
|
-
},
|
241
|
-
getAllPioneersBase: function () {
|
242
|
-
return get_all_pioneers_base();
|
243
|
-
},
|
244
|
-
getAllTokensEth: function (address) {
|
245
|
-
return get_all_tokens_blockbook(address);
|
246
|
-
},
|
247
|
-
getPercentPool: function (amountFox, amountEth, poolAddress) {
|
248
|
-
return get_pool_percent(amountFox, amountEth, poolAddress);
|
249
|
-
},
|
250
|
-
checkAirdropClaim: function (address) {
|
251
|
-
return check_airdrop_claim(address);
|
252
|
-
},
|
253
|
-
buildAirdropClaim: function (address) {
|
254
|
-
return build_airdrop_claim(address);
|
255
|
-
},
|
256
|
-
// getFees: function (params: XFeesParams & FeesParams): Promise<Fees> {
|
257
|
-
// return get_fees()
|
258
|
-
// },
|
259
|
-
// estimateGasNormalTx: function (address:string): Promise<BaseAmount> {
|
260
|
-
// return get_balance_tokens(address)
|
261
|
-
// },
|
262
|
-
// estimateGasERC20Tx: function (address:string): Promise<BaseAmount> {
|
263
|
-
// return get_balance_tokens(address)
|
264
|
-
// },
|
265
|
-
getGasPrice: function () {
|
266
|
-
return web3.eth.getGasPrice();
|
267
|
-
},
|
268
|
-
getTransaction: function (txid) {
|
269
|
-
return get_transaction(txid);
|
270
|
-
},
|
271
|
-
getTransactions: function (address, options) {
|
272
|
-
return get_transactions(address, options);
|
273
|
-
},
|
274
|
-
getBalance: function (address) {
|
275
|
-
return get_balance(address);
|
276
|
-
},
|
277
|
-
getBalances: function (addresses) {
|
278
|
-
return get_balances(addresses);
|
279
|
-
},
|
280
|
-
getBalanceAddress: function (address) {
|
281
|
-
return get_balance(address);
|
282
|
-
},
|
283
|
-
getBalanceToken: function (address, token) {
|
284
|
-
return get_balance_token(address, token);
|
285
|
-
},
|
286
|
-
getBalanceTokens: function (address) {
|
287
|
-
return get_balance_tokens(address);
|
288
|
-
},
|
289
|
-
//gasPrice by network
|
290
|
-
getGasPriceByNetwork: function (networkId) {
|
291
|
-
return get_gas_price_by_network(networkId);
|
292
|
-
},
|
293
|
-
// Nonce by network
|
294
|
-
getNonceByNetwork: function (networkId, address) {
|
295
|
-
return get_nonce_by_network(networkId, address);
|
296
|
-
},
|
297
|
-
// Estimate Gas by network
|
298
|
-
estimateGasByNetwork: function (networkId, transaction) {
|
299
|
-
return estimate_gas_by_network(networkId, transaction);
|
300
|
-
},
|
301
|
-
getTransactionByNetwork: function (networkId, transaction) {
|
302
|
-
return get_transaction_by_network(networkId, transaction);
|
303
|
-
},
|
304
|
-
getTransactionsByNetwork: function (networkId, address, options) {
|
305
|
-
return get_transactions_by_network(networkId, address, options);
|
306
|
-
},
|
307
|
-
//
|
308
|
-
getBalanceAddressByNetwork: function (networkId, address) {
|
309
|
-
return get_balance_by_network(networkId, address);
|
310
|
-
},
|
311
|
-
getBalanceTokenByNetwork: function (networkId, address, token) {
|
312
|
-
return get_balance_token_by_network(networkId, address, token);
|
313
|
-
},
|
314
|
-
getBalanceTokensByNetwork: function (networkId, address) {
|
315
|
-
return get_balance_tokens_by_network(networkId, address);
|
316
|
-
},
|
317
|
-
broadcastByNetwork: function (networkId, tx) {
|
318
|
-
return broadcast_transaction_by_network(networkId, tx);
|
319
|
-
},
|
320
|
-
broadcast: function (tx) {
|
321
|
-
return broadcast_transaction(tx);
|
322
|
-
}
|
323
|
-
};
|
324
|
-
const get_transactions_by_network = async function (networkId, address, options = { fromBlock: 'latest', toBlock: 'latest' }) {
|
325
|
-
let tag = TAG + " | get_transactions_by_network | ";
|
326
|
-
try {
|
327
|
-
// Find the node in the NODES array
|
328
|
-
let node = NODES.find((n) => n.networkId === networkId);
|
329
|
-
if (!node)
|
330
|
-
throw new Error("101: Missing node for network " + networkId);
|
331
|
-
// Initialize a new web3 instance
|
332
|
-
let web3 = new Web3(node.service);
|
333
|
-
const checksumAddress = web3.utils.toChecksumAddress(address);
|
334
|
-
// Get the current block height
|
335
|
-
const currentBlockHeight = await web3.eth.getBlockNumber();
|
336
|
-
// Define the block range
|
337
|
-
let fromBlock = options?.fromBlock || 0;
|
338
|
-
let toBlock = options?.toBlock || "latest";
|
339
|
-
if (toBlock === "latest") {
|
340
|
-
toBlock = currentBlockHeight;
|
341
|
-
}
|
342
|
-
if (fromBlock === "latest") {
|
343
|
-
fromBlock = currentBlockHeight;
|
344
|
-
}
|
345
|
-
// Adjust the range to ensure it scans only the last 100 blocks if the range is too large
|
346
|
-
if (fromBlock < toBlock - 100) {
|
347
|
-
fromBlock = toBlock - 100;
|
348
|
-
}
|
349
|
-
log.info(tag, `Scanning from block ${fromBlock} to block ${toBlock}`);
|
350
|
-
let transactions = [];
|
351
|
-
// Loop through each block in the range
|
352
|
-
for (let blockNumber = fromBlock; blockNumber <= toBlock; blockNumber++) {
|
353
|
-
let block = await web3.eth.getBlock(blockNumber, true); // Retrieve block with full transactions
|
354
|
-
log.info(tag, "block ", block.transactions.length);
|
355
|
-
if (block && block.transactions) {
|
356
|
-
for (let tx of block.transactions) {
|
357
|
-
// Check if the transaction is initiated by the specified address
|
358
|
-
if (tx.from?.toLowerCase() === checksumAddress.toLowerCase()) {
|
359
|
-
// Collect detailed transaction info
|
360
|
-
let receipt = await web3.eth.getTransactionReceipt(tx.hash);
|
361
|
-
transactions.push({
|
362
|
-
txHash: tx.hash,
|
363
|
-
from: tx.from,
|
364
|
-
to: tx.to,
|
365
|
-
value: tx.value,
|
366
|
-
gas: tx.gas,
|
367
|
-
gasPrice: tx.gasPrice,
|
368
|
-
blockNumber: tx.blockNumber,
|
369
|
-
receipt,
|
370
|
-
});
|
371
|
-
}
|
372
|
-
}
|
373
|
-
}
|
374
|
-
}
|
375
|
-
// Fetch current and pending nonces for the address
|
376
|
-
let currentNonce = await web3.eth.getTransactionCount(checksumAddress, "pending");
|
377
|
-
let confirmedNonce = await web3.eth.getTransactionCount(checksumAddress, "latest");
|
378
|
-
log.info(tag, `Current Nonce: ${currentNonce}, Confirmed Nonce: ${confirmedNonce}`);
|
379
|
-
// Determine if there are pending transactions
|
380
|
-
const hasPendingTransactions = currentNonce > confirmedNonce;
|
381
|
-
if (hasPendingTransactions) {
|
382
|
-
log.info(tag, "Pending transactions detected");
|
383
|
-
// Fetch the pending transactions from the transaction pool
|
384
|
-
// Note: This requires access to the node's txpool, which may not be available on all nodes
|
385
|
-
let pendingBlock = await web3.eth.getBlock("pending", true);
|
386
|
-
if (pendingBlock && pendingBlock.transactions) {
|
387
|
-
for (let tx of pendingBlock.transactions) {
|
388
|
-
if (tx.from?.toLowerCase() === checksumAddress.toLowerCase()) {
|
389
|
-
// Collect detailed transaction info
|
390
|
-
transactions.push({
|
391
|
-
txHash: tx.hash,
|
392
|
-
from: tx.from,
|
393
|
-
to: tx.to,
|
394
|
-
value: tx.value,
|
395
|
-
gas: tx.gas,
|
396
|
-
gasPrice: tx.gasPrice,
|
397
|
-
blockNumber: tx.blockNumber, // This will be null for pending transactions
|
398
|
-
receipt: null, // Receipt is not available for pending transactions
|
399
|
-
pending: true,
|
400
|
-
});
|
401
|
-
}
|
402
|
-
}
|
403
|
-
}
|
404
|
-
}
|
405
|
-
else {
|
406
|
-
log.info(tag, "No pending transactions detected");
|
407
|
-
}
|
408
|
-
return {
|
409
|
-
address,
|
410
|
-
networkId,
|
411
|
-
fromBlock,
|
412
|
-
toBlock,
|
413
|
-
currentBlockHeight,
|
414
|
-
currentNonce,
|
415
|
-
hasPendingTransactions,
|
416
|
-
transactions,
|
417
|
-
};
|
418
|
-
}
|
419
|
-
catch (e) {
|
420
|
-
console.error(tag, e);
|
421
|
-
throw e; // Rethrow the error to handle it upstream
|
422
|
-
}
|
423
|
-
};
|
424
|
-
// Broadcast transaction by network
|
425
|
-
const broadcast_transaction_by_network = async function (networkId, tx) {
|
426
|
-
let tag = TAG + " | broadcast_transaction_by_network | ";
|
427
|
-
try {
|
428
|
-
if (!tx)
|
429
|
-
throw Error("Transaction data required!");
|
430
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
431
|
-
if (!nodes || nodes.length === 0)
|
432
|
-
throw Error("101: missing nodes for network " + networkId);
|
433
|
-
log.info(tag, `Found ${nodes.length} nodes for network ${networkId}`);
|
434
|
-
let lastError;
|
435
|
-
for (let i = 0; i < nodes.length && i < 5; i++) {
|
436
|
-
let node = nodes[i];
|
437
|
-
try {
|
438
|
-
log.info(tag, `Attempt ${i + 1}/${Math.min(nodes.length, 5)}: Trying node ${node.service}`);
|
439
|
-
// Test if node is alive first with a quick health check
|
440
|
-
let web3 = new Web3(node.service);
|
441
|
-
web3.eth.transactionBlockTimeout = 5;
|
442
|
-
try {
|
443
|
-
await web3.eth.getChainId();
|
444
|
-
}
|
445
|
-
catch (healthError) {
|
446
|
-
log.warn(tag, `Node ${node.service} health check failed: ${healthError.message}`);
|
447
|
-
continue;
|
448
|
-
}
|
449
|
-
// Broadcast transaction and return txid immediately
|
450
|
-
// Don't wait for receipt as it can timeout and cause "Failed to check for transaction receipt" errors
|
451
|
-
let txHash = null;
|
452
|
-
let broadcastPromise = new Promise((resolve, reject) => {
|
453
|
-
web3.eth.sendSignedTransaction(tx)
|
454
|
-
.on('transactionHash', function (hash) {
|
455
|
-
log.info(tag, "Transaction Hash received:", hash);
|
456
|
-
txHash = hash;
|
457
|
-
// Return immediately with the txid - don't wait for receipt
|
458
|
-
resolve({ transactionHash: hash });
|
459
|
-
})
|
460
|
-
.on('receipt', function (receipt) {
|
461
|
-
log.info(tag, "Receipt (not waiting for this):", receipt);
|
462
|
-
})
|
463
|
-
.on('confirmation', function (confirmationNumber, receipt) {
|
464
|
-
log.debug(tag, "Confirmation Number:", confirmationNumber, "Receipt:", receipt);
|
465
|
-
})
|
466
|
-
.on('error', function (error) {
|
467
|
-
log.error(tag, "Broadcast Error:", error);
|
468
|
-
// Check if we already have a txid - if so, the broadcast succeeded
|
469
|
-
if (txHash) {
|
470
|
-
log.warn(tag, "Transaction was broadcast (txid: " + txHash + ") but receipt check failed");
|
471
|
-
resolve({ transactionHash: txHash, warning: "Transaction broadcast but receipt not confirmed" });
|
472
|
-
}
|
473
|
-
else {
|
474
|
-
reject(error);
|
475
|
-
}
|
476
|
-
});
|
477
|
-
// Set a timeout to return txid if we have it, even if no receipt
|
478
|
-
setTimeout(() => {
|
479
|
-
if (txHash) {
|
480
|
-
log.info(tag, "Timeout waiting for receipt, but we have txid:", txHash);
|
481
|
-
resolve({ transactionHash: txHash, warning: "Receipt timeout" });
|
482
|
-
}
|
483
|
-
}, 5000); // 5 second timeout
|
484
|
-
});
|
485
|
-
let result = await broadcastPromise;
|
486
|
-
log.info(tag, "Success with node:", node.service);
|
487
|
-
// Return in the format the SDK expects
|
488
|
-
return {
|
489
|
-
txid: result.transactionHash,
|
490
|
-
success: true,
|
491
|
-
warning: result.warning
|
492
|
-
};
|
493
|
-
}
|
494
|
-
catch (nodeError) {
|
495
|
-
lastError = nodeError;
|
496
|
-
log.warn(tag, `Node ${i + 1} failed: ${node.service}, Error: ${nodeError.message}`);
|
497
|
-
// Wait a bit before trying next node
|
498
|
-
if (i < nodes.length - 1 && i < 4) {
|
499
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
500
|
-
}
|
501
|
-
}
|
502
|
-
}
|
503
|
-
throw new Error("All nodes failed for network " + networkId + ". Last error: " + (lastError?.message || "Unknown error"));
|
504
|
-
}
|
505
|
-
catch (e) {
|
506
|
-
console.error(tag, e);
|
507
|
-
throw e;
|
508
|
-
}
|
509
|
-
};
|
510
|
-
const get_transaction_by_network = async function (networkId, txid) {
|
511
|
-
let tag = TAG + " | get_transaction | ";
|
512
|
-
try {
|
513
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
514
|
-
if (!nodes || nodes.length === 0)
|
515
|
-
throw Error("101: missing nodes for network " + networkId);
|
516
|
-
let lastError;
|
517
|
-
for (let i = 0; i < nodes.length && i < 5; i++) {
|
518
|
-
let node = nodes[i];
|
519
|
-
try {
|
520
|
-
log.debug(tag, "Trying node:", node.service);
|
521
|
-
let web3 = new Web3(node.service);
|
522
|
-
web3.eth.transactionBlockTimeout = 5;
|
523
|
-
let output = {};
|
524
|
-
//normal tx info
|
525
|
-
output.txInfo = await web3.eth.getTransaction(txid);
|
526
|
-
//if contract
|
527
|
-
output.receipt = await web3.eth.getTransactionReceipt(txid);
|
528
|
-
log.debug(tag, "Success with node:", node.service);
|
529
|
-
return output;
|
530
|
-
}
|
531
|
-
catch (nodeError) {
|
532
|
-
lastError = nodeError;
|
533
|
-
log.debug(tag, "Failed with node:", node.service, "Error:", nodeError.message);
|
534
|
-
}
|
535
|
-
}
|
536
|
-
throw new Error("All nodes failed for network " + networkId + ". Last error: " + lastError.message);
|
537
|
-
}
|
538
|
-
catch (e) {
|
539
|
-
console.error(tag, e);
|
540
|
-
throw e;
|
541
|
-
}
|
542
|
-
};
|
543
|
-
const get_gas_price_by_network = async function (networkId) {
|
544
|
-
let tag = TAG + " | get_gas_price_by_network | ";
|
545
|
-
try {
|
546
|
-
// Find all nodes for this network
|
547
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
548
|
-
if (!nodes || nodes.length === 0)
|
549
|
-
throw Error("101: missing nodes for network " + networkId);
|
550
|
-
log.info(tag, `Found ${nodes.length} nodes for network ${networkId}`);
|
551
|
-
let lastError;
|
552
|
-
// Try each node until one works
|
553
|
-
for (let i = 0; i < nodes.length && i < 5; i++) { // Try up to 5 nodes
|
554
|
-
let node = nodes[i];
|
555
|
-
try {
|
556
|
-
log.info(tag, `Attempt ${i + 1}/${Math.min(nodes.length, 5)}: Trying node ${node.service}`);
|
557
|
-
// Initialize new web3 instance
|
558
|
-
let web3 = new Web3(node.service);
|
559
|
-
// Set timeout for the request
|
560
|
-
web3.eth.transactionBlockTimeout = 5; // 5 seconds timeout
|
561
|
-
// Health check first
|
562
|
-
try {
|
563
|
-
await web3.eth.getChainId();
|
564
|
-
}
|
565
|
-
catch (healthError) {
|
566
|
-
log.warn(tag, `Node ${node.service} health check failed: ${healthError.message}`);
|
567
|
-
continue;
|
568
|
-
}
|
569
|
-
// Get gas price
|
570
|
-
let gasPrice = await web3.eth.getGasPrice();
|
571
|
-
log.info(tag, `Success with node ${node.service}, gasPrice: ${gasPrice}`);
|
572
|
-
return gasPrice;
|
573
|
-
}
|
574
|
-
catch (nodeError) {
|
575
|
-
lastError = nodeError;
|
576
|
-
log.warn(tag, `Node ${i + 1} failed: ${node.service}, Error: ${nodeError.message}`);
|
577
|
-
// Wait a bit before trying next node
|
578
|
-
if (i < nodes.length - 1 && i < 4) {
|
579
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
580
|
-
}
|
581
|
-
}
|
582
|
-
}
|
583
|
-
// If all nodes failed, throw the last error
|
584
|
-
throw new Error("All nodes failed for network " + networkId + ". Last error: " + (lastError?.message || "Unknown error"));
|
585
|
-
}
|
586
|
-
catch (e) {
|
587
|
-
console.error(tag, e);
|
588
|
-
throw e;
|
589
|
-
}
|
590
|
-
};
|
591
|
-
const get_nonce_by_network = async function (networkId, address) {
|
592
|
-
let tag = TAG + " | get_nonce_by_network | ";
|
593
|
-
try {
|
594
|
-
if (!address)
|
595
|
-
throw Error("Address required!");
|
596
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
597
|
-
if (!nodes || nodes.length === 0)
|
598
|
-
throw Error("101: missing nodes for network " + networkId);
|
599
|
-
let lastError;
|
600
|
-
for (let i = 0; i < nodes.length && i < 5; i++) {
|
601
|
-
let node = nodes[i];
|
602
|
-
try {
|
603
|
-
log.debug(tag, "Trying node:", node.service);
|
604
|
-
let web3 = new Web3(node.service);
|
605
|
-
web3.eth.transactionBlockTimeout = 5;
|
606
|
-
let nonce = await web3.eth.getTransactionCount(address, 'pending');
|
607
|
-
log.debug(tag, "Success with node:", node.service, "nonce:", nonce);
|
608
|
-
return nonce;
|
609
|
-
}
|
610
|
-
catch (nodeError) {
|
611
|
-
lastError = nodeError;
|
612
|
-
log.debug(tag, "Failed with node:", node.service, "Error:", nodeError.message);
|
613
|
-
}
|
614
|
-
}
|
615
|
-
throw new Error("All nodes failed for network " + networkId + ". Last error: " + lastError.message);
|
616
|
-
}
|
617
|
-
catch (e) {
|
618
|
-
console.error(tag, e);
|
619
|
-
throw e;
|
620
|
-
}
|
621
|
-
};
|
622
|
-
const estimate_gas_by_network = async function (networkId, transaction) {
|
623
|
-
let tag = TAG + " | estimate_gas_by_network | ";
|
624
|
-
try {
|
625
|
-
if (!transaction)
|
626
|
-
throw Error("Transaction object required!");
|
627
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
628
|
-
if (!nodes || nodes.length === 0)
|
629
|
-
throw Error("101: missing nodes for network " + networkId);
|
630
|
-
let lastError;
|
631
|
-
for (let i = 0; i < nodes.length && i < 5; i++) {
|
632
|
-
let node = nodes[i];
|
633
|
-
try {
|
634
|
-
log.debug(tag, "Trying node:", node.service);
|
635
|
-
let web3 = new Web3(node.service);
|
636
|
-
web3.eth.transactionBlockTimeout = 5;
|
637
|
-
let gas = await web3.eth.estimateGas(transaction);
|
638
|
-
log.debug(tag, "Success with node:", node.service, "gas:", gas);
|
639
|
-
return gas;
|
640
|
-
}
|
641
|
-
catch (nodeError) {
|
642
|
-
lastError = nodeError;
|
643
|
-
log.debug(tag, "Failed with node:", node.service, "Error:", nodeError.message);
|
644
|
-
}
|
645
|
-
}
|
646
|
-
throw new Error("All nodes failed for network " + networkId + ". Last error: " + lastError.message);
|
647
|
-
}
|
648
|
-
catch (e) {
|
649
|
-
console.error(tag, e);
|
650
|
-
throw e;
|
651
|
-
}
|
652
|
-
};
|
653
|
-
const get_all_pioneers = async function () {
|
654
|
-
let tag = TAG + " | get_all_pioneers | ";
|
655
|
-
try {
|
656
|
-
let output = {};
|
657
|
-
const nftContract = new web3.eth.Contract(constant_1.ERC721_ABI, constant_1.PIONEER_CONTRACT_ADDRESS);
|
658
|
-
const metadataContract = new web3.eth.Contract(constant_1.METADATA_ABI, constant_1.PIONEER_METADATA_CONTRACT_ADDRESS);
|
659
|
-
// Fetch the total supply of the NFTs
|
660
|
-
const totalSupply = await nftContract.methods.totalSupply().call();
|
661
|
-
log.debug("totalSupply: ", totalSupply);
|
662
|
-
output['totalSupply'] = totalSupply;
|
663
|
-
output['owners'] = [];
|
664
|
-
output['images'] = []; // add an images array to output
|
665
|
-
for (let i = 0; i < totalSupply; i++) {
|
666
|
-
//slow down
|
667
|
-
// await sleep(1000);
|
668
|
-
try {
|
669
|
-
const owner = await nftContract.methods.ownerOf(i).call();
|
670
|
-
log.debug(tag, "owner: ", owner);
|
671
|
-
output['owners'].push(owner.toLowerCase());
|
672
|
-
//get images
|
673
|
-
const imageInfo = await metadataContract.methods.getAttributes(i).call();
|
674
|
-
//log.debug(tag,"imageInfo: ",imageInfo)
|
675
|
-
// Parse the JSON string and get the image name
|
676
|
-
const imageName = JSON.parse(imageInfo['0'])["0-backgrounds"];
|
677
|
-
// Build the full image URL by replacing the image name in the base URL
|
678
|
-
const baseImageUrl = "https://ipfs.io/ipfs/bafybeiezdzjofkcpiwy5hlvxwzkgcztxc6xtodh3q7eddfjmqsguqs47aa/0-backgrounds/";
|
679
|
-
const fullImageUrl = baseImageUrl + imageName + ".png";
|
680
|
-
// Add this image URL to the images array in output
|
681
|
-
output['images'].push({ address: owner.toLowerCase(), image: fullImageUrl });
|
682
|
-
}
|
683
|
-
catch (e) {
|
684
|
-
log.debug("no image for: ", i);
|
685
|
-
}
|
686
|
-
}
|
687
|
-
return output;
|
688
|
-
}
|
689
|
-
catch (e) {
|
690
|
-
console.error(tag, e);
|
691
|
-
}
|
692
|
-
};
|
693
|
-
const get_all_pioneers_base = async function () {
|
694
|
-
let tag = TAG + " | get_all_pioneers_base | ";
|
695
|
-
try {
|
696
|
-
let output = {};
|
697
|
-
log.info(tag, "PIONEER_CONTRACT_ADDRESS_BASE: ", constant_1.PIONEER_CONTRACT_ADDRESS_BASE);
|
698
|
-
const nftContract = new web3Base.eth.Contract(constant_1.ERC721_ABI, constant_1.PIONEER_CONTRACT_ADDRESS_BASE);
|
699
|
-
const metadataContract = new web3Base.eth.Contract(constant_1.METADATA_ABI, constant_1.PIONEER_METADATA_CONTRACT_ADDRESS_BASE);
|
700
|
-
// Fetch the total supply of the NFTs
|
701
|
-
const totalSupply = await nftContract.methods.totalSupply().call();
|
702
|
-
log.info("totalSupply: ", totalSupply);
|
703
|
-
output['totalSupply'] = totalSupply;
|
704
|
-
output['owners'] = [];
|
705
|
-
output['images'] = []; // add an images array to output
|
706
|
-
for (let i = 0; i < totalSupply; i++) {
|
707
|
-
//slow down
|
708
|
-
// await sleep(1000);
|
709
|
-
try {
|
710
|
-
const owner = await nftContract.methods.ownerOf(i).call();
|
711
|
-
log.info(tag, "owner: ", owner);
|
712
|
-
output['owners'].push(owner.toLowerCase());
|
713
|
-
//get images
|
714
|
-
const imageInfo = await metadataContract.methods.getAttributes(i).call();
|
715
|
-
log.info(tag, "imageInfo: ", imageInfo);
|
716
|
-
// Parse the JSON string and get the image name
|
717
|
-
const imageName = JSON.parse(imageInfo['0'])["0-backgrounds"];
|
718
|
-
// Build the full image URL by replacing the image name in the base URL
|
719
|
-
const baseImageUrl = "https://ipfs.io/ipfs/bafybeiezdzjofkcpiwy5hlvxwzkgcztxc6xtodh3q7eddfjmqsguqs47aa/0-backgrounds/";
|
720
|
-
const fullImageUrl = baseImageUrl + imageName + ".png";
|
721
|
-
// Add this image URL to the images array in output
|
722
|
-
output['images'].push({ address: owner.toLowerCase(), image: fullImageUrl });
|
723
|
-
}
|
724
|
-
catch (e) {
|
725
|
-
log.debug("no image for: ", i);
|
726
|
-
}
|
727
|
-
}
|
728
|
-
return output;
|
729
|
-
}
|
730
|
-
catch (e) {
|
731
|
-
console.error(tag, e);
|
732
|
-
}
|
733
|
-
};
|
734
|
-
const decode_tx = async function (tx) {
|
735
|
-
let tag = TAG + " | decode_tx | ";
|
736
|
-
try {
|
737
|
-
const data = ethers.utils.parseTransaction(tx);
|
738
|
-
return data;
|
739
|
-
}
|
740
|
-
catch (e) {
|
741
|
-
console.error(tag, e);
|
742
|
-
}
|
743
|
-
};
|
744
|
-
const build_airdrop_claim = async function (address) {
|
745
|
-
let tag = TAG + " | build_airdrop_claim | ";
|
746
|
-
try {
|
747
|
-
const airdropContract = new web3.eth.Contract(constant_1.AIRDROP_ABI, constant_1.AIRDROP_CONTRACT);
|
748
|
-
let accountInfo = await axios({ method: 'GET', url: constant_1.CLAIM_URL + '/' + address });
|
749
|
-
//console.log("accountInfo: ",accountInfo)
|
750
|
-
if (!accountInfo.data.index)
|
751
|
-
throw Error("Not found in db! ");
|
752
|
-
//
|
753
|
-
//console.log("airdropContract: ",accountInfo.data.contract)
|
754
|
-
const AirDropInterface = new abi_1.Interface(constant_1.AIRDROP_ABI);
|
755
|
-
// const data = airdropContract.methods.claim(
|
756
|
-
// accountInfo.data.index,
|
757
|
-
// address,
|
758
|
-
// 150,
|
759
|
-
// accountInfo.data.proof
|
760
|
-
// )
|
761
|
-
// console.log("airdropContract: ",[
|
762
|
-
// accountInfo.data.index as number,
|
763
|
-
// address,
|
764
|
-
// '0x0821ab0d4414980000',
|
765
|
-
// accountInfo.data.proof
|
766
|
-
// ])
|
767
|
-
const data = AirDropInterface.encodeFunctionData('claim', [
|
768
|
-
accountInfo.data.index,
|
769
|
-
address,
|
770
|
-
'0x0821ab0d4414980000',
|
771
|
-
accountInfo.data.proof
|
772
|
-
]);
|
773
|
-
return data;
|
774
|
-
}
|
775
|
-
catch (e) {
|
776
|
-
console.error(tag, e);
|
777
|
-
}
|
778
|
-
};
|
779
|
-
const check_airdrop_claim = async function (address) {
|
780
|
-
let tag = TAG + " | check_airdrop_claim | ";
|
781
|
-
try {
|
782
|
-
//
|
783
|
-
let accountInfo = await axios({ method: 'GET', url: constant_1.CLAIM_URL + '/' + address });
|
784
|
-
//console.log("accountInfo: ",accountInfo)
|
785
|
-
let output = {};
|
786
|
-
if (accountInfo.data.index) {
|
787
|
-
output.isElgible = true;
|
788
|
-
output.contract = accountInfo.data.contractAddress;
|
789
|
-
//get index?
|
790
|
-
//check contract
|
791
|
-
//const AirDropInterface = new Interface(AirDropABI)
|
792
|
-
const airdropContract = new web3.eth.Contract(constant_1.AIRDROP_ABI, accountInfo.data.contractAddress);
|
793
|
-
//get index by address?
|
794
|
-
//log.debug("index: ",accountInfo.data.index)
|
795
|
-
let isClaimed = await airdropContract.methods.isClaimed(accountInfo.data.index).call();
|
796
|
-
output.isClaimed = isClaimed;
|
797
|
-
}
|
798
|
-
else {
|
799
|
-
output.isElgible = false;
|
800
|
-
}
|
801
|
-
return output;
|
802
|
-
}
|
803
|
-
catch (e) {
|
804
|
-
console.error(tag, e);
|
805
|
-
}
|
806
|
-
};
|
807
|
-
// const get_token_transfer_data = async function(toAddress: string, amount: string, contract: string) {
|
808
|
-
// const tag = TAG + " | get_token_transfer_data | ";
|
809
|
-
// try {
|
810
|
-
// const minABI = [
|
811
|
-
// // balanceOf
|
812
|
-
// {
|
813
|
-
// "constant": true,
|
814
|
-
// "inputs": [{ "name": "_owner", "type": "address" }],
|
815
|
-
// "name": "balanceOf",
|
816
|
-
// "outputs": [{ "name": "balance", "type": "uint256" }],
|
817
|
-
// "type": "function"
|
818
|
-
// },
|
819
|
-
// // decimals
|
820
|
-
// {
|
821
|
-
// "constant": true,
|
822
|
-
// "inputs": [],
|
823
|
-
// "name": "decimals",
|
824
|
-
// "outputs": [{ "name": "", "type": "uint8" }],
|
825
|
-
// "type": "function"
|
826
|
-
// }
|
827
|
-
// ];
|
828
|
-
// const newContract = new web3.eth.Contract(minABI, contract);
|
829
|
-
// const decimalPlaces = await newContract.methods.decimals().call();
|
830
|
-
//
|
831
|
-
// // Convert amount to the appropriate number of decimal places
|
832
|
-
// const amountInSmallestUnit = web3.utils.toBN(amount).mul(web3.utils.toBN(10).pow(decimalPlaces));
|
833
|
-
//
|
834
|
-
// // Convert the amount to a hexadecimal string
|
835
|
-
// const amountHex = amountInSmallestUnit.toString(16);
|
836
|
-
//
|
837
|
-
// // Pad the hexadecimal string with zeros to 64 characters
|
838
|
-
// const amountHexPadded = amountHex.padStart(64, '0');
|
839
|
-
//
|
840
|
-
// // Parse the token data
|
841
|
-
// const tokenData = web3.eth.abi.encodeFunctionCall({
|
842
|
-
// name: 'transfer',
|
843
|
-
// type: 'function',
|
844
|
-
// inputs: [
|
845
|
-
// {
|
846
|
-
// type: 'address',
|
847
|
-
// name: '_to'
|
848
|
-
// },
|
849
|
-
// {
|
850
|
-
// type: 'uint256',
|
851
|
-
// name: '_value'
|
852
|
-
// }
|
853
|
-
// ]
|
854
|
-
// }, [toAddress, amountInSmallestUnit.toString()]);
|
855
|
-
//
|
856
|
-
// return tokenData;
|
857
|
-
// } catch (e) {
|
858
|
-
// console.error(tag, e);
|
859
|
-
// }
|
860
|
-
// }
|
861
|
-
// const get_token_transfer_data = async function(toAddress: string, amount: string, contract: string) {
|
862
|
-
// const tag = TAG + " | get_token_transfer_data | ";
|
863
|
-
// try {
|
864
|
-
// const minABI = [
|
865
|
-
// // balanceOf
|
866
|
-
// {
|
867
|
-
// "constant": true,
|
868
|
-
// "inputs": [{ "name": "_owner", "type": "address" }],
|
869
|
-
// "name": "balanceOf",
|
870
|
-
// "outputs": [{ "name": "balance", "type": "uint256" }],
|
871
|
-
// "type": "function"
|
872
|
-
// },
|
873
|
-
// // decimals
|
874
|
-
// {
|
875
|
-
// "constant": true,
|
876
|
-
// "inputs": [],
|
877
|
-
// "name": "decimals",
|
878
|
-
// "outputs": [{ "name": "", "type": "uint8" }],
|
879
|
-
// "type": "function"
|
880
|
-
// }
|
881
|
-
// ];
|
882
|
-
// const newContract = new web3.eth.Contract(minABI, contract);
|
883
|
-
// const decimalPlaces = await newContract.methods.decimals().call();
|
884
|
-
//
|
885
|
-
// // Convert amount to the appropriate number of decimal places
|
886
|
-
// const amountInWei = web3.utils.toWei(amount, 'ether');
|
887
|
-
// const decimals = web3.utils.toBN(decimalPlaces);
|
888
|
-
// const amountInSmallestUnit = web3.utils.toBN(amountInWei).mul(web3.utils.toBN(10).pow(decimals));
|
889
|
-
//
|
890
|
-
// // Convert the amount to a hexadecimal string
|
891
|
-
// const amountHex = amountInSmallestUnit.toString(16);
|
892
|
-
//
|
893
|
-
// // Pad the hexadecimal string with zeros to 64 characters
|
894
|
-
// const amountHexPadded = amountHex.padStart(64, '0');
|
895
|
-
//
|
896
|
-
// // Parse the token data
|
897
|
-
// const tokenData = web3.eth.abi.encodeFunctionCall({
|
898
|
-
// name: 'transfer',
|
899
|
-
// type: 'function',
|
900
|
-
// inputs: [
|
901
|
-
// {
|
902
|
-
// type: 'address',
|
903
|
-
// name: '_to'
|
904
|
-
// },
|
905
|
-
// {
|
906
|
-
// type: 'uint256',
|
907
|
-
// name: '_value'
|
908
|
-
// }
|
909
|
-
// ]
|
910
|
-
// }, [toAddress, amountInSmallestUnit.toString()]);
|
911
|
-
//
|
912
|
-
// return tokenData;
|
913
|
-
// } catch (e) {
|
914
|
-
// console.error(tag, e);
|
915
|
-
// }
|
916
|
-
// }
|
917
|
-
const get_token_transfer_data = async function (toAddress, amount, contract) {
|
918
|
-
const tag = TAG + " | get_token_transfer_data | ";
|
919
|
-
try {
|
920
|
-
const minABI = [
|
921
|
-
// balanceOf
|
922
|
-
{
|
923
|
-
"constant": true,
|
924
|
-
"inputs": [{ "name": "_owner", "type": "address" }],
|
925
|
-
"name": "balanceOf",
|
926
|
-
"outputs": [{ "name": "balance", "type": "uint256" }],
|
927
|
-
"type": "function"
|
928
|
-
},
|
929
|
-
// decimals
|
930
|
-
{
|
931
|
-
"constant": true,
|
932
|
-
"inputs": [],
|
933
|
-
"name": "decimals",
|
934
|
-
"outputs": [{ "name": "", "type": "uint8" }],
|
935
|
-
"type": "function"
|
936
|
-
}
|
937
|
-
];
|
938
|
-
const newContract = new web3.eth.Contract(minABI, contract);
|
939
|
-
const decimalPlaces = await newContract.methods.decimals().call();
|
940
|
-
// Convert amount to the appropriate number of decimal places
|
941
|
-
// Note: we're assuming 'amount' is a string representing a decimal number
|
942
|
-
const amountInSmallestUnit = web3.utils.toBN(parseFloat(amount) * Math.pow(10, decimalPlaces));
|
943
|
-
// Convert the amount to a hexadecimal string
|
944
|
-
const amountHex = amountInSmallestUnit.toString(16);
|
945
|
-
// Pad the hexadecimal string with zeros to 64 characters
|
946
|
-
const amountHexPadded = amountHex.padStart(64, '0');
|
947
|
-
// Parse the token data
|
948
|
-
const tokenData = web3.eth.abi.encodeFunctionCall({
|
949
|
-
name: 'transfer',
|
950
|
-
type: 'function',
|
951
|
-
inputs: [
|
952
|
-
{
|
953
|
-
type: 'address',
|
954
|
-
name: '_to'
|
955
|
-
},
|
956
|
-
{
|
957
|
-
type: 'uint256',
|
958
|
-
name: '_value'
|
959
|
-
}
|
960
|
-
]
|
961
|
-
}, [toAddress, amountInSmallestUnit.toString()]);
|
962
|
-
return tokenData;
|
963
|
-
}
|
964
|
-
catch (e) {
|
965
|
-
console.error(tag, e);
|
966
|
-
}
|
967
|
-
};
|
968
|
-
// //get_token_transfer_data
|
969
|
-
// const get_token_transfer_data = async function(toAddress:string, amount:string, contract:string){
|
970
|
-
// let tag = TAG + " | get_token_transfer_data | "
|
971
|
-
// try{
|
972
|
-
//
|
973
|
-
// let minABI = [
|
974
|
-
// // balanceOf
|
975
|
-
// {
|
976
|
-
// "constant": true,
|
977
|
-
// "inputs": [{ "name": "_owner", "type": "address" }],
|
978
|
-
// "name": "balanceOf",
|
979
|
-
// "outputs": [{ "name": "balance", "type": "uint256" }],
|
980
|
-
// "type": "function"
|
981
|
-
// },
|
982
|
-
// // decimals
|
983
|
-
// {
|
984
|
-
// "constant": true,
|
985
|
-
// "inputs": [],
|
986
|
-
// "name": "decimals",
|
987
|
-
// "outputs": [{ "name": "", "type": "uint8" }],
|
988
|
-
// "type": "function"
|
989
|
-
// }
|
990
|
-
// ];
|
991
|
-
// const newContract = new web3.eth.Contract(minABI, contract);
|
992
|
-
// const decimalPlaces = await newContract.methods.decimals().call();
|
993
|
-
// //
|
994
|
-
// // // @ts-ignore
|
995
|
-
// // let value = parseInt(amount/Math.pow(10, decimals))
|
996
|
-
// // //const adjustedValue = value.mul(web3.utils.toBN(10).pow(web3.utils.toBN(decimals)));
|
997
|
-
// //
|
998
|
-
// // log.debug(tag, "adjustedValue: ", value.toString());
|
999
|
-
//
|
1000
|
-
// // Calculate the amount in the token's smallest unit
|
1001
|
-
// const amountInWei = web3.utils.toWei(amount, 'ether');
|
1002
|
-
// const decimals = web3.utils.toBN(decimalPlaces);
|
1003
|
-
// const amountInSmallestUnit = web3.utils.toBN(amountInWei).mul(web3.utils.toBN(10).pow(decimals));
|
1004
|
-
//
|
1005
|
-
// // Convert the amount to a hexadecimal string
|
1006
|
-
// const amountHex = amountInSmallestUnit.toString(16);
|
1007
|
-
//
|
1008
|
-
// // Pad the hexadecimal string with zeros to 64 characters
|
1009
|
-
// const amountHexPadded = amountHex.padStart(64, '0');
|
1010
|
-
//
|
1011
|
-
// //parse to prescision?
|
1012
|
-
// let tokenData = await web3.eth.abi.encodeFunctionCall({
|
1013
|
-
// name: 'transfer',
|
1014
|
-
// type: 'function',
|
1015
|
-
// inputs: [
|
1016
|
-
// {
|
1017
|
-
// type: 'address',
|
1018
|
-
// name: '_to'
|
1019
|
-
// },
|
1020
|
-
// {
|
1021
|
-
// type: 'uint256',
|
1022
|
-
// name: '_value'
|
1023
|
-
// }
|
1024
|
-
// ]
|
1025
|
-
// }, [toAddress, amountInSmallestUnit]);
|
1026
|
-
//
|
1027
|
-
// return tokenData
|
1028
|
-
// }catch(e){
|
1029
|
-
// console.error(tag,e)
|
1030
|
-
// }
|
1031
|
-
// }
|
1032
|
-
const get_symbol_from_contract = async function (address) {
|
1033
|
-
let tag = TAG + " | get_symbol_from_contract | ";
|
1034
|
-
try {
|
1035
|
-
//get total LP tokens
|
1036
|
-
//LP token
|
1037
|
-
const contract = new web3.eth.Contract(constant_1.ERC20ABI, address);
|
1038
|
-
//log.debug(tag,"contract: ",contract)
|
1039
|
-
let tokenName = await contract.methods.name().call();
|
1040
|
-
//log.debug(tag,"tokenName: ",tokenName)
|
1041
|
-
return tokenName;
|
1042
|
-
}
|
1043
|
-
catch (e) {
|
1044
|
-
console.error(tag, e);
|
1045
|
-
}
|
1046
|
-
};
|
1047
|
-
const get_stream = async function (streamId) {
|
1048
|
-
let tag = TAG + " | get_stream | ";
|
1049
|
-
try {
|
1050
|
-
//get total LP tokens
|
1051
|
-
//LP token
|
1052
|
-
const sablierContract = new web3.eth.Contract(constant_1.SABLIER_ABI, constant_1.PROXY_CONTRACT_SABLIER);
|
1053
|
-
//log.debug(tag,"sablierContract: ",sablierContract)
|
1054
|
-
//log.debug(tag,"streamId: ",streamId)
|
1055
|
-
streamId = parseInt(streamId);
|
1056
|
-
let totalFox = await sablierContract.methods.getSalary(streamId).call();
|
1057
|
-
//log.debug(tag,"totalFox: ",totalFox)
|
1058
|
-
return totalFox;
|
1059
|
-
}
|
1060
|
-
catch (e) {
|
1061
|
-
console.error(tag, e);
|
1062
|
-
}
|
1063
|
-
};
|
1064
|
-
const get_tx_count = async function (address, options) {
|
1065
|
-
let tag = TAG + " | get_tx_count | ";
|
1066
|
-
try {
|
1067
|
-
log.debug(tag, "address: ", address);
|
1068
|
-
if (!address)
|
1069
|
-
throw Error("102: address required!");
|
1070
|
-
//confirmed
|
1071
|
-
let txsConfirmed = await web3.eth.getTransactionCount(address);
|
1072
|
-
//pending
|
1073
|
-
let txsWithPending = await web3.eth.getTransactionCount(address, 'pending');
|
1074
|
-
//count pending
|
1075
|
-
let pending = txsConfirmed - txsWithPending;
|
1076
|
-
return {
|
1077
|
-
confirmed: txsConfirmed,
|
1078
|
-
total: txsWithPending,
|
1079
|
-
pending
|
1080
|
-
};
|
1081
|
-
}
|
1082
|
-
catch (e) {
|
1083
|
-
console.error(tag, e);
|
1084
|
-
}
|
1085
|
-
};
|
1086
|
-
const get_pool_percent = async function (amountFox, amountEth, poolAddress) {
|
1087
|
-
let tag = TAG + " | get_pool_percent | ";
|
1088
|
-
try {
|
1089
|
-
//get total LP tokens
|
1090
|
-
//LP token
|
1091
|
-
const lpContract = new web3.eth.Contract(constant_1.ERC20ABI, constant_1.UNISWAP_V2_WETH_FOX_POOL_ADDRESS);
|
1092
|
-
const foxContract = new web3.eth.Contract(constant_1.ERC20ABI, constant_1.FOX_TOKEN_CONTRACT_ADDRESS);
|
1093
|
-
const wethContract = new web3.eth.Contract(constant_1.ERC20ABI, constant_1.WETH_TOKEN_CONTRACT_ADDRESS);
|
1094
|
-
//log.debug("lpContract: ",lpContract)
|
1095
|
-
let totalSupply = await lpContract.methods.totalSupply().call();
|
1096
|
-
totalSupply = totalSupply / BASE;
|
1097
|
-
log.debug("LP totalSupply: ", totalSupply);
|
1098
|
-
//get total fox in pool
|
1099
|
-
let totalFox = await foxContract.methods.balanceOf(constant_1.UNISWAP_V2_WETH_FOX_POOL_ADDRESS).call();
|
1100
|
-
totalFox = totalFox / BASE;
|
1101
|
-
log.debug("totalFox: ", totalFox / BASE);
|
1102
|
-
//get total eth in pool
|
1103
|
-
let totalEth = await wethContract.methods.balanceOf(constant_1.UNISWAP_V2_WETH_FOX_POOL_ADDRESS).call();
|
1104
|
-
totalEth = totalEth / BASE;
|
1105
|
-
log.debug("totalEth: ", totalEth);
|
1106
|
-
//token math
|
1107
|
-
let result = totalFox / totalEth;
|
1108
|
-
log.debug("result: ", result);
|
1109
|
-
//balance
|
1110
|
-
let lpTokens = (amountFox * totalSupply) / totalFox;
|
1111
|
-
log.debug("lpTokens: ", lpTokens);
|
1112
|
-
//total LP tokens
|
1113
|
-
//liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
|
1114
|
-
let percent = (lpTokens / totalSupply) * 100;
|
1115
|
-
log.debug("percent: ", percent);
|
1116
|
-
return percent;
|
1117
|
-
}
|
1118
|
-
catch (e) {
|
1119
|
-
console.error(tag, e);
|
1120
|
-
}
|
1121
|
-
};
|
1122
|
-
const get_balances = async function (addresses) {
|
1123
|
-
let tag = TAG + " | get_balances | ";
|
1124
|
-
try {
|
1125
|
-
let actions = [];
|
1126
|
-
for (let i = 0; i < addresses.length; i++) {
|
1127
|
-
let address = addresses[i];
|
1128
|
-
let action = {
|
1129
|
-
method: "eth_getBalance",
|
1130
|
-
params: [address]
|
1131
|
-
};
|
1132
|
-
actions.push(action);
|
1133
|
-
}
|
1134
|
-
let result = await rpcCallBatch(actions);
|
1135
|
-
//covert
|
1136
|
-
let output = [];
|
1137
|
-
for (let i = 0; i < result.length; i++) {
|
1138
|
-
let entry = result[i];
|
1139
|
-
let balance = entry.result;
|
1140
|
-
balance = Web3Utils.hexToNumberString(balance);
|
1141
|
-
balance = balance / BASE;
|
1142
|
-
output.push(balance);
|
1143
|
-
}
|
1144
|
-
return output;
|
1145
|
-
}
|
1146
|
-
catch (e) {
|
1147
|
-
console.error(tag, e);
|
1148
|
-
}
|
1149
|
-
};
|
1150
|
-
const rpcCallBatch = async (actions) => {
|
1151
|
-
let tag = TAG + " | post_request | ";
|
1152
|
-
try {
|
1153
|
-
let body = [];
|
1154
|
-
for (let i = 0; i < actions.length; i++) {
|
1155
|
-
let action = actions[i];
|
1156
|
-
let req = {
|
1157
|
-
"jsonrpc": "2.0",
|
1158
|
-
"method": action.method,
|
1159
|
-
"params": action.params,
|
1160
|
-
"id": 1
|
1161
|
-
};
|
1162
|
-
body.push(req);
|
1163
|
-
}
|
1164
|
-
let options = {
|
1165
|
-
method: "POST",
|
1166
|
-
url: NODE_URL,
|
1167
|
-
headers: { 'content-type': 'application/json' },
|
1168
|
-
body: JSON.stringify(body)
|
1169
|
-
};
|
1170
|
-
//console.log("options: ",options)
|
1171
|
-
let result = await request(options);
|
1172
|
-
//console.log("result: ",result)
|
1173
|
-
result = JSON.parse(result);
|
1174
|
-
if (result.error)
|
1175
|
-
throw JSON.stringify(result.error);
|
1176
|
-
return result;
|
1177
|
-
}
|
1178
|
-
catch (err) {
|
1179
|
-
throw new Error(err);
|
1180
|
-
}
|
1181
|
-
};
|
1182
|
-
//get_approval_status
|
1183
|
-
const get_allowance = async function (tokenAddress, spender, sender) {
|
1184
|
-
let tag = TAG + " | get_allowance | ";
|
1185
|
-
try {
|
1186
|
-
let contract = new web3.eth.Contract(constant_1.ERC20ABI, tokenAddress);
|
1187
|
-
let allowance = await contract.methods.allowance(spender, sender).call();
|
1188
|
-
return allowance;
|
1189
|
-
}
|
1190
|
-
catch (e) {
|
1191
|
-
console.error(tag, e);
|
1192
|
-
}
|
1193
|
-
};
|
1194
|
-
const get_all_tokens_blockbook = async function (address) {
|
1195
|
-
let tag = TAG + " | get_all_tokens_blockbook | ";
|
1196
|
-
try {
|
1197
|
-
//
|
1198
|
-
let ethInto = await blockbook.getEthInfo(address);
|
1199
|
-
log.debug(tag, "ethInto: ", ethInto);
|
1200
|
-
return true;
|
1201
|
-
}
|
1202
|
-
catch (e) {
|
1203
|
-
console.error(tag, e);
|
1204
|
-
}
|
1205
|
-
};
|
1206
|
-
const get_nfts = async function (address) {
|
1207
|
-
let tag = TAG + " | get_nfts | ";
|
1208
|
-
try {
|
1209
|
-
//get nfts from etherscan (v3 uniswap)
|
1210
|
-
//
|
1211
|
-
let ethInfo = await blockbook.getAddressInfo('ETH', address);
|
1212
|
-
//TODO filter by LP contracts
|
1213
|
-
log.debug(tag, "ethInfo: ", ethInfo);
|
1214
|
-
return ethInfo;
|
1215
|
-
}
|
1216
|
-
catch (e) {
|
1217
|
-
console.error(tag, e);
|
1218
|
-
}
|
1219
|
-
};
|
1220
|
-
const get_pool_positions = async function (address) {
|
1221
|
-
let tag = TAG + " | get_pool_positions | ";
|
1222
|
-
try {
|
1223
|
-
//get nfts from etherscan (v3 uniswap)
|
1224
|
-
//
|
1225
|
-
let ethInfo = await blockbook.getAddressInfo('ETH', address);
|
1226
|
-
//TODO filter by LP contracts
|
1227
|
-
log.debug(tag, "ethInfo: ", ethInfo);
|
1228
|
-
return ethInfo;
|
1229
|
-
}
|
1230
|
-
catch (e) {
|
1231
|
-
console.error(tag, e);
|
1232
|
-
}
|
1233
|
-
};
|
1234
|
-
/*
|
1235
|
-
let swap = {
|
1236
|
-
inboundAddress: {
|
1237
|
-
chain: 'ETH',
|
1238
|
-
pub_key: 'tthorpub1addwnpepqvuy8vh6yj4h28xp6gfpjsztpj6p46y2rs0763t6uw9f6lkky0ly5uvwla6',
|
1239
|
-
address: '0x36286e570c412531aad366154eea9867b0e71755',
|
1240
|
-
router: '0x9d496De78837f5a2bA64Cb40E62c19FBcB67f55a',
|
1241
|
-
halted: false
|
1242
|
-
},
|
1243
|
-
asset: {
|
1244
|
-
chain: 'ETH',
|
1245
|
-
symbol: 'ETH',
|
1246
|
-
ticker: 'ETH',
|
1247
|
-
iconPath: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/binance/assets/ETH-1C9/logo.png'
|
1248
|
-
},
|
1249
|
-
memo: '=:THOR.RUNE:tthor1veu9u5h4mtdq34fjgu982s8pympp6w87ag58nh',
|
1250
|
-
amount: "0.1"
|
1251
|
-
}
|
1252
|
-
*/
|
1253
|
-
let get_memo_data = async function (swap) {
|
1254
|
-
let tag = TAG + " | get_memo_data | ";
|
1255
|
-
try {
|
1256
|
-
const web3 = new Web3();
|
1257
|
-
if (!swap.inboundAddress.router)
|
1258
|
-
throw Error("Router required!");
|
1259
|
-
const routerContract = new web3.eth.Contract(constant_1.TCRopstenAbi, swap.inboundAddress.router);
|
1260
|
-
const memo = swap.memo;
|
1261
|
-
//TODO support tokens?
|
1262
|
-
const data = routerContract.methods
|
1263
|
-
.deposit(swap.inboundAddress.address, '0x0000000000000000000000000000000000000000', // 0 = ETH
|
1264
|
-
web3.utils.toBN(swap.amount * BASE), memo)
|
1265
|
-
.encodeABI();
|
1266
|
-
return data;
|
1267
|
-
}
|
1268
|
-
catch (e) {
|
1269
|
-
log.error(tag, e);
|
1270
|
-
throw e;
|
1271
|
-
}
|
1272
|
-
};
|
1273
|
-
/*
|
1274
|
-
X-chain compatible call
|
1275
|
-
*/
|
1276
|
-
let estimate_fee = async function (sourceAsset, params) {
|
1277
|
-
let tag = TAG + " | estimate_fee | ";
|
1278
|
-
try {
|
1279
|
-
let checkSummedAddress;
|
1280
|
-
let decimal;
|
1281
|
-
if (sourceAsset.symbol === 'ETH') {
|
1282
|
-
checkSummedAddress = '0x0000000000000000000000000000000000000000';
|
1283
|
-
decimal = utils_1.ETH_DECIMAL;
|
1284
|
-
}
|
1285
|
-
else {
|
1286
|
-
throw Error("TODO");
|
1287
|
-
// const assetAddress = sourceAsset.symbol.slice(sourceAsset.ticker.length + 1);
|
1288
|
-
// const strip0x = assetAddress.substr(2);
|
1289
|
-
// checkSummedAddress = ethers.utils.getAddress(strip0x);
|
1290
|
-
//
|
1291
|
-
// const tokenContract = new ethers.Contract(checkSummedAddress, erc20ABI, wallet);
|
1292
|
-
// const tokenDecimals = await tokenContract.decimals();
|
1293
|
-
// decimal = tokenDecimals.toNumber();
|
1294
|
-
}
|
1295
|
-
// Connect to the network
|
1296
|
-
let provider = PROVIDER;
|
1297
|
-
// Get the current block
|
1298
|
-
let block = await provider.getBlockNumber();
|
1299
|
-
log.debug(tag, "block: ", block);
|
1300
|
-
const { average: averageGP, fast: fastGP, fastest: fastestGP } = (0, utils_1.getDefaultGasPrices)();
|
1301
|
-
let assetAddress;
|
1302
|
-
// @ts-ignore
|
1303
|
-
if (sourceAsset && (0, xchain_util_1.assetToString)(sourceAsset) !== (0, xchain_util_1.assetToString)(xchain_util_1.AssetETH)) {
|
1304
|
-
// @ts-ignore
|
1305
|
-
assetAddress = (0, utils_1.getTokenAddress)(sourceAsset);
|
1306
|
-
}
|
1307
|
-
let gasLimit;
|
1308
|
-
if (assetAddress && assetAddress !== utils_1.ETHAddress) {
|
1309
|
-
if (!params.sender || !params.recipient)
|
1310
|
-
throw Error("missing params! need sender and recipient on token tx!");
|
1311
|
-
let contract = new ethers.Contract(assetAddress, constant_1.ERC20ABI, provider);
|
1312
|
-
gasLimit = params.data
|
1313
|
-
? await contract.estimateGas.transfer(params.recipient, params.data, { from: params.sender })
|
1314
|
-
: await contract.estimateGas.transfer(params.recipient, 1, { from: params.sender }); // amount = 1 as a dummy value
|
1315
|
-
}
|
1316
|
-
else {
|
1317
|
-
// ETH transfer
|
1318
|
-
if (!params.sender || !params.recipient)
|
1319
|
-
throw Error("missing params! need sender and recipient");
|
1320
|
-
const gasEstimate = await provider.estimateGas({
|
1321
|
-
from: params.sender,
|
1322
|
-
to: params.recipient,
|
1323
|
-
value: ethers.utils.parseEther('0.000001') // using dummy ETH amount for gas calculation
|
1324
|
-
});
|
1325
|
-
gasLimit = gasEstimate.add(0); // Add extra gas as buffer
|
1326
|
-
console.log("ETH gasEstimate: ", gasEstimate.toString());
|
1327
|
-
console.log("ETH gasEstimate + buffer: ", gasLimit.toString());
|
1328
|
-
}
|
1329
|
-
return {
|
1330
|
-
gasPrices: {
|
1331
|
-
average: averageGP,
|
1332
|
-
fast: fastGP,
|
1333
|
-
fastest: fastestGP
|
1334
|
-
},
|
1335
|
-
gasLimit,
|
1336
|
-
fees: {
|
1337
|
-
type: 'byte',
|
1338
|
-
average: (0, utils_1.getFee)({ gasPrice: averageGP, gasLimit }),
|
1339
|
-
fast: (0, utils_1.getFee)({ gasPrice: fastGP, gasLimit }),
|
1340
|
-
fastest: (0, utils_1.getFee)({ gasPrice: fastestGP, gasLimit })
|
1341
|
-
}
|
1342
|
-
};
|
1343
|
-
}
|
1344
|
-
catch (e) {
|
1345
|
-
console.error(tag, "Error: ", e);
|
1346
|
-
throw e;
|
1347
|
-
}
|
1348
|
-
};
|
1349
|
-
let get_gas_limit = async function ({ asset, recipient, amount, memo }) {
|
1350
|
-
let tag = TAG + " | get_gas_limit | ";
|
1351
|
-
try {
|
1352
|
-
log.debug(tag, "input: ", { asset, recipient, amount, memo });
|
1353
|
-
const txAmount = BigNumber.from(amount?.amount().toFixed());
|
1354
|
-
let assetAddress;
|
1355
|
-
// @ts-ignore
|
1356
|
-
if (asset && (0, xchain_util_1.assetToString)(asset) !== (0, xchain_util_1.assetToString)(xchain_util_1.AssetETH)) {
|
1357
|
-
// @ts-ignore
|
1358
|
-
assetAddress = (0, utils_1.getTokenAddress)(asset);
|
1359
|
-
}
|
1360
|
-
let estimate;
|
1361
|
-
//NOTE: I changed the from to recipient because this module has no context to address of the sender.
|
1362
|
-
// I hope I dont skrew the pooch and the differnce +-1 byte between addresses actually matter
|
1363
|
-
if (assetAddress && assetAddress !== utils_1.ETHAddress) {
|
1364
|
-
// ERC20 gas estimate
|
1365
|
-
const contract = new ethers.Contract(assetAddress, utils_1.erc20ABI, PROVIDER);
|
1366
|
-
estimate = await contract.estimateGas.transfer(recipient, txAmount, {
|
1367
|
-
from: recipient,
|
1368
|
-
});
|
1369
|
-
}
|
1370
|
-
else {
|
1371
|
-
// ETH gas estimate
|
1372
|
-
const transactionRequest = {
|
1373
|
-
from: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", //address with lots of eth
|
1374
|
-
to: recipient,
|
1375
|
-
value: txAmount,
|
1376
|
-
data: memo ? toUtf8Bytes(memo) : undefined,
|
1377
|
-
};
|
1378
|
-
estimate = await PROVIDER.estimateGas(transactionRequest);
|
1379
|
-
}
|
1380
|
-
return estimate;
|
1381
|
-
}
|
1382
|
-
catch (e) {
|
1383
|
-
log.error(tag, e);
|
1384
|
-
throw e;
|
1385
|
-
}
|
1386
|
-
};
|
1387
|
-
let get_fees = async function (params) {
|
1388
|
-
let tag = TAG + " | get_fees | ";
|
1389
|
-
try {
|
1390
|
-
const response = await etherscanAPI.getGasOracle(ETHERSCAN.baseUrl, ETHERSCAN.apiKey);
|
1391
|
-
// Convert result of gas prices: `Gwei` -> `Wei`
|
1392
|
-
const averageWei = parseUnits(response.SafeGasPrice, 'gwei');
|
1393
|
-
const fastWei = parseUnits(response.ProposeGasPrice, 'gwei');
|
1394
|
-
const fastestWei = parseUnits(response.FastGasPrice, 'gwei');
|
1395
|
-
let gasPrices = {
|
1396
|
-
average: (0, xchain_util_1.baseAmount)(averageWei.toString(), utils_1.ETH_DECIMAL),
|
1397
|
-
fast: (0, xchain_util_1.baseAmount)(fastWei.toString(), utils_1.ETH_DECIMAL),
|
1398
|
-
fastest: (0, xchain_util_1.baseAmount)(fastestWei.toString(), utils_1.ETH_DECIMAL),
|
1399
|
-
};
|
1400
|
-
const { fast: fastGP, fastest: fastestGP, average: averageGP } = gasPrices;
|
1401
|
-
if (!params.amount || !params?.amount?.amount) {
|
1402
|
-
// @ts-ignore
|
1403
|
-
params.amount = {
|
1404
|
-
// @ts-ignore
|
1405
|
-
amount: function () { return .98; }
|
1406
|
-
};
|
1407
|
-
}
|
1408
|
-
log.debug(tag, "get_gas_limit: ", {
|
1409
|
-
asset: params.asset,
|
1410
|
-
amount: params.amount,
|
1411
|
-
recipient: params.recipient,
|
1412
|
-
memo: params.memo,
|
1413
|
-
});
|
1414
|
-
const gasLimit = await get_gas_limit({
|
1415
|
-
asset: params.asset,
|
1416
|
-
amount: params.amount,
|
1417
|
-
recipient: params.recipient,
|
1418
|
-
memo: params.memo,
|
1419
|
-
});
|
1420
|
-
let output = {
|
1421
|
-
gasPrices,
|
1422
|
-
fees: {
|
1423
|
-
type: 'byte',
|
1424
|
-
average: (0, utils_1.getFee)({ gasPrice: averageGP, gasLimit }).amount().toString(),
|
1425
|
-
fast: (0, utils_1.getFee)({ gasPrice: fastGP, gasLimit }).amount().toString(),
|
1426
|
-
fastest: (0, utils_1.getFee)({ gasPrice: fastestGP, gasLimit }).amount().toString(),
|
1427
|
-
},
|
1428
|
-
gasLimit,
|
1429
|
-
};
|
1430
|
-
return output;
|
1431
|
-
}
|
1432
|
-
catch (e) {
|
1433
|
-
log.error(tag, e);
|
1434
|
-
throw e;
|
1435
|
-
}
|
1436
|
-
};
|
1437
|
-
let broadcast_transaction = async function (tx) {
|
1438
|
-
let tag = TAG + " | broadcast_transaction | ";
|
1439
|
-
try {
|
1440
|
-
let output = {};
|
1441
|
-
output.success = false;
|
1442
|
-
log.debug(tag, "tx: ", tx);
|
1443
|
-
if (!tx)
|
1444
|
-
throw Error("101: missing tx!");
|
1445
|
-
//push node
|
1446
|
-
// web3.eth.sendSignedTransaction(tx)
|
1447
|
-
//push etherscan
|
1448
|
-
//https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=0xf904808000831cfde080&apikey=YourApiKeyToken
|
1449
|
-
let resp = await axios({
|
1450
|
-
method: 'GET',
|
1451
|
-
url: 'https://api.etherscan.io/api?module=proxy&action=eth_sendRawTransaction&hex=' + tx + '&apikey=' + process.env['ETHERSCAN_API_KEY']
|
1452
|
-
});
|
1453
|
-
resp = resp.data;
|
1454
|
-
console.log(resp);
|
1455
|
-
//push blockbook
|
1456
|
-
//TODO lifecycle hook?
|
1457
|
-
// let resp2 = await web3.eth.sendSignedTransaction(tx)
|
1458
|
-
// .on('transactionHash', function(hash:any){
|
1459
|
-
// console.log("hash: ",hash)
|
1460
|
-
// })
|
1461
|
-
// .on('receipt', function(receipt:any){
|
1462
|
-
// console.log("receipt: ",receipt)
|
1463
|
-
// })
|
1464
|
-
// .on('confirmation', function(confirmationNumber:any, receipt:any){
|
1465
|
-
// console.log(confirmationNumber,receipt)
|
1466
|
-
// })
|
1467
|
-
// .on('error', console.error);
|
1468
|
-
//console.log("resp: ",resp)
|
1469
|
-
if (!resp.error)
|
1470
|
-
output.success = true;
|
1471
|
-
if (resp.error)
|
1472
|
-
output.error = resp;
|
1473
|
-
if (resp.result)
|
1474
|
-
output.txid = resp.result;
|
1475
|
-
return output;
|
1476
|
-
}
|
1477
|
-
catch (e) {
|
1478
|
-
log.error(tag, e);
|
1479
|
-
throw e;
|
1480
|
-
}
|
1481
|
-
};
|
1482
|
-
const get_balance_tokens_by_network = async function (networkId, address) {
|
1483
|
-
let tag = TAG + " | get_balance_tokens_by_network | ";
|
1484
|
-
try {
|
1485
|
-
let output = {};
|
1486
|
-
//@TODO zapper? covalent?
|
1487
|
-
return "";
|
1488
|
-
}
|
1489
|
-
catch (e) {
|
1490
|
-
console.error(tag, e);
|
1491
|
-
}
|
1492
|
-
};
|
1493
|
-
const get_balance_tokens = async function (address) {
|
1494
|
-
let tag = TAG + " | get_balance_tokens | ";
|
1495
|
-
try {
|
1496
|
-
let balances = {};
|
1497
|
-
let valueUsds = {};
|
1498
|
-
let coinInfo = {};
|
1499
|
-
//TODO other? backup?
|
1500
|
-
//ethpolorer.io
|
1501
|
-
let resp = await axios({
|
1502
|
-
method: 'GET',
|
1503
|
-
url: 'http://api.ethplorer.io/getAddressInfo/' + address + '?apiKey=' + ETHPLORER_API_KEY
|
1504
|
-
});
|
1505
|
-
log.debug(tag, "resp: ", resp.data);
|
1506
|
-
balances['ETH'] = resp.data.ETH.balance;
|
1507
|
-
valueUsds['ETH'] = parseFloat(resp.data.ETH.balance) * parseFloat(resp.data.ETH.price.rate);
|
1508
|
-
//infura
|
1509
|
-
let tokenInfo = resp.data.tokens;
|
1510
|
-
log.debug(tag, "tokenInfo: ", tokenInfo);
|
1511
|
-
//
|
1512
|
-
if (tokenInfo && Object.keys(tokenInfo).length > 0) {
|
1513
|
-
for (let i = 0; i < tokenInfo.length; i++) {
|
1514
|
-
let info = tokenInfo[i];
|
1515
|
-
if (info) {
|
1516
|
-
log.debug(tag, "info: ", info);
|
1517
|
-
//let symbol
|
1518
|
-
let symbol = info.tokenInfo.symbol;
|
1519
|
-
log.debug(tag, "symbol: ", symbol);
|
1520
|
-
//rate
|
1521
|
-
let rate = 0;
|
1522
|
-
if (info.tokenInfo.price && info.tokenInfo.price.rate) {
|
1523
|
-
log.debug(tag, "rate: ", info.tokenInfo.price.rate);
|
1524
|
-
rate = info.tokenInfo.price.rate;
|
1525
|
-
}
|
1526
|
-
// @ts-ignore
|
1527
|
-
let balance = info.balance / parseInt(Math.pow(10, info.tokenInfo.decimals));
|
1528
|
-
log.debug({ rate, symbol, balance });
|
1529
|
-
balances[symbol] = balance;
|
1530
|
-
valueUsds[symbol] = balance * rate;
|
1531
|
-
coinInfo[symbol] = info.tokenInfo;
|
1532
|
-
}
|
1533
|
-
}
|
1534
|
-
}
|
1535
|
-
return { balances, valueUsds, coinInfo };
|
1536
|
-
}
|
1537
|
-
catch (e) {
|
1538
|
-
console.error(tag, e);
|
1539
|
-
}
|
1540
|
-
};
|
1541
|
-
const get_balance_token_by_network = async function (networkId, address, token) {
|
1542
|
-
let tag = TAG + " | get_balance_token_by_network | ";
|
1543
|
-
try {
|
1544
|
-
let output = {};
|
1545
|
-
//find in node array node by networkId
|
1546
|
-
let node = NODES.find((n) => { return n.networkId == networkId; });
|
1547
|
-
if (!node)
|
1548
|
-
throw Error("101: missing node! for network " + networkId);
|
1549
|
-
//init new web3
|
1550
|
-
let web3 = new Web3(node.service);
|
1551
|
-
//decimals
|
1552
|
-
let contract = new web3.eth.Contract(constant_1.ERC20ABI, token);
|
1553
|
-
let decimals = await contract.methods.decimals().call();
|
1554
|
-
//log.debug(tag,"decimals: ",decimals)
|
1555
|
-
let balance = await contract.methods.balanceOf(address).call();
|
1556
|
-
//log.debug(tag,"balance: ",balance)
|
1557
|
-
return balance / Math.pow(10, decimals);
|
1558
|
-
}
|
1559
|
-
catch (e) {
|
1560
|
-
console.error(tag, e);
|
1561
|
-
}
|
1562
|
-
};
|
1563
|
-
const get_balance_token = async function (address, token) {
|
1564
|
-
let tag = TAG + " | get_balance | ";
|
1565
|
-
try {
|
1566
|
-
//decimals
|
1567
|
-
let contract = new web3.eth.Contract(constant_1.ERC20ABI, token);
|
1568
|
-
let decimals = await contract.methods.decimals().call();
|
1569
|
-
//log.debug(tag,"decimals: ",decimals)
|
1570
|
-
let balance = await contract.methods.balanceOf(address).call();
|
1571
|
-
//log.debug(tag,"balance: ",balance)
|
1572
|
-
return balance / Math.pow(10, decimals);
|
1573
|
-
}
|
1574
|
-
catch (e) {
|
1575
|
-
console.error(tag, e);
|
1576
|
-
}
|
1577
|
-
};
|
1578
|
-
const get_balance_by_network = async function (networkId, address) {
|
1579
|
-
let tag = TAG + " | get_balance_by_network | ";
|
1580
|
-
try {
|
1581
|
-
let output = {};
|
1582
|
-
// Find all nodes for this network
|
1583
|
-
let nodes = NODES.filter((n) => n.networkId === networkId);
|
1584
|
-
if (!nodes || nodes.length === 0)
|
1585
|
-
throw Error("101: missing node! for network " + networkId);
|
1586
|
-
// Try each node until one works
|
1587
|
-
let lastError = null;
|
1588
|
-
for (let i = 0; i < nodes.length && i < 5; i++) {
|
1589
|
-
try {
|
1590
|
-
let node = nodes[i];
|
1591
|
-
log.info(tag, `Attempting to get balance from node ${i + 1}/${nodes.length}: ${node.service}`);
|
1592
|
-
//init new web3
|
1593
|
-
let web3 = new Web3(node.service);
|
1594
|
-
//normal tx info
|
1595
|
-
output = (await web3.eth.getBalance(address)) / BASE;
|
1596
|
-
log.info(tag, `Successfully got balance: ${output}`);
|
1597
|
-
return output;
|
1598
|
-
}
|
1599
|
-
catch (nodeError) {
|
1600
|
-
lastError = nodeError;
|
1601
|
-
log.warn(tag, `Node ${i + 1} failed: ${nodeError.message}`);
|
1602
|
-
continue;
|
1603
|
-
}
|
1604
|
-
}
|
1605
|
-
// If all nodes failed, throw the last error
|
1606
|
-
throw lastError || new Error("All nodes failed to get balance");
|
1607
|
-
}
|
1608
|
-
catch (e) {
|
1609
|
-
console.error(tag, e);
|
1610
|
-
throw e; // Re-throw the error instead of swallowing it
|
1611
|
-
}
|
1612
|
-
};
|
1613
|
-
const get_balance = async function (address) {
|
1614
|
-
let tag = TAG + " | get_balance | ";
|
1615
|
-
try {
|
1616
|
-
let output = {};
|
1617
|
-
//normal tx info
|
1618
|
-
output = (await web3.eth.getBalance(address)) / BASE;
|
1619
|
-
return output;
|
1620
|
-
}
|
1621
|
-
catch (e) {
|
1622
|
-
console.error(tag, e);
|
1623
|
-
}
|
1624
|
-
};
|
1625
|
-
const get_transactions = async function (address, options) {
|
1626
|
-
let tag = TAG + " | get_transactions | ";
|
1627
|
-
try {
|
1628
|
-
let output = {};
|
1629
|
-
let ethInfo = await blockbook.getAddressInfo('ETH', address);
|
1630
|
-
return ethInfo;
|
1631
|
-
}
|
1632
|
-
catch (e) {
|
1633
|
-
console.error(tag, e);
|
1634
|
-
}
|
1635
|
-
};
|
1636
|
-
const get_transaction = async function (txid) {
|
1637
|
-
let tag = TAG + " | get_transaction | ";
|
1638
|
-
try {
|
1639
|
-
let output = {};
|
1640
|
-
//normal tx info
|
1641
|
-
output.txInfo = await web3.eth.getTransaction(txid);
|
1642
|
-
//if contract
|
1643
|
-
output.receipt = await web3.eth.getTransactionReceipt(txid);
|
1644
|
-
return output;
|
1645
|
-
}
|
1646
|
-
catch (e) {
|
1647
|
-
console.error(tag, e);
|
1648
|
-
}
|
1649
|
-
};
|
1650
|
-
let check_online_status = async function () {
|
1651
|
-
let tag = TAG + " | check_online_status | ";
|
1652
|
-
try {
|
1653
|
-
let output = {};
|
1654
|
-
//isTestnet
|
1655
|
-
output.version = await web3.eth.getNodeInfo();
|
1656
|
-
output.chainId = await web3.eth.getChainId();
|
1657
|
-
output.height = await web3.eth.getBlockNumber();
|
1658
|
-
//TODO get peer count
|
1659
|
-
output.peers = await web3.eth.net.getPeerCount();
|
1660
|
-
let networkName;
|
1661
|
-
switch (output.chainId.toString()) {
|
1662
|
-
case "1":
|
1663
|
-
networkName = "Main";
|
1664
|
-
break;
|
1665
|
-
case "2":
|
1666
|
-
networkName = "Morden";
|
1667
|
-
break;
|
1668
|
-
case "3":
|
1669
|
-
networkName = "Ropsten";
|
1670
|
-
break;
|
1671
|
-
case "4":
|
1672
|
-
networkName = "Rinkeby";
|
1673
|
-
break;
|
1674
|
-
case "42":
|
1675
|
-
networkName = "Kovan";
|
1676
|
-
break;
|
1677
|
-
default:
|
1678
|
-
networkName = "Unknown";
|
1679
|
-
}
|
1680
|
-
output.networkName = networkName;
|
1681
|
-
//
|
1682
|
-
output.gasPrice = await web3.eth.getGasPrice();
|
1683
|
-
//
|
1684
|
-
output.syncing = await web3.eth.isSyncing();
|
1685
|
-
return output;
|
1686
|
-
}
|
1687
|
-
catch (e) {
|
1688
|
-
console.error(tag, e);
|
1689
|
-
}
|
1690
|
-
};
|
1691
|
-
// Helper functions for ethers v5 compatibility
|
1692
|
-
const parseUnits = (value, unit) => ethers.utils.parseUnits(value, unit);
|
1693
|
-
const toUtf8Bytes = (text) => ethers.utils.toUtf8Bytes(text);
|
1694
|
-
// Define our own FeeOption enum
|
1695
|
-
var FeeOption;
|
1696
|
-
(function (FeeOption) {
|
1697
|
-
FeeOption["Average"] = "average";
|
1698
|
-
FeeOption["Fast"] = "fast";
|
1699
|
-
FeeOption["Fastest"] = "fastest";
|
1700
|
-
})(FeeOption || (FeeOption = {}));
|
1701
|
-
//# sourceMappingURL=index.js.map
|