@faast/ethereum-payments
Version:
Library to assist in processing ethereum payments, such as deriving addresses and sweeping funds
131 lines • 4.91 kB
JavaScript
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