diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 3960faf98b9..a8374e00bb1 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -573,14 +573,8 @@ } }, "packages/bridge-controller/src/utils/validators.ts": { - "@typescript-eslint/explicit-function-return-type": { - "count": 1 - }, "@typescript-eslint/naming-convention": { "count": 1 - }, - "id-length": { - "count": 4 } }, "packages/bridge-controller/tests/mock-sse.ts": { @@ -591,16 +585,6 @@ "count": 2 } }, - "packages/bridge-status-controller/src/bridge-status-controller.test.ts": { - "no-new": { - "count": 1 - } - }, - "packages/bridge-status-controller/src/utils/gas.ts": { - "@typescript-eslint/explicit-function-return-type": { - "count": 4 - } - }, "packages/bridge-status-controller/src/utils/snaps.ts": { "@typescript-eslint/explicit-function-return-type": { "count": 1 @@ -611,15 +595,7 @@ "count": 3 } }, - "packages/bridge-status-controller/src/utils/transaction.test.ts": { - "@typescript-eslint/explicit-function-return-type": { - "count": 3 - } - }, "packages/bridge-status-controller/src/utils/transaction.ts": { - "@typescript-eslint/explicit-function-return-type": { - "count": 7 - }, "@typescript-eslint/prefer-nullish-coalescing": { "count": 1 } diff --git a/packages/bridge-controller/CHANGELOG.md b/packages/bridge-controller/CHANGELOG.md index 7857fdea4ca..709091c8fdd 100644 --- a/packages/bridge-controller/CHANGELOG.md +++ b/packages/bridge-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **BREAKING:** Narrow the HexStringSchema and HexStringSchema's inferred type (string -> Hex) to fix type errors surfaced when using the QuoteReponse's `trade` and `approval` data ([#8188](https://github.com/MetaMask/core/pull/8188)) + ## [69.1.1] ### Changed diff --git a/packages/bridge-controller/src/utils/bridge.ts b/packages/bridge-controller/src/utils/bridge.ts index c9b5be725f8..a77653fb513 100644 --- a/packages/bridge-controller/src/utils/bridge.ts +++ b/packages/bridge-controller/src/utils/bridge.ts @@ -126,7 +126,7 @@ export const getEthUsdtResetData = ( '0', ]); - return data; + return data as Hex; }; export const isEthUsdt = ( diff --git a/packages/bridge-controller/src/utils/validators.ts b/packages/bridge-controller/src/utils/validators.ts index f2e85cb1bb8..c0f0834b9f7 100644 --- a/packages/bridge-controller/src/utils/validators.ts +++ b/packages/bridge-controller/src/utils/validators.ts @@ -40,23 +40,23 @@ export enum ActionTypes { REFUEL = 'refuel', } -const HexAddressSchema = define('HexAddress', (v: unknown) => - isValidHexAddress(v as string, { allowNonPrefixed: false }), +const HexAddressSchema = define<`0x${string}`>('HexAddress', (data: unknown) => + isValidHexAddress(data as string, { allowNonPrefixed: false }), ); -const HexStringSchema = define('HexString', (v: unknown) => - isStrictHexString(v as string), +const HexStringSchema = define<`0x${string}`>('HexString', (data: unknown) => + isStrictHexString(data as string), ); const VersionStringSchema = define( 'VersionString', - (v: unknown) => - typeof v === 'string' && - /^(\d+\.*){2}\d+$/u.test(v) && - v.split('.').length === 3, + (data: unknown) => + typeof data === 'string' && + /^(\d+\.*){2}\d+$/u.test(data) && + data.split('.').length === 3, ); -export const truthyString = (s: string) => Boolean(s?.length); +export const truthyString = (data: string): boolean => Boolean(data?.length); const TruthyDigitStringSchema = pattern(string(), /^\d+$/u); const ChainIdSchema = number(); diff --git a/packages/bridge-status-controller/CHANGELOG.md b/packages/bridge-status-controller/CHANGELOG.md index cf9dce7d9fe..10849c13d1f 100644 --- a/packages/bridge-status-controller/CHANGELOG.md +++ b/packages/bridge-status-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** Replace transaction handlers provided to the `BridgeStatusController` constructor with calls to the TransactionController, through the controller messenger. Clients will need to add the `TransactionControllerUpdateTransactionAction`, `TransactionControllerAddTransactionAction`, `TransactionControllerAddTransactionBatchAction` and `TransactionControllerEstimateGasFeeAction` permissions to their controller init modules in addition to updating the constructor ([#8188](https://github.com/MetaMask/core/pull/8188)) + ## [69.0.0] ### Added diff --git a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap index 1b6525aa611..c352e6dc48b 100644 --- a/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap +++ b/packages/bridge-status-controller/src/__snapshots__/bridge-status-controller.test.ts.snap @@ -599,6 +599,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should call handleMobileHar [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": true, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -613,6 +650,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should call handleMobileHar [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": true, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -847,6 +921,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should delay after submitti [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -861,6 +972,40 @@ exports[`BridgeStatusController submitTx: EVM bridge should delay after submitti [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": undefined, + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -1095,6 +1240,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should delay after submitti [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -1109,6 +1291,40 @@ exports[`BridgeStatusController submitTx: EVM bridge should delay after submitti [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": undefined, + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -1290,58 +1506,6 @@ exports[`BridgeStatusController submitTx: EVM bridge should handle smart transac `; exports[`BridgeStatusController submitTx: EVM bridge should handle smart transactions and include quotesReceivedContext 3`] = ` -[ - [ - { - "chainId": "0xa4b1", - "networkClientId": "arbitrum", - "transactionParams": { - "data": "0xdata", - "from": "0xaccount1", - "gas": "21000", - "to": "0xbridgeContract", - "value": "0x0", - }, - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should handle smart transactions and include quotesReceivedContext 4`] = ` -[ - [ - { - "disable7702": true, - "from": "0xaccount1", - "isGasFeeIncluded": false, - "isGasFeeSponsored": false, - "networkClientId": "arbitrum", - "origin": "metamask", - "requireApproval": false, - "transactions": [ - { - "assetsFiatValues": { - "receiving": "2.9999", - "sending": "2.00", - }, - "params": { - "data": "0xdata", - "from": "0xaccount1", - "gas": "0x5208", - "maxFeePerGas": "0x0", - "maxPriorityFeePerGas": "0x0", - "to": "0xbridgeContract", - "value": "0x0", - }, - "type": "bridge", - }, - ], - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should handle smart transactions and include quotesReceivedContext 5`] = ` [ [ "BridgeController:stopPollingForQuotes", @@ -1411,12 +1575,81 @@ exports[`BridgeStatusController submitTx: EVM bridge should handle smart transac "GasFeeController:getState", ], [ - "TransactionController:getState", - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobileHardwareWalletDelay on extension 1`] = ` + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransactionBatch", + { + "disable7702": true, + "from": "0xaccount1", + "isGasFeeIncluded": false, + "isGasFeeSponsored": false, + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "transactions": [ + { + "assetsFiatValues": { + "receiving": "2.9999", + "sending": "2.00", + }, + "params": { + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "maxFeePerGas": "0x0", + "maxPriorityFeePerGas": "0x0", + "to": "0xbridgeContract", + "value": "0x0", + }, + "type": "bridge", + }, + ], + }, + ], + [ + "TransactionController:getState", + ], + [ + "TransactionController:updateTransaction", + { + "batchId": "batchId1", + "chainId": "0xa4b1", + "hash": "0xevmTxHash", + "id": "test-tx-id", + "status": "unapproved", + "time": 1234567890, + "txParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gasLimit": "0x5208", + "to": "0xbridgeContract", + "value": "0x0", + }, + "txReceipt": { + "effectiveGasPrice": "0x1880a", + "gasUsed": "0x2c92a", + }, + "type": "bridge", + }, + "Update tx type to bridge", + ], +] +`; + +exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobileHardwareWalletDelay on extension 1`] = ` { "chainId": "0xa4b1", "hash": "0xevmTxHash", @@ -1619,6 +1852,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobil [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -1633,6 +1903,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobil [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -1867,6 +2174,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobil [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -1881,6 +2225,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should not call handleMobil [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -2063,6 +2444,60 @@ exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance 3`] = ` [ [ + "BridgeController:stopPollingForQuotes", + "Transaction submitted", + undefined, + ], + [ + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "BridgeController:trackUnifiedSwapBridgeEvent", + "Unified SwapBridge Submitted", + { + "action_type": "swapbridge-v1", + "chain_id_destination": "eip155:10", + "chain_id_source": "eip155:42161", + "custom_slippage": false, + "gas_included": false, + "gas_included_7702": false, + "is_hardware_wallet": false, + "location": "Main View", + "price_impact": 0, + "provider": "lifi_across", + "quoted_time_minutes": 0.25, + "stx_enabled": false, + "swap_type": "crosschain", + "token_symbol_destination": "ETH", + "token_symbol_source": "ETH", + "usd_amount_source": 1.01, + "usd_quoted_gas": 2.5778, + "usd_quoted_return": 0, + }, + ], + [ + "TransactionController:isAtomicBatchSupported", + { + "address": "0xaccount1", + "chainIds": [ + "0xa4b1", + ], + }, + ], + [ + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "NetworkController:findNetworkClientIdByChainId", + "0x1", + ], + [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", { "chainId": "0x1", "networkClientId": "arbitrum-client-id", @@ -2078,41 +2513,7 @@ exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance }, ], [ - { - "chainId": "0xa4b1", - "networkClientId": "arbitrum-client-id", - "transactionParams": { - "chainId": "0xa4b1", - "data": "0xapprovalData", - "from": "0xaccount1", - "gas": "21000", - "gasLimit": "21000", - "to": "0xtokenContract", - "value": "0x0", - }, - }, - ], - [ - { - "chainId": "0xa4b1", - "networkClientId": "arbitrum", - "transactionParams": { - "chainId": "0xa4b1", - "data": "0xdata", - "from": "0xaccount1", - "gas": "21000", - "gasLimit": "21000", - "to": "0xbridgeContract", - "value": "0x0", - }, - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance 4`] = ` -[ - [ + "TransactionController:addTransaction", { "chainId": "0x1", "data": "0x095ea7b3000000000000000000000000881d40237659c251811cec9c364ef91dc08d300c0000000000000000000000000000000000000000000000000000000000000000", @@ -2133,103 +2534,56 @@ exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance }, ], [ + "TransactionController:getState", + ], + [ + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "NetworkController:findNetworkClientIdByChainId", + "0xa4b1", + ], + [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", { "chainId": "0xa4b1", - "data": "0xapprovalData", - "from": "0xaccount1", - "gas": "0x5208", - "gasLimit": "21000", - "maxFeePerGas": undefined, - "maxPriorityFeePerGas": undefined, - "to": "0xtokenContract", - "value": "0x0", - }, - { - "actionId": "1234567890.456", "networkClientId": "arbitrum-client-id", - "origin": "metamask", - "requireApproval": false, - "type": "bridgeApproval", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, }, ], [ + "TransactionController:addTransaction", { "chainId": "0xa4b1", - "data": "0xdata", + "data": "0xapprovalData", "from": "0xaccount1", "gas": "0x5208", "gasLimit": "21000", "maxFeePerGas": undefined, "maxPriorityFeePerGas": undefined, - "to": "0xbridgeContract", + "to": "0xtokenContract", "value": "0x0", }, { "actionId": "1234567890.456", - "networkClientId": "arbitrum", + "networkClientId": "arbitrum-client-id", "origin": "metamask", "requireApproval": false, - "type": "bridge", - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance 5`] = ` -[ - [ - "BridgeController:stopPollingForQuotes", - "Transaction submitted", - undefined, - ], - [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], - [ - "BridgeController:trackUnifiedSwapBridgeEvent", - "Unified SwapBridge Submitted", - { - "action_type": "swapbridge-v1", - "chain_id_destination": "eip155:10", - "chain_id_source": "eip155:42161", - "custom_slippage": false, - "gas_included": false, - "gas_included_7702": false, - "is_hardware_wallet": false, - "location": "Main View", - "price_impact": 0, - "provider": "lifi_across", - "quoted_time_minutes": 0.25, - "stx_enabled": false, - "swap_type": "crosschain", - "token_symbol_destination": "ETH", - "token_symbol_source": "ETH", - "usd_amount_source": 1.01, - "usd_quoted_gas": 2.5778, - "usd_quoted_return": 0, - }, - ], - [ - "TransactionController:isAtomicBatchSupported", - { - "address": "0xaccount1", - "chainIds": [ - "0xa4b1", - ], + "type": "bridgeApproval", }, ], - [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], - [ - "NetworkController:findNetworkClientIdByChainId", - "0x1", - ], - [ - "GasFeeController:getState", - ], [ "TransactionController:getState", ], @@ -2245,18 +2599,41 @@ exports[`BridgeStatusController submitTx: EVM bridge should reset USDT allowance "GasFeeController:getState", ], [ - "TransactionController:getState", - ], - [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], - [ - "NetworkController:findNetworkClientIdByChainId", - "0xa4b1", + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, ], [ - "GasFeeController:getState", + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, ], [ "TransactionController:getState", @@ -2413,51 +2790,6 @@ exports[`BridgeStatusController submitTx: EVM bridge should successfully submit `; exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with approval 3`] = ` -[ - [ - { - "chainId": "0xa4b1", - "data": "0xapprovalData", - "from": "0xaccount1", - "gas": "0x5208", - "gasLimit": "21000", - "maxFeePerGas": undefined, - "maxPriorityFeePerGas": undefined, - "to": "0xtokenContract", - "value": "0x0", - }, - { - "actionId": "1234567890.456", - "networkClientId": "arbitrum-client-id", - "origin": "metamask", - "requireApproval": false, - "type": "bridgeApproval", - }, - ], - [ - { - "chainId": "0xa4b1", - "data": "0xdata", - "from": "0xaccount1", - "gas": "0x5208", - "gasLimit": "21000", - "maxFeePerGas": undefined, - "maxPriorityFeePerGas": undefined, - "to": "0xbridgeContract", - "value": "0x0", - }, - { - "actionId": "1234567890.456", - "networkClientId": "arbitrum", - "origin": "metamask", - "requireApproval": false, - "type": "bridge", - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with approval 4`] = ` [ [ "BridgeController:stopPollingForQuotes", @@ -2512,6 +2844,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should successfully submit [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, + ], [ "TransactionController:getState", ], @@ -2526,6 +2895,43 @@ exports[`BridgeStatusController submitTx: EVM bridge should successfully submit [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "bridge", + }, + ], [ "TransactionController:getState", ], @@ -2681,51 +3087,6 @@ exports[`BridgeStatusController submitTx: EVM bridge should successfully submit `; exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with no approval 3`] = ` -[ - [ - { - "chainId": "0xa4b1", - "networkClientId": "arbitrum", - "transactionParams": { - "chainId": "0xa4b1", - "data": "0xdata", - "from": "0xaccount1", - "gas": "21000", - "gasLimit": "21000", - "to": "0xbridgeContract", - "value": "0x0", - }, - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with no approval 4`] = ` -[ - [ - { - "chainId": "0xa4b1", - "data": "0xdata", - "from": "0xaccount1", - "gas": "0x5208", - "gasLimit": "21000", - "maxFeePerGas": undefined, - "maxPriorityFeePerGas": undefined, - "to": "0xbridgeContract", - "value": "0x0", - }, - { - "actionId": "1234567890.456", - "networkClientId": "arbitrum", - "origin": "metamask", - "requireApproval": false, - "type": "bridge", - }, - ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should successfully submit an EVM bridge transaction with no approval 5`] = ` [ [ "BridgeController:stopPollingForQuotes", @@ -2781,37 +3142,49 @@ exports[`BridgeStatusController submitTx: EVM bridge should successfully submit "GasFeeController:getState", ], [ - "TransactionController:getState", + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx fails 1`] = ` -[ [ + "TransactionController:addTransaction", { "chainId": "0xa4b1", - "data": "0xapprovalData", + "data": "0xdata", "from": "0xaccount1", "gas": "0x5208", "gasLimit": "21000", "maxFeePerGas": undefined, "maxPriorityFeePerGas": undefined, - "to": "0xtokenContract", + "to": "0xbridgeContract", "value": "0x0", }, { "actionId": "1234567890.456", - "networkClientId": "arbitrum-client-id", + "networkClientId": "arbitrum", "origin": "metamask", "requireApproval": false, - "type": "bridgeApproval", + "type": "bridge", }, ], + [ + "TransactionController:getState", + ], ] `; -exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx fails 2`] = ` +exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx fails 1`] = ` [ [ "BridgeController:stopPollingForQuotes", @@ -2866,12 +3239,24 @@ exports[`BridgeStatusController submitTx: EVM bridge should throw an error if ap [ "GasFeeController:getState", ], -] -`; - -exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx meta does not exist 1`] = ` -[ [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", { "chainId": "0xa4b1", "data": "0xapprovalData", @@ -2894,7 +3279,7 @@ exports[`BridgeStatusController submitTx: EVM bridge should throw an error if ap ] `; -exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx meta does not exist 2`] = ` +exports[`BridgeStatusController submitTx: EVM bridge should throw an error if approval tx meta does not exist 1`] = ` [ [ "BridgeController:stopPollingForQuotes", @@ -2939,15 +3324,52 @@ exports[`BridgeStatusController submitTx: EVM bridge should throw an error if ap }, ], [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], - [ - "NetworkController:findNetworkClientIdByChainId", - "0xa4b1", - ], - [ - "GasFeeController:getState", + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "NetworkController:findNetworkClientIdByChainId", + "0xa4b1", + ], + [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "bridgeApproval", + }, ], [ "TransactionController:getState", @@ -3237,6 +3659,60 @@ exports[`BridgeStatusController submitTx: EVM swap should handle smart transacti exports[`BridgeStatusController submitTx: EVM swap should handle smart transactions 3`] = ` [ [ + "BridgeController:stopPollingForQuotes", + "Transaction submitted", + undefined, + ], + [ + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "BridgeController:trackUnifiedSwapBridgeEvent", + "Unified SwapBridge Submitted", + { + "action_type": "swapbridge-v1", + "chain_id_destination": "eip155:42161", + "chain_id_source": "eip155:42161", + "custom_slippage": false, + "gas_included": false, + "gas_included_7702": false, + "is_hardware_wallet": false, + "location": "Main View", + "price_impact": 0, + "provider": "lifi_across", + "quoted_time_minutes": 0, + "stx_enabled": true, + "swap_type": "single_chain", + "token_symbol_destination": "ETH", + "token_symbol_source": "ETH", + "usd_amount_source": 1.01, + "usd_quoted_gas": 2.5778, + "usd_quoted_return": 0, + }, + ], + [ + "TransactionController:isAtomicBatchSupported", + { + "address": "0xaccount1", + "chainIds": [ + "0xa4b1", + ], + }, + ], + [ + "AccountsController:getAccountByAddress", + "0xaccount1", + ], + [ + "NetworkController:findNetworkClientIdByChainId", + "0xa4b1", + ], + [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", { "chainId": "0xa4b1", "networkClientId": "arbitrum", @@ -3250,6 +3726,10 @@ exports[`BridgeStatusController submitTx: EVM swap should handle smart transacti }, ], [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", { "chainId": "0xa4b1", "networkClientId": "arbitrum", @@ -3262,12 +3742,8 @@ exports[`BridgeStatusController submitTx: EVM swap should handle smart transacti }, }, ], -] -`; - -exports[`BridgeStatusController submitTx: EVM swap should handle smart transactions 4`] = ` -[ [ + "TransactionController:addTransactionBatch", { "disable7702": true, "from": "0xaccount1", @@ -3308,69 +3784,29 @@ exports[`BridgeStatusController submitTx: EVM swap should handle smart transacti ], }, ], -] -`; - -exports[`BridgeStatusController submitTx: EVM swap should handle smart transactions 5`] = ` -[ - [ - "BridgeController:stopPollingForQuotes", - "Transaction submitted", - undefined, - ], - [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], [ - "BridgeController:trackUnifiedSwapBridgeEvent", - "Unified SwapBridge Submitted", - { - "action_type": "swapbridge-v1", - "chain_id_destination": "eip155:42161", - "chain_id_source": "eip155:42161", - "custom_slippage": false, - "gas_included": false, - "gas_included_7702": false, - "is_hardware_wallet": false, - "location": "Main View", - "price_impact": 0, - "provider": "lifi_across", - "quoted_time_minutes": 0, - "stx_enabled": true, - "swap_type": "single_chain", - "token_symbol_destination": "ETH", - "token_symbol_source": "ETH", - "usd_amount_source": 1.01, - "usd_quoted_gas": 2.5778, - "usd_quoted_return": 0, - }, + "TransactionController:getState", ], [ - "TransactionController:isAtomicBatchSupported", + "TransactionController:updateTransaction", { - "address": "0xaccount1", - "chainIds": [ - "0xa4b1", - ], + "batchId": "batchId1", + "chainId": "0xa4b1", + "hash": "0xevmTxHash", + "id": "test-tx-id", + "status": "unapproved", + "time": 1234567890, + "txParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gasLimit": "0x5208", + "to": "0xbridgeContract", + "value": "0x0", + }, + "type": "swap", }, - ], - [ - "AccountsController:getAccountByAddress", - "0xaccount1", - ], - [ - "NetworkController:findNetworkClientIdByChainId", - "0xa4b1", - ], - [ - "GasFeeController:getState", - ], - [ - "GasFeeController:getState", - ], - [ - "TransactionController:getState", + "Update tx type to swap", ], ] `; @@ -3444,6 +3880,43 @@ exports[`BridgeStatusController submitTx: EVM swap should successfully submit an [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum-client-id", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xtokenContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xapprovalData", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xtokenContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum-client-id", + "origin": "metamask", + "requireApproval": false, + "type": "swapApproval", + }, + ], [ "TransactionController:getState", ], @@ -3458,6 +3931,43 @@ exports[`BridgeStatusController submitTx: EVM swap should successfully submit an [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "swap", + }, + ], [ "TransactionController:getState", ], @@ -3663,6 +4173,43 @@ exports[`BridgeStatusController submitTx: EVM swap should successfully submit an [ "GasFeeController:getState", ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0xa4b1", + "networkClientId": "arbitrum", + "transactionParams": { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "21000", + "gasLimit": "21000", + "to": "0xbridgeContract", + "value": "0x0", + }, + }, + ], + [ + "TransactionController:addTransaction", + { + "chainId": "0xa4b1", + "data": "0xdata", + "from": "0xaccount1", + "gas": "0x5208", + "gasLimit": "21000", + "maxFeePerGas": undefined, + "maxPriorityFeePerGas": undefined, + "to": "0xbridgeContract", + "value": "0x0", + }, + { + "actionId": "1234567890.456", + "networkClientId": "arbitrum", + "origin": "metamask", + "requireApproval": false, + "type": "swap", + }, + ], [ "TransactionController:getState", ], diff --git a/packages/bridge-status-controller/src/bridge-status-controller.intent-manager.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.intent-manager.test.ts index 6761dcb8452..57e5d2291f1 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.intent-manager.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.intent-manager.test.ts @@ -36,7 +36,6 @@ const createManagerOptions = (overrides?: { fetchFn?: ReturnType; }): IntentManagerConstructorOptions => ({ messenger: overrides?.messenger ?? { call: jest.fn() }, - updateTransactionFn: overrides?.updateTransactionFn ?? jest.fn(), customBridgeApiBaseUrl: 'https://example.com', fetchFn: overrides?.fetchFn ?? jest.fn(), getJwt: jest.fn().mockResolvedValue(undefined), @@ -55,7 +54,6 @@ describe('IntentManager', () => { }), ); - expect(options.updateTransactionFn).not.toHaveBeenCalled(); expect(options.messenger.call).not.toHaveBeenCalled(); }); @@ -111,19 +109,24 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, txReceipt: { status: '0x0' }, }; - const updateTransactionFn = jest.fn(); const completedOrder = { id: 'order-3', status: IntentOrderStatus.COMPLETED, txHash: '0xhash', metadata: {}, }; + const mockCall = jest.fn((...args: unknown[]) => { + const [method] = args; + if (method === 'TransactionController:updateTransaction') { + return { transactions: [existingTxMeta] }; + } + return { transactions: [existingTxMeta] }; + }); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: (...args: unknown[]) => mockCall(...args), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue(completedOrder), }), ); @@ -144,18 +147,26 @@ describe('IntentManager', () => { ); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx-2', - status: TransactionStatus.confirmed, - hash: '0xhash', - txReceipt: expect.objectContaining({ - transactionHash: '0xhash', - status: '0x1', - }), - }), - expect.stringContaining('Intent order status updated'), - ); + expect(mockCall.mock.calls).toMatchInlineSnapshot(` + [ + [ + "TransactionController:getState", + ], + [ + "TransactionController:updateTransaction", + { + "hash": "0xhash", + "id": "tx-2", + "status": "confirmed", + "txReceipt": { + "status": "0x1", + "transactionHash": "0xhash", + }, + }, + "BridgeStatusController - Intent order status updated: completed", + ], + ] + `); }); it('getIntentTransactionStatus returns undefined when getOrderStatus rejects with non-Error', async () => { @@ -278,19 +289,24 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, txReceipt: { status: '0x0' }, }; - const updateTransactionFn = jest.fn(); const completedOrder = { id: 'order-3', status: IntentOrderStatus.COMPLETED, txHash: '0xhash', metadata: {}, }; + const mockCall = jest.fn((...args: unknown[]) => { + const [method] = args; + if (method === 'TransactionController:updateTransaction') { + return undefined; + } + return { transactions: [existingTxMeta] }; + }); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: (...args: unknown[]) => mockCall(...args), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue(completedOrder), }), ); @@ -311,11 +327,30 @@ describe('IntentManager', () => { ); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledTimes(1); + expect(mockCall).toHaveBeenCalledTimes(2); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledTimes(1); + expect(mockCall.mock.calls).toMatchInlineSnapshot(` + [ + [ + "TransactionController:getState", + ], + [ + "TransactionController:updateTransaction", + { + "hash": "0xhash", + "id": "tx-2", + "status": "confirmed", + "txReceipt": { + "status": "0x1", + "transactionHash": "0xhash", + }, + }, + "BridgeStatusController - Intent order status updated: completed", + ], + ] + `); }); it('syncTransactionFromIntentStatus cleans up intent statuses map when order has failed', async () => { @@ -324,19 +359,24 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, txReceipt: { status: '0x0' }, }; - const updateTransactionFn = jest.fn(); const failedOrder = { id: 'order-3', status: IntentOrderStatus.FAILED, txHash: '0xhash', metadata: {}, }; + const mockCall = jest.fn((...args: unknown[]) => { + const [method] = args; + if (method === 'TransactionController:updateTransaction') { + return undefined; + } + return { transactions: [existingTxMeta] }; + }); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: (...args: unknown[]) => mockCall(...args), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue(failedOrder), }), ); @@ -357,11 +397,30 @@ describe('IntentManager', () => { ); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledTimes(1); + expect(mockCall).toHaveBeenCalledTimes(2); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledTimes(1); + expect(mockCall.mock.calls).toMatchInlineSnapshot(` + [ + [ + "TransactionController:getState", + ], + [ + "TransactionController:updateTransaction", + { + "hash": "0xhash", + "id": "tx-2", + "status": "failed", + "txReceipt": { + "status": "0x0", + "transactionHash": "0xhash", + }, + }, + "BridgeStatusController - Intent order status updated: failed", + ], + ] + `); }); it('syncTransactionFromIntentStatus logs warn when transaction is not found', async () => { @@ -406,19 +465,24 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, txReceipt: { status: '0x0' }, }; - const updateTransactionFn = jest.fn(); const submittedOrder = { id: 'order-3', status: IntentOrderStatus.SUBMITTED, txHash: '0xhash', metadata: {}, }; + const mockCall = jest.fn((...args: unknown[]) => { + const [method] = args; + if (method === 'TransactionController:updateTransaction') { + return { transactions: [existingTxMeta] }; + } + return { transactions: [existingTxMeta] }; + }); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: (...args: unknown[]) => mockCall(...args), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue(submittedOrder), }), ); @@ -439,7 +503,8 @@ describe('IntentManager', () => { ); manager.syncTransactionFromIntentStatus('order-3', historyItem); - expect(updateTransactionFn).toHaveBeenCalledWith( + expect(mockCall).toHaveBeenCalledWith( + 'TransactionController:updateTransaction', expect.objectContaining({ id: 'tx-2', txReceipt: expect.objectContaining({ @@ -457,18 +522,23 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, hash: undefined, }; - const updateTransactionFn = jest.fn(); const orderWithoutTxHash = { id: 'order-3', status: IntentOrderStatus.SUBMITTED, metadata: {}, }; + const mockCall = jest.fn((...args: unknown[]) => { + const [method] = args; + if (method === 'TransactionController:updateTransaction') { + return { transactions: [existingTxMeta] }; + } + return { transactions: [existingTxMeta] }; + }); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: (...args: unknown[]) => mockCall(...args), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue(orderWithoutTxHash), }), ); @@ -488,8 +558,22 @@ describe('IntentManager', () => { ); manager.syncTransactionFromIntentStatus('order-3', historyItem); - const call = updateTransactionFn.mock.calls[0][0]; - expect(call.hash).toBeUndefined(); + expect(mockCall.mock.calls).toMatchInlineSnapshot(` + [ + [ + "TransactionController:getState", + ], + [ + "TransactionController:updateTransaction", + { + "hash": undefined, + "id": "tx-2", + "status": "submitted", + }, + "BridgeStatusController - Intent order status updated: submitted", + ], + ] + `); }); it('syncTransactionFromIntentStatus logs error when updateTransactionFn throws', async () => { @@ -498,18 +582,19 @@ describe('IntentManager', () => { status: TransactionStatus.submitted, txReceipt: {}, }; - const updateTransactionFn = jest.fn().mockImplementation(() => { - throw new Error('update failed'); - }); const errorSpy = jest .spyOn(console, 'error') .mockImplementation(() => undefined); const manager = new IntentManager( createManagerOptions({ messenger: { - call: jest.fn(() => ({ transactions: [existingTxMeta] })), + call: jest.fn((method) => { + if (method === 'TransactionController:updateTransaction') { + throw new Error('update failed'); + } + return { transactions: [existingTxMeta] }; + }), }, - updateTransactionFn, fetchFn: jest.fn().mockResolvedValue({ id: 'order-3', status: IntentOrderStatus.COMPLETED, diff --git a/packages/bridge-status-controller/src/bridge-status-controller.intent.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.intent.test.ts index ab479eb9055..b27d2baa3d0 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.intent.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.intent.test.ts @@ -5,7 +5,6 @@ import { BridgeClientId, StatusTypes } from '@metamask/bridge-controller'; import type { GasFeeEstimates, TransactionMeta, - TransactionParams, } from '@metamask/transaction-controller'; import { TransactionStatus, @@ -134,6 +133,7 @@ const createMessengerHarness = ( accountAddress: string, selectedChainId: string = '0x1', keyringType: string = 'HD Key Tree', + approvalStatus?: TransactionStatus, ): any => { const transactions: TransactionMeta[] = []; @@ -158,6 +158,52 @@ const createMessengerHarness = ( } case 'TransactionController:getState': return { transactions }; + case 'TransactionController:estimateGasFee': + return { estimates: {} as GasFeeEstimates }; + case 'TransactionController:addTransaction': { + // Approval TX path (submitIntent -> #handleApprovalTx -> #handleEvmTransaction) + if ( + args[1]?.type === TransactionType.bridgeApproval || + args[1]?.type === TransactionType.swapApproval + ) { + const hash = '0xapprovalhash1'; + + const approvalTx = { + id: 'approvalTxId1', + type: args[1]?.type, + status: approvalStatus ?? TransactionStatus.failed, + chainId: args[0]?.chainId ?? '0x1', + hash, + networkClientId: 'network-client-id-1', + time: Date.now(), + txParams: args[0], + }; + transactions.push(approvalTx); + + return { + result: Promise.resolve(hash), + transactionMeta: approvalTx, + }; + } + + // Intent “display tx” path + const intentTx = { + id: 'intentDisplayTxId1', + type: args[1]?.type, + status: TransactionStatus.submitted, + chainId: args[0]?.chainId ?? '0x1', + hash: undefined, + networkClientId: 'network-client-id-1', + time: Date.now(), + txParams: args[0], + }; + transactions.push(intentTx); + + return { + result: Promise.resolve('0xunused'), + transactionMeta: intentTx, + }; + } case 'NetworkController:findNetworkClientIdByChainId': return 'network-client-id-1'; case 'NetworkController:getState': @@ -191,53 +237,7 @@ const setup = (options?: { accountAddress, options?.selectedChainId ?? '0x1', options?.keyringType, - ); - - const addTransactionFn = jest.fn( - async (txParams: TransactionParams, reqOpts: any) => { - // Approval TX path (submitIntent -> #handleApprovalTx -> #handleEvmTransaction) - if ( - reqOpts?.type === TransactionType.bridgeApproval || - reqOpts?.type === TransactionType.swapApproval - ) { - const hash = '0xapprovalhash1'; - - const approvalTx = { - id: 'approvalTxId1', - type: reqOpts.type, - status: options?.approvalStatus ?? TransactionStatus.failed, - chainId: txParams.chainId ?? '0x1', - hash, - networkClientId: 'network-client-id-1', - time: Date.now(), - txParams, - }; - transactions.push(approvalTx); - - return { - result: Promise.resolve(hash), - transactionMeta: approvalTx, - }; - } - - // Intent “display tx” path - const intentTx = { - id: 'intentDisplayTxId1', - type: reqOpts?.type, - status: TransactionStatus.submitted, - chainId: txParams.chainId ?? '0x1', - hash: undefined, - networkClientId: 'network-client-id-1', - time: Date.now(), - txParams, - }; - transactions.push(intentTx); - - return { - result: Promise.resolve('0xunused'), - transactionMeta: intentTx, - }; - }, + options?.approvalStatus, ); const mockFetchFn = jest.fn(); @@ -248,12 +248,6 @@ const setup = (options?: { }, clientId: options?.clientId ?? BridgeClientId.EXTENSION, fetchFn: (...args: any[]) => mockFetchFn(...args), - addTransactionFn, - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(async () => ({ - estimates: {} as GasFeeEstimates, - })), config: { customBridgeApiBaseUrl: 'http://localhost' }, traceFn: (_req: any, fn?: any): any => fn?.(), }); @@ -269,7 +263,6 @@ const setup = (options?: { controller, messenger, transactions, - addTransactionFn, startPollingSpy, stopPollingSpy, accountAddress, @@ -1059,10 +1052,6 @@ describe('BridgeStatusController (target uncovered branches)', () => { state, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), config: { customBridgeApiBaseUrl: 'http://localhost' }, traceFn: (_r: any, fn?: any): any => fn?.(), }); diff --git a/packages/bridge-status-controller/src/bridge-status-controller.intent.ts b/packages/bridge-status-controller/src/bridge-status-controller.intent.ts index 4eb0bc3ed0f..3f82dca357d 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.intent.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.intent.ts @@ -1,6 +1,5 @@ import { BridgeClientId, StatusTypes } from '@metamask/bridge-controller'; -import type { TransactionController } from '@metamask/transaction-controller'; -import { TransactionMeta } from '@metamask/transaction-controller'; +import type { TransactionMeta } from '@metamask/transaction-controller'; import type { BridgeStatusControllerMessenger, FetchFunction } from './types'; import type { BridgeHistoryItem } from './types'; @@ -22,8 +21,6 @@ type IntentStatuses = { export class IntentManager { readonly #messenger: BridgeStatusControllerMessenger; - readonly #updateTransactionFn: typeof TransactionController.prototype.updateTransaction; - readonly intentApi: IntentApi; readonly #intentStatusesByBridgeTxMetaId: Map = @@ -31,19 +28,16 @@ export class IntentManager { constructor({ messenger, - updateTransactionFn, customBridgeApiBaseUrl, fetchFn, getJwt, }: { messenger: BridgeStatusControllerMessenger; - updateTransactionFn: typeof TransactionController.prototype.updateTransaction; customBridgeApiBaseUrl: string; fetchFn: FetchFunction; getJwt: GetJwtFn; }) { this.#messenger = messenger; - this.#updateTransactionFn = updateTransactionFn; this.intentApi = new IntentApiImpl(customBridgeApiBaseUrl, fetchFn, getJwt); } @@ -182,7 +176,8 @@ export class IntentManager { ...txReceiptUpdate, } as TransactionMeta; - this.#updateTransactionFn( + this.#messenger.call( + 'TransactionController:updateTransaction', updatedTxMeta, `BridgeStatusController - Intent order status updated: ${orderStatus}`, ); diff --git a/packages/bridge-status-controller/src/bridge-status-controller.test.ts b/packages/bridge-status-controller/src/bridge-status-controller.test.ts index 8c65af6c1a4..119a9ea7607 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.test.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.test.ts @@ -13,7 +13,6 @@ import { ChainId, FeeType, StatusTypes, - BridgeController, getNativeAssetForChainId, FeatureId, getQuotesReceivedProperties, @@ -608,10 +607,6 @@ const executePollingWithPendingStatus = async () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), config: {}, }); const startPollingSpy = jest.spyOn(bridgeStatusController, 'startPolling'); @@ -648,11 +643,6 @@ const mockSelectedAccount = { }, }; -const addTransactionFn = jest.fn(); -const addTransactionBatchFn = jest.fn(); -const updateTransactionFn = jest.fn(); -const estimateGasFeeFn = jest.fn(); - const getController = ( call: jest.Mock, traceFn?: jest.Mock, @@ -669,10 +659,6 @@ const getController = ( } as never, clientId, fetchFn: mockFetchFn, - addTransactionFn, - addTransactionBatchFn, - estimateGasFeeFn, - updateTransactionFn, traceFn, }); @@ -696,10 +682,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); expect(bridgeStatusController.state).toStrictEqual(EMPTY_INIT_STATE); expect(mockMessengerSubscribe.mock.calls).toMatchSnapshot(); @@ -711,10 +693,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: MockTxHistory.getPending(), }, @@ -748,10 +726,6 @@ describe('BridgeStatusController', () => { .fn() .mockResolvedValueOnce(MockStatusResponse.getPending()) .mockResolvedValueOnce(MockStatusResponse.getComplete()), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); jest.advanceTimersByTime(10000); await flushPromises(); @@ -792,10 +766,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: mockFetchFn, - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); // Execution @@ -853,10 +823,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: failedFetch, - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); // Execution @@ -930,10 +896,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const argsWithoutId = getMockStartPollingForBridgeTxStatusArgs(); @@ -954,10 +916,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const argsWithoutMeta = getMockStartPollingForBridgeTxStatusArgs(); @@ -981,10 +939,6 @@ describe('BridgeStatusController', () => { fetchFn: jest .fn() .mockResolvedValueOnce(MockStatusResponse.getPending()), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); // Execution @@ -1024,10 +978,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const fetchBridgeTxStatusSpy = jest.spyOn( bridgeStatusUtils, @@ -1111,10 +1061,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); // Start polling with args that have no srcTxHash @@ -1152,10 +1098,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const fetchBridgeTxStatusSpy = jest @@ -1201,10 +1143,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); // Execution @@ -1271,10 +1209,6 @@ describe('BridgeStatusController', () => { fetchFn: jest .fn() .mockResolvedValueOnce(MockStatusResponse.getPending()), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), traceFn: jest.fn(), }); @@ -1351,10 +1285,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: EMPTY_INIT_STATE, }); @@ -1370,10 +1300,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { bridgeTxMetaId1: { @@ -1466,10 +1392,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const fetchBridgeTxStatusSpy = jest .spyOn(bridgeStatusUtils, 'fetchBridgeTxStatus') @@ -1572,10 +1494,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const fetchBridgeTxStatusSpy = jest .spyOn(bridgeStatusUtils, 'fetchBridgeTxStatus') @@ -1691,10 +1609,6 @@ describe('BridgeStatusController', () => { messenger: messengerMock, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), }); const fetchBridgeTxStatusSpy = jest .spyOn(bridgeStatusUtils, 'fetchBridgeTxStatus') @@ -2547,8 +2461,8 @@ describe('BridgeStatusController', () => { mockCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: mockApprovalTxMeta, result: Promise.resolve('0xapprovalTxHash'), }); @@ -2563,8 +2477,8 @@ describe('BridgeStatusController', () => { mockCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockResolvedValueOnce({ + mockCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockCall.mockResolvedValueOnce({ transactionMeta: mockEvmTxMeta, result: Promise.resolve('0xevmTxHash'), }); @@ -2585,8 +2499,8 @@ describe('BridgeStatusController', () => { mockCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionBatchFn.mockResolvedValueOnce({ + mockCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockCall.mockReturnValueOnce({ @@ -2616,7 +2530,6 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(addTransactionFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); controller.stopAllPolling(); }); @@ -2653,15 +2566,13 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(estimateGasFeeFn.mock.calls).toMatchSnapshot(); - expect(addTransactionFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); it('should handle smart transactions and include quotesReceivedContext', async () => { setupEventTrackingMocks(mockMessengerCall); setupBridgeStxMocks(mockMessengerCall); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); @@ -2679,9 +2590,6 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(estimateGasFeeFn.mock.calls).toMatchSnapshot(); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -2705,7 +2613,10 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn).not.toHaveBeenCalled(); + const addTransactionCall = mockMessengerCall.mock.calls.find( + (call) => call[0] === 'TransactionController:addTransaction', + ); + expect(addTransactionCall).toBeUndefined(); }); it('should throw an error if EVM trade data is not valid', async () => { @@ -2731,7 +2642,10 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn).not.toHaveBeenCalled(); + const addTransactionCall = mockMessengerCall.mock.calls.find( + (call) => call[0] === 'TransactionController:addTransaction', + ); + expect(addTransactionCall).toBeUndefined(); }); it('should throw an error if Solana trade data is not valid', async () => { @@ -2760,7 +2674,10 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn).not.toHaveBeenCalled(); + const addTransactionCall = mockMessengerCall.mock.calls.find( + (call) => call[0] === 'TransactionController:addTransaction', + ); + expect(addTransactionCall).toBeUndefined(); }); it('should reset USDT allowance', async () => { @@ -2798,8 +2715,6 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(estimateGasFeeFn.mock.calls).toMatchSnapshot(); - expect(addTransactionFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -2810,16 +2725,16 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -2852,10 +2767,28 @@ describe('BridgeStatusController', () => { expect(quote).toBeDefined(); expect(txMetaId).toBe(result.id); expect(batchId).toBe('batchId1'); - expect(estimateGasFeeFn).toHaveBeenCalledTimes(3); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn).toHaveBeenCalledTimes(1); - expect(mockMessengerCall).toHaveBeenCalledTimes(10); + const mockCalls = mockMessengerCall.mock.calls; + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(3); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(0); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(1); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:updateTransaction', + ), + ).toHaveLength(1); + expect(mockMessengerCall).toHaveBeenCalledTimes(15); }); it('should throw an error if approval tx fails', async () => { @@ -2865,8 +2798,8 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockRejectedValueOnce(new Error('Approval tx failed')); + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockRejectedValueOnce(new Error('Approval tx failed')); const { controller, startPollingForBridgeTxStatusSpy } = getController(mockMessengerCall); @@ -2880,7 +2813,6 @@ describe('BridgeStatusController', () => { ).rejects.toThrow('Approval tx failed'); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -2891,8 +2823,8 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: undefined, result: new Promise((resolve) => resolve('0xevmTxHash')), }); @@ -2915,7 +2847,6 @@ describe('BridgeStatusController', () => { ); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -3190,7 +3121,7 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ estimates: { high: { suggestedMaxFeePerGas: '0x1234', @@ -3200,7 +3131,7 @@ describe('BridgeStatusController', () => { }); // Trade tx fails during submission - addTransactionFn.mockRejectedValueOnce( + mockMessengerCall.mockRejectedValueOnce( new Error('Trade tx submission failed'), ); @@ -3376,8 +3307,8 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: mockApprovalTxMeta, result: Promise.resolve('0xapprovalTxHash'), }); @@ -3392,8 +3323,8 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: mockEvmTxMeta, result: Promise.resolve('0xevmTxHash'), }); @@ -3402,9 +3333,6 @@ describe('BridgeStatusController', () => { }); mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); - // mockMessengerCall.mockReturnValueOnce({ - // transactions: [mockEvmTxMeta], - // }); }; it('should successfully submit an EVM swap transaction with approval', async () => { @@ -3425,8 +3353,12 @@ describe('BridgeStatusController', () => { expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); const { approvalTxId } = controller.state.txHistory[result.id]; expect(approvalTxId).toBe('test-approval-tx-id'); - expect(addTransactionFn).toHaveBeenCalledTimes(2); - expect(mockMessengerCall).toHaveBeenCalledTimes(12); + expect( + mockMessengerCall.mock.calls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(2); + expect(mockMessengerCall).toHaveBeenCalledTimes(16); }); it('should successfully submit an EVM swap transaction with featureId', async () => { @@ -3454,8 +3386,6 @@ describe('BridgeStatusController', () => { expect(controller.state.txHistory[result.id].featureId).toBe( FeatureId.PERPS, ); - expect(addTransactionFn).toHaveBeenCalledTimes(2); - expect(mockMessengerCall).toHaveBeenCalledTimes(11); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -3463,7 +3393,7 @@ describe('BridgeStatusController', () => { setupEventTrackingMocks(mockMessengerCall); mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); mockMessengerCall.mockReturnValueOnce('arbitrum'); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -3504,9 +3434,12 @@ describe('BridgeStatusController', () => { } `); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn).toHaveBeenCalledTimes(1); - expect(mockMessengerCall).toHaveBeenCalledTimes(7); + expect( + mockMessengerCall.mock.calls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(0); + expect(mockMessengerCall).toHaveBeenCalledTimes(9); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); }); @@ -3543,8 +3476,6 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(estimateGasFeeFn).toHaveBeenCalledTimes(1); - expect(addTransactionFn).toHaveBeenCalledTimes(1); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -3554,7 +3485,7 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); mockMessengerCall.mockReturnValueOnce('arbitrum'); // Skip GasFeeController mock since we use quote's txFee directly - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: mockEvmTxMeta, result: Promise.resolve('0xevmTxHash'), }); @@ -3588,15 +3519,21 @@ describe('BridgeStatusController', () => { ); controller.stopAllPolling(); - // Should use single tx path (addTransactionFn), NOT batch path - expect(addTransactionFn).toHaveBeenCalledTimes(1); - expect(addTransactionBatchFn).not.toHaveBeenCalled(); + const mockCalls = mockMessengerCall.mock.calls; + // Should use single tx path (addTransactionFn), NOT batch path + const addTransactionCalls = mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ); + expect(addTransactionCalls).toHaveLength(1); // Should NOT estimate gas (uses quote's txFee instead) - expect(estimateGasFeeFn).not.toHaveBeenCalled(); + const estimateGasFeeCalls = mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ); + expect(estimateGasFeeCalls).toHaveLength(0); // Verify the tx params have hex-converted gas fees from quote - const txParams = addTransactionFn.mock.calls[0][0]; + const txParams = addTransactionCalls[0]?.[1]; expect(txParams.maxFeePerGas).toBe('0x154a94'); // toHex(1395348) expect(txParams.maxPriorityFeePerGas).toBe('0xf4241'); // toHex(1000001) expect(txParams.gas).toBe('0x5208'); @@ -3612,7 +3549,7 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); mockMessengerCall.mockReturnValueOnce('arbitrum'); // Skip GasFeeController mock since we use quote's txFee directly - addTransactionFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ transactionMeta: mockEvmTxMeta, result: Promise.resolve('0xevmTxHash'), }); @@ -3642,7 +3579,7 @@ describe('BridgeStatusController', () => { }, }, trade: { - ...quoteWithoutApproval.trade, + ...(quoteWithoutApproval.trade as TxData), gasLimit: undefined, }, sentAmount: { amount: null, valueInCurrency: null, usd: null }, @@ -3651,15 +3588,27 @@ describe('BridgeStatusController', () => { ); controller.stopAllPolling(); - // Should use single tx path (addTransactionFn), NOT batch path - expect(addTransactionFn).toHaveBeenCalledTimes(1); - expect(addTransactionBatchFn).not.toHaveBeenCalled(); + const mockCalls = mockMessengerCall.mock.calls; // Should NOT estimate gas (uses quote's txFee instead) - expect(estimateGasFeeFn).not.toHaveBeenCalled(); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(0); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(0); + // Should use single tx path (addTransactionFn), NOT batch path + const addTransactionCalls = mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ); + expect(addTransactionCalls).toHaveLength(1); // Verify the tx params have hex-converted gas fees from quote - const txParams = addTransactionFn.mock.calls[0][0]; + const txParams = addTransactionCalls[0]?.[1]; expect(txParams.maxFeePerGas).toBe('0x154a94'); // toHex(1395348) expect(txParams.maxPriorityFeePerGas).toBe('0xf4241'); // toHex(1000001) expect(txParams.gas).toBeUndefined(); @@ -3692,9 +3641,22 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); // Should estimate gas since gasIncluded is false - expect(estimateGasFeeFn).toHaveBeenCalledTimes(1); - expect(addTransactionFn).toHaveBeenCalledTimes(1); - expect(addTransactionBatchFn).not.toHaveBeenCalled(); + const mockCalls = mockMessengerCall.mock.calls; + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(1); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(1); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(0); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(result).toMatchSnapshot(); }); @@ -3703,7 +3665,7 @@ describe('BridgeStatusController', () => { setupEventTrackingMocks(mockMessengerCall); mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); mockMessengerCall.mockReturnValueOnce('arbitrum'); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -3736,8 +3698,17 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); // Should use batch path because gasIncluded7702 = true - expect(addTransactionBatchFn).toHaveBeenCalledTimes(1); - expect(addTransactionFn).not.toHaveBeenCalled(); + const mockCalls = mockMessengerCall.mock.calls; + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(1); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(0); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(result).toMatchSnapshot(); }); @@ -3746,7 +3717,7 @@ describe('BridgeStatusController', () => { setupEventTrackingMocks(mockMessengerCall); mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); mockMessengerCall.mockReturnValueOnce('arbitrum'); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -3789,12 +3760,12 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -3813,9 +3784,6 @@ describe('BridgeStatusController', () => { expect(result).toMatchSnapshot(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); expect(controller.state.txHistory[result.id]).toMatchSnapshot(); - expect(estimateGasFeeFn.mock.calls).toMatchSnapshot(); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn.mock.calls).toMatchSnapshot(); expect(mockMessengerCall.mock.calls).toMatchSnapshot(); }); @@ -3837,9 +3805,22 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); expect(startPollingForBridgeTxStatusSpy).not.toHaveBeenCalled(); - expect(estimateGasFeeFn).not.toHaveBeenCalled(); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn).not.toHaveBeenCalled(); + const mockCalls = mockMessengerCall.mock.calls; + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(0); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(0); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(0); expect(mockMessengerCall).toHaveBeenCalledTimes(5); }); @@ -3850,12 +3831,12 @@ describe('BridgeStatusController', () => { mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); mockMessengerCall.mockReturnValueOnce({ gasFeeEstimates: { estimatedBaseFee: '0x1234' }, }); - estimateGasFeeFn.mockResolvedValueOnce(mockEstimateGasFeeResult); - addTransactionBatchFn.mockResolvedValueOnce({ + mockMessengerCall.mockResolvedValueOnce(mockEstimateGasFeeResult); + mockMessengerCall.mockResolvedValueOnce({ batchId: 'batchId1', }); mockMessengerCall.mockReturnValueOnce({ @@ -3876,35 +3857,23 @@ describe('BridgeStatusController', () => { controller.stopAllPolling(); expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0); - expect(estimateGasFeeFn).toHaveBeenCalledTimes(2); - expect(addTransactionFn).not.toHaveBeenCalled(); - expect(addTransactionBatchFn).toHaveBeenCalledTimes(1); - expect(mockMessengerCall).toHaveBeenCalledTimes(9); - }); - - it('should gracefully handle isAtomicBatchSupported failure', async () => { - // Manually set up mocks without setupEventTrackingMocks - // to control the isAtomicBatchSupported mock - mockMessengerCall.mockReturnValueOnce(mockSelectedAccount); // getAccountByAddress - mockMessengerCall.mockImplementationOnce(jest.fn()); // track event - mockMessengerCall.mockRejectedValueOnce( - new Error('isAtomicBatchSupported failed'), - ); // isAtomicBatchSupported throws - setupApprovalMocks(); - setupBridgeMocks(); - - const { controller } = getController(mockMessengerCall); - const result = await controller.submitTx( - (mockEvmQuoteResponse.trade as TxData).from, - mockEvmQuoteResponse, - false, // STX disabled - uses non-batch path - ); - controller.stopAllPolling(); - - // Should fall back to non-batch path when isAtomicBatchSupported throws - expect(addTransactionFn).toHaveBeenCalledTimes(2); - expect(addTransactionBatchFn).not.toHaveBeenCalled(); - expect(result).toBeDefined(); + const mockCalls = mockMessengerCall.mock.calls; + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(2); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransaction', + ), + ).toHaveLength(0); + expect( + mockCalls.filter( + ([action]) => action === 'TransactionController:addTransactionBatch', + ), + ).toHaveLength(1); + expect(mockMessengerCall).toHaveBeenCalledTimes(12); }); }); @@ -3918,10 +3887,6 @@ describe('BridgeStatusController', () => { messenger: mockMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { ...MockTxHistory.getPending({ @@ -3944,10 +3909,6 @@ describe('BridgeStatusController', () => { messenger: mockMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { bridgeTxMetaId1: { @@ -3984,10 +3945,6 @@ describe('BridgeStatusController', () => { messenger: mockMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { bridgeTxMetaId1: { @@ -4024,10 +3981,6 @@ describe('BridgeStatusController', () => { messenger: mockMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { bridgeTxMetaId1: { @@ -4092,10 +4045,6 @@ describe('BridgeStatusController', () => { messenger: getMessengerMock(), clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { bridgeTxMetaId1: { @@ -4194,10 +4143,6 @@ describe('BridgeStatusController', () => { messenger: mockMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: jest.fn(), - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { noHashTx: { @@ -4254,7 +4199,6 @@ describe('BridgeStatusController', () => { MessengerEvents, RootMessenger >; - let mockTrackEventFn: jest.Mock; let bridgeStatusController: BridgeStatusController; let mockFetchFn: jest.Mock; @@ -4291,25 +4235,6 @@ describe('BridgeStatusController', () => { return Promise.resolve(); }); - const mockBridgeMessenger = new Messenger< - 'BridgeController', - MessengerActions, - MessengerEvents, - RootMessenger - >({ - namespace: 'BridgeController', - parent: mockMessenger, - }); - mockTrackEventFn = jest.fn(); - new BridgeController({ - messenger: mockBridgeMessenger, - clientId: BridgeClientId.EXTENSION, - fetchFn: jest.fn(), - trackMetaMetricsFn: mockTrackEventFn, - getLayer1GasFee: jest.fn(), - clientVersion: '13.4.0', - }); - mockFetchFn = jest .fn() .mockResolvedValueOnce(MockStatusResponse.getPending()); @@ -4321,10 +4246,6 @@ describe('BridgeStatusController', () => { messenger: mockBridgeStatusMessenger, clientId: BridgeClientId.EXTENSION, fetchFn: mockFetchFn, - addTransactionFn: jest.fn(), - addTransactionBatchFn: jest.fn(), - updateTransactionFn: jest.fn(), - estimateGasFeeFn: jest.fn(), state: { txHistory: { ...MockTxHistory.getPending(), diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index d4dbc2f39dd..510fd2ada24 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -130,14 +130,6 @@ export class BridgeStatusController extends StaticIntervalPollingController; clientId: BridgeClientId; fetchFn: FetchFunction; - addTransactionFn: typeof TransactionController.prototype.addTransaction; - addTransactionBatchFn: typeof TransactionController.prototype.addTransactionBatch; - updateTransactionFn: typeof TransactionController.prototype.updateTransaction; - estimateGasFeeFn: typeof TransactionController.prototype.estimateGasFee; config?: { customBridgeApiBaseUrl?: string; }; @@ -178,10 +162,6 @@ export class BridgeStatusController extends StaticIntervalPollingController fn?.()) as TraceCallback); this.#intentManager = new IntentManager({ messenger: this.messenger, - updateTransactionFn: this.#updateTransactionFn, customBridgeApiBaseUrl: this.#config.customBridgeApiBaseUrl, fetchFn: this.#fetchFn, getJwt: this.#getJwt, @@ -1136,8 +1115,7 @@ export class BridgeStatusController extends StaticIntervalPollingController => { // Use provided actionId (for pre-submission history) or generate one - const actionId = providedActionId ?? generateActionId().toString(); - + const actionId = providedActionId ?? generateActionId(); const selectedAccount = this.messenger.call( 'AccountsController:getAccountByAddress', trade.from, @@ -1185,7 +1163,8 @@ export class BridgeStatusController extends StaticIntervalPollingController => { const transactionParams = await getAddTransactionBatchParams({ messenger: this.messenger, - estimateGasFeeFn: this.#estimateGasFeeFn, ...args, }); const txDataByType = { @@ -1286,11 +1263,13 @@ export class BridgeStatusController extends StaticIntervalPollingController | BridgeControllerAction diff --git a/packages/bridge-status-controller/src/utils/gas.test.ts b/packages/bridge-status-controller/src/utils/gas.test.ts index b955d96e768..7ff944f1603 100644 --- a/packages/bridge-status-controller/src/utils/gas.test.ts +++ b/packages/bridge-status-controller/src/utils/gas.test.ts @@ -124,7 +124,6 @@ describe('gas calculation utils', () => { const result = await calculateGasFees( true, null as never, - jest.fn(), mockTrade, 'mainnet', '0x1', @@ -136,7 +135,6 @@ describe('gas calculation utils', () => { const result = await calculateGasFees( false, null as never, - jest.fn(), mockTrade, 'mainnet', '0x1', @@ -169,7 +167,7 @@ describe('gas calculation utils', () => { estimatedBaseFee: '0x1234', }, }); - const mockEstimateGasFeeFn = jest.fn().mockResolvedValueOnce({ + mockCall.mockResolvedValueOnce({ estimates: { [GasFeeEstimateLevel.Medium]: { maxFeePerGas: '0x1234567890', @@ -180,7 +178,6 @@ describe('gas calculation utils', () => { const result = await calculateGasFees( false, { call: mockCall } as never, - mockEstimateGasFeeFn, { ...mockTrade, gasLimit }, 'mainnet', '0x1', diff --git a/packages/bridge-status-controller/src/utils/gas.ts b/packages/bridge-status-controller/src/utils/gas.ts index d4286205536..0f75b84e0ff 100644 --- a/packages/bridge-status-controller/src/utils/gas.ts +++ b/packages/bridge-status-controller/src/utils/gas.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ import { BRIDGE_PREFERRED_GAS_ESTIMATE } from '@metamask/bridge-controller'; import type { TokenAmountValues, TxData } from '@metamask/bridge-controller'; import { toHex } from '@metamask/controller-utils'; @@ -65,7 +66,6 @@ export const getTxGasEstimates = ({ export const calculateGasFees = async ( skipGasFields: boolean, messenger: BridgeStatusControllerMessenger, - estimateGasFeeFn: typeof TransactionController.prototype.estimateGasFee, { chainId: _, gasLimit, ...trade }: TxData, networkClientId: string, chainId: Hex, @@ -80,16 +80,15 @@ export const calculateGasFees = async ( const transactionParams = { ...trade, gas: gasLimit?.toString(), - data: trade.data as `0x${string}`, - to: trade.to as `0x${string}`, - value: trade.value as `0x${string}`, + data: trade.data, + to: trade.to, + value: trade.value, }; const { gasFeeEstimates } = messenger.call('GasFeeController:getState'); - const { estimates: txGasFeeEstimates } = await estimateGasFeeFn({ - transactionParams, - chainId, - networkClientId, - }); + const { estimates: txGasFeeEstimates } = await messenger.call( + 'TransactionController:estimateGasFee', + { transactionParams, chainId, networkClientId }, + ); const { maxFeePerGas, maxPriorityFeePerGas } = getTxGasEstimates({ networkGasFeeEstimates: gasFeeEstimates, txGasFeeEstimates, diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 5d2227e9cb4..af52f413051 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ import { ChainId, FeeType, @@ -1730,7 +1731,7 @@ describe('Bridge Status Controller Transaction Utils', () => { from: '0x1', value: '0x1', }; - const result = toBatchTxParams(true, mockTrade, {}); + const result = toBatchTxParams(true, mockTrade as TxData, {}); expect(result).toStrictEqual({ data: '0x1', from: '0x1', @@ -1822,42 +1823,52 @@ describe('Bridge Status Controller Transaction Utils', () => { }, }) as never; - const createMockMessagingSystem = () => ({ - call: jest.fn().mockImplementation((method: string) => { - if (method === 'AccountsController:getAccountByAddress') { - return mockAccount; - } - if (method === 'NetworkController:getNetworkConfiguration') { - return { - chainId: '0x1', - rpcUrl: 'https://mainnet.infura.io/v3/API_KEY', - }; - } - if (method === 'GasFeeController:getState') { - return { - gasFeeEstimates: { - low: { - suggestedMaxFeePerGas: '20', - suggestedMaxPriorityFeePerGas: '1', - }, - medium: { - suggestedMaxFeePerGas: '30', - suggestedMaxPriorityFeePerGas: '2', - }, - high: { - suggestedMaxFeePerGas: '40', - suggestedMaxPriorityFeePerGas: '3', + const createMockMessagingSystem = ( + estimateGasFeeOverrides: Record = {}, + ) => + ({ + call: jest.fn().mockImplementation((method: string) => { + if (method === 'AccountsController:getAccountByAddress') { + return mockAccount; + } + if (method === 'NetworkController:getNetworkConfiguration') { + return { + chainId: '0x1', + rpcUrl: 'https://mainnet.infura.io/v3/API_KEY', + }; + } + if (method === 'GasFeeController:getState') { + return { + gasFeeEstimates: { + low: { + suggestedMaxFeePerGas: '20', + suggestedMaxPriorityFeePerGas: '1', + }, + medium: { + suggestedMaxFeePerGas: '30', + suggestedMaxPriorityFeePerGas: '2', + }, + high: { + suggestedMaxFeePerGas: '40', + suggestedMaxPriorityFeePerGas: '3', + }, }, - }, - }; - } - return undefined; - }), - }); + }; + } + if (method === 'TransactionController:estimateGasFee') { + return ( + estimateGasFeeOverrides ?? { + estimates: {}, + } + ); + } + return undefined; + }), + }) as unknown as BridgeStatusControllerMessenger; beforeEach(() => { - mockMessagingSystem = - createMockMessagingSystem() as unknown as BridgeStatusControllerMessenger; + jest.clearAllMocks(); + mockMessagingSystem = createMockMessagingSystem(); }); it('should handle gasIncluded7702 flag set to true', async () => { @@ -1872,7 +1883,6 @@ describe('Bridge Status Controller Transaction Utils', () => { isBridgeTx: true, trade: mockQuoteResponse.trade, approval: mockQuoteResponse.approval, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.disable7702).toBe(false); @@ -1894,7 +1904,6 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.disable7702).toBe(true); @@ -1916,7 +1925,6 @@ describe('Bridge Status Controller Transaction Utils', () => { isBridgeTx: false, trade: mockQuoteResponse.trade, approval: mockQuoteResponse.approval, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.transactions).toHaveLength(2); @@ -1935,7 +1943,6 @@ describe('Bridge Status Controller Transaction Utils', () => { isBridgeTx: false, trade: mockQuoteResponse.trade, resetApproval: mockQuoteResponse.resetApproval, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.transactions).toHaveLength(2); @@ -1956,7 +1963,6 @@ describe('Bridge Status Controller Transaction Utils', () => { isBridgeTx: true, trade: mockQuoteResponse.trade, resetApproval: mockQuoteResponse.resetApproval, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.disable7702).toBe(true); @@ -1978,7 +1984,6 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.isGasFeeIncluded).toBe(false); @@ -1995,7 +2000,6 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.isGasFeeIncluded).toBe(true); @@ -2012,7 +2016,6 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, isBridgeTx: false, trade: mockQuoteResponse.trade, - estimateGasFeeFn: jest.fn().mockResolvedValue({}), }); expect(result.isGasFeeIncluded).toBe(false); @@ -2024,7 +2027,7 @@ describe('Bridge Status Controller Transaction Utils', () => { gasIncluded7702: false, }); - const mockEstimateGasFeeFn = jest.fn().mockResolvedValue({ + const mockMessenger = createMockMessagingSystem({ estimates: { medium: { maxFeePerGas: '0xabc', @@ -2032,22 +2035,49 @@ describe('Bridge Status Controller Transaction Utils', () => { }, }, }); + const callSpy = jest.spyOn(mockMessenger, 'call'); const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, - messenger: mockMessagingSystem, + messenger: mockMessenger, isBridgeTx: true, trade: mockQuoteResponse.trade, isDelegatedAccount: true, - estimateGasFeeFn: mockEstimateGasFeeFn, }); // 7702 should be enabled for delegated accounts expect(result.disable7702).toBe(false); // Gas is NOT sponsored expect(result.isGasFeeIncluded).toBe(false); - // Gas estimation should have been called (not skipped) - expect(mockEstimateGasFeeFn).toHaveBeenCalled(); + expect(callSpy.mock.calls).toMatchInlineSnapshot(` + [ + [ + "AccountsController:getAccountByAddress", + "0xUserAddress", + ], + [ + "NetworkController:findNetworkClientIdByChainId", + "0x1", + ], + [ + "GasFeeController:getState", + ], + [ + "TransactionController:estimateGasFee", + { + "chainId": "0x1", + "networkClientId": undefined, + "transactionParams": { + "data": "0xbridgeData", + "from": "0xUserAddress", + "gas": "21000", + "to": "0xBridgeContract", + "value": "0x1000", + }, + }, + ], + ] + `); // Transaction params should include gas fields expect(result.transactions).toHaveLength(1); expect(result.transactions[0].params).toHaveProperty('gas'); @@ -2062,15 +2092,13 @@ describe('Bridge Status Controller Transaction Utils', () => { gasIncluded7702: true, }); - const mockEstimateGasFeeFn = jest.fn().mockResolvedValue({}); - + const callSpy = jest.spyOn(mockMessagingSystem, 'call'); const result = await getAddTransactionBatchParams({ quoteResponse: mockQuoteResponse, messenger: mockMessagingSystem, isBridgeTx: true, trade: mockQuoteResponse.trade, isDelegatedAccount: true, - estimateGasFeeFn: mockEstimateGasFeeFn, }); // 7702 should be enabled @@ -2078,7 +2106,11 @@ describe('Bridge Status Controller Transaction Utils', () => { // Gas IS sponsored expect(result.isGasFeeIncluded).toBe(true); // Gas estimation should NOT have been called (skipped because gas is sponsored) - expect(mockEstimateGasFeeFn).not.toHaveBeenCalled(); + expect( + callSpy.mock.calls.filter( + ([action]) => action === 'TransactionController:estimateGasFee', + ), + ).toHaveLength(0); // Transaction params should NOT include gas fields expect(result.transactions).toHaveLength(1); expect(result.transactions[0].params).not.toHaveProperty('gas'); @@ -2090,9 +2122,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }); describe('findAndUpdateTransactionsInBatch', () => { - const mockUpdateTransactionFn = jest.fn(); const batchId = 'test-batch-id'; - let mockMessagingSystem: BridgeStatusControllerMessenger; const createMockTransaction = (overrides: { id: string; @@ -2119,9 +2149,21 @@ describe('Bridge Status Controller Transaction Utils', () => { // Helper function to create mock messaging system with transactions const createMockMessagingSystemWithTxs = ( txs: ReturnType[], - ) => ({ - call: jest.fn().mockReturnValue({ transactions: txs }), - }); + ) => { + return { + call: jest.fn((method: string, ..._args: unknown[]) => { + if (method === 'TransactionController:getState') { + return { transactions: txs }; + } + if (method === 'TransactionController:updateTransaction') { + return { + transactionMeta: { id: 'tx1', type: TransactionType.swap }, + }; + } + return undefined; + }), + } as unknown as BridgeStatusControllerMessenger; + }; beforeEach(() => { jest.clearAllMocks(); @@ -2140,10 +2182,8 @@ describe('Bridge Status Controller Transaction Utils', () => { data: '0xapprovalData', }), ]; - - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; + const mockMessagingSystem = createMockMessagingSystemWithTxs(txs); + const callSpy = jest.spyOn(mockMessagingSystem, 'call'); const txDataByType = { [TransactionType.swap]: '0xswapData', @@ -2154,26 +2194,43 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); - // Should update the 7702 batch transaction to swap type - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx1', - type: TransactionType.swap, - }), - 'Update tx type to swap', - ); - - // Should update the approval transaction - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx2', - type: TransactionType.swapApproval, - }), - 'Update tx type to swapApproval', - ); + expect( + callSpy.mock.calls.filter( + ([action]) => action === 'TransactionController:updateTransaction', + ), + ).toMatchInlineSnapshot(` + [ + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "id": "tx1", + "txParams": { + "authorizationList": [ + "0xAuth1", + ], + "data": "0xbatchExecuteData", + }, + "type": "swap", + }, + "Update tx type to swap", + ], + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "id": "tx2", + "txParams": { + "data": "0xapprovalData", + }, + "type": "swapApproval", + }, + "Update tx type to swapApproval", + ], + ] + `); }); it('should handle 7702 transactions with delegationAddress', () => { @@ -2186,29 +2243,38 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; - + const mockMessenger = createMockMessagingSystemWithTxs(txs); + const callSpy = jest.spyOn(mockMessenger, 'call'); const txDataByType = { [TransactionType.swap]: '0xswapData', }; findAndUpdateTransactionsInBatch({ - messenger: mockMessagingSystem, + messenger: mockMessenger as unknown as BridgeStatusControllerMessenger, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); // Should identify and update 7702 transaction with delegationAddress - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx1', - type: TransactionType.swap, - }), - 'Update tx type to swap', - ); + expect( + callSpy.mock.calls.find( + ([action]) => action === 'TransactionController:updateTransaction', + ), + ).toMatchInlineSnapshot(` + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "delegationAddress": "0xDelegationAddress", + "id": "tx1", + "txParams": { + "data": "0xbatchData", + }, + "type": "swap", + }, + "Update tx type to swap", + ] + `); }); it('should handle 7702 approval transactions', () => { @@ -2220,29 +2286,42 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; - + const mockMessenger = createMockMessagingSystemWithTxs(txs); + const callSpy = jest.spyOn(mockMessenger, 'call'); const txDataByType = { [TransactionType.swapApproval]: '0xapprovalData', }; findAndUpdateTransactionsInBatch({ - messenger: mockMessagingSystem, + messenger: mockMessenger as unknown as BridgeStatusControllerMessenger, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); // Should match 7702 approval transaction by data - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx1', - type: TransactionType.swapApproval, - }), - 'Update tx type to swapApproval', - ); + expect( + callSpy.mock.calls.filter( + (call) => call[0] === 'TransactionController:updateTransaction', + ), + ).toMatchInlineSnapshot(` + [ + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "id": "tx1", + "txParams": { + "authorizationList": [ + "0xAuth1", + ], + "data": "0xapprovalData", + }, + "type": "swapApproval", + }, + "Update tx type to swapApproval", + ], + ] + `); }); it('should handle non-7702 transactions normally', () => { @@ -2257,38 +2336,52 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; - + const mockMessenger = createMockMessagingSystemWithTxs(txs); + const callSpy = jest.spyOn(mockMessenger, 'call'); const txDataByType = { [TransactionType.bridge]: '0xswapData', [TransactionType.bridgeApproval]: '0xapprovalData', }; findAndUpdateTransactionsInBatch({ - messenger: mockMessagingSystem, + messenger: mockMessenger as unknown as BridgeStatusControllerMessenger, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); // Should update regular transactions by matching data - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx1', - type: TransactionType.bridge, - }), - 'Update tx type to bridge', - ); - - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'tx2', - type: TransactionType.bridgeApproval, - }), - 'Update tx type to bridgeApproval', - ); + expect( + callSpy.mock.calls.filter( + (call) => call[0] === 'TransactionController:updateTransaction', + ), + ).toMatchInlineSnapshot(` + [ + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "id": "tx1", + "txParams": { + "data": "0xswapData", + }, + "type": "bridge", + }, + "Update tx type to bridge", + ], + [ + "TransactionController:updateTransaction", + { + "batchId": "test-batch-id", + "id": "tx2", + "txParams": { + "data": "0xapprovalData", + }, + "type": "bridgeApproval", + }, + "Update tx type to bridgeApproval", + ], + ] + `); }); it('should not update transactions without matching batchId', () => { @@ -2300,10 +2393,8 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; - + const mockMessagingSystem = createMockMessagingSystemWithTxs(txs); + const callSpy = jest.spyOn(mockMessagingSystem, 'call'); const txDataByType = { [TransactionType.swap]: '0xswapData', }; @@ -2312,11 +2403,14 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); // Should not update transactions with different batchId - expect(mockUpdateTransactionFn).not.toHaveBeenCalled(); + expect( + callSpy.mock.calls.filter( + (call) => call[0] === 'TransactionController:updateTransaction', + ), + ).toHaveLength(0); }); it('should handle 7702 bridge transactions', () => { @@ -2329,9 +2423,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( - txs, - ) as unknown as BridgeStatusControllerMessenger; + const mockMessagingSystem = createMockMessagingSystemWithTxs(txs); const txDataByType = { [TransactionType.bridge]: '0xbridgeData', @@ -2342,12 +2434,20 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); // Should match since 7702 bridge transactions use batch type - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ id: 'tx1', type: TransactionType.bridge }), + expect(mockMessagingSystem.call).toHaveBeenCalledWith( + 'TransactionController:updateTransaction', + { + batchId, + id: 'tx1', + txParams: { + authorizationList: ['0xAuth1'], + data: '0xbatchData', + }, + type: TransactionType.bridge, + }, 'Update tx type to bridge', ); expect(result.tradeMeta).toStrictEqual( @@ -2365,7 +2465,7 @@ describe('Bridge Status Controller Transaction Utils', () => { }), ]; - mockMessagingSystem = createMockMessagingSystemWithTxs( + const mockMessagingSystem = createMockMessagingSystemWithTxs( txs, ) as unknown as BridgeStatusControllerMessenger; @@ -2377,14 +2477,19 @@ describe('Bridge Status Controller Transaction Utils', () => { messenger: mockMessagingSystem, batchId, txDataByType, - updateTransactionFn: mockUpdateTransactionFn, }); - expect(mockUpdateTransactionFn).toHaveBeenCalledWith( - expect.objectContaining({ + expect(mockMessagingSystem.call).toHaveBeenCalledWith( + 'TransactionController:updateTransaction', + { + batchId, id: 'tx1', + txParams: { + authorizationList: ['0xAuth1'], + data: '0xapprovalData', + }, type: TransactionType.bridgeApproval, - }), + }, 'Update tx type to bridgeApproval', ); expect(result.approvalMeta).toStrictEqual( diff --git a/packages/bridge-status-controller/src/utils/transaction.ts b/packages/bridge-status-controller/src/utils/transaction.ts index 930cf3940af..dabc7855844 100644 --- a/packages/bridge-status-controller/src/utils/transaction.ts +++ b/packages/bridge-status-controller/src/utils/transaction.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ import type { AccountsControllerState } from '@metamask/accounts-controller'; import { ChainId, @@ -310,9 +311,9 @@ export const toBatchTxParams = ( ): BatchTransactionParams => { const params = { ...trade, - data: trade.data as `0x${string}`, - to: trade.to as `0x${string}`, - value: trade.value as `0x${string}`, + data: trade.data, + to: trade.to, + value: trade.value, }; if (skipGasFields) { return params; @@ -344,14 +345,12 @@ export const getAddTransactionBatchParams = async ({ }, requireApproval = false, isDelegatedAccount = false, - estimateGasFeeFn, }: { messenger: BridgeStatusControllerMessenger; isBridgeTx: boolean; trade: TxData; quoteResponse: Omit & Partial; - estimateGasFeeFn: typeof TransactionController.prototype.estimateGasFee; approval?: TxData; resetApproval?: TxData; requireApproval?: boolean; @@ -384,7 +383,6 @@ export const getAddTransactionBatchParams = async ({ const gasFees = await calculateGasFees( skipGasFields, messenger, - estimateGasFeeFn, resetApproval, networkClientId, hexChainId, @@ -401,7 +399,6 @@ export const getAddTransactionBatchParams = async ({ const gasFees = await calculateGasFees( skipGasFields, messenger, - estimateGasFeeFn, approval, networkClientId, hexChainId, @@ -417,7 +414,6 @@ export const getAddTransactionBatchParams = async ({ const gasFees = await calculateGasFees( skipGasFields, messenger, - estimateGasFeeFn, trade, networkClientId, hexChainId, @@ -440,7 +436,7 @@ export const getAddTransactionBatchParams = async ({ networkClientId, requireApproval, origin: 'metamask', - from: trade.from as `0x${string}`, + from: trade.from, transactions, }; @@ -449,12 +445,10 @@ export const getAddTransactionBatchParams = async ({ export const findAndUpdateTransactionsInBatch = ({ messenger, - updateTransactionFn, batchId, txDataByType, }: { messenger: BridgeStatusControllerMessenger; - updateTransactionFn: typeof TransactionController.prototype.updateTransaction; batchId: string; txDataByType: { [key in TransactionType]?: string }; }) => { @@ -476,7 +470,7 @@ export const findAndUpdateTransactionsInBatch = ({ } // Find transaction by batchId and either matching data or delegation characteristics - const txMeta = txs.find((tx) => { + const txMeta = txs.find((tx: TransactionMeta) => { if (tx.batchId !== batchId) { return false; } @@ -514,7 +508,11 @@ export const findAndUpdateTransactionsInBatch = ({ if (txMeta) { const updatedTx = { ...txMeta, type: txType as TransactionType }; - updateTransactionFn(updatedTx, `Update tx type to ${txType}`); + messenger.call( + 'TransactionController:updateTransaction', + updatedTx, + `Update tx type to ${txType}`, + ); txBatch[ [TransactionType.bridgeApproval, TransactionType.swapApproval].includes( txType as TransactionType, diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index cbf0d4ebbde..f9cfe8422ec 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Remove type assertions when reading hex values from bridge quote data ([#8188](https://github.com/MetaMask/core/pull/8188)) + ## [17.1.0] ### Added diff --git a/packages/transaction-pay-controller/src/strategy/bridge/bridge-quotes.ts b/packages/transaction-pay-controller/src/strategy/bridge/bridge-quotes.ts index ce0602309b8..bbddb96f65d 100644 --- a/packages/transaction-pay-controller/src/strategy/bridge/bridge-quotes.ts +++ b/packages/transaction-pay-controller/src/strategy/bridge/bridge-quotes.ts @@ -6,7 +6,6 @@ import { toChecksumHexAddress, toHex } from '@metamask/controller-utils'; import { TransactionType } from '@metamask/transaction-controller'; import type { BatchTransaction } from '@metamask/transaction-controller'; import type { TransactionMeta } from '@metamask/transaction-controller'; -import type { Hex } from '@metamask/utils'; import { createModuleLogger } from '@metamask/utils'; import { BigNumber } from 'bignumber.js'; import { orderBy } from 'lodash'; @@ -167,10 +166,10 @@ export async function refreshQuote( * @returns Batch transaction. */ function getBatchTransaction(transaction: TxData): BatchTransaction { - const data = transaction.data as Hex; + const { data } = transaction; const gas = transaction.gasLimit ? toHex(transaction.gasLimit) : undefined; - const to = transaction.to as Hex; - const value = transaction.value as Hex; + const { to } = transaction; + const { value } = transaction; return { data,