median gas price init
This commit is contained in:
parent
b39073aa53
commit
8d86bfc0fc
@ -49,9 +49,9 @@ const chainlink: OnChainOracle = {
|
||||
|
||||
export const offChainOracles: { [key: string]: OffChainOracle } = {
|
||||
ethgasstation,
|
||||
zoltu,
|
||||
poa,
|
||||
etherchain,
|
||||
zoltu,
|
||||
};
|
||||
|
||||
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
||||
|
80
src/index.ts
80
src/index.ts
@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import config from './config';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs } from './types';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs, GasPriceKey } from './types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export class GasPriceOracle {
|
||||
@ -50,6 +50,78 @@ export class GasPriceOracle {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
|
||||
async fetchMedianGasPriceOffChain(): Promise<GasPrice> {
|
||||
const allGasPrices: GasPrice[] = [];
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
const {
|
||||
name,
|
||||
url,
|
||||
instantPropertyName,
|
||||
fastPropertyName,
|
||||
standardPropertyName,
|
||||
lowPropertyName,
|
||||
denominator,
|
||||
} = oracle;
|
||||
try {
|
||||
const response = await axios.get(url, { timeout: 10000 });
|
||||
// todo parallel requests
|
||||
if (response.status === 200) {
|
||||
const gas = response.data;
|
||||
if (Number(gas[fastPropertyName]) === 0) {
|
||||
throw new Error(`${name} oracle provides corrupted values`);
|
||||
}
|
||||
const gasPrices: GasPrice = {
|
||||
instant: parseFloat(gas[instantPropertyName]) / denominator,
|
||||
fast: parseFloat(gas[fastPropertyName]) / denominator,
|
||||
standard: parseFloat(gas[standardPropertyName]) / denominator,
|
||||
low: parseFloat(gas[lowPropertyName]) / denominator,
|
||||
};
|
||||
allGasPrices.push(gasPrices);
|
||||
} else {
|
||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
if (allGasPrices.length === 0) {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
return this.median(allGasPrices);
|
||||
}
|
||||
|
||||
median(gasPrices: GasPrice[]): GasPrice {
|
||||
const medianGasPrice: GasPrice = { instant: 0, fast: 0, standard: 0, low: 0 };
|
||||
|
||||
const results: { [key in GasPriceKey]: number[] } = {
|
||||
instant: [],
|
||||
fast: [],
|
||||
standard: [],
|
||||
low: [],
|
||||
};
|
||||
|
||||
for (const gasPrice of gasPrices) {
|
||||
results.instant.push(gasPrice.instant);
|
||||
results.fast.push(gasPrice.fast);
|
||||
results.standard.push(gasPrice.standard);
|
||||
results.low.push(gasPrice.low);
|
||||
}
|
||||
|
||||
for (const type of Object.keys(medianGasPrice) as Array<keyof GasPrice>) {
|
||||
const allPrices = results[type].sort((a, b) => a - b);
|
||||
if (allPrices.length === 1) {
|
||||
medianGasPrice[type] = allPrices[0];
|
||||
continue;
|
||||
} else if (allPrices.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const isEven = allPrices.length % 2 === 0;
|
||||
const middle = Math.floor(allPrices.length / 2);
|
||||
medianGasPrice[type] = isEven ? (allPrices[middle - 1] + allPrices[middle]) / 2.0 : allPrices[middle];
|
||||
}
|
||||
return medianGasPrice;
|
||||
}
|
||||
|
||||
async fetchGasPricesOnChain(): Promise<number> {
|
||||
for (let oracle of Object.values(this.onChainOracles)) {
|
||||
const { name, callData, contract, denominator } = oracle;
|
||||
@ -81,7 +153,7 @@ export class GasPriceOracle {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
|
||||
async gasPrices(fallbackGasPrices?: GasPrice): Promise<GasPrice> {
|
||||
async gasPrices(fallbackGasPrices?: GasPrice, median = true): Promise<GasPrice> {
|
||||
const defaultFastGas = 22;
|
||||
const defaultFallbackGasPrices = {
|
||||
instant: defaultFastGas * 1.3,
|
||||
@ -91,7 +163,9 @@ export class GasPriceOracle {
|
||||
};
|
||||
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || defaultFallbackGasPrices;
|
||||
try {
|
||||
this.lastGasPrice = await this.fetchGasPricesOffChain();
|
||||
this.lastGasPrice = median
|
||||
? await this.fetchMedianGasPriceOffChain()
|
||||
: await this.fetchGasPricesOffChain();
|
||||
return this.lastGasPrice;
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
||||
|
@ -17,12 +17,11 @@ export type OnChainOracle = {
|
||||
};
|
||||
|
||||
export type GasPrice = {
|
||||
instant: number;
|
||||
fast: number;
|
||||
standard: number;
|
||||
low: number;
|
||||
[key in GasPriceKey]: number;
|
||||
};
|
||||
|
||||
export type GasPriceKey = 'instant' | 'fast' | 'standard' | 'low';
|
||||
|
||||
export interface ConstructorArgs {
|
||||
defaultRpc?: string;
|
||||
}
|
||||
|
@ -144,6 +144,51 @@ describe('gasPrice', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('median', function () {
|
||||
it('should work', async function () {
|
||||
const gas1 = { instant: 100, fast: 100, standard: 100, low: 100 };
|
||||
const gas2 = { instant: 90, fast: 90, standard: 90, low: 90 };
|
||||
const gas3 = { instant: 70, fast: 70, standard: 70, low: 70 };
|
||||
const gas4 = { instant: 110.1, fast: 110.1, standard: 110.1, low: 110.1 };
|
||||
let gas: GasPrice = await oracle.median([gas1, gas2, gas3]);
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.eq(90);
|
||||
gas.fast.should.be.eq(90);
|
||||
gas.standard.should.be.eq(90);
|
||||
gas.low.should.be.eq(90);
|
||||
|
||||
gas = await oracle.median([gas1, gas2, gas3, gas4]);
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.eq(95);
|
||||
gas.fast.should.be.eq(95);
|
||||
gas.standard.should.be.eq(95);
|
||||
gas.low.should.be.eq(95);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchMedianGasPriceOffChain', function () {
|
||||
it('should work', async function () {
|
||||
let gas: GasPrice = await oracle.fetchMedianGasPriceOffChain();
|
||||
gas.instant.should.be.a('number');
|
||||
gas.fast.should.be.a('number');
|
||||
gas.standard.should.be.a('number');
|
||||
gas.low.should.be.a('number');
|
||||
|
||||
gas.instant.should.be.at.least(gas.fast); // greater than or equal to the given number.
|
||||
gas.fast.should.be.at.least(gas.standard);
|
||||
gas.standard.should.be.at.least(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
after('after', function () {
|
||||
after(function () {
|
||||
mockery.disable();
|
||||
|
Loading…
Reference in New Issue
Block a user