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 } = {
|
export const offChainOracles: { [key: string]: OffChainOracle } = {
|
||||||
ethgasstation,
|
ethgasstation,
|
||||||
zoltu,
|
|
||||||
poa,
|
poa,
|
||||||
etherchain,
|
etherchain,
|
||||||
|
zoltu,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
||||||
|
80
src/index.ts
80
src/index.ts
@ -1,6 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs } from './types';
|
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs, GasPriceKey } from './types';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
export class GasPriceOracle {
|
export class GasPriceOracle {
|
||||||
@ -50,6 +50,78 @@ export class GasPriceOracle {
|
|||||||
throw new Error('All oracles are down. Probably a network error.');
|
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> {
|
async fetchGasPricesOnChain(): Promise<number> {
|
||||||
for (let oracle of Object.values(this.onChainOracles)) {
|
for (let oracle of Object.values(this.onChainOracles)) {
|
||||||
const { name, callData, contract, denominator } = oracle;
|
const { name, callData, contract, denominator } = oracle;
|
||||||
@ -81,7 +153,7 @@ export class GasPriceOracle {
|
|||||||
throw new Error('All oracles are down. Probably a network error.');
|
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 defaultFastGas = 22;
|
||||||
const defaultFallbackGasPrices = {
|
const defaultFallbackGasPrices = {
|
||||||
instant: defaultFastGas * 1.3,
|
instant: defaultFastGas * 1.3,
|
||||||
@ -91,7 +163,9 @@ export class GasPriceOracle {
|
|||||||
};
|
};
|
||||||
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || defaultFallbackGasPrices;
|
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || defaultFallbackGasPrices;
|
||||||
try {
|
try {
|
||||||
this.lastGasPrice = await this.fetchGasPricesOffChain();
|
this.lastGasPrice = median
|
||||||
|
? await this.fetchMedianGasPriceOffChain()
|
||||||
|
: await this.fetchGasPricesOffChain();
|
||||||
return this.lastGasPrice;
|
return this.lastGasPrice;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
||||||
|
@ -17,12 +17,11 @@ export type OnChainOracle = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type GasPrice = {
|
export type GasPrice = {
|
||||||
instant: number;
|
[key in GasPriceKey]: number;
|
||||||
fast: number;
|
|
||||||
standard: number;
|
|
||||||
low: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GasPriceKey = 'instant' | 'fast' | 'standard' | 'low';
|
||||||
|
|
||||||
export interface ConstructorArgs {
|
export interface ConstructorArgs {
|
||||||
defaultRpc?: string;
|
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('after', function () {
|
||||||
after(function () {
|
after(function () {
|
||||||
mockery.disable();
|
mockery.disable();
|
||||||
|
Loading…
Reference in New Issue
Block a user