Skip to content

Commit

Permalink
make more configurable (#513)
Browse files Browse the repository at this point in the history
  • Loading branch information
brad-decker authored Jul 6, 2021
1 parent 2bdcd7d commit cc471dc
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 32 deletions.
65 changes: 55 additions & 10 deletions src/gas/GasFeeController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
GasFeeStateChange,
LegacyGasPriceEstimate,
} from './GasFeeController';
import { EXTERNAL_GAS_PRICES_API_URL } from './gas-util';

const GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/';
const TEST_GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/<chain_id>';
const TEST_LEGACY_FEE_API = 'https://test/<chain_id>';

const name = 'GasFeeController';

Expand All @@ -30,8 +30,9 @@ function getRestrictedMessenger() {

describe('GasFeeController', () => {
let gasFeeController: GasFeeController;
let getIsMainnet: jest.Mock<boolean>;
let getCurrentNetworkLegacyGasAPICompatibility: jest.Mock<boolean>;
let getIsEIP1559Compatible: jest.Mock<Promise<boolean>>;
let getChainId: jest.Mock<`0x${string}` | `${number}` | number>;

beforeAll(() => {
nock.disableNetConnect();
Expand All @@ -42,11 +43,14 @@ describe('GasFeeController', () => {
});

beforeEach(() => {
getIsMainnet = jest.fn().mockImplementation(() => false);
getChainId = jest.fn().mockImplementation(() => '0x1');
getCurrentNetworkLegacyGasAPICompatibility = jest
.fn()
.mockImplementation(() => false);
getIsEIP1559Compatible = jest
.fn()
.mockImplementation(() => Promise.resolve(true));
nock(GAS_FEE_API)
nock(TEST_GAS_FEE_API.replace('<chain_id>', '1'))
.get(/.+/u)
.reply(200, {
low: {
Expand All @@ -71,7 +75,7 @@ describe('GasFeeController', () => {
})
.persist();

nock(EXTERNAL_GAS_PRICES_API_URL)
nock(TEST_LEGACY_FEE_API.replace('<chain_id>', '0x1'))
.get(/.+/u)
.reply(200, {
SafeGasPrice: '22',
Expand All @@ -84,8 +88,11 @@ describe('GasFeeController', () => {
interval: 10000,
messenger: getRestrictedMessenger(),
getProvider: () => stub(),
getChainId,
legacyAPIEndpoint: TEST_LEGACY_FEE_API,
EIP1559APIEndpoint: TEST_GAS_FEE_API,
onNetworkStateChange: () => stub(),
getIsMainnet,
getCurrentNetworkLegacyGasAPICompatibility,
getCurrentNetworkEIP1559Compatibility: getIsEIP1559Compatible, // change this for networkController.state.properties.isEIP1559Compatible ???
});
});
Expand Down Expand Up @@ -113,9 +120,47 @@ describe('GasFeeController', () => {
);
});

describe('when on mainnet before london', () => {
describe('when on any network supporting legacy gas estimation api', () => {
it('should _fetchGasFeeEstimateData', async () => {
getIsMainnet.mockImplementation(() => true);
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
getIsEIP1559Compatible.mockImplementation(() => Promise.resolve(false));
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
expect(estimates).toHaveProperty('gasFeeEstimates');
expect(
(gasFeeController.state.gasFeeEstimates as LegacyGasPriceEstimate).high,
).toBe('30');
});
});

describe('getChainId', () => {
it('should work with a number input', async () => {
getChainId.mockImplementation(() => 1);
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
getIsEIP1559Compatible.mockImplementation(() => Promise.resolve(false));
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
expect(estimates).toHaveProperty('gasFeeEstimates');
expect(
(gasFeeController.state.gasFeeEstimates as LegacyGasPriceEstimate).high,
).toBe('30');
});

it('should work with a hexstring input', async () => {
getChainId.mockImplementation(() => '0x1');
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
getIsEIP1559Compatible.mockImplementation(() => Promise.resolve(false));
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
expect(estimates).toHaveProperty('gasFeeEstimates');
expect(
(gasFeeController.state.gasFeeEstimates as LegacyGasPriceEstimate).high,
).toBe('30');
});

it('should work with a numeric string input', async () => {
getChainId.mockImplementation(() => '1');
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
getIsEIP1559Compatible.mockImplementation(() => Promise.resolve(false));
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
Expand All @@ -128,7 +173,7 @@ describe('GasFeeController', () => {

describe('when on any network supporting EIP-1559', () => {
it('should _fetchGasFeeEstimateData', async () => {
getIsMainnet.mockImplementation(() => true);
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
expect(estimates).toHaveProperty('gasFeeEstimates');
Expand Down
44 changes: 36 additions & 8 deletions src/gas/GasFeeController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Patch } from 'immer';

import EthQuery from 'eth-query';
import { v1 as random } from 'uuid';
import { isHexString } from 'ethereumjs-util';
import { BaseController } from '../BaseControllerV2';
import { safelyExecute } from '../util';
import type { RestrictedControllerMessenger } from '../ControllerMessenger';
Expand All @@ -16,6 +17,9 @@ import {
calculateTimeEstimate,
} from './gas-util';

const GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/';
export const LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;

export type unknownString = 'unknown';

// Fee Market describes the way gas is set after the london hardfork, and was
Expand Down Expand Up @@ -197,6 +201,10 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {

private pollTokens: Set<string>;

private legacyAPIEndpoint: string;

private EIP1559APIEndpoint: string;

private fetchGasEstimates;

private fetchEthGasPriceEstimate;
Expand All @@ -205,9 +213,11 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {

private getCurrentNetworkEIP1559Compatibility;

private getCurrentNetworkLegacyGasAPICompatibility;

private getCurrentAccountEIP1559Compatibility;

private getIsMainnet;
private getChainId;

private ethQuery: any;

Expand All @@ -224,9 +234,12 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
fetchLegacyGasPriceEstimates = defaultFetchLegacyGasPriceEstimates,
getCurrentNetworkEIP1559Compatibility,
getCurrentAccountEIP1559Compatibility,
getIsMainnet,
getChainId,
getCurrentNetworkLegacyGasAPICompatibility,
getProvider,
onNetworkStateChange,
legacyAPIEndpoint = LEGACY_GAS_PRICES_API_URL,
EIP1559APIEndpoint = GAS_FEE_API,
}: {
interval?: number;
messenger: RestrictedControllerMessenger<
Expand All @@ -241,10 +254,13 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
fetchEthGasPriceEstimate?: typeof defaultFetchEthGasPriceEstimate;
fetchLegacyGasPriceEstimates?: typeof defaultFetchLegacyGasPriceEstimates;
getCurrentNetworkEIP1559Compatibility: () => Promise<boolean>;
getCurrentNetworkLegacyGasAPICompatibility: () => boolean;
getCurrentAccountEIP1559Compatibility?: () => boolean;
getIsMainnet: () => boolean;
getChainId: () => `0x${string}` | `${number}` | number;
getProvider: () => NetworkController['provider'];
onNetworkStateChange: (listener: (state: NetworkState) => void) => void;
legacyAPIEndpoint?: string;
EIP1559APIEndpoint?: string;
}) {
super({
name,
Expand All @@ -258,8 +274,11 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
this.fetchLegacyGasPriceEstimates = fetchLegacyGasPriceEstimates;
this.pollTokens = new Set();
this.getCurrentNetworkEIP1559Compatibility = getCurrentNetworkEIP1559Compatibility;
this.getCurrentNetworkLegacyGasAPICompatibility = getCurrentNetworkLegacyGasAPICompatibility;
this.getCurrentAccountEIP1559Compatibility = getCurrentAccountEIP1559Compatibility;
this.getIsMainnet = getIsMainnet;
this.EIP1559APIEndpoint = EIP1559APIEndpoint;
this.legacyAPIEndpoint = legacyAPIEndpoint;
this.getChainId = getChainId;

const provider = getProvider();
this.ethQuery = new EthQuery(provider);
Expand Down Expand Up @@ -294,7 +313,12 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
*/
async _fetchGasFeeEstimateData(): Promise<GasFeeState | undefined> {
let isEIP1559Compatible;
const isMainnet = this.getIsMainnet();
const isLegacyGasAPICompatible = this.getCurrentNetworkLegacyGasAPICompatibility();

let chainId = this.getChainId();
if (typeof chainId === 'string' && isHexString(chainId)) {
chainId = parseInt(chainId, 16);
}
try {
isEIP1559Compatible = await this.getEIP1559Compatibility();
} catch (e) {
Expand All @@ -310,7 +334,9 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {

try {
if (isEIP1559Compatible) {
const estimates = await this.fetchGasEstimates();
const estimates = await this.fetchGasEstimates(
this.EIP1559APIEndpoint.replace('<chain_id>', `${chainId}`),
);
const {
suggestedMaxPriorityFeePerGas,
suggestedMaxFeePerGas,
Expand All @@ -324,8 +350,10 @@ export class GasFeeController extends BaseController<typeof name, GasFeeState> {
estimatedGasFeeTimeBounds,
gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET,
};
} else if (isMainnet) {
const estimates = await this.fetchLegacyGasPriceEstimates();
} else if (isLegacyGasAPICompatible) {
const estimates = await this.fetchLegacyGasPriceEstimates(
this.legacyAPIEndpoint.replace('<chain_id>', `${chainId}`),
);
newState = {
gasFeeEstimates: estimates,
estimatedGasFeeTimeBounds: {},
Expand Down
11 changes: 5 additions & 6 deletions src/gas/gas-util.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import nock from 'nock';
import {
EXTERNAL_GAS_PRICES_API_URL,
fetchLegacyGasPriceEstimates,
} from './gas-util';
import { fetchLegacyGasPriceEstimates } from './gas-util';

describe('gas utils', () => {
describe('fetchLegacyGasPriceEstimates', () => {
it('should fetch external gasPrices and return high/medium/low', async () => {
const scope = nock(EXTERNAL_GAS_PRICES_API_URL)
const scope = nock('https://not-a-real-url/')
.get(/.+/u)
.reply(200, {
SafeGasPrice: '22',
ProposeGasPrice: '25',
FastGasPrice: '30',
})
.persist();
const result = await fetchLegacyGasPriceEstimates();
const result = await fetchLegacyGasPriceEstimates(
'https://not-a-real-url/',
);
expect(result).toMatchObject({
high: '30',
medium: '25',
Expand Down
15 changes: 7 additions & 8 deletions src/gas/gas-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ import {
LegacyGasPriceEstimate,
} from './GasFeeController';

const GAS_FEE_API = 'https://mock-gas-server.herokuapp.com/';
export const EXTERNAL_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;

export async function fetchGasEstimates(): Promise<GasFeeEstimates> {
return await handleFetch(GAS_FEE_API);
export async function fetchGasEstimates(url: string): Promise<GasFeeEstimates> {
return await handleFetch(url);
}

/**
* Hit the legacy MetaSwaps gasPrices estimate api and return the low, medium
* high values from that API.
*/
export async function fetchLegacyGasPriceEstimates(): Promise<LegacyGasPriceEstimate> {
const result = await handleFetch(EXTERNAL_GAS_PRICES_API_URL, {
referrer: EXTERNAL_GAS_PRICES_API_URL,
export async function fetchLegacyGasPriceEstimates(
url: string,
): Promise<LegacyGasPriceEstimate> {
const result = await handleFetch(url, {
referrer: url,
referrerPolicy: 'no-referrer-when-downgrade',
method: 'GET',
mode: 'cors',
Expand Down

0 comments on commit cc471dc

Please sign in to comment.