Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a21dcd282 | ||
|
|
d3b942fdc7 | ||
|
|
4f346472c3 | ||
|
|
c2584615d2 | ||
|
|
c24202608e | ||
|
|
b029bcc89b | ||
|
|
5c28cc6025 | ||
|
|
4548e6badb | ||
|
|
82b6650a1c | ||
|
|
4db4d8cf93 | ||
|
|
1be22264cd | ||
|
|
2b355c5da1 |
65
.github/workflows/nodejs.yml
vendored
Normal file
65
.github/workflows/nodejs.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
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: npm ci
|
||||
- run: npm test
|
||||
- name: TSLint checks
|
||||
uses: mooyoul/tslint-actions@v1.1.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
project: tsconfig.json
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- run: npm install
|
||||
- run: npm 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
|
||||
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.
|
||||
53
README.md
53
README.md
@@ -1,6 +1,16 @@
|
||||
# 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
|
||||
|
||||
## Instalation
|
||||
Current offchain list:
|
||||
- https://ethgasstation.info/json/ethgasAPI.json
|
||||
- https://gas-oracle.zoltu.io/
|
||||
- https://www.etherchain.org/api/gasPriceOracle
|
||||
- https://gasprice.poa.network/
|
||||
|
||||
Current onchain list:
|
||||
- [chainlink](https://etherscan.io/address/0xA417221ef64b1549575C977764E651c9FAB50141)
|
||||
|
||||
## Installation
|
||||
`npm i gas-price-oracle`
|
||||
|
||||
## Import
|
||||
@@ -9,11 +19,19 @@ const { GasPriceOracle } = require('gas-price-oracle');
|
||||
```
|
||||
## Usage
|
||||
### Basic
|
||||
```js
|
||||
const oracle = new GasPriceOracle();
|
||||
|
||||
oracle.gasPrices().then((gas) => {
|
||||
console.log(gas)
|
||||
```js
|
||||
|
||||
const options = {
|
||||
defaultRpc: 'https://api.mycryptoapi.com/eth'
|
||||
}
|
||||
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 }
|
||||
});
|
||||
```
|
||||
|
||||
@@ -21,28 +39,17 @@ oracle.gasPrices().then((gas) => {
|
||||
```js
|
||||
const oracle = new GasPriceOracle();
|
||||
|
||||
oracle.fetchGasPricesOffChain().then((gas) => {
|
||||
console.log(gas)
|
||||
oracle.fetchGasPricesOffChain().then((gasPrices) => {
|
||||
console.log(gasPrices) // { instant: 50, fast: 21, standard: 10, low: 3 }
|
||||
});
|
||||
```
|
||||
|
||||
### Custom RPC URL for onchain oracles
|
||||
```js
|
||||
const customRpc = 'https://mainnet.infura.io/v3/<API_KEY>'
|
||||
const oracle = new GasPriceOracle(customRpc);
|
||||
const defaultRpc = 'https://mainnet.infura.io/v3/<API_KEY>'
|
||||
const oracle = new GasPriceOracle({ defaultRpc });
|
||||
|
||||
oracle.fetchGasPricesOnChain().then((gas) => {
|
||||
console.log(gas)
|
||||
});
|
||||
```
|
||||
|
||||
### Don't throw an error if oracles are down
|
||||
```js
|
||||
oracle.fetchGasPricesOnChain(false).then((gas) => {
|
||||
console.log(gas)
|
||||
});
|
||||
|
||||
oracle.fetchGasPricesOffChain(false).then((gas) => {
|
||||
console.log(gas)
|
||||
oracle.fetchGasPricesOnChain().then((gasPrices) => {
|
||||
console.log(gasPrices) // 21
|
||||
});
|
||||
```
|
||||
|
||||
112
package-lock.json
generated
112
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gas-price-oracle",
|
||||
"version": "1.0.0",
|
||||
"version": "0.1.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -51,16 +51,8 @@
|
||||
"@types/node": {
|
||||
"version": "14.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
|
||||
"integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA=="
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
|
||||
"integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"form-data": "^3.0.0"
|
||||
}
|
||||
"integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-colors": {
|
||||
"version": "3.2.3",
|
||||
@@ -96,7 +88,8 @@
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
@@ -113,10 +106,13 @@
|
||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
|
||||
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
@@ -163,7 +159,8 @@
|
||||
"buffer-from": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1",
|
||||
@@ -287,14 +284,6 @@
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
@@ -340,15 +329,11 @@
|
||||
"object-keys": "^1.0.12"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "0.7.2",
|
||||
@@ -441,14 +426,27 @@
|
||||
"is-buffer": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||
"follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
"debug": "=3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
@@ -665,9 +663,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"dev": true
|
||||
},
|
||||
"log-symbols": {
|
||||
@@ -682,20 +680,8 @@
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.27",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||
"requires": {
|
||||
"mime-db": "1.44.0"
|
||||
}
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
@@ -816,11 +802,6 @@
|
||||
"semver": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -969,12 +950,14 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
@@ -1075,6 +1058,7 @@
|
||||
"version": "8.10.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.1.tgz",
|
||||
"integrity": "sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
@@ -1165,7 +1149,8 @@
|
||||
"typescript": {
|
||||
"version": "3.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
|
||||
"integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ=="
|
||||
"integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
@@ -1312,7 +1297,8 @@
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
package.json
27
package.json
@@ -1,8 +1,13 @@
|
||||
{
|
||||
"name": "gas-price-oracle",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.5",
|
||||
"description": "Gas Price Oracle library for Ethereum dApps.",
|
||||
"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",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm test && npm run lint",
|
||||
@@ -12,27 +17,31 @@
|
||||
"lint": "tslint -p tsconfig.json"
|
||||
},
|
||||
"author": "Alexey Pertsev <alexey@peppersec.com> (https://peppersec.com)",
|
||||
"keywords": ["Gas", "Gas price", "Ethereum", "Oracle"],
|
||||
"keywords": [
|
||||
"Gas",
|
||||
"Gas price",
|
||||
"Ethereum",
|
||||
"Oracle"
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/mockery": "^1.4.29",
|
||||
"@types/node": "^14.0.5",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"mocha": "^7.2.0",
|
||||
"mockery": "^2.1.0",
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-config-standard": "^9.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^14.0.5",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"tslint-config-standard": "^9.0.0",
|
||||
"ts-node": "^8.10.1",
|
||||
"typescript": "^3.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"bignumber.js": "^9.0.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
]
|
||||
|
||||
@@ -44,7 +44,7 @@ const poa: OffChainOracle = {
|
||||
const chainlink: OnChainOracle = {
|
||||
name: 'chainlink',
|
||||
callData: '0x50d25bcd',
|
||||
contract: '0xA417221ef64b1549575C977764E651c9FAB50141',
|
||||
contract: '0x169E633A2D1E6c10dD91238Ba11c4A708dfEF37C',
|
||||
denominator: '1000000000'
|
||||
};
|
||||
|
||||
|
||||
84
src/index.ts
84
src/index.ts
@@ -1,32 +1,27 @@
|
||||
import fetch from 'node-fetch';
|
||||
import axios from 'axios';
|
||||
import config from './config';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle } from './types';
|
||||
import { GasPrice, OffChainOracle, OnChainOracle, ConstructorArgs } from './types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export class GasPriceOracle {
|
||||
lastGasPrice: GasPrice = {
|
||||
instant: 40,
|
||||
fast: 21,
|
||||
standard: 10,
|
||||
low: 1
|
||||
};
|
||||
lastGasPrice: GasPrice;
|
||||
defaultRpc = 'https://api.mycryptoapi.com/eth';
|
||||
offChainOracles = { ...config.offChainOracles };
|
||||
onChainOracles = { ...config.onChainOracles };
|
||||
|
||||
constructor(defaultRpc?: string) {
|
||||
if (defaultRpc) {
|
||||
this.defaultRpc = defaultRpc;
|
||||
constructor(options: ConstructorArgs) {
|
||||
if (options && options.defaultRpc) {
|
||||
this.defaultRpc = options.defaultRpc;
|
||||
}
|
||||
}
|
||||
|
||||
async fetchGasPricesOffChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
||||
async fetchGasPricesOffChain(): Promise<GasPrice> {
|
||||
for (let oracle of Object.values(this.offChainOracles)) {
|
||||
const { name, url, instantPropertyName, fastPropertyName, standardPropertyName, lowPropertyName, denominator } = oracle;
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const response = await axios.get(url, { timeout: 10000 });
|
||||
if (response.status === 200) {
|
||||
const gas = await response.json();
|
||||
const gas = response.data;
|
||||
if (Number(gas[fastPropertyName]) === 0) {
|
||||
throw new Error(`${name} oracle provides corrupted values`);
|
||||
}
|
||||
@@ -36,8 +31,7 @@ export class GasPriceOracle {
|
||||
standard: parseFloat(gas[standardPropertyName]) / denominator,
|
||||
low: parseFloat(gas[lowPropertyName]) / denominator
|
||||
};
|
||||
this.lastGasPrice = gasPrices;
|
||||
return this.lastGasPrice;
|
||||
return gasPrices;
|
||||
} else {
|
||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||
}
|
||||
@@ -45,45 +39,30 @@ export class GasPriceOracle {
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
if (throwIfFailsToFetch) {
|
||||
throw new Error('All oracles are down. Probaly network error.');
|
||||
}
|
||||
return this.lastGasPrice;
|
||||
}
|
||||
|
||||
async fetchGasPricesOnChain(throwIfFailsToFetch = true): Promise<GasPrice> {
|
||||
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 body = { jsonrpc: '2.0',
|
||||
const body = {
|
||||
jsonrpc: '2.0',
|
||||
id: 1337,
|
||||
method: 'eth_call',
|
||||
params: [{ 'data': callData, 'to': contract }, 'latest']
|
||||
};
|
||||
try {
|
||||
const response = await fetch(rpc, {
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
method: 'POST'
|
||||
});
|
||||
const response = await axios.post(rpc, body, { timeout: 10000 });
|
||||
if (response.status === 200) {
|
||||
const { result } = await response.json();
|
||||
const { result } = response.data;
|
||||
let fastGasPrice = new BigNumber(result);
|
||||
if (fastGasPrice.isZero()) {
|
||||
throw new Error(`${name} oracle provides corrupted values`);
|
||||
}
|
||||
fastGasPrice = fastGasPrice.div(denominator);
|
||||
const gasPrices: GasPrice = {
|
||||
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;
|
||||
return fastGasPrice.toNumber();
|
||||
} else {
|
||||
throw new Error(`Fetch gasPrice from ${name} oracle failed. Trying another one...`);
|
||||
}
|
||||
@@ -91,29 +70,38 @@ export class GasPriceOracle {
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
if (throwIfFailsToFetch) {
|
||||
throw new Error('All oracles are down. Probaly network error.');
|
||||
}
|
||||
return this.lastGasPrice;
|
||||
}
|
||||
|
||||
async gasPrices(): Promise<GasPrice> {
|
||||
let gas = this.lastGasPrice;
|
||||
async gasPrices(fallbackGasPrices?: GasPrice): Promise<GasPrice> {
|
||||
const defaultFastGas = 22;
|
||||
const defaultFallbackGasPrices = {
|
||||
instant: defaultFastGas * 1.3,
|
||||
fast: defaultFastGas,
|
||||
standard: defaultFastGas * 0.85,
|
||||
low: defaultFastGas * 0.5
|
||||
};
|
||||
this.lastGasPrice = this.lastGasPrice || fallbackGasPrices || defaultFallbackGasPrices;
|
||||
try {
|
||||
gas = await this.fetchGasPricesOffChain();
|
||||
return gas;
|
||||
this.lastGasPrice = await this.fetchGasPricesOffChain();
|
||||
return this.lastGasPrice;
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch gas prices from offchain oracles. Trying onchain ones...');
|
||||
}
|
||||
|
||||
try {
|
||||
gas = await this.fetchGasPricesOnChain();
|
||||
return gas;
|
||||
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. Last known gas will be returned');
|
||||
}
|
||||
|
||||
return gas;
|
||||
return this.lastGasPrice;
|
||||
}
|
||||
|
||||
addOffChainOracle(oracle: OffChainOracle) {
|
||||
|
||||
@@ -22,3 +22,7 @@ export type GasPrice = {
|
||||
standard: number;
|
||||
low: number;
|
||||
};
|
||||
|
||||
export interface ConstructorArgs {
|
||||
defaultRpc?: string;
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@ chai.should();
|
||||
let oracle = new GasPriceOracle();
|
||||
|
||||
before('before', function () {
|
||||
let fetchMock = () => {
|
||||
throw new Error('Mocked for tests');
|
||||
let axiosMock = {
|
||||
get: () => {
|
||||
throw new Error('axios GET methdod is mocked for tests');
|
||||
},
|
||||
post: () => {
|
||||
throw new Error('axios POST methdod is mocked for tests');
|
||||
}
|
||||
};
|
||||
mockery.registerMock('node-fetch', fetchMock);
|
||||
mockery.registerMock('axios', axiosMock);
|
||||
});
|
||||
|
||||
beforeEach('beforeEach', function () {
|
||||
@@ -28,9 +33,9 @@ describe('fetchGasPricesOffChain', function () {
|
||||
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.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);
|
||||
});
|
||||
|
||||
@@ -41,46 +46,26 @@ describe('fetchGasPricesOffChain', function () {
|
||||
await oracle.fetchGasPricesOffChain().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.fetchGasPricesOffChain(false);
|
||||
mockery.disable();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchGasPricesOnChain', function () {
|
||||
it('should work', async function () {
|
||||
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);
|
||||
const gas: number = await oracle.fetchGasPricesOnChain();
|
||||
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(rpc);
|
||||
const oracle = new GasPriceOracle({ defaultRpc: rpc });
|
||||
oracle.defaultRpc.should.be.equal(rpc);
|
||||
const gas: GasPrice = await oracle.fetchGasPricesOnChain();
|
||||
const gas: number = 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.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);
|
||||
gas.should.be.above(1);
|
||||
gas.should.not.be.equal(0);
|
||||
});
|
||||
|
||||
it('should remove oracle', async function () {
|
||||
@@ -95,17 +80,10 @@ describe('fetchGasPricesOnChain', function () {
|
||||
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();
|
||||
const gas: number = 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);
|
||||
gas.should.be.a('number');
|
||||
gas.should.not.be.equal(0);
|
||||
});
|
||||
|
||||
it('should throw if all onchain oracles are down', async function () {
|
||||
@@ -116,13 +94,6 @@ describe('fetchGasPricesOnChain', function () {
|
||||
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 () {
|
||||
@@ -134,11 +105,36 @@ describe('gasPrice', function () {
|
||||
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.instant.should.be.at.least(gas.fast);
|
||||
gas.fast.should.be.at.least(gas.standard);
|
||||
gas.standard.should.be.at.least(gas.low);
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
after('after', function () {
|
||||
|
||||
Reference in New Issue
Block a user