diff --git a/README.md b/README.md index 5c28748..ff42822 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,8 @@ A library that has a collection of onchain and offchain gas price oracle URLs Current offchain list: - https://ethgasstation.info/json/ethgasAPI.json -- https://www.etherchain.org/api/gasPriceOracle -- https://gasprice.poa.network/ -- https://www.gasnow.org/api/v3/gas/price +- https://etherchain.org/api/gasnow +- https://blockscout.com/eth/mainnet/api/v1/gas-price-oracle Current onchain list: @@ -21,13 +20,13 @@ Current onchain list: Current offchain list: -- https://bscgas.info/ +- https://ztake.org/ ### xDAI Chain Current offchain list: -- https://www.xdaichain.com/for-developers/developer-resources/gas-price-oracle +- https://blockscout.com/xdai/mainnet/api/v1/gas-price-oracle ### Polygon (Matic) Network @@ -35,6 +34,18 @@ Current offchain list: - https://gasstation-mainnet.matic.network/ +### Arbitrum One + +Current offchain list: + +- https://ztake.org/ + +### Avalanche Mainnet + +Current offchain list: + +- https://ztake.org/ + ## Installation `npm i gas-price-oracle` diff --git a/package.json b/package.json index 6e3f35f..690c33a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gas-price-oracle", - "version": "0.4.0", + "version": "0.4.1", "description": "Gas Price Oracle library for Ethereum dApps.", "main": "lib/index.js", "homepage": "https://github.com/peppersec/gas-price-oracle", diff --git a/src/config/arbitrum.ts b/src/config/arbitrum.ts new file mode 100644 index 0000000..f7e17f9 --- /dev/null +++ b/src/config/arbitrum.ts @@ -0,0 +1,23 @@ +import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types'; + +const ztake: OffChainOracle = { + name: 'ztake', + url: 'https://blockchains.ztake.org/api/h6WnmwNqw9CAJHzej5W4gD6LZ9n7v8EK/gasprice/arb/', + instantPropertyName: 'percentile_90', + fastPropertyName: 'percentile_80', + standardPropertyName: 'percentile_60', + lowPropertyName: 'percentile_30', + denominator: 1, + additionalDataProperty: null, +}; + +export const offChainOracles: OffChainOracles = { + ztake, +}; + +export const onChainOracles: OnChainOracles = {}; + +export default { + offChainOracles, + onChainOracles, +}; diff --git a/src/config/avalanche.ts b/src/config/avalanche.ts new file mode 100644 index 0000000..f59029a --- /dev/null +++ b/src/config/avalanche.ts @@ -0,0 +1,23 @@ +import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types'; + +const ztake: OffChainOracle = { + name: 'ztake', + url: 'https://blockchains.ztake.org/api/h6WnmwNqw9CAJHzej5W4gD6LZ9n7v8EK/gasprice/avax/', + instantPropertyName: 'percentile_90', + fastPropertyName: 'percentile_80', + standardPropertyName: 'percentile_60', + lowPropertyName: 'percentile_30', + denominator: 1, + additionalDataProperty: null, +}; + +export const offChainOracles: OffChainOracles = { + ztake, +}; + +export const onChainOracles: OnChainOracles = {}; + +export default { + offChainOracles, + onChainOracles, +}; diff --git a/src/config/binance.ts b/src/config/binance.ts deleted file mode 100644 index 2678770..0000000 --- a/src/config/binance.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types'; - -const bscgas: OffChainOracle = { - name: 'bscgas', - url: 'https://bscgas.info/gas', - instantPropertyName: 'instant', - fastPropertyName: 'fast', - standardPropertyName: 'standard', - lowPropertyName: 'slow', - denominator: 1, - additionalDataProperty: null, -}; - -export const offChainOracles: OffChainOracles = { - bscgas, -}; - -export const onChainOracles: OnChainOracles = {}; - -export default { - offChainOracles, - onChainOracles, -}; diff --git a/src/config/bsc.ts b/src/config/bsc.ts new file mode 100644 index 0000000..e745d0e --- /dev/null +++ b/src/config/bsc.ts @@ -0,0 +1,23 @@ +import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types'; + +const ztake: OffChainOracle = { + name: 'ztake', + url: 'https://blockchains.ztake.org/api/h6WnmwNqw9CAJHzej5W4gD6LZ9n7v8EK/gasprice/bsc/', + instantPropertyName: 'percentile_90', + fastPropertyName: 'percentile_80', + standardPropertyName: 'percentile_60', + lowPropertyName: 'percentile_30', + denominator: 1, + additionalDataProperty: null, +}; + +export const offChainOracles: OffChainOracles = { + ztake, +}; + +export const onChainOracles: OnChainOracles = {}; + +export default { + offChainOracles, + onChainOracles, +}; diff --git a/src/config/index.ts b/src/config/index.ts index 461edb5..e97aade 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,20 +1,26 @@ import { NetworkConfig } from '../types'; import mainnetOracles from './mainnet'; -import binanceOracles from './binance'; +import bscOracles from './bsc'; import xdaiOracles from './xdai'; import polygonOracles from './polygon'; +import arbitrumOracles from './arbitrum'; +import avalancheOracles from './avalanche'; export enum ChainId { MAINNET = 1, - BINANCE = 56, + BSC = 56, XDAI = 100, POLYGON = 137, + ARBITRUM = 42161, + AVALANCHE = 43114, } -export const networks: NetworkConfig = { +export const NETWORKS: NetworkConfig = { [ChainId.MAINNET]: mainnetOracles, - [ChainId.BINANCE]: binanceOracles, + [ChainId.BSC]: bscOracles, [ChainId.XDAI]: xdaiOracles, [ChainId.POLYGON]: polygonOracles, + [ChainId.ARBITRUM]: arbitrumOracles, + [ChainId.AVALANCHE]: avalancheOracles, }; diff --git a/src/config/mainnet.ts b/src/config/mainnet.ts index 6b3e040..378fb26 100644 --- a/src/config/mainnet.ts +++ b/src/config/mainnet.ts @@ -13,37 +13,26 @@ const ethgasstation: OffChainOracle = { const etherchain: OffChainOracle = { name: 'etherchain', - url: 'https://www.etherchain.org/api/gasPriceOracle', - instantPropertyName: 'fastest', - fastPropertyName: 'fast', - standardPropertyName: 'standard', - lowPropertyName: 'safeLow', - denominator: 1, - additionalDataProperty: null, -}; - -const poa: OffChainOracle = { - name: 'poa', - url: 'https://gasprice.poa.network/', - instantPropertyName: 'instant', - fastPropertyName: 'fast', - standardPropertyName: 'standard', - lowPropertyName: 'slow', - denominator: 1, - additionalDataProperty: null, -}; - -const gasNow: OffChainOracle = { - name: 'gasNow', - url: 'https://www.gasnow.org/api/v3/gas/price?utm_source=gas-price-oracle', + url: 'https://etherchain.org/api/gasnow', instantPropertyName: 'rapid', fastPropertyName: 'fast', standardPropertyName: 'standard', lowPropertyName: 'slow', - denominator: 1e9, + denominator: 1, additionalDataProperty: 'data', }; +const blockscout: OffChainOracle = { + name: 'blockscout', + url: 'https://blockscout.com/eth/mainnet/api/v1/gas-price-oracle', + instantPropertyName: 'fast', + fastPropertyName: 'average', + standardPropertyName: 'slow', + lowPropertyName: 'slow', + denominator: 1, + additionalDataProperty: null, +}; + const anyblock: OffChainOracle = { name: 'anyblock', url: 'https://api.anyblock.tools/ethereum/latest-minimum-gasprice', @@ -65,8 +54,7 @@ const chainlink: OnChainOracle = { export const offChainOracles: OffChainOracles = { ethgasstation, anyblock, - gasNow, - poa, + blockscout, etherchain, }; diff --git a/src/index.ts b/src/index.ts index 82e41ae..bc86599 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,8 @@ import axios from 'axios'; -import { ChainId, networks } from './config'; +import BigNumber from 'bignumber.js'; + +import { ChainId, NETWORKS } from './config'; + import { Config, Options, @@ -10,7 +13,6 @@ import { OnChainOracles, OffChainOracles, } from './types'; -import BigNumber from 'bignumber.js'; const defaultFastGas = 22; export class GasPriceOracle { @@ -34,7 +36,7 @@ export class GasPriceOracle { Object.assign(this.configuration, options); } - const network = networks[this.configuration.chainId]; + const network = NETWORKS[this.configuration.chainId]; if (network) { const { offChainOracles, onChainOracles } = network; @@ -215,26 +217,31 @@ export class GasPriceOracle { async gasPrices(fallbackGasPrices?: GasPrice, median = true): Promise { this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || this.configuration.defaultFallbackGasPrices; - try { - 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...'); + + if (Object.keys(this.offChainOracles).length > 0) { + try { + 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...'); + } } - try { - const fastGas = await this.fetchGasPricesOnChain(); - this.lastGasPrice = { - instant: fastGas * 1.3, - fast: fastGas, - standard: fastGas * 0.85, - low: fastGas * 0.5, - }; - return this.lastGasPrice; - } catch (e) { - console.log('Failed to fetch gas prices from onchain oracles. Trying from default RPC...'); + if (Object.keys(this.onChainOracles).length > 0) { + try { + const fastGas = await this.fetchGasPricesOnChain(); + this.lastGasPrice = { + instant: fastGas * 1.3, + fast: fastGas, + standard: fastGas * 0.85, + low: fastGas * 0.5, + }; + return this.lastGasPrice; + } catch (e) { + console.log('Failed to fetch gas prices from onchain oracles. Trying from default RPC...'); + } } try { diff --git a/tests/index.test.ts b/tests/index.test.ts index e67d8df..d532524 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,12 +1,16 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-var-requires */ -import { GasPrice, OffChainOracle } from '../src/types'; -import mockery from 'mockery'; import chai from 'chai'; +import mockery from 'mockery'; +import { ChainId, NETWORKS } from '../src/config'; import { GasPriceOracle } from '../src/index'; + +import { GasPrice, OffChainOracle } from '../src/types'; + chai.use(require('chai-as-promised')); chai.should(); + let oracle = new GasPriceOracle(); let { onChainOracles, offChainOracles } = oracle; @@ -237,25 +241,34 @@ describe('fetchMedianGasPriceOffChain', function () { }); describe('askOracle', function () { - it('all oracles should answer', async function () { - for (const o of Object.values(offChainOracles) as Array) { - try { - const gas: GasPrice = await oracle.askOracle(o); + const chains = Object.keys(NETWORKS).map(id => Number(id)); - gas.instant.should.be.a('number'); - gas.fast.should.be.a('number'); - gas.standard.should.be.a('number'); - gas.low.should.be.a('number'); + chains.forEach(chainId => { + describe(`all ${ChainId[chainId]} oracles should answer`, function () { + oracle = new GasPriceOracle({ chainId }); + ({ offChainOracles } = oracle); - 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); - } catch (e) { - console.error(`Failed to get data from ${o.name} oracle`); - throw new Error(e); + for (const o of Object.values(offChainOracles) as Array) { + it(`check ${o.name}`, async function () { + try { + const gas: GasPrice = await oracle.askOracle(o); + + 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); + } catch (e) { + console.error(`Failed to get data from ${o.name} oracle`); + throw new Error(e); + } + }); } - } + }); }); });