Use 0x gas api instead of eth gas station api [TKR-502] (#532)
* Use 0x gas api instead of eth gas station api * Add integration test for `ProtocolFeeUtils` * Update CHANGELOG.json
This commit is contained in:
parent
3ef5de93bb
commit
d6d79e51e7
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "16.65.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Use 0x gas api instead of eth gas station api",
|
||||||
|
"pr": 532
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "16.64.0",
|
"version": "16.64.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -109,6 +109,7 @@
|
|||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
|
"msw": "^0.44.2",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"nyc": "^11.0.1",
|
"nyc": "^11.0.1",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||||
} from './utils/market_operation_utils/constants';
|
} from './utils/market_operation_utils/constants';
|
||||||
|
|
||||||
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
|
const ZERO_EX_GAS_API_URL = 'https://gas.api.0x.org/source/median';
|
||||||
const NULL_BYTES = '0x';
|
const NULL_BYTES = '0x';
|
||||||
const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000';
|
const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000';
|
||||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
@ -48,7 +48,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
|||||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||||
...DEFAULT_ORDER_PRUNER_OPTS,
|
...DEFAULT_ORDER_PRUNER_OPTS,
|
||||||
samplerGasLimit: 500e6,
|
samplerGasLimit: 500e6,
|
||||||
ethGasStationUrl: ETH_GAS_STATION_API_URL,
|
zeroExGasApiUrl: ZERO_EX_GAS_API_URL,
|
||||||
rfqt: {
|
rfqt: {
|
||||||
integratorsWhitelist: [],
|
integratorsWhitelist: [],
|
||||||
makerAssetOfferings: {},
|
makerAssetOfferings: {},
|
||||||
@ -99,7 +99,7 @@ export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
|
|||||||
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
|
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
ETH_GAS_STATION_API_URL,
|
ZERO_EX_GAS_API_URL,
|
||||||
PROTOCOL_FEE_MULTIPLIER,
|
PROTOCOL_FEE_MULTIPLIER,
|
||||||
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
|
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
|
||||||
NULL_BYTES,
|
NULL_BYTES,
|
||||||
|
@ -109,7 +109,7 @@ export class SwapQuoter {
|
|||||||
};
|
};
|
||||||
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
||||||
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||||
options.ethGasStationUrl,
|
options.zeroExGasApiUrl,
|
||||||
);
|
);
|
||||||
// Allow the sampler bytecode to be overwritten using geths override functionality
|
// Allow the sampler bytecode to be overwritten using geths override functionality
|
||||||
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
||||||
|
@ -337,7 +337,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
|||||||
contractAddresses?: AssetSwapperContractAddresses;
|
contractAddresses?: AssetSwapperContractAddresses;
|
||||||
samplerGasLimit?: number;
|
samplerGasLimit?: number;
|
||||||
multiBridgeAddress?: string;
|
multiBridgeAddress?: string;
|
||||||
ethGasStationUrl?: string;
|
zeroExGasApiUrl?: string;
|
||||||
rfqt?: SwapQuoterRfqOpts;
|
rfqt?: SwapQuoterRfqOpts;
|
||||||
samplerOverrides?: SamplerOverrides;
|
samplerOverrides?: SamplerOverrides;
|
||||||
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
||||||
|
@ -6,28 +6,36 @@ import { SwapQuoterError } from '../types';
|
|||||||
|
|
||||||
const MAX_ERROR_COUNT = 5;
|
const MAX_ERROR_COUNT = 5;
|
||||||
|
|
||||||
|
interface GasOracleResponse {
|
||||||
|
result: {
|
||||||
|
// gas price in wei
|
||||||
|
fast: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export class ProtocolFeeUtils {
|
export class ProtocolFeeUtils {
|
||||||
private static _instance: ProtocolFeeUtils;
|
private static _instance: ProtocolFeeUtils;
|
||||||
private readonly _ethGasStationUrl!: string;
|
private readonly _zeroExGasApiUrl: string;
|
||||||
private readonly _gasPriceHeart: any;
|
private readonly _gasPriceHeart: any;
|
||||||
private _gasPriceEstimation: BigNumber = constants.ZERO_AMOUNT;
|
private _gasPriceEstimation: BigNumber = constants.ZERO_AMOUNT;
|
||||||
private _errorCount: number = 0;
|
private _errorCount: number = 0;
|
||||||
|
|
||||||
public static getInstance(
|
public static getInstance(
|
||||||
gasPricePollingIntervalInMs: number,
|
gasPricePollingIntervalInMs: number,
|
||||||
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
|
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
|
||||||
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
|
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
|
||||||
): ProtocolFeeUtils {
|
): ProtocolFeeUtils {
|
||||||
if (!ProtocolFeeUtils._instance) {
|
if (!ProtocolFeeUtils._instance) {
|
||||||
ProtocolFeeUtils._instance = new ProtocolFeeUtils(
|
ProtocolFeeUtils._instance = new ProtocolFeeUtils(
|
||||||
gasPricePollingIntervalInMs,
|
gasPricePollingIntervalInMs,
|
||||||
ethGasStationUrl,
|
zeroExGasApiUrl,
|
||||||
initialGasPrice,
|
initialGasPrice,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return ProtocolFeeUtils._instance;
|
return ProtocolFeeUtils._instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @returns gas price (in wei) */
|
||||||
public async getGasPriceEstimationOrThrowAsync(shouldHardRefresh?: boolean): Promise<BigNumber> {
|
public async getGasPriceEstimationOrThrowAsync(shouldHardRefresh?: boolean): Promise<BigNumber> {
|
||||||
if (this._gasPriceEstimation.eq(constants.ZERO_AMOUNT)) {
|
if (this._gasPriceEstimation.eq(constants.ZERO_AMOUNT)) {
|
||||||
return this._getGasPriceFromGasStationOrThrowAsync();
|
return this._getGasPriceFromGasStationOrThrowAsync();
|
||||||
@ -48,27 +56,21 @@ export class ProtocolFeeUtils {
|
|||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
gasPricePollingIntervalInMs: number,
|
gasPricePollingIntervalInMs: number,
|
||||||
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
|
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
|
||||||
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
|
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
|
||||||
) {
|
) {
|
||||||
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
|
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
|
||||||
this._gasPriceEstimation = initialGasPrice;
|
this._gasPriceEstimation = initialGasPrice;
|
||||||
this._ethGasStationUrl = ethGasStationUrl;
|
this._zeroExGasApiUrl = zeroExGasApiUrl;
|
||||||
this._initializeHeartBeat();
|
this._initializeHeartBeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line: prefer-function-over-method
|
// tslint:disable-next-line: prefer-function-over-method
|
||||||
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
|
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(this._ethGasStationUrl);
|
const res = await fetch(this._zeroExGasApiUrl);
|
||||||
const gasInfo = await res.json();
|
const gasInfo: GasOracleResponse = await res.json();
|
||||||
// Eth Gas Station result is gwei * 10
|
const gasPriceWei = new BigNumber(gasInfo.result.fast);
|
||||||
// tslint:disable-next-line:custom-no-magic-numbers
|
|
||||||
const BASE_TEN = 10;
|
|
||||||
const gasPriceGwei = new BigNumber(gasInfo.fast / BASE_TEN);
|
|
||||||
// tslint:disable-next-line:custom-no-magic-numbers
|
|
||||||
const unit = new BigNumber(BASE_TEN).pow(9);
|
|
||||||
const gasPriceWei = unit.times(gasPriceGwei);
|
|
||||||
// Reset the error count to 0 once we have a successful response
|
// Reset the error count to 0 once we have a successful response
|
||||||
this._errorCount = 0;
|
this._errorCount = 0;
|
||||||
return gasPriceWei;
|
return gasPriceWei;
|
||||||
|
45
packages/asset-swapper/test/protocol_fee_utils_test.ts
Normal file
45
packages/asset-swapper/test/protocol_fee_utils_test.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import * as chai from 'chai';
|
||||||
|
import 'mocha';
|
||||||
|
import { rest } from 'msw';
|
||||||
|
import { setupServer } from 'msw/node';
|
||||||
|
|
||||||
|
import { ProtocolFeeUtils } from '../src';
|
||||||
|
|
||||||
|
import { chaiSetup } from './utils/chai_setup';
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const server = setupServer(
|
||||||
|
rest.get('https://mock-0x-gas-api.org/median', (_req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.json({
|
||||||
|
result: {
|
||||||
|
source: 'MEDIAN',
|
||||||
|
timestamp: 1659386474,
|
||||||
|
instant: 22000000000,
|
||||||
|
fast: 18848500000,
|
||||||
|
standard: 14765010000,
|
||||||
|
low: 13265000000,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('ProtocolFeeUtils', () => {
|
||||||
|
describe('getGasPriceEstimationOrThrowAsync', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
server.listen();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses fast gas price response correctly', async () => {
|
||||||
|
const utils = ProtocolFeeUtils.getInstance(420000, 'https://mock-0x-gas-api.org/median');
|
||||||
|
const gasPrice = await utils.getGasPriceEstimationOrThrowAsync();
|
||||||
|
expect(gasPrice.toNumber()).to.eq(18848500000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": ["@0x/tslint-config"],
|
"extends": ["@0x/tslint-config"],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"custom-no-magic-numbers": false,
|
||||||
"max-file-line-count": false,
|
"max-file-line-count": false,
|
||||||
"binary-expression-operand-order": false
|
"binary-expression-operand-order": false
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user