Skip to content

Commit

Permalink
Add GasFeeController (#494)
Browse files Browse the repository at this point in the history
Co-authored-by: ricky <ricky.miller@gmail.com>
Co-authored-by: Niranjana Binoy <43930900+NiranjanaBinoy@users.noreply.github.com>
Co-authored-by: Brad Decker <git@braddecker.dev>
  • Loading branch information
4 people authored Jul 7, 2021
1 parent 08cde66 commit e594293
Show file tree
Hide file tree
Showing 14 changed files with 972 additions and 1 deletion.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"eth-sig-util": "^3.0.0",
"ethereumjs-util": "^7.0.10",
"ethereumjs-wallet": "^1.0.1",
"ethjs-unit": "^0.1.6",
"ethjs-util": "^0.1.6",
"human-standard-collectible-abi": "^1.0.2",
"human-standard-token-abi": "^2.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/ComposableController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ describe('ComposableController', () => {
},
NetworkController: {
network: 'loading',
properties: { isEIP1559Compatible: false },
provider: { type: 'mainnet', chainId: NetworksChainId.mainnet },
},
PreferencesController: {
Expand Down Expand Up @@ -188,6 +189,7 @@ describe('ComposableController', () => {
ipfsGateway: 'https://ipfs.io/ipfs/',
lostIdentities: {},
network: 'loading',
properties: { isEIP1559Compatible: false },
provider: { type: 'mainnet', chainId: NetworksChainId.mainnet },
selectedAddress: '',
suggestedAssets: [],
Expand Down
2 changes: 2 additions & 0 deletions src/dependencies.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ declare module 'ethjs-provider-http';

declare module 'ethjs-util';

declare module 'ethjs-unit';

declare module 'human-standard-collectible-abi';

declare module 'human-standard-token-abi';
Expand Down
185 changes: 185 additions & 0 deletions src/gas/GasFeeController.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { stub } from 'sinon';
import nock from 'nock';
import { ControllerMessenger } from '../ControllerMessenger';
import {
GasFeeController,
GetGasFeeState,
GasFeeStateChange,
LegacyGasPriceEstimate,
} from './GasFeeController';

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';

function getRestrictedMessenger() {
const controllerMessenger = new ControllerMessenger<
GetGasFeeState,
GasFeeStateChange
>();
const messenger = controllerMessenger.getRestricted<
typeof name,
never,
never
>({
name,
});
return messenger;
}

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

beforeAll(() => {
nock.disableNetConnect();
});

afterAll(() => {
nock.enableNetConnect();
});

beforeEach(() => {
getChainId = jest.fn().mockImplementation(() => '0x1');
getCurrentNetworkLegacyGasAPICompatibility = jest
.fn()
.mockImplementation(() => false);
getIsEIP1559Compatible = jest
.fn()
.mockImplementation(() => Promise.resolve(true));
nock(TEST_GAS_FEE_API.replace('<chain_id>', '1'))
.get(/.+/u)
.reply(200, {
low: {
minWaitTimeEstimate: 60000,
maxWaitTimeEstimate: 600000,
suggestedMaxPriorityFeePerGas: '1',
suggestedMaxFeePerGas: '35',
},
medium: {
minWaitTimeEstimate: 15000,
maxWaitTimeEstimate: 60000,
suggestedMaxPriorityFeePerGas: '1.8',
suggestedMaxFeePerGas: '38',
},
high: {
minWaitTimeEstimate: 0,
maxWaitTimeEstimate: 15000,
suggestedMaxPriorityFeePerGas: '2',
suggestedMaxFeePerGas: '50',
},
estimatedBaseFee: '28',
})
.persist();

nock(TEST_LEGACY_FEE_API.replace('<chain_id>', '0x1'))
.get(/.+/u)
.reply(200, {
SafeGasPrice: '22',
ProposeGasPrice: '25',
FastGasPrice: '30',
})
.persist();

gasFeeController = new GasFeeController({
interval: 10000,
messenger: getRestrictedMessenger(),
getProvider: () => stub(),
getChainId,
legacyAPIEndpoint: TEST_LEGACY_FEE_API,
EIP1559APIEndpoint: TEST_GAS_FEE_API,
onNetworkStateChange: () => stub(),
getCurrentNetworkLegacyGasAPICompatibility,
getCurrentNetworkEIP1559Compatibility: getIsEIP1559Compatible, // change this for networkController.state.properties.isEIP1559Compatible ???
});
});

afterEach(() => {
nock.cleanAll();
gasFeeController.destroy();
});

it('should initialize', async () => {
expect(gasFeeController.name).toBe(name);
});

it('should getGasFeeEstimatesAndStartPolling', async () => {
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const result = await gasFeeController.getGasFeeEstimatesAndStartPolling(
undefined,
);
expect(result).toHaveLength(36);
expect(gasFeeController.state.gasFeeEstimates).toHaveProperty('low');
expect(gasFeeController.state.gasFeeEstimates).toHaveProperty('medium');
expect(gasFeeController.state.gasFeeEstimates).toHaveProperty('high');
expect(gasFeeController.state.gasFeeEstimates).toHaveProperty(
'estimatedBaseFee',
);
});

describe('when on any network supporting legacy gas estimation api', () => {
it('should _fetchGasFeeEstimateData', async () => {
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();
expect(estimates).toHaveProperty('gasFeeEstimates');
expect(
(gasFeeController.state.gasFeeEstimates as LegacyGasPriceEstimate).high,
).toBe('30');
});
});

describe('when on any network supporting EIP-1559', () => {
it('should _fetchGasFeeEstimateData', async () => {
getCurrentNetworkLegacyGasAPICompatibility.mockImplementation(() => true);
expect(gasFeeController.state.gasFeeEstimates).toStrictEqual({});
const estimates = await gasFeeController._fetchGasFeeEstimateData();
expect(estimates).toHaveProperty('gasFeeEstimates');
expect(gasFeeController.state.gasFeeEstimates).toHaveProperty(
'estimatedBaseFee',
);
});
});
});
Loading

0 comments on commit e594293

Please sign in to comment.