Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d65b3dfe6c | ||
|
|
be905f2b71 | ||
|
|
4373641a30 | ||
|
|
76f8cb036d | ||
|
|
64c5a68639 | ||
|
|
1dbd778166 | ||
|
|
47089200af | ||
|
|
4cf2521880 | ||
|
|
cd5bca4963 | ||
|
|
6be967e8c9 | ||
|
|
b13a4252ab | ||
|
|
5036c65643 | ||
|
|
89a69519b2 | ||
|
|
3fce50efbb | ||
|
|
9b0c229ace | ||
|
|
a508e41652 | ||
|
|
bb58318b6f | ||
|
|
32c256bc4e | ||
|
|
efd3cb3c7c | ||
|
|
88a757bb45 | ||
|
|
c30c7aed63 | ||
|
|
ca24732a9b | ||
|
|
a05a1e62d4 | ||
|
|
4c6acfd559 | ||
|
|
bd010c360b | ||
|
|
9bd9a3b165 | ||
|
|
a2a4e67845 | ||
|
|
092d863bd2 | ||
|
|
edc9b377a6 | ||
|
|
bb8dc28075 | ||
|
|
305072f198 | ||
|
|
402732a84b | ||
|
|
8d86bfc0fc | ||
|
|
b39073aa53 | ||
|
|
0b706b704f | ||
|
|
8a21dcd282 | ||
|
|
d3b942fdc7 | ||
|
|
4f346472c3 | ||
|
|
c2584615d2 | ||
|
|
c24202608e | ||
|
|
b029bcc89b | ||
|
|
5c28cc6025 | ||
|
|
4548e6badb | ||
|
|
82b6650a1c | ||
|
|
4db4d8cf93 | ||
|
|
1be22264cd | ||
|
|
2b355c5da1 |
17
.eslintrc.js
Normal file
17
.eslintrc.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
|
||||||
|
sourceType: 'module', // Allows for the use of imports
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
|
||||||
|
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
|
||||||
|
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 2],
|
||||||
|
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
|
||||||
|
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
},
|
||||||
|
};
|
||||||
61
.github/workflows/nodejs.yml
vendored
Normal file
61
.github/workflows/nodejs.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['*']
|
||||||
|
tags: ['v[0-9]+.[0-9]+.[0-9]+']
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12
|
||||||
|
- run: yarn install --frozen-lockfile
|
||||||
|
- run: yarn test
|
||||||
|
- run: yarn lint
|
||||||
|
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
if: startsWith(github.ref, 'refs/tags')
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn run build
|
||||||
|
- name: NPM login
|
||||||
|
# NPM doesn't understand env vars and needs auth file lol
|
||||||
|
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
||||||
|
env:
|
||||||
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
- name: Set vars
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "::set-output name=version::$(echo ${GITHUB_REF#refs/tags/v})"
|
||||||
|
echo "::set-output name=repo_name::$(echo ${GITHUB_REPOSITORY#*/})"
|
||||||
|
- name: Check package.json version vs tag
|
||||||
|
run: |
|
||||||
|
[ ${{ steps.vars.outputs.version }} = $(grep '"version":' package.json | grep -o "[0-9.]*") ] || (echo "Git tag doesn't match version in package.json" && false)
|
||||||
|
- name: Publish to npm
|
||||||
|
run: npm publish
|
||||||
|
- name: Create GitHub Release Draft
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: Release ${{ steps.vars.outputs.version }}
|
||||||
|
draft: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Telegram Message Notify
|
||||||
|
uses: appleboy/telegram-action@0.0.7
|
||||||
|
with:
|
||||||
|
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||||
|
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
message: 🚀 Published [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}) version [${{ steps.vars.outputs.version }}](https://www.npmjs.com/package/${{ steps.vars.outputs.repo_name }}/v/${{ steps.vars.outputs.version }}) to npm
|
||||||
|
debug: true
|
||||||
|
format: markdown
|
||||||
8
.prettierrc
Normal file
8
.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 110,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Mocha Current File",
|
|
||||||
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
|
|
||||||
"args": [
|
|
||||||
"--no-timeouts",
|
|
||||||
"--colors",
|
|
||||||
"${file}",
|
|
||||||
"--require",
|
|
||||||
"ts-node/register"
|
|
||||||
],
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"sourceMaps": true,
|
|
||||||
"internalConsoleOptions": "neverOpen"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 PepperSec
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
120
README.md
120
README.md
@@ -1,48 +1,116 @@
|
|||||||
# Gas Price Oracle library for Ethereum dApps
|
# Gas Price Oracle library for Ethereum dApps [](https://github.com/peppersec/gas-price-oracle/actions) [](https://www.npmjs.com/package/gas-price-oracle)
|
||||||
|
|
||||||
|
A library that has a collection of onchain and offchain gas price oracle URLs
|
||||||
|
|
||||||
|
## Supported networks
|
||||||
|
|
||||||
|
### Ethereum Mainnet
|
||||||
|
|
||||||
|
Current offchain list:
|
||||||
|
|
||||||
|
- https://ethgasstation.info/json/ethgasAPI.json
|
||||||
|
- https://etherchain.org/api/gasnow
|
||||||
|
- https://blockscout.com/eth/mainnet/api/v1/gas-price-oracle
|
||||||
|
|
||||||
|
Current onchain list:
|
||||||
|
|
||||||
|
- [chainlink](https://etherscan.io/address/0x169e633a2d1e6c10dd91238ba11c4a708dfef37c#readContract)
|
||||||
|
|
||||||
|
### Binance Smart Chain
|
||||||
|
|
||||||
|
Current offchain list:
|
||||||
|
|
||||||
|
- https://ztake.org/
|
||||||
|
|
||||||
|
### xDAI Chain
|
||||||
|
|
||||||
|
Current offchain list:
|
||||||
|
|
||||||
|
- https://blockscout.com/xdai/mainnet/api/v1/gas-price-oracle
|
||||||
|
|
||||||
|
### Polygon (Matic) Network
|
||||||
|
|
||||||
|
Current offchain list:
|
||||||
|
|
||||||
|
- https://gasstation-mainnet.matic.network/
|
||||||
|
|
||||||
|
### Avalanche Mainnet
|
||||||
|
|
||||||
|
Current offchain list:
|
||||||
|
|
||||||
|
- https://ztake.org/
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
## Instalation
|
|
||||||
`npm i gas-price-oracle`
|
`npm i gas-price-oracle`
|
||||||
|
|
||||||
## Import
|
## Import
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { GasPriceOracle } = require('gas-price-oracle');
|
const { GasPriceOracle } = require('gas-price-oracle');
|
||||||
```
|
```
|
||||||
## Usage
|
|
||||||
### Basic
|
|
||||||
```js
|
|
||||||
const oracle = new GasPriceOracle();
|
|
||||||
|
|
||||||
oracle.gasPrices().then((gas) => {
|
## Usage
|
||||||
console.log(gas)
|
|
||||||
|
### Basic
|
||||||
|
|
||||||
|
```js
|
||||||
|
const options = {
|
||||||
|
chainId: 1,
|
||||||
|
defaultRpc: 'https://api.mycryptoapi.com/eth',
|
||||||
|
timeout: 10000,
|
||||||
|
defaultFallbackGasPrices: {
|
||||||
|
instant: 28,
|
||||||
|
fast: 22,
|
||||||
|
standard: 17,
|
||||||
|
low: 11,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const oracle = new GasPriceOracle(options);
|
||||||
|
// optional fallbackGasPrices
|
||||||
|
const fallbackGasPrices = {
|
||||||
|
instant: 70,
|
||||||
|
fast: 31,
|
||||||
|
standard: 20,
|
||||||
|
low: 7,
|
||||||
|
};
|
||||||
|
oracle.gasPrices(fallbackGasPrices).then(gasPrices => {
|
||||||
|
console.log(gasPrices); // { instant: 50, fast: 21, standard: 10, low: 3 }
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `gasPrices` method also accepts `median` argument (`true`) by default. For more details see [below](#offchain-oracles-only-get-median-price).
|
||||||
|
Under the hood it's a combination of `fetchMedianGasPriceOffChain`(`fetchGasPricesOffChain`) and `fetchGasPricesOnChain` methods.
|
||||||
|
|
||||||
### Offchain oracles only
|
### Offchain oracles only
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const oracle = new GasPriceOracle();
|
const oracle = new GasPriceOracle();
|
||||||
|
|
||||||
oracle.fetchGasPricesOffChain().then((gas) => {
|
oracle.fetchGasPricesOffChain().then(gasPrices => {
|
||||||
console.log(gas)
|
console.log(gasPrices); // { instant: 50, fast: 21, standard: 10, low: 3 }
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Offchain oracles only (get median price)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const oracle = new GasPriceOracle();
|
||||||
|
|
||||||
|
oracle.fetchMedianGasPriceOffChain().then(gasPrices => {
|
||||||
|
console.log(gasPrices); // { instant: 50, fast: 21, standard: 10, low: 3 }
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
it returns the median gas price of all the oracles configured.
|
||||||
|
|
||||||
### Custom RPC URL for onchain oracles
|
### Custom RPC URL for onchain oracles
|
||||||
```js
|
|
||||||
const customRpc = 'https://mainnet.infura.io/v3/<API_KEY>'
|
|
||||||
const oracle = new GasPriceOracle(customRpc);
|
|
||||||
|
|
||||||
oracle.fetchGasPricesOnChain().then((gas) => {
|
```js
|
||||||
console.log(gas)
|
const defaultRpc = 'https://mainnet.infura.io/v3/<API_KEY>';
|
||||||
});
|
const oracle = new GasPriceOracle({ defaultRpc });
|
||||||
```
|
|
||||||
|
oracle.fetchGasPricesOnChain().then(gasPrices => {
|
||||||
### Don't throw an error if oracles are down
|
console.log(gasPrices); // 21
|
||||||
```js
|
|
||||||
oracle.fetchGasPricesOnChain(false).then((gas) => {
|
|
||||||
console.log(gas)
|
|
||||||
});
|
|
||||||
|
|
||||||
oracle.fetchGasPricesOffChain(false).then((gas) => {
|
|
||||||
console.log(gas)
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
1318
package-lock.json
generated
1318
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -1,37 +1,54 @@
|
|||||||
{
|
{
|
||||||
"name": "gas-price-oracle",
|
"name": "gas-price-oracle",
|
||||||
"version": "0.1.1",
|
"version": "0.4.6",
|
||||||
"description": "Gas Price Oracle library for Ethereum dApps.",
|
"description": "Gas Price Oracle library for Ethereum dApps.",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
|
"homepage": "https://github.com/peppersec/gas-price-oracle",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/peppersec/gas-price-oracle.git"
|
||||||
|
},
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"prepublishOnly": "npm test && npm run lint",
|
"prepublishOnly": "npm test && npm run lint",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --timeout 30000 -r ts-node/register tests/*.test.ts",
|
"test": "mocha -r ts-node/register --timeout 30000 --exit src tests/*.test.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "tslint -p tsconfig.json"
|
"eslint": "eslint 'src/*.ts'",
|
||||||
|
"prettier:check": "prettier --check . --config .prettierrc",
|
||||||
|
"prettier:fix": "prettier --write . --config .prettierrc",
|
||||||
|
"lint": "yarn eslint && yarn prettier:check"
|
||||||
},
|
},
|
||||||
"author": "Alexey Pertsev <alexey@peppersec.com> (https://peppersec.com)",
|
"author": "Alexey Pertsev <alexey@peppersec.com> (https://peppersec.com)",
|
||||||
"keywords": ["Gas", "Gas price", "Ethereum", "Oracle"],
|
"keywords": [
|
||||||
|
"Gas",
|
||||||
|
"Gas price",
|
||||||
|
"Ethereum",
|
||||||
|
"Oracle"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.11",
|
"@types/chai": "^4.2.11",
|
||||||
|
"@types/chai-as-promised": "^7.1.3",
|
||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/mockery": "^1.4.29",
|
"@types/mockery": "^1.4.29",
|
||||||
|
"@types/node": "^14.0.5",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.4.1",
|
||||||
|
"@typescript-eslint/parser": "^4.4.1",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
|
"eslint": "^7.11.0",
|
||||||
|
"eslint-config-prettier": "^6.13.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"mocha": "^7.2.0",
|
"mocha": "^7.2.0",
|
||||||
"mockery": "^2.1.0",
|
"mockery": "^2.1.0",
|
||||||
"tslint": "^6.1.2",
|
"prettier": "^2.1.2",
|
||||||
"tslint-config-standard": "^9.0.0"
|
"ts-node": "^8.10.1",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^14.0.5",
|
"axios": "^0.21.2",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"bignumber.js": "^9.0.0"
|
||||||
"bignumber.js": "^9.0.0",
|
|
||||||
"node-fetch": "^2.6.0",
|
|
||||||
"ts-node": "^8.10.1",
|
|
||||||
"typescript": "^3.9.3"
|
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib/**/*"
|
"lib/**/*"
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
|
|
||||||
import { OffChainOracle, OnChainOracle } from './types';
|
|
||||||
|
|
||||||
const ethgasstation: OffChainOracle = {
|
|
||||||
name: 'ethgasstation',
|
|
||||||
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
|
||||||
instantPropertyName: 'fastest',
|
|
||||||
fastPropertyName: 'fast',
|
|
||||||
standardPropertyName: 'average',
|
|
||||||
lowPropertyName: 'safeLow',
|
|
||||||
denominator: 10
|
|
||||||
};
|
|
||||||
|
|
||||||
const zoltu: OffChainOracle = {
|
|
||||||
name: 'zoltu',
|
|
||||||
url: 'https://gas-oracle.zoltu.io/',
|
|
||||||
instantPropertyName: 'percentile_99',
|
|
||||||
fastPropertyName: 'percentile_90',
|
|
||||||
standardPropertyName: 'percentile_60',
|
|
||||||
lowPropertyName: 'percentile_30',
|
|
||||||
denominator: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
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 const offChainOracles: { [key: string]: OffChainOracle } = {
|
|
||||||
ethgasstation, zoltu, poa, etherchain
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onChainOracles: { [key: string]: OnChainOracle } = {
|
|
||||||
chainlink
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
offChainOracles,
|
|
||||||
onChainOracles
|
|
||||||
};
|
|
||||||
23
src/config/avalanche.ts
Normal file
23
src/config/avalanche.ts
Normal file
@@ -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,
|
||||||
|
};
|
||||||
23
src/config/bsc.ts
Normal file
23
src/config/bsc.ts
Normal file
@@ -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,
|
||||||
|
};
|
||||||
23
src/config/index.ts
Normal file
23
src/config/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { NetworkConfig } from '../types';
|
||||||
|
|
||||||
|
import mainnetOracles from './mainnet';
|
||||||
|
import bscOracles from './bsc';
|
||||||
|
import xdaiOracles from './xdai';
|
||||||
|
import polygonOracles from './polygon';
|
||||||
|
import avalancheOracles from './avalanche';
|
||||||
|
|
||||||
|
export enum ChainId {
|
||||||
|
MAINNET = 1,
|
||||||
|
BSC = 56,
|
||||||
|
XDAI = 100,
|
||||||
|
POLYGON = 137,
|
||||||
|
AVALANCHE = 43114,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NETWORKS: NetworkConfig = {
|
||||||
|
[ChainId.MAINNET]: mainnetOracles,
|
||||||
|
[ChainId.BSC]: bscOracles,
|
||||||
|
[ChainId.XDAI]: xdaiOracles,
|
||||||
|
[ChainId.POLYGON]: polygonOracles,
|
||||||
|
[ChainId.AVALANCHE]: avalancheOracles,
|
||||||
|
};
|
||||||
68
src/config/mainnet.ts
Normal file
68
src/config/mainnet.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { OffChainOracle, OnChainOracle, OffChainOracles, OnChainOracles } from '../types';
|
||||||
|
|
||||||
|
const ethgasstation: OffChainOracle = {
|
||||||
|
name: 'ethgasstation',
|
||||||
|
url: 'https://ethgasstation.info/json/ethgasAPI.json',
|
||||||
|
instantPropertyName: 'fastest',
|
||||||
|
fastPropertyName: 'fast',
|
||||||
|
standardPropertyName: 'average',
|
||||||
|
lowPropertyName: 'safeLow',
|
||||||
|
denominator: 10,
|
||||||
|
additionalDataProperty: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const etherchain: OffChainOracle = {
|
||||||
|
name: 'etherchain',
|
||||||
|
url: 'https://etherchain.org/api/gasnow',
|
||||||
|
instantPropertyName: 'rapid',
|
||||||
|
fastPropertyName: 'fast',
|
||||||
|
standardPropertyName: 'standard',
|
||||||
|
lowPropertyName: 'slow',
|
||||||
|
denominator: 1e9,
|
||||||
|
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',
|
||||||
|
// instantPropertyName: 'instant',
|
||||||
|
// fastPropertyName: 'fast',
|
||||||
|
// standardPropertyName: 'standard',
|
||||||
|
// lowPropertyName: 'slow',
|
||||||
|
// denominator: 1,
|
||||||
|
// additionalDataProperty: null,
|
||||||
|
// };
|
||||||
|
|
||||||
|
const chainlink: OnChainOracle = {
|
||||||
|
name: 'chainlink',
|
||||||
|
callData: '0x50d25bcd',
|
||||||
|
contract: '0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C',
|
||||||
|
denominator: '1000000000',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const offChainOracles: OffChainOracles = {
|
||||||
|
ethgasstation,
|
||||||
|
// anyblock,
|
||||||
|
// blockscout,
|
||||||
|
etherchain,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onChainOracles: OnChainOracles = {
|
||||||
|
chainlink,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
offChainOracles,
|
||||||
|
onChainOracles,
|
||||||
|
};
|
||||||
23
src/config/polygon.ts
Normal file
23
src/config/polygon.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types';
|
||||||
|
|
||||||
|
const maticGasStation: OffChainOracle = {
|
||||||
|
name: 'maticGasStation',
|
||||||
|
url: 'https://gasstation-mainnet.matic.network',
|
||||||
|
instantPropertyName: 'fastest',
|
||||||
|
fastPropertyName: 'fast',
|
||||||
|
standardPropertyName: 'standard',
|
||||||
|
lowPropertyName: 'safeLow',
|
||||||
|
denominator: 1,
|
||||||
|
additionalDataProperty: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const offChainOracles: OffChainOracles = {
|
||||||
|
maticGasStation,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onChainOracles: OnChainOracles = {};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
offChainOracles,
|
||||||
|
onChainOracles,
|
||||||
|
};
|
||||||
23
src/config/xdai.ts
Normal file
23
src/config/xdai.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { OffChainOracle, OffChainOracles, OnChainOracles } from '../types';
|
||||||
|
|
||||||
|
const blockscout: OffChainOracle = {
|
||||||
|
name: 'blockscout',
|
||||||
|
url: 'https://blockscout.com/xdai/mainnet/api/v1/gas-price-oracle',
|
||||||
|
instantPropertyName: 'fast',
|
||||||
|
fastPropertyName: 'average',
|
||||||
|
standardPropertyName: 'slow',
|
||||||
|
lowPropertyName: 'slow',
|
||||||
|
denominator: 1,
|
||||||
|
additionalDataProperty: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const offChainOracles: OffChainOracles = {
|
||||||
|
blockscout,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onChainOracles: OnChainOracles = {};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
offChainOracles,
|
||||||
|
onChainOracles,
|
||||||
|
};
|
||||||
276
src/index.ts
276
src/index.ts
@@ -1,32 +1,66 @@
|
|||||||
import fetch from 'node-fetch';
|
import axios from 'axios';
|
||||||
import config from './config';
|
|
||||||
import { GasPrice, OffChainOracle, OnChainOracle } from './types';
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
import { ChainId, NETWORKS } from './config';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Config,
|
||||||
|
Options,
|
||||||
|
GasPrice,
|
||||||
|
GasPriceKey,
|
||||||
|
OffChainOracle,
|
||||||
|
OnChainOracle,
|
||||||
|
OnChainOracles,
|
||||||
|
OffChainOracles,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
const defaultFastGas = 22;
|
||||||
export class GasPriceOracle {
|
export class GasPriceOracle {
|
||||||
lastGasPrice: GasPrice = {
|
lastGasPrice: GasPrice;
|
||||||
instant: 40,
|
offChainOracles: OffChainOracles = {};
|
||||||
fast: 21,
|
onChainOracles: OnChainOracles = {};
|
||||||
standard: 10,
|
configuration: Config = {
|
||||||
low: 1
|
chainId: ChainId.MAINNET,
|
||||||
|
defaultRpc: 'https://api.mycryptoapi.com/eth',
|
||||||
|
timeout: 10000,
|
||||||
|
defaultFallbackGasPrices: {
|
||||||
|
instant: defaultFastGas * 1.3,
|
||||||
|
fast: defaultFastGas,
|
||||||
|
standard: defaultFastGas * 0.85,
|
||||||
|
low: defaultFastGas * 0.5,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
defaultRpc = 'https://api.mycryptoapi.com/eth';
|
|
||||||
offChainOracles = { ...config.offChainOracles };
|
|
||||||
onChainOracles = { ...config.onChainOracles };
|
|
||||||
|
|
||||||
constructor(defaultRpc?: string) {
|
constructor(options?: Options) {
|
||||||
if (defaultRpc) {
|
if (options) {
|
||||||
this.defaultRpc = defaultRpc;
|
Object.assign(this.configuration, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configuration.defaultFallbackGasPrices = this.normalize(this.configuration.defaultFallbackGasPrices);
|
||||||
|
|
||||||
|
const network = NETWORKS[this.configuration.chainId];
|
||||||
|
|
||||||
|
if (network) {
|
||||||
|
const { offChainOracles, onChainOracles } = network;
|
||||||
|
this.offChainOracles = { ...offChainOracles };
|
||||||
|
this.onChainOracles = { ...onChainOracles };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchGasPricesOffChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
async askOracle(oracle: OffChainOracle): Promise<GasPrice> {
|
||||||
for (let oracle of Object.values(this.offChainOracles)) {
|
const {
|
||||||
const { name, url, instantPropertyName, fastPropertyName, standardPropertyName, lowPropertyName, denominator } = oracle;
|
name,
|
||||||
try {
|
url,
|
||||||
const response = await fetch(url);
|
instantPropertyName,
|
||||||
|
fastPropertyName,
|
||||||
|
standardPropertyName,
|
||||||
|
lowPropertyName,
|
||||||
|
denominator,
|
||||||
|
additionalDataProperty,
|
||||||
|
} = oracle;
|
||||||
|
const response = await axios.get(url, { timeout: this.configuration.timeout });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
const gas = await response.json();
|
const gas = additionalDataProperty ? response.data[additionalDataProperty] : response.data;
|
||||||
if (Number(gas[fastPropertyName]) === 0) {
|
if (Number(gas[fastPropertyName]) === 0) {
|
||||||
throw new Error(`${name} oracle provides corrupted values`);
|
throw new Error(`${name} oracle provides corrupted values`);
|
||||||
}
|
}
|
||||||
@@ -34,56 +68,117 @@ export class GasPriceOracle {
|
|||||||
instant: parseFloat(gas[instantPropertyName]) / denominator,
|
instant: parseFloat(gas[instantPropertyName]) / denominator,
|
||||||
fast: parseFloat(gas[fastPropertyName]) / denominator,
|
fast: parseFloat(gas[fastPropertyName]) / denominator,
|
||||||
standard: parseFloat(gas[standardPropertyName]) / denominator,
|
standard: parseFloat(gas[standardPropertyName]) / denominator,
|
||||||
low: parseFloat(gas[lowPropertyName]) / denominator
|
low: parseFloat(gas[lowPropertyName]) / denominator,
|
||||||
};
|
};
|
||||||
this.lastGasPrice = gasPrices;
|
return this.normalize(gasPrices);
|
||||||
return this.lastGasPrice;
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||||
|
for (const oracle of Object.values(this.offChainOracles)) {
|
||||||
|
try {
|
||||||
|
return await this.askOracle(oracle);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message);
|
console.info(e.message);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (throwIfFailsToFetch) {
|
throw new Error('All oracles are down. Probably a network error.');
|
||||||
throw new Error('All oracles are down. Probaly network error.');
|
|
||||||
}
|
|
||||||
return this.lastGasPrice;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchGasPricesOnChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
async fetchMedianGasPriceOffChain(): Promise<GasPrice> {
|
||||||
for (let oracle of Object.values(this.onChainOracles)) {
|
const promises: Promise<GasPrice>[] = [];
|
||||||
const { name, callData, contract, denominator } = oracle;
|
for (const oracle of Object.values(this.offChainOracles) as Array<OffChainOracle>) {
|
||||||
let { rpc } = oracle;
|
promises.push(this.askOracle(oracle));
|
||||||
rpc = rpc ? rpc : this.defaultRpc;
|
}
|
||||||
const body = { jsonrpc: '2.0',
|
|
||||||
|
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.');
|
||||||
|
}
|
||||||
|
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 this.normalize(medianGasPrice);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Normalizes GasPrice values to Gwei. No more than 9 decimals basically
|
||||||
|
* @param GasPrice _gas
|
||||||
|
*/
|
||||||
|
normalize(_gas: GasPrice): GasPrice {
|
||||||
|
const format = {
|
||||||
|
decimalSeparator: '.',
|
||||||
|
groupSeparator: '',
|
||||||
|
};
|
||||||
|
const decimals = 9;
|
||||||
|
|
||||||
|
const gas: GasPrice = { ..._gas };
|
||||||
|
for (const type of Object.keys(gas) as Array<keyof GasPrice>) {
|
||||||
|
gas[type] = Number(new BigNumber(gas[type]).toFormat(decimals, format));
|
||||||
|
}
|
||||||
|
|
||||||
|
return gas;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchGasPricesOnChain(): Promise<number> {
|
||||||
|
for (const oracle of Object.values(this.onChainOracles)) {
|
||||||
|
const { name, callData, contract, denominator, rpc } = oracle;
|
||||||
|
const rpcUrl = rpc || this.configuration.defaultRpc;
|
||||||
|
const body = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
id: 1337,
|
id: 1337,
|
||||||
method: 'eth_call',
|
method: 'eth_call',
|
||||||
params: [{ 'data': callData, 'to': contract }, 'latest']
|
params: [{ data: callData, to: contract }, 'latest'],
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const response = await fetch(rpc, {
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
headers: {
|
const response = await axios.post(rpcUrl!, body, { timeout: this.configuration.timeout });
|
||||||
'content-type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
const { result } = await response.json();
|
const { result } = response.data;
|
||||||
let fastGasPrice = new BigNumber(result);
|
let fastGasPrice = new BigNumber(result);
|
||||||
if (fastGasPrice.isZero()) {
|
if (fastGasPrice.isZero()) {
|
||||||
throw new Error(`${name} oracle provides corrupted values`);
|
throw new Error(`${name} oracle provides corrupted values`);
|
||||||
}
|
}
|
||||||
fastGasPrice = fastGasPrice.div(denominator);
|
fastGasPrice = fastGasPrice.div(denominator);
|
||||||
const gasPrices: GasPrice = {
|
return fastGasPrice.toNumber();
|
||||||
instant: fastGasPrice.multipliedBy(1.3).toNumber(),
|
|
||||||
fast: fastGasPrice.toNumber(),
|
|
||||||
standard: fastGasPrice.multipliedBy(0.85).toNumber(),
|
|
||||||
low: fastGasPrice.multipliedBy(0.5).toNumber()
|
|
||||||
};
|
|
||||||
this.lastGasPrice = gasPrices;
|
|
||||||
return this.lastGasPrice;
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||||
}
|
}
|
||||||
@@ -91,44 +186,93 @@ export class GasPriceOracle {
|
|||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (throwIfFailsToFetch) {
|
throw new Error('All oracles are down. Probably a network error.');
|
||||||
throw new Error('All oracles are down. Probaly network error.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fetchGasPriceFromRpc(): Promise<number> {
|
||||||
|
const rpcUrl = this.configuration.defaultRpc;
|
||||||
|
const body = {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: 1337,
|
||||||
|
method: 'eth_gasPrice',
|
||||||
|
params: [],
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const response = await axios.post(rpcUrl!, body, { timeout: this.configuration.timeout });
|
||||||
|
if (response.status === 200) {
|
||||||
|
const { result } = response.data;
|
||||||
|
let fastGasPrice = new BigNumber(result);
|
||||||
|
if (fastGasPrice.isZero()) {
|
||||||
|
throw new Error(`Default RPC provides corrupted values`);
|
||||||
|
}
|
||||||
|
fastGasPrice = fastGasPrice.div(1e9);
|
||||||
|
return fastGasPrice.toNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Fetch gasPrice from default RPC failed..`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message);
|
||||||
|
throw new Error('Default RPC is down. Probably a network error.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async gasPrices(fallbackGasPrices?: GasPrice, median = true): Promise<GasPrice> {
|
||||||
|
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || this.configuration.defaultFallbackGasPrices;
|
||||||
|
|
||||||
|
if (Object.keys(this.offChainOracles).length > 0) {
|
||||||
|
try {
|
||||||
|
this.lastGasPrice = median
|
||||||
|
? await this.fetchMedianGasPriceOffChain()
|
||||||
|
: await this.fetchGasPricesOffChain();
|
||||||
return this.lastGasPrice;
|
return this.lastGasPrice;
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Failed to fetch gas prices from offchain oracles...');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async gasPrices(): Promise<GasPrice> {
|
if (Object.keys(this.onChainOracles).length > 0) {
|
||||||
let gas = this.lastGasPrice;
|
|
||||||
try {
|
try {
|
||||||
gas = await this.fetchGasPricesOffChain();
|
const fastGas = await this.fetchGasPricesOnChain();
|
||||||
return gas;
|
this.lastGasPrice = this.categorize(fastGas);
|
||||||
|
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 onchain oracles...');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
gas = await this.fetchGasPricesOnChain();
|
const fastGas = await this.fetchGasPriceFromRpc();
|
||||||
return gas;
|
this.lastGasPrice = this.categorize(fastGas);
|
||||||
|
return this.lastGasPrice;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Failed to fetch gas prices from onchain oracles. Last known gas will be returned');
|
console.log('Failed to fetch gas prices from default RPC. Last known gas will be returned');
|
||||||
|
}
|
||||||
|
return this.normalize(this.lastGasPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
return gas;
|
categorize(gasPrice: number): GasPrice {
|
||||||
|
return this.normalize({
|
||||||
|
instant: gasPrice * 1.3,
|
||||||
|
fast: gasPrice,
|
||||||
|
standard: gasPrice * 0.85,
|
||||||
|
low: gasPrice * 0.5,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addOffChainOracle(oracle: OffChainOracle) {
|
addOffChainOracle(oracle: OffChainOracle): void {
|
||||||
this.offChainOracles[oracle.name] = oracle;
|
this.offChainOracles[oracle.name] = oracle;
|
||||||
}
|
}
|
||||||
|
|
||||||
addOnChainOracle(oracle: OnChainOracle) {
|
addOnChainOracle(oracle: OnChainOracle): void {
|
||||||
this.onChainOracles[oracle.name] = oracle;
|
this.onChainOracles[oracle.name] = oracle;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOnChainOracle(name: string) {
|
removeOnChainOracle(name: string): void {
|
||||||
delete this.onChainOracles[name];
|
delete this.onChainOracles[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOffChainOracle(name: string) {
|
removeOffChainOracle(name: string): void {
|
||||||
delete this.offChainOracles[name];
|
delete this.offChainOracles[name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/types.ts
32
src/types.ts
@@ -6,8 +6,11 @@ export type OffChainOracle = {
|
|||||||
standardPropertyName: string;
|
standardPropertyName: string;
|
||||||
lowPropertyName: string;
|
lowPropertyName: string;
|
||||||
denominator: number;
|
denominator: number;
|
||||||
|
additionalDataProperty: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OffChainOracles = { [key: string]: OffChainOracle };
|
||||||
|
|
||||||
export type OnChainOracle = {
|
export type OnChainOracle = {
|
||||||
name: string;
|
name: string;
|
||||||
rpc?: string;
|
rpc?: string;
|
||||||
@@ -16,9 +19,28 @@ export type OnChainOracle = {
|
|||||||
denominator: string;
|
denominator: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GasPrice = {
|
export type OnChainOracles = { [key: string]: OnChainOracle };
|
||||||
instant: number;
|
|
||||||
fast: number;
|
export type AllOracles = {
|
||||||
standard: number;
|
offChainOracles: OffChainOracles;
|
||||||
low: number;
|
onChainOracles: OnChainOracles;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GasPrice = {
|
||||||
|
[key in GasPriceKey]: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GasPriceKey = 'instant' | 'fast' | 'standard' | 'low';
|
||||||
|
|
||||||
|
export type Options = {
|
||||||
|
chainId?: number;
|
||||||
|
defaultRpc?: string;
|
||||||
|
timeout?: number;
|
||||||
|
defaultFallbackGasPrices?: GasPrice;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Config = Required<Options>;
|
||||||
|
|
||||||
|
export type NetworkConfig = {
|
||||||
|
[key in number]: AllOracles;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +1,48 @@
|
|||||||
import { GasPrice } from '../src/types';
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import mockery from 'mockery';
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import chai from 'chai';
|
import chai from 'chai';
|
||||||
import { onChainOracles } from '../src/config';
|
import mockery from 'mockery';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
import { ChainId, NETWORKS } from '../src/config';
|
||||||
|
import { GasPriceOracle } from '../src/index';
|
||||||
|
|
||||||
|
import { GasPrice, OffChainOracle } from '../src/types';
|
||||||
|
|
||||||
const { GasPriceOracle } = require('../src/index');
|
|
||||||
chai.use(require('chai-as-promised'));
|
chai.use(require('chai-as-promised'));
|
||||||
chai.should();
|
chai.should();
|
||||||
|
|
||||||
let oracle = new GasPriceOracle();
|
let oracle = new GasPriceOracle();
|
||||||
|
let { onChainOracles, offChainOracles } = oracle;
|
||||||
|
|
||||||
before('before', function () {
|
before('before', function () {
|
||||||
let fetchMock = () => {
|
const axiosMock = {
|
||||||
throw new Error('Mocked for tests');
|
get: () => {
|
||||||
|
throw new Error('axios GET method is mocked for tests');
|
||||||
|
},
|
||||||
|
post: () => {
|
||||||
|
throw new Error('axios POST method is mocked for tests');
|
||||||
|
},
|
||||||
};
|
};
|
||||||
mockery.registerMock('node-fetch', fetchMock);
|
mockery.registerMock('axios', axiosMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach('beforeEach', function () {
|
beforeEach('beforeEach', function () {
|
||||||
oracle = new GasPriceOracle();
|
oracle = new GasPriceOracle();
|
||||||
|
({ onChainOracles, offChainOracles } = oracle);
|
||||||
|
});
|
||||||
|
|
||||||
|
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 () {
|
describe('fetchGasPricesOffChain', function () {
|
||||||
@@ -28,9 +54,9 @@ describe('fetchGasPricesOffChain', function () {
|
|||||||
gas.standard.should.be.a('number');
|
gas.standard.should.be.a('number');
|
||||||
gas.low.should.be.a('number');
|
gas.low.should.be.a('number');
|
||||||
|
|
||||||
gas.instant.should.be.above(gas.fast);
|
gas.instant.should.be.at.least(gas.fast); // greater than or equal to the given number.
|
||||||
gas.fast.should.be.above(gas.standard);
|
gas.fast.should.be.at.least(gas.standard);
|
||||||
gas.standard.should.be.above(gas.low);
|
gas.standard.should.be.at.least(gas.low);
|
||||||
gas.low.should.not.be.equal(0);
|
gas.low.should.not.be.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,89 +64,93 @@ describe('fetchGasPricesOffChain', function () {
|
|||||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
const { GasPriceOracle } = require('../src/index');
|
const { GasPriceOracle } = require('../src/index');
|
||||||
oracle = new GasPriceOracle();
|
oracle = new GasPriceOracle();
|
||||||
await oracle.fetchGasPricesOffChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
await oracle
|
||||||
mockery.disable();
|
.fetchGasPricesOffChain()
|
||||||
});
|
.should.be.rejectedWith('All oracles are down. Probably a network error.');
|
||||||
|
|
||||||
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.fetchGasPricesOffChain(false);
|
|
||||||
mockery.disable();
|
mockery.disable();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fetchGasPricesOnChain', function () {
|
describe('fetchGasPricesOnChain', function () {
|
||||||
it('should work', async function () {
|
it('should work', async function () {
|
||||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||||
|
gas.should.be.a('number');
|
||||||
gas.instant.should.be.a('number');
|
gas.should.be.above(1);
|
||||||
gas.fast.should.be.a('number');
|
gas.should.not.be.equal(0);
|
||||||
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 work with custom rpc', async function () {
|
it('should work with custom rpc', async function () {
|
||||||
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
||||||
const oracle = new GasPriceOracle(rpc);
|
const oracle = new GasPriceOracle({ defaultRpc: rpc });
|
||||||
oracle.defaultRpc.should.be.equal(rpc);
|
oracle.configuration.defaultRpc.should.be.equal(rpc);
|
||||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||||
|
|
||||||
gas.instant.should.be.a('number');
|
gas.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.should.be.above(1);
|
||||||
gas.fast.should.be.above(gas.standard);
|
gas.should.not.be.equal(0);
|
||||||
gas.standard.should.be.above(gas.low);
|
|
||||||
gas.low.should.not.be.equal(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove oracle', async function () {
|
it('should remove oracle', async function () {
|
||||||
await oracle.fetchGasPricesOnChain();
|
await oracle.fetchGasPricesOnChain();
|
||||||
oracle.removeOnChainOracle('chainlink');
|
oracle.removeOnChainOracle('chainlink');
|
||||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
await oracle
|
||||||
|
.fetchGasPricesOnChain()
|
||||||
|
.should.be.rejectedWith('All oracles are down. Probably a network error.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add oracle', async function () {
|
it('should add oracle', async function () {
|
||||||
const { chainlink } = onChainOracles;
|
const { chainlink } = onChainOracles;
|
||||||
await oracle.fetchGasPricesOnChain();
|
await oracle.fetchGasPricesOnChain();
|
||||||
oracle.removeOnChainOracle('chainlink');
|
oracle.removeOnChainOracle('chainlink');
|
||||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
await oracle
|
||||||
|
.fetchGasPricesOnChain()
|
||||||
|
.should.be.rejectedWith('All oracles are down. Probably a network error.');
|
||||||
oracle.addOnChainOracle(chainlink);
|
oracle.addOnChainOracle(chainlink);
|
||||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||||
|
|
||||||
gas.instant.should.be.a('number');
|
gas.should.be.a('number');
|
||||||
gas.fast.should.be.a('number');
|
gas.should.not.be.equal(0);
|
||||||
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 () {
|
it('should throw if all onchain oracles are down', async function () {
|
||||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
const { GasPriceOracle } = require('../src/index');
|
const { GasPriceOracle } = require('../src/index');
|
||||||
oracle = new GasPriceOracle();
|
oracle = new GasPriceOracle();
|
||||||
await oracle.fetchGasPricesOnChain().should.be.rejectedWith('All oracles are down. Probaly network error.');
|
await oracle
|
||||||
|
.fetchGasPricesOnChain()
|
||||||
|
.should.be.rejectedWith('All oracles are down. Probably a network error.');
|
||||||
mockery.disable();
|
mockery.disable();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should not throw if throwIfFailsToFetch is false', async function () {
|
describe('fetchGasPriceFromRpc', function () {
|
||||||
|
it('should work', async function () {
|
||||||
|
const gas: number = await oracle.fetchGasPriceFromRpc();
|
||||||
|
gas.should.be.a('number');
|
||||||
|
gas.should.be.above(1);
|
||||||
|
gas.should.not.be.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with custom rpc', async function () {
|
||||||
|
const rpc = 'https://ethereum-rpc.trustwalletapp.com';
|
||||||
|
const oracle = new GasPriceOracle({ defaultRpc: rpc });
|
||||||
|
oracle.configuration.defaultRpc.should.be.equal(rpc);
|
||||||
|
const gas: number = await oracle.fetchGasPriceFromRpc();
|
||||||
|
|
||||||
|
gas.should.be.a('number');
|
||||||
|
|
||||||
|
gas.should.be.above(1);
|
||||||
|
gas.should.not.be.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if default rpc is down', async function () {
|
||||||
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
const { GasPriceOracle } = require('../src/index');
|
const { GasPriceOracle } = require('../src/index');
|
||||||
oracle = new GasPriceOracle();
|
oracle = new GasPriceOracle();
|
||||||
await oracle.fetchGasPricesOnChain(false);
|
await oracle
|
||||||
|
.fetchGasPriceFromRpc()
|
||||||
|
.should.be.rejectedWith('Default RPC is down. Probably a network error.');
|
||||||
mockery.disable();
|
mockery.disable();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -134,11 +164,165 @@ describe('gasPrice', function () {
|
|||||||
gas.standard.should.be.a('number');
|
gas.standard.should.be.a('number');
|
||||||
gas.low.should.be.a('number');
|
gas.low.should.be.a('number');
|
||||||
|
|
||||||
gas.instant.should.be.above(gas.fast);
|
gas.instant.should.be.at.least(gas.fast);
|
||||||
gas.fast.should.be.above(gas.standard);
|
gas.fast.should.be.at.least(gas.standard);
|
||||||
gas.standard.should.be.above(gas.low);
|
gas.standard.should.be.at.least(gas.low);
|
||||||
gas.low.should.not.be.equal(0);
|
gas.low.should.not.be.equal(0);
|
||||||
});
|
});
|
||||||
|
it('should fallback', async function () {
|
||||||
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
|
const { GasPriceOracle } = require('../src/index');
|
||||||
|
oracle = new GasPriceOracle();
|
||||||
|
const gas: GasPrice = await oracle.gasPrices();
|
||||||
|
|
||||||
|
gas.instant.should.be.equal(28.6);
|
||||||
|
gas.fast.should.be.equal(22);
|
||||||
|
gas.standard.should.be.equal(18.7);
|
||||||
|
gas.low.should.be.equal(11);
|
||||||
|
mockery.disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to set values', async function () {
|
||||||
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
|
const { GasPriceOracle } = require('../src/index');
|
||||||
|
oracle = new GasPriceOracle();
|
||||||
|
const gas: GasPrice = await oracle.gasPrices({ instant: 50, fast: 21, standard: 10, low: 3 });
|
||||||
|
|
||||||
|
gas.instant.should.be.equal(50);
|
||||||
|
gas.fast.should.be.equal(21);
|
||||||
|
gas.standard.should.be.equal(10);
|
||||||
|
gas.low.should.be.equal(3);
|
||||||
|
mockery.disable();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
const 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalize result values', function () {
|
||||||
|
const wrongDecimalsGas = {
|
||||||
|
instant: 1.1,
|
||||||
|
fast: 2.12345678901,
|
||||||
|
standard: 3.12345678901,
|
||||||
|
low: 3.1234567890123456789,
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkDecimals = (gas: GasPrice) => {
|
||||||
|
const gasPrices: number[] = Object.values(gas);
|
||||||
|
|
||||||
|
for (const gas of gasPrices) {
|
||||||
|
new BigNumber(gas).dp().should.be.at.most(9);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
it('default fallback should be normalized', function () {
|
||||||
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
|
|
||||||
|
const { GasPriceOracle } = require('../src/index');
|
||||||
|
oracle = new GasPriceOracle({
|
||||||
|
defaultFallbackGasPrices: wrongDecimalsGas,
|
||||||
|
});
|
||||||
|
const { configuration } = oracle;
|
||||||
|
|
||||||
|
checkDecimals(configuration.defaultFallbackGasPrices);
|
||||||
|
|
||||||
|
mockery.disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fallback should be normalized', async function () {
|
||||||
|
mockery.enable({ useCleanCache: true, warnOnUnregistered: false });
|
||||||
|
|
||||||
|
const { GasPriceOracle } = require('../src/index');
|
||||||
|
oracle = new GasPriceOracle();
|
||||||
|
|
||||||
|
const gas = await oracle.gasPrices(wrongDecimalsGas);
|
||||||
|
|
||||||
|
checkDecimals(gas);
|
||||||
|
mockery.disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rpc fallback should be normalized', async function () {
|
||||||
|
const { GasPriceOracle } = require('../src/index');
|
||||||
|
oracle = new GasPriceOracle({ chainId: 42161, defaultRpc: 'https://arb1.arbitrum.io/rpc' });
|
||||||
|
|
||||||
|
const gas = await oracle.gasPrices();
|
||||||
|
|
||||||
|
checkDecimals(gas);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('askOracle', function () {
|
||||||
|
const chains = Object.keys(NETWORKS).map(id => Number(id));
|
||||||
|
|
||||||
|
chains.forEach(chainId => {
|
||||||
|
describe(`all ${ChainId[chainId]} oracles should answer`, function () {
|
||||||
|
oracle = new GasPriceOracle({ chainId });
|
||||||
|
({ offChainOracles } = oracle);
|
||||||
|
|
||||||
|
for (const o of Object.values(offChainOracles) as Array<OffChainOracle>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
after('after', function () {
|
after('after', function () {
|
||||||
|
|||||||
@@ -6,13 +6,15 @@
|
|||||||
"lib": [
|
"lib": [
|
||||||
"es2017",
|
"es2017",
|
||||||
"esnext.asynciterable",
|
"esnext.asynciterable",
|
||||||
"es2019"
|
"es2019",
|
||||||
|
"ES2020.Promise"
|
||||||
] /* Specify library files to be included in the compilation. */,
|
] /* Specify library files to be included in the compilation. */,
|
||||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
"outDir": "./lib" /* Redirect output structure to the directory. */,
|
||||||
"strict": true /* Enable all strict type-checking options. */,
|
"strict": true /* Enable all strict type-checking options. */,
|
||||||
"strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */,
|
"strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */,
|
||||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
||||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
"declaration": true /* Generates corresponding '.d.ts' file. */,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
// "checkJs": true, /* Report errors in .js files. */
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
@@ -60,11 +62,7 @@
|
|||||||
/* Experimental Options */
|
/* Experimental Options */
|
||||||
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
||||||
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
||||||
"plugins": [
|
"plugins": []
|
||||||
{
|
|
||||||
"name": "typescript-tslint-plugin"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "tests"]
|
"exclude": ["node_modules", "tests"]
|
||||||
|
|||||||
10
tslint.json
10
tslint.json
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "tslint-config-standard",
|
|
||||||
"defaultSeverity": "error",
|
|
||||||
"rules": {
|
|
||||||
"semicolon": [true, "always"],
|
|
||||||
"space-before-function-paren": [true, {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
|
||||||
"type-literal-delimiter": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user