Skip to content

Commit

Permalink
Fix layer 1 gas fee polling (#4149)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewwalsh0 authored Apr 12, 2024
1 parent bfe71c4 commit ef9b0a1
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 151 deletions.
8 changes: 4 additions & 4 deletions packages/transaction-controller/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 94.1,
functions: 98.56,
lines: 99.03,
statements: 99.04,
branches: 93.92,
functions: 98.61,
lines: 98.96,
statements: 98.97,
},
},

Expand Down
38 changes: 16 additions & 22 deletions packages/transaction-controller/src/TransactionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ jest.mock('./utils/gas-fees');
jest.mock('./utils/swaps');
jest.mock('./utils/layer1-gas-fee-flow');
jest.mock('./utils/simulation');

jest.mock('uuid');

// TODO: Replace `any` with type
Expand Down Expand Up @@ -477,6 +476,9 @@ describe('TransactionController', () => {
const lineaGasFeeFlowClassMock = jest.mocked(LineaGasFeeFlow);
const gasFeePollerClassMock = jest.mocked(GasFeePoller);
const getSimulationDataMock = jest.mocked(getSimulationData);
const getTransactionLayer1GasFeeMock = jest.mocked(
getTransactionLayer1GasFee,
);

let mockEthQuery: EthQuery;
let getNonceLockSpy: jest.Mock;
Expand Down Expand Up @@ -4449,7 +4451,7 @@ describe('TransactionController', () => {

expect(signSpy).toHaveBeenCalledTimes(1);

expect(updateTransactionSpy).toHaveBeenCalledTimes(3);
expect(updateTransactionSpy).toHaveBeenCalledTimes(2);
expect(updateTransactionSpy).toHaveBeenCalledWith(
expect.objectContaining({
txParams: expect.objectContaining(paramsMock),
Expand Down Expand Up @@ -4493,7 +4495,7 @@ describe('TransactionController', () => {
await wait(0);

expect(signSpy).toHaveBeenCalledTimes(1);
expect(updateTransactionSpy).toHaveBeenCalledTimes(2);
expect(updateTransactionSpy).toHaveBeenCalledTimes(1);
});

it('adds a transaction, signs and skips publish the transaction', async () => {
Expand All @@ -4519,7 +4521,7 @@ describe('TransactionController', () => {

expect(signSpy).toHaveBeenCalledTimes(1);

expect(updateTransactionSpy).toHaveBeenCalledTimes(3);
expect(updateTransactionSpy).toHaveBeenCalledTimes(2);
expect(updateTransactionSpy).toHaveBeenCalledWith(
expect.objectContaining({
txParams: expect.objectContaining(paramsMock),
Expand Down Expand Up @@ -5659,30 +5661,22 @@ describe('TransactionController', () => {
});

describe('getLayer1GasFee', () => {
it('calls getLayer1GasFee with the correct parameters', async () => {
it('calls getTransactionLayer1GasFee with the correct parameters', async () => {
const chainIdMock = '0x1';
const networkClientIdMock = 'mainnet';
const transactionParamsMock = {
from: ACCOUNT_MOCK,
to: ACCOUNT_2_MOCK,
gas: '0x0',
gasPrice: '0x50fd51da',
value: '0x0',
};
const layer1GasFeeMock = '0x12356';
(getTransactionLayer1GasFee as jest.Mock).mockResolvedValueOnce(
layer1GasFeeMock,
);

getTransactionLayer1GasFeeMock.mockResolvedValueOnce(layer1GasFeeMock);

const { controller } = setupController();

expect(
await controller.getLayer1GasFee(
chainIdMock,
networkClientIdMock,
transactionParamsMock,
),
).toBe(layer1GasFeeMock);
const result = await controller.getLayer1GasFee({
transactionParams: TRANSACTION_META_MOCK.txParams,
chainId: chainIdMock,
networkClientId: networkClientIdMock,
});

expect(result).toBe(layer1GasFeeMock);
expect(getTransactionLayer1GasFee).toHaveBeenCalledTimes(1);
});
});
Expand Down
153 changes: 105 additions & 48 deletions packages/transaction-controller/src/TransactionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import type {
SecurityAlertResponse,
GasFeeFlow,
SimulationData,
GasFeeEstimates,
} from './types';
import {
TransactionEnvelopeType,
Expand Down Expand Up @@ -875,8 +876,9 @@ export class TransactionController extends BaseController<
},
});

gasFeePoller.hub.on('transaction-updated', (transactionMeta) =>
this.#updateTransactionInternal(transactionMeta, { skipHistory: true }),
gasFeePoller.hub.on(
'transaction-updated',
this.#onGasFeePollerTransactionUpdate.bind(this),
);

// when transactionsController state changes
Expand Down Expand Up @@ -1553,10 +1555,12 @@ export class TransactionController extends BaseController<
* @param note - A note or update reason to include in the transaction history.
*/
updateTransaction(transactionMeta: TransactionMeta, note: string) {
this.#updateTransactionInternal(transactionMeta, {
note,
skipHistory: this.isHistoryDisabled,
});
const { id: transactionId } = transactionMeta;

this.#updateTransactionInternal(
{ transactionId, note, skipHistory: this.isHistoryDisabled },
() => ({ ...transactionMeta }),
);
}

/**
Expand Down Expand Up @@ -2259,31 +2263,35 @@ export class TransactionController extends BaseController<
}

/**
* Utility method to get the layer 1 gas fee for given transaction params.
* Determine the layer 1 gas fee for the given transaction parameters.
*
* @param chainId - Estimated transaction chainId.
* @param networkClientId - Estimated transaction networkClientId.
* @param transactionParams - The transaction params to estimate layer 1 gas fee for.
* @param request - The request object.
* @param request.transactionParams - The transaction parameters to estimate the layer 1 gas fee for.
* @param request.chainId - The ID of the chain where the transaction will be executed.
* @param request.networkClientId - The ID of a specific network client to process the transaction.
*/
async getLayer1GasFee(
chainId: Hex,
networkClientId: NetworkClientId,
transactionParams: TransactionParams,
): Promise<Hex | undefined> {
async getLayer1GasFee({
transactionParams,
chainId,
networkClientId,
}: {
transactionParams: TransactionParams;
chainId?: Hex;
networkClientId?: NetworkClientId;
}): Promise<Hex | undefined> {
const provider = this.#multichainTrackingHelper.getProvider({
networkClientId,
chainId,
});

const layer1GasFee = await getTransactionLayer1GasFee({
return await getTransactionLayer1GasFee({
layer1GasFeeFlows: this.layer1GasFeeFlows,
provider,
transactionMeta: {
txParams: transactionParams,
chainId,
} as TransactionMeta,
});
return layer1GasFee;
}

private async signExternalTransaction(
Expand Down Expand Up @@ -3532,38 +3540,51 @@ export class TransactionController extends BaseController<
}

#updateTransactionInternal(
transactionMeta: TransactionMeta,
{ note, skipHistory }: { note?: string; skipHistory?: boolean },
{
transactionId,
note,
skipHistory,
}: { transactionId: string; note?: string; skipHistory?: boolean },
callback: (transactionMeta: TransactionMeta) => TransactionMeta | void,
) {
const normalizedTransaction = {
...transactionMeta,
txParams: normalizeTransactionParams(transactionMeta.txParams),
};
let updatedTransactionParams: (keyof TransactionParams)[] = [];

validateTxParams(normalizedTransaction.txParams);
this.update((state) => {
const index = state.transactions.findIndex(
({ id }) => id === transactionId,
);

const updatedTransactionParams = this.#checkIfTransactionParamsUpdated(
normalizedTransaction,
);
let transactionMeta = state.transactions[index];

const transactionWithUpdatedHistory =
skipHistory === true
? normalizedTransaction
: updateTransactionHistory(
normalizedTransaction,
note ?? 'Transaction updated',
);
// eslint-disable-next-line n/callback-return
transactionMeta = callback(transactionMeta) ?? transactionMeta;

this.update((state) => {
const index = state.transactions.findIndex(
({ id }) => transactionMeta.id === id,
transactionMeta.txParams = normalizeTransactionParams(
transactionMeta.txParams,
);
state.transactions[index] = transactionWithUpdatedHistory;

validateTxParams(transactionMeta.txParams);

updatedTransactionParams =
this.#checkIfTransactionParamsUpdated(transactionMeta);

if (skipHistory !== true) {
transactionMeta = updateTransactionHistory(
transactionMeta,
note ?? 'Transaction updated',
);
}

state.transactions[index] = transactionMeta;
});

const transactionMeta = this.getTransaction(
transactionId,
) as TransactionMeta;

if (updatedTransactionParams.length > 0) {
this.#onTransactionParamsUpdated(
normalizedTransaction,
transactionMeta,
updatedTransactionParams,
);
}
Expand Down Expand Up @@ -3611,7 +3632,7 @@ export class TransactionController extends BaseController<
}

async #updateSimulationData(transactionMeta: TransactionMeta) {
const { id, chainId, txParams } = transactionMeta;
const { id: transactionId, chainId, txParams } = transactionMeta;
const { from, to, value, data } = txParams;

let simulationData: SimulationData = {
Expand All @@ -3624,8 +3645,10 @@ export class TransactionController extends BaseController<

if (this.#isSimulationEnabled()) {
this.#updateTransactionInternal(
{ ...transactionMeta, simulationData: undefined },
{ skipHistory: true },
{ transactionId, skipHistory: true },
(txMeta) => {
txMeta.simulationData = undefined;
},
);

simulationData = await getSimulationData({
Expand All @@ -3637,23 +3660,57 @@ export class TransactionController extends BaseController<
});
}

const finalTransactionMeta = this.getTransaction(id);
const finalTransactionMeta = this.getTransaction(transactionId);

if (!finalTransactionMeta) {
log(
'Cannot update simulation data as transaction not found',
id,
transactionId,
simulationData,
);

return;
}

this.updateTransaction(
{ ...finalTransactionMeta, simulationData },
'TransactionController#updateSimulationData - Update simulation data',
this.#updateTransactionInternal(
{
transactionId,
note: 'TransactionController#updateSimulationData - Update simulation data',
},
(txMeta) => {
txMeta.simulationData = simulationData;
},
);

log('Updated simulation data', id, simulationData);
log('Updated simulation data', transactionId, simulationData);
}

#onGasFeePollerTransactionUpdate({
transactionId,
gasFeeEstimates,
gasFeeEstimatesLoaded,
layer1GasFee,
}: {
transactionId: string;
gasFeeEstimates?: GasFeeEstimates;
gasFeeEstimatesLoaded?: boolean;
layer1GasFee?: Hex;
}) {
this.#updateTransactionInternal(
{ transactionId, skipHistory: true },
(txMeta) => {
if (gasFeeEstimates) {
txMeta.gasFeeEstimates = gasFeeEstimates;
}

if (gasFeeEstimatesLoaded !== undefined) {
txMeta.gasFeeEstimatesLoaded = gasFeeEstimatesLoaded;
}

if (layer1GasFee) {
txMeta.layer1GasFee = layer1GasFee;
}
},
);
}
}
Loading

0 comments on commit ef9b0a1

Please sign in to comment.