UNPKG

@faast/ethereum-payments

Version:

Library to assist in processing ethereum payments, such as deriving addresses and sweeping funds

131 lines 4.91 kB
import * as request from 'request-promise-native'; import { BigNumber } from 'bignumber.js'; import { DelegateLogger } from '@faast/ts-common'; import { DEFAULT_GAS_PRICE_IN_WEI, GAS_STATION_URL, GAS_STATION_FEE_SPEED, MAXIMUM_GAS, GAS_ESTIMATE_MULTIPLIER, } from './constants'; import { retryIfDisconnected } from './utils'; export class NetworkData { constructor(eth, gasStationUrl = GAS_STATION_URL, parityUrl, logger) { this.eth = eth; this.gasStationUrl = gasStationUrl; this.parityUrl = parityUrl; this.logger = new DelegateLogger(logger, 'NetworkData'); } async getNetworkData(txType, speed, from, to, data) { const pricePerGasUnit = await this.getGasPrice(speed); const nonce = await this.getNonce(from); const amountOfGas = await this.estimateGas({ from, to, data }, txType); return { pricePerGasUnit, amountOfGas, nonce, }; } async getNonce(address) { const web3Nonce = await this.getWeb3Nonce(address) || '0'; const parityNonce = await this.getParityNonce(address) || '0'; const nonce = BigNumber.maximum(web3Nonce, parityNonce); return nonce.toNumber() ? nonce.toString() : '0'; } async getGasPrice(speed) { let gasPrice = await this.getGasStationGasPrice(speed); if (gasPrice) return gasPrice; gasPrice = await this.getWeb3GasPrice(); if (gasPrice) return gasPrice; return DEFAULT_GAS_PRICE_IN_WEI; } async estimateGas(txObject, txType) { try { let gas = await this._retryDced(() => this.eth.estimateGas({ ...txObject })); if (gas > 21000) { gas = gas * GAS_ESTIMATE_MULTIPLIER; } const maxGas = MAXIMUM_GAS[txType]; if (gas > maxGas) { gas = maxGas; } const result = Math.ceil(gas); this.logger.debug(`Estimated gas limit of ${result} for ${txType}`); return result; } catch (e) { this.logger.warn(`Failed to estimate gas for ${txType} -- ${e}`); return MAXIMUM_GAS[txType]; } } async getWeb3Nonce(address) { try { const nonce = await this._retryDced(() => this.eth.getTransactionCount(address, 'pending')); return (new BigNumber(nonce)).toString(); } catch (e) { return ''; } } async getParityNonce(address) { const data = { method: 'parity_nextNonce', params: [address], id: 1, jsonrpc: '2.0' }; const options = { url: this.parityUrl || '', json: data }; let body; try { body = await request.post(options); } catch (e) { this.logger.warn('Failed to retrieve nonce from parity - ', e.toString()); return ''; } if (!body || !body.result) { this.logger.warn('Bad result or missing fields in parity nextNonce response', body); return ''; } return (new BigNumber(body.result, 16)).toString(); } async getGasStationGasPrice(level) { const hasKey = /\?api-key=/.test(this.gasStationUrl || ''); const options = { url: hasKey ? `${this.gasStationUrl}` : `${this.gasStationUrl}/json/ethgasAPI.json`, json: true, timeout: 5000 }; let body; try { body = await this._retryDced(() => request.get(options)); } catch (e) { this.logger.warn('Failed to retrieve gas price from ethgasstation - ', e.toString()); return ''; } const speed = GAS_STATION_FEE_SPEED[level]; if (!(body && body.blockNum && body[speed])) { this.logger.warn('Bad result or missing fields in ethgasstation response', body); return ''; } const price10xGwei = body[speed]; const gwei = new BigNumber(price10xGwei).dividedBy(10); this.logger.log(`Retrieved gas price of ${gwei} Gwei from ethgasstation using speed ${speed}`); return gwei.multipliedBy(1e9).dp(0, BigNumber.ROUND_DOWN).toFixed(); } async getWeb3GasPrice() { try { const wei = new BigNumber(await this._retryDced(() => this.eth.getGasPrice())); this.logger.log(`Retrieved gas price of ${wei.div(1e9)} Gwei from web3`); return wei.dp(0, BigNumber.ROUND_DOWN).toFixed(); } catch (e) { this.logger.warn('Failed to retrieve gas price from web3 - ', e.toString()); return ''; } } async _retryDced(fn) { return retryIfDisconnected(fn, this.logger); } } //# sourceMappingURL=NetworkData.js.map