0.1.0 version
This commit is contained in:
parent
2bcfa0003d
commit
d34559deff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gas-price-oracle",
|
||||
"version": "1.0.0",
|
||||
"version": "0.1.0",
|
||||
"description": "Gas Price Oracle library for Ethereum dApps.",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
|
@ -21,18 +21,42 @@ const zoltu: OffChainOracle = {
|
||||
denominator: 1
|
||||
};
|
||||
|
||||
const chainLink: OnChainOracle = {
|
||||
name: 'chainLink',
|
||||
const etherchain: OffChainOracle = {
|
||||
name: 'etherchain',
|
||||
url: 'https://www.etherchain.org/api/gasPriceOracle',
|
||||
instantPropertyName: 'fastest',
|
||||
fastPropertyName: 'fast',
|
||||
standardPropertyName: 'standard',
|
||||
lowPropertyName: 'safeLow',
|
||||
denominator: 1
|
||||
};
|
||||
|
||||
const poa: OffChainOracle = {
|
||||
name: 'poa',
|
||||
url: 'https://gasprice.poa.network/',
|
||||
instantPropertyName: 'instant',
|
||||
fastPropertyName: 'fast',
|
||||
standardPropertyName: 'standard',
|
||||
lowPropertyName: 'slow',
|
||||
denominator: 1
|
||||
};
|
||||
|
||||
const chainlink: OnChainOracle = {
|
||||
name: 'chainlink',
|
||||
callData: '0x50d25bcd',
|
||||
contract: '0xA417221ef64b1549575C977764E651c9FAB50141',
|
||||
denominator: '1000000000'
|
||||
};
|
||||
|
||||
export default {
|
||||
offChainOracles: [
|
||||
ethgasstation, zoltu
|
||||
],
|
||||
onChainOracles: [
|
||||
chainLink
|
||||
]
|
||||
export const offChainOracles: { [key: string]: OffChainOracle } = {
|
||||
ethgasstation, zoltu, poa, etherchain
|
||||
};
|
||||
|
||||
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
||||
chainlink
|
||||
};
|
||||
|
||||
export default {
|
||||
offChainOracles,
|
||||
onChainOracles
|
||||
};
|
||||
|
37
src/index.ts
37
src/index.ts
@ -11,6 +11,8 @@ export class GasPriceOracle {
|
||||
low: 1
|
||||
};
|
||||
defaultRpc = 'https://api.mycryptoapi.com/eth';
|
||||
offChainOracles = { ...config.offChainOracles };
|
||||
onChainOracles = { ...config.onChainOracles };
|
||||
|
||||
constructor(defaultRpc?: string) {
|
||||
if (defaultRpc) {
|
||||
@ -19,7 +21,7 @@ export class GasPriceOracle {
|
||||
}
|
||||
|
||||
async fetchGasPricesOffChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
||||
for (let oracle of config.offChainOracles) {
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
const { name, url, instantPropertyName, fastPropertyName, standardPropertyName, lowPropertyName, denominator } = oracle;
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
@ -50,7 +52,7 @@ export class GasPriceOracle {
|
||||
}
|
||||
|
||||
async fetchGasPricesOnChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
||||
for (let oracle of config.onChainOracles) {
|
||||
for (let oracle of Object.values(this.onChainOracles)) {
|
||||
const { name, callData, contract, denominator } = oracle;
|
||||
let { rpc } = oracle;
|
||||
rpc = rpc ? rpc : this.defaultRpc;
|
||||
@ -95,11 +97,38 @@ export class GasPriceOracle {
|
||||
return this.lastGasPrice;
|
||||
}
|
||||
|
||||
async gasPrices(): Promise<GasPrice> {
|
||||
let gas = this.lastGasPrice;
|
||||
try {
|
||||
gas = await this.fetchGasPricesOffChain();
|
||||
return gas;
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
||||
}
|
||||
|
||||
try {
|
||||
gas = await this.fetchGasPricesOnChain();
|
||||
return gas;
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch gas prices from onchain oracles. Last known gas will be returned');
|
||||
}
|
||||
|
||||
return gas;
|
||||
}
|
||||
|
||||
addOffChainOracle(oracle: OffChainOracle) {
|
||||
config.offChainOracles.push(oracle);
|
||||
this.offChainOracles[oracle.name] = oracle;
|
||||
}
|
||||
|
||||
addOnChainOracle(oracle: OnChainOracle) {
|
||||
config.onChainOracles.push(oracle);
|
||||
this.onChainOracles[oracle.name] = oracle;
|
||||
}
|
||||
|
||||
removeOnChainOracle(name: string) {
|
||||
delete this.onChainOracles[name];
|
||||
}
|
||||
|
||||
removeOffChainOracle(name: string) {
|
||||
delete this.offChainOracles[name];
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,26 @@
|
||||
import { GasPriceOracle } from '../src/index';
|
||||
import { GasPrice } from '../src/types';
|
||||
import mockery from 'mockery';
|
||||
import chai from 'chai';
|
||||
import { onChainOracles } from '../src/config';
|
||||
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
chai.use(require('chai-as-promised'));
|
||||
chai.should();
|
||||
let oracle = new GasPriceOracle();
|
||||
|
||||
before('before', function () {
|
||||
let fetchMock = () => {
|
||||
throw new Error('Mocked for tests');
|
||||
};
|
||||
mockery.registerMock('node-fetch', fetchMock);
|
||||
});
|
||||
|
||||
beforeEach('beforeEach', function () {
|
||||
oracle = new GasPriceOracle();
|
||||
});
|
||||
|
||||
describe('fetchGasPricesOffChain', function () {
|
||||
it('should work', async function () {
|
||||
const oracle = new GasPriceOracle();
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOffChain();
|
||||
|
||||
gas.instant.should.be.a('number');
|
||||
@ -21,61 +33,26 @@ describe('fetchGasPricesOffChain', function () {
|
||||
gas.standard.should.be.above(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('throw checks', function () {
|
||||
before('before', function () {
|
||||
// Mocking the mod1 module
|
||||
let fetchMock = () => {
|
||||
throw new Error('Mocked for tests');
|
||||
};
|
||||
|
||||
// replace the module with mock for any `require`
|
||||
mockery.registerMock('node-fetch', fetchMock);
|
||||
|
||||
// set additional parameters
|
||||
mockery.enable({
|
||||
useCleanCache: true,
|
||||
// warnOnReplace: false,
|
||||
warnOnUnregistered: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if all offchain oracles are down', async function () {
|
||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
const oracle = new GasPriceOracle();
|
||||
oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOffChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
||||
});
|
||||
|
||||
it('should throw if all onchain oracles are down', async function () {
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
const oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
||||
mockery.disable();
|
||||
});
|
||||
|
||||
it('should not throw if throwIfFailsToFetch is false', async function () {
|
||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
const oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOnChain(false);
|
||||
});
|
||||
|
||||
it('should not throw if throwIfFailsToFetch is false', async function () {
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
const oracle = new GasPriceOracle();
|
||||
oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOffChain(false);
|
||||
});
|
||||
|
||||
after('after', function () {
|
||||
after(function () {
|
||||
mockery.disable();
|
||||
mockery.deregisterMock('node-fetch');
|
||||
});
|
||||
mockery.disable();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchGasPricesOnChain', function () {
|
||||
it('should work', async function () {
|
||||
const oracle = new GasPriceOracle();
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
||||
|
||||
gas.instant.should.be.a('number');
|
||||
@ -88,4 +65,85 @@ describe('fetchGasPricesOnChain', function () {
|
||||
gas.standard.should.be.above(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
|
||||
it('should work with custom rpc', async function () {
|
||||
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
||||
const oracle = new GasPriceOracle(rpc);
|
||||
oracle.defaultRpc.should.be.equal(rpc);
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
||||
|
||||
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.above(gas.fast);
|
||||
gas.fast.should.be.above(gas.standard);
|
||||
gas.standard.should.be.above(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
|
||||
it('should remove oracle', async function () {
|
||||
await oracle.fetchGasPricesOnChain();
|
||||
oracle.removeOnChainOracle('chainlink');
|
||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
||||
});
|
||||
|
||||
it('should add oracle', async function () {
|
||||
const { chainlink } = onChainOracles;
|
||||
await oracle.fetchGasPricesOnChain();
|
||||
oracle.removeOnChainOracle('chainlink');
|
||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
||||
oracle.addOnChainOracle(chainlink);
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
||||
|
||||
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.above(gas.fast);
|
||||
gas.fast.should.be.above(gas.standard);
|
||||
gas.standard.should.be.above(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
|
||||
it('should throw if all onchain oracles are down', async function () {
|
||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
||||
mockery.disable();
|
||||
});
|
||||
|
||||
it('should not throw if throwIfFailsToFetch is false', async function () {
|
||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||
const { GasPriceOracle } = require('../src/index');
|
||||
oracle = new GasPriceOracle();
|
||||
await oracle.fetchGasPricesOnChain(false);
|
||||
mockery.disable();
|
||||
});
|
||||
});
|
||||
|
||||
describe('gasPrice', function () {
|
||||
it('should work', async function () {
|
||||
const gas: GasPrice = await oracle.gasPrices();
|
||||
|
||||
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.above(gas.fast);
|
||||
gas.fast.should.be.above(gas.standard);
|
||||
gas.standard.should.be.above(gas.low);
|
||||
gas.low.should.not.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
after('after', function () {
|
||||
after(function () {
|
||||
mockery.disable();
|
||||
mockery.deregisterMock('node-fetch');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user