Common gasprice (#179)
* Common gas price normalization. * only e2e jobs * One func * More extraction. * Fixed the tests * skip gasPriceWithinLimits * test fix * tos tring * boundaries * Extracted fetching gas price from contract * Refactored oracle gas price * lint * lint * Commentary * Using common gas price from oracle in ui * Fix lint * lint * Log * Using common gas price in monitor * cosmetics * all jobs * lint * lint * tests * more tests * incljdes * Tests in oracle * Tests in commons * Lint * moved tests from ui to commons * chai * Changed order of fetching gas price
This commit is contained in:
parent
2be0e9f363
commit
055a444fae
@ -4,7 +4,8 @@
|
||||
"../.eslintrc"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-expressions": "off"
|
||||
"no-unused-expressions": "off",
|
||||
"import/no-extraneous-dependencies": "off"
|
||||
},
|
||||
"env": {
|
||||
"mocha": true
|
||||
|
@ -6,5 +6,8 @@
|
||||
"scripts": {
|
||||
"lint": "eslint . --ignore-path ../.eslintignore",
|
||||
"test": "NODE_ENV=test mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"web3-utils": "1.0.0-beta.30"
|
||||
}
|
||||
}
|
||||
|
162
commons/test/gas.js
Normal file
162
commons/test/gas.js
Normal file
@ -0,0 +1,162 @@
|
||||
const { expect } = require('chai')
|
||||
const Web3Utils = require('web3-utils')
|
||||
const { gasPriceWithinLimits, normalizeGasPrice } = require('..')
|
||||
|
||||
const GAS_PRICE_BOUNDARIES = {
|
||||
MIN: 1,
|
||||
MAX: 250
|
||||
}
|
||||
|
||||
describe('gas', () => {
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 30
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 300
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
})
|
||||
|
||||
describe('gasPriceWithinLimits', () => {
|
||||
it('should return gas price if gas price is between boundaries', () => {
|
||||
// given
|
||||
const minGasPrice = 1
|
||||
const middleGasPrice = 10
|
||||
const maxGasPrice = 250
|
||||
|
||||
// when
|
||||
const minGasPriceWithinLimits = gasPriceWithinLimits(minGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
const middleGasPriceWithinLimits = gasPriceWithinLimits(middleGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
const maxGasPriceWithinLimits = gasPriceWithinLimits(maxGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// then
|
||||
expect(minGasPriceWithinLimits).to.equal(minGasPrice)
|
||||
expect(middleGasPriceWithinLimits).to.equal(middleGasPrice)
|
||||
expect(maxGasPriceWithinLimits).to.equal(maxGasPrice)
|
||||
})
|
||||
|
||||
it('should return min limit if gas price is below min boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 0.5
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MIN)
|
||||
})
|
||||
|
||||
it('should return max limit if gas price is above max boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 260
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice, GAS_PRICE_BOUNDARIES)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MAX)
|
||||
})
|
||||
|
||||
it('should return gas price if boundaries not provided', () => {
|
||||
// Given
|
||||
const initialGasPrice = 260
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice)
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(initialGasPrice)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
})
|
||||
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
})
|
||||
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
|
||||
it('should respect gas price max limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 4
|
||||
const maxInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MAX.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor, GAS_PRICE_BOUNDARIES).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(maxInWei)
|
||||
})
|
||||
|
||||
it('should respect gas price min limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 1
|
||||
const factor = 0.01
|
||||
const minInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MIN.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor, GAS_PRICE_BOUNDARIES).toString()
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(minInWei)
|
||||
})
|
||||
})
|
||||
})
|
@ -1,3 +1,4 @@
|
||||
const { toWei, toBN } = require('web3-utils')
|
||||
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
|
||||
|
||||
function decodeBridgeMode(bridgeModeHash) {
|
||||
@ -65,10 +66,77 @@ const getUnit = bridgeMode => {
|
||||
return { unitHome, unitForeign }
|
||||
}
|
||||
|
||||
const gasPriceWithinLimits = (gasPrice, limits) => {
|
||||
if (!limits) {
|
||||
return gasPrice
|
||||
}
|
||||
if (gasPrice < limits.MIN) {
|
||||
return limits.MIN
|
||||
} else if (gasPrice > limits.MAX) {
|
||||
return limits.MAX
|
||||
} else {
|
||||
return gasPrice
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeGasPrice = (oracleGasPrice, factor, limits = null) => {
|
||||
let gasPrice = oracleGasPrice * factor
|
||||
gasPrice = gasPriceWithinLimits(gasPrice, limits)
|
||||
return toBN(toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
||||
}
|
||||
|
||||
// fetchFn has to be supplied (instead of just url to oracle),
|
||||
// because this utility function is shared between Browser and Node,
|
||||
// we use built-in 'fetch' on browser side, and `node-fetch` package in Node.
|
||||
const gasPriceFromOracle = async (fetchFn, options = {}) => {
|
||||
try {
|
||||
const response = await fetchFn()
|
||||
const json = await response.json()
|
||||
const oracleGasPrice = json[options.speedType]
|
||||
|
||||
if (!oracleGasPrice) {
|
||||
options.logger &&
|
||||
options.logger.error &&
|
||||
options.logger.error(`Response from Oracle didn't include gas price for ${options.speedType} type.`)
|
||||
return null
|
||||
}
|
||||
|
||||
const normalizedGasPrice = normalizeGasPrice(oracleGasPrice, options.factor, options.limits)
|
||||
|
||||
options.logger &&
|
||||
options.logger.debug &&
|
||||
options.logger.debug({ oracleGasPrice, normalizedGasPrice }, 'Gas price updated using the API')
|
||||
|
||||
return normalizedGasPrice
|
||||
} catch (e) {
|
||||
options.logger && options.logger.error && options.logger.error(`Gas Price API is not available. ${e.message}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const gasPriceFromContract = async (bridgeContract, options = {}) => {
|
||||
try {
|
||||
const gasPrice = await bridgeContract.methods.gasPrice().call()
|
||||
options.logger &&
|
||||
options.logger.debug &&
|
||||
options.logger.debug({ gasPrice }, 'Gas price updated using the contracts')
|
||||
return gasPrice
|
||||
} catch (e) {
|
||||
options.logger &&
|
||||
options.logger.error &&
|
||||
options.logger.error(`There was a problem getting the gas price from the contract. ${e.message}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
decodeBridgeMode,
|
||||
decodeFeeManagerMode,
|
||||
getBridgeMode,
|
||||
getTokenType,
|
||||
getUnit
|
||||
getUnit,
|
||||
normalizeGasPrice,
|
||||
gasPriceFromOracle,
|
||||
gasPriceFromContract,
|
||||
gasPriceWithinLimits
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ require('dotenv').config()
|
||||
const Web3 = require('web3')
|
||||
const fetch = require('node-fetch')
|
||||
const logger = require('./logger')('validators')
|
||||
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI } = require('../commons')
|
||||
const { getBridgeABIs, BRIDGE_VALIDATORS_ABI, gasPriceFromOracle } = require('../commons')
|
||||
const { getValidatorList } = require('./utils/validatorUtils')
|
||||
const { getBlockNumber } = require('./utils/contract')
|
||||
|
||||
@ -33,40 +33,24 @@ const web3Home = new Web3(homeProvider)
|
||||
const foreignProvider = new Web3.providers.HttpProvider(FOREIGN_RPC_URL)
|
||||
const web3Foreign = new Web3(foreignProvider)
|
||||
|
||||
const homeGasOracleOpts = {
|
||||
speedType: HOME_GAS_PRICE_SPEED_TYPE,
|
||||
factor: HOME_GAS_PRICE_FACTOR,
|
||||
logger
|
||||
}
|
||||
|
||||
const foreignGasOracleOpts = {
|
||||
speedType: FOREIGN_GAS_PRICE_SPEED_TYPE,
|
||||
factor: FOREIGN_GAS_PRICE_FACTOR,
|
||||
logger
|
||||
}
|
||||
|
||||
const asyncForEach = async (array, callback) => {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array)
|
||||
}
|
||||
}
|
||||
|
||||
function getGasPrices(url, type, factor, fallback) {
|
||||
if (!url) {
|
||||
return Web3Utils.toBN(fallback)
|
||||
}
|
||||
return fetchGasPrices(url, type, factor, fallback)
|
||||
}
|
||||
|
||||
async function fetchGasPrices(url, type, factor, fallback) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const json = await response.json()
|
||||
logger.log('Fetched gasprice: ' + json[type])
|
||||
const gasPrice = json[type]
|
||||
if (!gasPrice) {
|
||||
throw new Error(`Response from Oracle didn't include gas price for ${type} type.`)
|
||||
}
|
||||
return normalizeGasPrice(gasPrice, factor)
|
||||
} catch (e) {
|
||||
logger.error('Gas Price API is not available', e)
|
||||
return Web3Utils.toBN(fallback)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeGasPrice(oracleGasPrice, factor) {
|
||||
const gasPrice = oracleGasPrice * factor
|
||||
return Web3Utils.toBN(Web3Utils.toWei(gasPrice.toFixed(2).toString(), 'gwei'))
|
||||
}
|
||||
|
||||
async function main(bridgeMode) {
|
||||
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
|
||||
const homeBridge = new web3Home.eth.Contract(HOME_ABI, HOME_BRIDGE_ADDRESS)
|
||||
@ -106,22 +90,16 @@ async function main(bridgeMode) {
|
||||
const homeVBalances = {}
|
||||
|
||||
logger.debug('calling home getGasPrices')
|
||||
const homeGasPrice = await getGasPrices(
|
||||
HOME_GAS_PRICE_ORACLE_URL,
|
||||
HOME_GAS_PRICE_SPEED_TYPE,
|
||||
HOME_GAS_PRICE_FACTOR,
|
||||
HOME_GAS_PRICE_FALLBACK
|
||||
)
|
||||
const homeGasPrice =
|
||||
(await gasPriceFromOracle(() => fetch(HOME_GAS_PRICE_ORACLE_URL), homeGasOracleOpts)) ||
|
||||
Web3Utils.toBN(HOME_GAS_PRICE_FALLBACK)
|
||||
const homeGasPriceGwei = Web3Utils.fromWei(homeGasPrice.toString(), 'gwei')
|
||||
const homeTxCost = homeGasPrice.mul(Web3Utils.toBN(HOME_GAS_LIMIT))
|
||||
|
||||
logger.debug('calling foreign getGasPrices')
|
||||
const foreignGasPrice = await getGasPrices(
|
||||
FOREIGN_GAS_PRICE_ORACLE_URL,
|
||||
FOREIGN_GAS_PRICE_SPEED_TYPE,
|
||||
FOREIGN_GAS_PRICE_FACTOR,
|
||||
FOREIGN_GAS_PRICE_FALLBACK
|
||||
)
|
||||
const foreignGasPrice =
|
||||
(await gasPriceFromOracle(() => fetch(FOREIGN_GAS_PRICE_ORACLE_URL), foreignGasOracleOpts)) ||
|
||||
Web3Utils.toBN(FOREIGN_GAS_PRICE_FALLBACK)
|
||||
const foreignGasPriceGwei = Web3Utils.fromWei(foreignGasPrice.toString(), 'gwei')
|
||||
const foreignTxCost = foreignGasPrice.mul(Web3Utils.toBN(FOREIGN_GAS_LIMIT))
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
require('../../env')
|
||||
const fetch = require('node-fetch')
|
||||
const Web3Utils = require('web3-utils')
|
||||
const { web3Home, web3Foreign } = require('../services/web3')
|
||||
const { bridgeConfig } = require('../../config/base.config')
|
||||
const logger = require('../services/logger').child({
|
||||
@ -8,6 +7,7 @@ const logger = require('../services/logger').child({
|
||||
})
|
||||
const { setIntervalAndRun } = require('../utils/utils')
|
||||
const { DEFAULT_UPDATE_INTERVAL, GAS_PRICE_BOUNDARIES, DEFAULT_GAS_PRICE_FACTOR } = require('../utils/constants')
|
||||
const { gasPriceFromOracle, gasPriceFromContract } = require('../../../commons')
|
||||
|
||||
const HomeABI = bridgeConfig.homeBridgeAbi
|
||||
const ForeignABI = bridgeConfig.foreignBridgeAbi
|
||||
@ -33,53 +33,18 @@ const foreignBridge = new web3Foreign.eth.Contract(ForeignABI, FOREIGN_BRIDGE_AD
|
||||
|
||||
let cachedGasPrice = null
|
||||
|
||||
function gasPriceWithinLimits(gasPrice) {
|
||||
if (gasPrice < GAS_PRICE_BOUNDARIES.MIN) {
|
||||
return GAS_PRICE_BOUNDARIES.MIN
|
||||
} else if (gasPrice > GAS_PRICE_BOUNDARIES.MAX) {
|
||||
return GAS_PRICE_BOUNDARIES.MAX
|
||||
} else {
|
||||
return gasPrice
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeGasPrice(oracleGasPrice, factor) {
|
||||
const gasPriceGwei = oracleGasPrice * factor
|
||||
const gasPrice = gasPriceWithinLimits(gasPriceGwei)
|
||||
return Web3Utils.toWei(gasPrice.toFixed(2).toString(), 'gwei')
|
||||
}
|
||||
|
||||
async function fetchGasPriceFromOracle(oracleUrl, speedType, factor) {
|
||||
const response = await fetch(oracleUrl)
|
||||
const json = await response.json()
|
||||
const oracleGasPrice = json[speedType]
|
||||
if (!oracleGasPrice) {
|
||||
throw new Error(`Response from Oracle didn't include gas price for ${speedType} type.`)
|
||||
}
|
||||
|
||||
return normalizeGasPrice(oracleGasPrice, factor)
|
||||
}
|
||||
|
||||
async function fetchGasPrice({ bridgeContract, oracleFn }) {
|
||||
let gasPrice = null
|
||||
try {
|
||||
gasPrice = await oracleFn()
|
||||
logger.debug({ gasPrice }, 'Gas price updated using the oracle')
|
||||
} catch (e) {
|
||||
logger.error(`Gas Price API is not available. ${e.message}`)
|
||||
|
||||
try {
|
||||
gasPrice = await bridgeContract.methods.gasPrice().call()
|
||||
logger.debug({ gasPrice }, 'Gas price updated using the contracts')
|
||||
} catch (e) {
|
||||
logger.error(`There was a problem getting the gas price from the contract. ${e.message}`)
|
||||
}
|
||||
}
|
||||
return gasPrice
|
||||
}
|
||||
|
||||
let fetchGasPriceInterval = null
|
||||
|
||||
const fetchGasPrice = async (speedType, factor, bridgeContract, oracleFetchFn) => {
|
||||
const contractOptions = { logger }
|
||||
const oracleOptions = { speedType, factor, limits: GAS_PRICE_BOUNDARIES, logger }
|
||||
cachedGasPrice =
|
||||
(await gasPriceFromOracle(oracleFetchFn, oracleOptions)) ||
|
||||
(await gasPriceFromContract(bridgeContract, contractOptions)) ||
|
||||
cachedGasPrice
|
||||
return cachedGasPrice
|
||||
}
|
||||
|
||||
async function start(chainId) {
|
||||
clearInterval(fetchGasPriceInterval)
|
||||
|
||||
@ -108,13 +73,10 @@ async function start(chainId) {
|
||||
throw new Error(`Unrecognized chainId '${chainId}'`)
|
||||
}
|
||||
|
||||
fetchGasPriceInterval = setIntervalAndRun(async () => {
|
||||
const gasPrice = await fetchGasPrice({
|
||||
bridgeContract,
|
||||
oracleFn: () => fetchGasPriceFromOracle(oracleUrl, speedType, factor)
|
||||
})
|
||||
cachedGasPrice = gasPrice || cachedGasPrice
|
||||
}, updateInterval)
|
||||
fetchGasPriceInterval = setIntervalAndRun(
|
||||
() => fetchGasPrice(speedType, factor, bridgeContract, () => fetch(oracleUrl)),
|
||||
updateInterval
|
||||
)
|
||||
}
|
||||
|
||||
function getPrice() {
|
||||
@ -123,8 +85,6 @@ function getPrice() {
|
||||
|
||||
module.exports = {
|
||||
start,
|
||||
fetchGasPrice,
|
||||
getPrice,
|
||||
gasPriceWithinLimits,
|
||||
normalizeGasPrice
|
||||
fetchGasPrice
|
||||
}
|
||||
|
@ -1,80 +1,9 @@
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const proxyquire = require('proxyquire').noPreserveCache()
|
||||
const Web3Utils = require('web3-utils')
|
||||
const { fetchGasPrice, gasPriceWithinLimits, normalizeGasPrice } = require('../src/services/gasPrice')
|
||||
const { DEFAULT_UPDATE_INTERVAL, GAS_PRICE_BOUNDARIES } = require('../src/utils/constants')
|
||||
const { DEFAULT_UPDATE_INTERVAL } = require('../src/utils/constants')
|
||||
|
||||
describe('gasPrice', () => {
|
||||
describe('fetchGasPrice', () => {
|
||||
beforeEach(() => {
|
||||
sinon.stub(console, 'error')
|
||||
})
|
||||
afterEach(() => {
|
||||
console.error.restore()
|
||||
})
|
||||
|
||||
it('should fetch the gas price from the oracle by default', async () => {
|
||||
// given
|
||||
const oracleFnMock = () => Promise.resolve('1')
|
||||
const bridgeContractMock = {
|
||||
methods: {
|
||||
gasPrice: {
|
||||
call: sinon.stub().returns(Promise.resolve('2'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// when
|
||||
const gasPrice = await fetchGasPrice({
|
||||
bridgeContract: bridgeContractMock,
|
||||
oracleFn: oracleFnMock
|
||||
})
|
||||
|
||||
// then
|
||||
expect(gasPrice).to.equal('1')
|
||||
})
|
||||
it('should fetch the gas price from the contract if the oracle fails', async () => {
|
||||
// given
|
||||
const oracleFnMock = () => Promise.reject(new Error('oracle failed'))
|
||||
const bridgeContractMock = {
|
||||
methods: {
|
||||
gasPrice: sinon.stub().returns({
|
||||
call: sinon.stub().returns(Promise.resolve('2'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// when
|
||||
const gasPrice = await fetchGasPrice({
|
||||
bridgeContract: bridgeContractMock,
|
||||
oracleFn: oracleFnMock
|
||||
})
|
||||
|
||||
// then
|
||||
expect(gasPrice).to.equal('2')
|
||||
})
|
||||
it('should return null if both the oracle and the contract fail', async () => {
|
||||
// given
|
||||
const oracleFnMock = () => Promise.reject(new Error('oracle failed'))
|
||||
const bridgeContractMock = {
|
||||
methods: {
|
||||
gasPrice: sinon.stub().returns({
|
||||
call: sinon.stub().returns(Promise.reject(new Error('contract failed')))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// when
|
||||
const gasPrice = await fetchGasPrice({
|
||||
bridgeContract: bridgeContractMock,
|
||||
oracleFn: oracleFnMock
|
||||
})
|
||||
|
||||
// then
|
||||
expect(gasPrice).to.equal(null)
|
||||
})
|
||||
})
|
||||
describe('start', () => {
|
||||
const utils = { setIntervalAndRun: sinon.spy() }
|
||||
beforeEach(() => {
|
||||
@ -131,101 +60,104 @@ describe('gasPrice', () => {
|
||||
expect(utils.setIntervalAndRun.args[0][1]).to.equal(DEFAULT_UPDATE_INTERVAL)
|
||||
})
|
||||
})
|
||||
describe('gasPriceWithinLimits', () => {
|
||||
it('should return gas price if gas price is between boundaries', () => {
|
||||
|
||||
describe('fetching gas price', () => {
|
||||
const utils = { setIntervalAndRun: () => {} }
|
||||
|
||||
it('should fall back to default if contract and oracle/supplier are not working', async () => {
|
||||
// given
|
||||
const minGasPrice = 1
|
||||
const middleGasPrice = 10
|
||||
const maxGasPrice = 250
|
||||
process.env.HOME_GAS_PRICE_FALLBACK = '101000000000'
|
||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
||||
await gasPrice.start('home')
|
||||
|
||||
// when
|
||||
const minGasPriceWithinLimits = gasPriceWithinLimits(minGasPrice)
|
||||
const middleGasPriceWithinLimits = gasPriceWithinLimits(middleGasPrice)
|
||||
const maxGasPriceWithinLimits = gasPriceWithinLimits(maxGasPrice)
|
||||
await gasPrice.fetchGasPrice('standard', 1, null, null)
|
||||
|
||||
// then
|
||||
expect(minGasPriceWithinLimits).to.equal(minGasPrice)
|
||||
expect(middleGasPriceWithinLimits).to.equal(middleGasPrice)
|
||||
expect(maxGasPriceWithinLimits).to.equal(maxGasPrice)
|
||||
expect(gasPrice.getPrice()).to.equal('101000000000')
|
||||
})
|
||||
it('should return min limit if gas price is below min boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 0.5
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice)
|
||||
it('should fetch gas from oracle/supplier', async () => {
|
||||
// given
|
||||
process.env.HOME_GAS_PRICE_FALLBACK = '101000000000'
|
||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
||||
await gasPrice.start('home')
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MIN)
|
||||
const oracleFetchFn = () => ({
|
||||
json: () => ({
|
||||
standard: '103'
|
||||
})
|
||||
})
|
||||
|
||||
// when
|
||||
await gasPrice.fetchGasPrice('standard', 1, null, oracleFetchFn)
|
||||
|
||||
// then
|
||||
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
||||
})
|
||||
it('should return max limit if gas price is above max boundary', () => {
|
||||
// Given
|
||||
const initialGasPrice = 260
|
||||
|
||||
// When
|
||||
const gasPrice = gasPriceWithinLimits(initialGasPrice)
|
||||
it('should fetch gas from contract', async () => {
|
||||
// given
|
||||
process.env.HOME_GAS_PRICE_FALLBACK = '101000000000'
|
||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
||||
await gasPrice.start('home')
|
||||
|
||||
// Then
|
||||
expect(gasPrice).to.equal(GAS_PRICE_BOUNDARIES.MAX)
|
||||
const bridgeContractMock = {
|
||||
methods: {
|
||||
gasPrice: sinon.stub().returns({
|
||||
call: sinon.stub().returns(Promise.resolve('102000000000'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// when
|
||||
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, null)
|
||||
|
||||
// then
|
||||
expect(gasPrice.getPrice().toString()).to.equal('102000000000')
|
||||
})
|
||||
})
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
it('should fetch the gas price from the oracle first', async () => {
|
||||
// given
|
||||
process.env.HOME_GAS_PRICE_FALLBACK = '101000000000'
|
||||
const gasPrice = proxyquire('../src/services/gasPrice', { '../utils/utils': utils })
|
||||
await gasPrice.start('home')
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
const bridgeContractMock = {
|
||||
methods: {
|
||||
gasPrice: sinon.stub().returns({
|
||||
call: sinon.stub().returns(Promise.resolve('102000000000'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const oracleFetchFn = () => ({
|
||||
json: () => ({
|
||||
standard: '103'
|
||||
})
|
||||
})
|
||||
|
||||
// when
|
||||
await gasPrice.fetchGasPrice('standard', 1, bridgeContractMock, oracleFetchFn)
|
||||
|
||||
// then
|
||||
expect(gasPrice.getPrice().toString()).to.equal('103000000000')
|
||||
})
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
it('log errors using the logger', async () => {
|
||||
// given
|
||||
const fakeLogger = { error: sinon.spy() }
|
||||
const gasPrice = proxyquire('../src/services/gasPrice', {
|
||||
'../utils/utils': utils,
|
||||
'../services/logger': { child: () => fakeLogger }
|
||||
})
|
||||
await gasPrice.start('home')
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('20000000000')
|
||||
})
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
// when
|
||||
await gasPrice.fetchGasPrice('standard', 1, null, null)
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal('30000000000')
|
||||
})
|
||||
it('should respect gas price max limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 200
|
||||
const factor = 4
|
||||
const maxInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MAX.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(maxInWei)
|
||||
})
|
||||
it('should respect gas price min limit', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 1
|
||||
const factor = 0.01
|
||||
const minInWei = Web3Utils.toWei(GAS_PRICE_BOUNDARIES.MIN.toString(), 'gwei')
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).to.equal(minInWei)
|
||||
// then
|
||||
expect(fakeLogger.error.calledTwice).to.equal(true) // two errors
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { observable, computed } from 'mobx'
|
||||
import { toHex } from 'web3-utils'
|
||||
import { fetchGasPrice, fetchGasPriceFromOracle } from './utils/gas'
|
||||
import { gasPriceFromOracle } from '../../../commons'
|
||||
|
||||
const HOME_GAS_PRICE_FALLBACK = process.env.REACT_APP_HOME_GAS_PRICE_FALLBACK
|
||||
const HOME_GAS_PRICE_ORACLE_URL = process.env.REACT_APP_HOME_GAS_PRICE_ORACLE_URL
|
||||
@ -50,11 +50,9 @@ class GasPriceStore {
|
||||
this.factor = Number(FOREIGN_GAS_PRICE_FACTOR) || DEFAULT_GAS_PRICE_FACTOR
|
||||
}
|
||||
|
||||
const newGasPrice = await fetchGasPrice({
|
||||
oracleFn: () => fetchGasPriceFromOracle(this.oracleUrl, this.speedType, this.factor)
|
||||
})
|
||||
const oracleOptions = { speedType: this.speedType, factor: this.factor, logger: console }
|
||||
this.gasPrice = (await gasPriceFromOracle(() => fetch(this.oracleUrl), oracleOptions)) || this.gasPrice
|
||||
|
||||
this.gasPrice = newGasPrice || this.gasPrice
|
||||
setTimeout(() => this.updateGasPrice(), this.updateInterval)
|
||||
}
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
import { normalizeGasPrice } from '../gas'
|
||||
|
||||
describe('normalizeGasPrice', () => {
|
||||
it('should work with oracle gas price in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 30
|
||||
const factor = 1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).toEqual('30000000000')
|
||||
})
|
||||
it('should work with oracle gas price not in gwei', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 300
|
||||
const factor = 0.1
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).toEqual('30000000000')
|
||||
})
|
||||
it('should increase gas price value from oracle', () => {
|
||||
// Given
|
||||
const oracleGasPrice = 20
|
||||
const factor = 1.5
|
||||
|
||||
// When
|
||||
const result = normalizeGasPrice(oracleGasPrice, factor)
|
||||
|
||||
// Then
|
||||
expect(result).toEqual('30000000000')
|
||||
})
|
||||
})
|
@ -1,31 +0,0 @@
|
||||
const { toWei } = require('web3-utils')
|
||||
|
||||
export async function fetchGasPrice({ oracleFn }) {
|
||||
let gasPrice = null
|
||||
try {
|
||||
gasPrice = await oracleFn()
|
||||
} catch (e) {
|
||||
if (!e.message.includes('Gas Price Oracle url not defined')) {
|
||||
console.error(`Gas Price API is not available. ${e.message}`)
|
||||
}
|
||||
}
|
||||
return gasPrice
|
||||
}
|
||||
|
||||
export async function fetchGasPriceFromOracle(oracleUrl, speedType, factor) {
|
||||
if (!oracleUrl) {
|
||||
throw new Error(`Gas Price Oracle url not defined`)
|
||||
}
|
||||
const response = await fetch(oracleUrl)
|
||||
const json = await response.json()
|
||||
const gasPrice = json[speedType]
|
||||
if (!gasPrice) {
|
||||
throw new Error(`Response from Oracle didn't include gas price for ${speedType} type.`)
|
||||
}
|
||||
return normalizeGasPrice(gasPrice, factor)
|
||||
}
|
||||
|
||||
export function normalizeGasPrice(oracleGasPrice, factor) {
|
||||
const gasPrice = oracleGasPrice * factor
|
||||
return toWei(gasPrice.toFixed(2).toString(), 'gwei')
|
||||
}
|
Loading…
Reference in New Issue
Block a user