diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index 9ea7ee8ba34..8b72494ebe8 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -98,6 +98,7 @@ import type { TransactionBatchRequest, TransactionBatchResult, BatchTransactionParams, + AfterSimulateHook, } from './types'; import { TransactionEnvelopeType, @@ -350,6 +351,8 @@ export type TransactionControllerOptions = { signedTx: TypedTransaction, ) => boolean; + afterSimulate?: AfterSimulateHook; + /** * Additional logic to execute before checking pending transactions. * Return false to prevent the broadcast of the transaction. @@ -706,6 +709,8 @@ export class TransactionController extends BaseController< private readonly beforePublish: (transactionMeta: TransactionMeta) => boolean; + readonly #afterSimulate: AfterSimulateHook; + private readonly publish: ( transactionMeta: TransactionMeta, rawTx: string, @@ -850,6 +855,8 @@ export class TransactionController extends BaseController< this.#trace = trace ?? (((_request, fn) => fn?.()) as TraceCallback); this.afterSign = hooks?.afterSign ?? (() => true); + this.#afterSimulate = + hooks?.afterSimulate ?? (() => Promise.resolve(undefined)); this.beforeCheckPendingTransaction = hooks?.beforeCheckPendingTransaction ?? /* istanbul ignore next */ @@ -1227,6 +1234,7 @@ export class TransactionController extends BaseController< if (requireApproval !== false) { this.#updateSimulationData(addedTransactionMeta, { + ethQuery, traceContext, }).catch((error) => { log('Error while updating simulation data', error); @@ -3811,9 +3819,11 @@ export class TransactionController extends BaseController< transactionMeta: TransactionMeta, { blockTime, + ethQuery, traceContext, }: { blockTime?: number; + ethQuery?: EthQuery; traceContext?: TraceContext; } = {}, ) { @@ -3864,7 +3874,7 @@ export class TransactionController extends BaseController< } } - const finalTransactionMeta = this.getTransaction(transactionId); + let finalTransactionMeta = this.getTransaction(transactionId); /* istanbul ignore if */ if (!finalTransactionMeta) { @@ -3877,7 +3887,7 @@ export class TransactionController extends BaseController< return; } - this.#updateTransactionInternal( + finalTransactionMeta = this.#updateTransactionInternal( { transactionId, note: 'TransactionController#updateSimulationData - Update simulation data', @@ -3888,7 +3898,38 @@ export class TransactionController extends BaseController< }, ); - log('Updated simulation data', transactionId, simulationData); + if (ethQuery) { + log('Calling afterSimulate hook', finalTransactionMeta); + + const result = await this.#afterSimulate({ + transactionMeta: finalTransactionMeta, + }); + + if (result?.updateTransaction) { + log('Updating transaction with afterSimulate data'); + + finalTransactionMeta = this.#updateTransactionInternal( + { transactionId }, + result.updateTransaction, + ); + + const { estimatedGas } = await estimateGas( + { ...finalTransactionMeta.txParams, gas: undefined }, + ethQuery, + ); + + log('Updating gas following afterSimulate hook', estimatedGas); + + finalTransactionMeta = this.#updateTransactionInternal( + { transactionId }, + (txMeta) => { + txMeta.txParams.gas = estimatedGas; + }, + ); + } + } + + log('Updated simulation data', transactionId, finalTransactionMeta); } #onGasFeePollerTransactionUpdate({ diff --git a/packages/transaction-controller/src/index.ts b/packages/transaction-controller/src/index.ts index f1edacd1559..afe0b699d74 100644 --- a/packages/transaction-controller/src/index.ts +++ b/packages/transaction-controller/src/index.ts @@ -30,6 +30,7 @@ export { TransactionController, } from './TransactionController'; export type { + AfterSimulateHook, Authorization, AuthorizationList, BatchTransactionParams, diff --git a/packages/transaction-controller/src/types.ts b/packages/transaction-controller/src/types.ts index 9443eb322c4..73cb8d50ba6 100644 --- a/packages/transaction-controller/src/types.ts +++ b/packages/transaction-controller/src/types.ts @@ -1485,3 +1485,12 @@ export type TransactionBatchResult = { /** ID of the batch to locate related transactions. */ batchId: Hex; }; + +export type AfterSimulateHook = (request: { + transactionMeta: TransactionMeta; +}) => Promise< + | { + updateTransaction?: (transactionMeta: TransactionMeta) => void; + } + | undefined +>; diff --git a/packages/transaction-controller/src/utils/feature-flags.ts b/packages/transaction-controller/src/utils/feature-flags.ts index 6099540c162..759a19ca77f 100644 --- a/packages/transaction-controller/src/utils/feature-flags.ts +++ b/packages/transaction-controller/src/utils/feature-flags.ts @@ -4,8 +4,8 @@ import { isValidSignature } from './signature'; import { projectLogger } from '../logger'; import type { TransactionControllerMessenger } from '../TransactionController'; -export const FEATURE_FLAG_TRANSACTIONS = 'confirmations-transactions'; -export const FEATURE_FLAG_EIP_7702 = 'confirmations-eip-7702'; +export const FEATURE_FLAG_TRANSACTIONS = 'confirmations_transactions'; +export const FEATURE_FLAG_EIP_7702 = 'confirmations_eip_7702'; const DEFAULT_BATCH_SIZE_LIMIT = 10;