parallel requests for median aproach
This commit is contained in:
parent
8d86bfc0fc
commit
402732a84b
@ -41,7 +41,7 @@
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-plugin-prettier": "^2.3.0",
|
||||
"typescript": "^3.9.3"
|
||||
"typescript": "^4.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
|
79
src/index.ts
79
src/index.ts
@ -1,22 +1,24 @@
|
||||
import axios from 'axios';
|
||||
import config from './config';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs, GasPriceKey } from './types';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, Config, GasPriceKey } from './types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export class GasPriceOracle {
|
||||
lastGasPrice: GasPrice;
|
||||
defaultRpc = 'https://api.mycryptoapi.com/eth';
|
||||
offChainOracles = { ...config.offChainOracles };
|
||||
onChainOracles = { ...config.onChainOracles };
|
||||
configuration: Config = {
|
||||
defaultRpc: 'https://api.mycryptoapi.com/eth',
|
||||
timeout: 10000,
|
||||
};
|
||||
|
||||
constructor(options: ConstructorArgs) {
|
||||
if (options && options.defaultRpc) {
|
||||
this.defaultRpc = options.defaultRpc;
|
||||
constructor(options: Config) {
|
||||
if (options) {
|
||||
Object.assign(this.configuration, options);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
async askOracle(oracle: OffChainOracle): Promise<GasPrice> {
|
||||
const {
|
||||
name,
|
||||
url,
|
||||
@ -26,8 +28,7 @@ export class GasPriceOracle {
|
||||
lowPropertyName,
|
||||
denominator,
|
||||
} = oracle;
|
||||
try {
|
||||
const response = await axios.get(url, { timeout: 10000 });
|
||||
const response = await axios.get(url, { timeout: this.configuration.timeout });
|
||||
if (response.status === 200) {
|
||||
const gas = response.data;
|
||||
if (Number(gas[fastPropertyName]) === 0) {
|
||||
@ -43,47 +44,34 @@ export class GasPriceOracle {
|
||||
} else {
|
||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||
}
|
||||
}
|
||||
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
try {
|
||||
return await this.askOracle(oracle);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
console.info(e.message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
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);
|
||||
async fetchMedianGasPriceOffChain() {
|
||||
const promises: Promise<GasPrice>[] = [];
|
||||
for (let oracle of Object.values(this.offChainOracles) as Array<OffChainOracle>) {
|
||||
promises.push(this.askOracle(oracle));
|
||||
}
|
||||
|
||||
const settledPromises = await Promise.allSettled(promises);
|
||||
const allGasPrices = settledPromises.reduce((acc: GasPrice[], result) => {
|
||||
if (result.status === 'fulfilled') {
|
||||
acc.push(result.value);
|
||||
return acc;
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (allGasPrices.length === 0) {
|
||||
throw new Error('All oracles are down. Probably a network error.');
|
||||
}
|
||||
@ -124,9 +112,8 @@ export class GasPriceOracle {
|
||||
|
||||
async fetchGasPricesOnChain(): Promise<number> {
|
||||
for (let oracle of Object.values(this.onChainOracles)) {
|
||||
const { name, callData, contract, denominator } = oracle;
|
||||
let { rpc } = oracle;
|
||||
rpc = rpc ? rpc : this.defaultRpc;
|
||||
const { name, callData, contract, denominator, rpc } = oracle;
|
||||
const rpcUrl = rpc || this.configuration.defaultRpc;
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
id: 1337,
|
||||
@ -134,7 +121,7 @@ export class GasPriceOracle {
|
||||
params: [{ data: callData, to: contract }, 'latest'],
|
||||
};
|
||||
try {
|
||||
const response = await axios.post(rpc, body, { timeout: 10000 });
|
||||
const response = await axios.post(rpcUrl!, body, { timeout: this.configuration.timeout });
|
||||
if (response.status === 200) {
|
||||
const { result } = response.data;
|
||||
let fastGasPrice = new BigNumber(result);
|
||||
|
@ -22,6 +22,7 @@ export type GasPrice = {
|
||||
|
||||
export type GasPriceKey = 'instant' | 'fast' | 'standard' | 'low';
|
||||
|
||||
export interface ConstructorArgs {
|
||||
export interface Config {
|
||||
defaultRpc?: string;
|
||||
timeout?: number;
|
||||
}
|
||||
|
@ -24,6 +24,19 @@ beforeEach('beforeEach', function () {
|
||||
oracle = new GasPriceOracle();
|
||||
});
|
||||
|
||||
describe('constructor', function () {
|
||||
it('should set default values', async function () {
|
||||
oracle.configuration.defaultRpc.should.be.equal('https://api.mycryptoapi.com/eth');
|
||||
oracle.configuration.timeout.should.be.equal(10000);
|
||||
});
|
||||
|
||||
it('should set passed values', async function () {
|
||||
const newOracle = new GasPriceOracle({ timeout: 1337 });
|
||||
newOracle.configuration.defaultRpc.should.be.equal('https://api.mycryptoapi.com/eth');
|
||||
newOracle.configuration.timeout.should.be.equal(1337);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchGasPricesOffChain', function () {
|
||||
it('should work', async function () {
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOffChain();
|
||||
@ -61,7 +74,7 @@ describe('fetchGasPricesOnChain', function () {
|
||||
it('should work with custom rpc', async function () {
|
||||
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
||||
const oracle = new GasPriceOracle({ defaultRpc: rpc });
|
||||
oracle.defaultRpc.should.be.equal(rpc);
|
||||
oracle.configuration.defaultRpc.should.be.equal(rpc);
|
||||
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||
|
||||
gas.should.be.a('number');
|
||||
|
@ -6,7 +6,8 @@
|
||||
"lib": [
|
||||
"es2017",
|
||||
"esnext.asynciterable",
|
||||
"es2019"
|
||||
"es2019",
|
||||
"ES2020.Promise"
|
||||
] /* Specify library files to be included in the compilation. */,
|
||||
"outDir": "./lib" /* Redirect output structure to the directory. */,
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
|
54
yarn.lock
54
yarn.lock
@ -269,14 +269,6 @@ diff@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
doctrine@0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523"
|
||||
integrity sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=
|
||||
dependencies:
|
||||
esutils "^1.1.6"
|
||||
isarray "0.0.1"
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
@ -344,11 +336,6 @@ esprima@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
esutils@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375"
|
||||
integrity sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=
|
||||
|
||||
fast-diff@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
@ -541,11 +528,6 @@ is-symbol@^1.0.2:
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
isarray@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@ -919,11 +901,6 @@ ts-node@^8.10.1:
|
||||
source-map-support "^0.5.17"
|
||||
yn "3.1.1"
|
||||
|
||||
tslib@1.9.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
|
||||
integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==
|
||||
|
||||
tslib@^1.13.0, tslib@^1.7.1, tslib@^1.8.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
@ -934,22 +911,6 @@ tslint-config-prettier@^1.18.0:
|
||||
resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37"
|
||||
integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==
|
||||
|
||||
tslint-config-standard@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tslint-config-standard/-/tslint-config-standard-9.0.0.tgz#349a94819d93d5f8d803e3c71cb58ef38eff88e0"
|
||||
integrity sha512-CAw9J743RnPMemQV/XQ4YyNreC+A1NItACfkm+cBedrOkz6CQfwlnbKn8anUXBfoa4Zo4tjAhblRbsMNcSLfSw==
|
||||
dependencies:
|
||||
tslint-eslint-rules "^5.3.1"
|
||||
|
||||
tslint-eslint-rules@^5.3.1:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5"
|
||||
integrity sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==
|
||||
dependencies:
|
||||
doctrine "0.7.2"
|
||||
tslib "1.9.0"
|
||||
tsutils "^3.0.0"
|
||||
|
||||
tslint-plugin-prettier@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tslint-plugin-prettier/-/tslint-plugin-prettier-2.3.0.tgz#73fe71bf9f03842ac48c104122ca9b1de012ecf4"
|
||||
@ -985,22 +946,15 @@ tsutils@^2.29.0:
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
tsutils@^3.0.0:
|
||||
version "3.17.1"
|
||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
|
||||
integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
typescript@^3.9.3:
|
||||
version "3.9.7"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||
typescript@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5"
|
||||
integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user