From b6036b5a3a1ea04e533d5bbc98da840beea00b67 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:04:27 -0500 Subject: [PATCH 01/48] chore: move bridge status mocks to own folder --- .../bridge-status-controller.test.ts | 262 +--------------- .../controllers/bridge-status/mocks.ts | 292 ++++++++++++++++++ 2 files changed, 297 insertions(+), 257 deletions(-) create mode 100644 app/scripts/controllers/bridge-status/mocks.ts diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts index 7d82d32284f4..e10df4a6f521 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts @@ -1,271 +1,19 @@ import { flushPromises } from '../../../../test/lib/timer-helpers'; import { Numeric } from '../../../../shared/modules/Numeric'; -import { - StatusTypes, - ActionTypes, - BridgeId, -} from '../../../../shared/types/bridge-status'; import BridgeStatusController from './bridge-status-controller'; import { BridgeStatusControllerMessenger } from './types'; import { DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE } from './constants'; import * as bridgeStatusUtils from './utils'; +import { + MockStatusResponse, + MockTxHistory, + getMockStartPollingForBridgeTxStatusArgs, +} from './mocks'; const EMPTY_INIT_STATE = { bridgeStatusState: DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE, }; -const getMockQuote = ({ srcChainId = 42161, destChainId = 10 } = {}) => ({ - requestId: '197c402f-cb96-4096-9f8c-54aed84ca776', - srcChainId, - srcTokenAmount: '991250000000000', - srcAsset: { - address: '0x0000000000000000000000000000000000000000', - chainId: srcChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.7', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - destChainId, - destTokenAmount: '990654755978612', - destAsset: { - address: '0x0000000000000000000000000000000000000000', - chainId: destChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.63', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - feeData: { - metabridge: { - amount: '8750000000000', - asset: { - address: '0x0000000000000000000000000000000000000000', - chainId: srcChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.7', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - }, - }, - bridgeId: 'lifi', - bridges: ['across'], - steps: [ - { - action: 'bridge' as ActionTypes, - srcChainId, - destChainId, - protocol: { - name: 'across', - displayName: 'Across', - icon: 'https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png', - }, - srcAsset: { - address: '0x0000000000000000000000000000000000000000', - chainId: srcChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.7', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - destAsset: { - address: '0x0000000000000000000000000000000000000000', - chainId: destChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.63', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - srcAmount: '991250000000000', - destAmount: '990654755978612', - }, - ], -}); - -const getMockStartPollingForBridgeTxStatusArgs = ({ - srcTxHash = '0xsrcTxHash1', - account = '0xaccount1', - srcChainId = 42161, - destChainId = 10, -} = {}) => ({ - statusRequest: { - bridgeId: 'lifi', - srcTxHash, - bridge: 'across', - srcChainId, - destChainId, - quote: getMockQuote({ srcChainId, destChainId }), - refuel: false, - }, - quoteResponse: { - quote: getMockQuote({ srcChainId, destChainId }), - trade: { - chainId: srcChainId, - to: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', - from: account, - value: '0x038d7ea4c68000', - data: '0x3ce33bff0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d6c6966694164617074657256320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038589602234000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000007f544a44c0000000000000000000000000056ca675c3633cc16bd6849e2b431d4e8de5e23bf000000000000000000000000000000000000000000000000000000000000006c5a39b10a4f4f0747826140d2c5fe6ef47965741f6f7a4734bf784bf3ae3f24520000000a000222266cc2dca0671d2a17ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00dfeeddeadbeef8932eb23bad9bddb5cf81426f78279a53c6c3b7100000000000000000000000000000000000000009ce3c510b3f58edc8d53ae708056e30926f62d0b42d5c9b61c391bb4e8a2c1917f8ed995169ffad0d79af2590303e83c57e15a9e0b248679849556c2e03a1c811b', - gasLimit: 282915, - }, - approval: null, - estimatedProcessingTimeInSeconds: 15, - }, - startTime: 1729964825189, - slippagePercentage: 0, - pricingData: undefined, - initialDestAssetBalance: undefined, - targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', -}); - -const MockStatusResponse = { - getPending: ({ - srcTxHash = '0xsrcTxHash1', - srcChainId = 42161, - destChainId = 10, - } = {}) => ({ - status: 'PENDING' as StatusTypes, - srcChain: { - chainId: srcChainId, - txHash: srcTxHash, - amount: '991250000000000', - token: { - address: '0x0000000000000000000000000000000000000000', - chainId: srcChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2518.47', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - }, - destChain: { - chainId: destChainId, - token: {}, - }, - }), - getComplete: ({ - srcTxHash = '0xsrcTxHash1', - destTxHash = '0xdestTxHash1', - srcChainId = 42161, - destChainId = 10, - } = {}) => ({ - status: 'COMPLETE' as StatusTypes, - isExpectedToken: true, - bridge: 'across' as BridgeId, - srcChain: { - chainId: srcChainId, - txHash: srcTxHash, - amount: '991250000000000', - token: { - address: '0x0000000000000000000000000000000000000000', - chainId: srcChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.7', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - }, - destChain: { - chainId: destChainId, - txHash: destTxHash, - amount: '990654755978611', - token: { - address: '0x0000000000000000000000000000000000000000', - chainId: destChainId, - symbol: 'ETH', - decimals: 18, - name: 'ETH', - coinKey: 'ETH', - logoURI: - 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - priceUSD: '2478.63', - icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', - }, - }, - }), -}; - -const MockTxHistory = { - getInit: ({ - srcTxHash = '0xsrcTxHash1', - account = '0xaccount1', - srcChainId = 42161, - destChainId = 10, - } = {}) => ({ - [srcTxHash]: { - quote: getMockQuote({ srcChainId, destChainId }), - startTime: 1729964825189, - estimatedProcessingTimeInSeconds: 15, - slippagePercentage: 0, - account, - targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', - }, - }), - getPending: ({ - srcTxHash = '0xsrcTxHash1', - account = '0xaccount1', - srcChainId = 42161, - destChainId = 10, - } = {}) => ({ - [srcTxHash]: { - quote: getMockQuote({ srcChainId, destChainId }), - startTime: 1729964825189, - estimatedProcessingTimeInSeconds: 15, - slippagePercentage: 0, - account, - status: MockStatusResponse.getPending({ - srcTxHash, - srcChainId, - }), - targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', - }, - }), - getComplete: ({ - srcTxHash = '0xsrcTxHash1', - account = '0xaccount1', - srcChainId = 42161, - destChainId = 10, - } = {}) => ({ - [srcTxHash]: { - quote: getMockQuote({ srcChainId, destChainId }), - startTime: 1729964825189, - estimatedProcessingTimeInSeconds: 15, - slippagePercentage: 0, - account, - status: MockStatusResponse.getComplete({ srcTxHash }), - targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', - }, - }), -}; - const getMessengerMock = ({ account = '0xaccount1', srcChainId = 42161, diff --git a/app/scripts/controllers/bridge-status/mocks.ts b/app/scripts/controllers/bridge-status/mocks.ts new file mode 100644 index 000000000000..c631fe84ab3a --- /dev/null +++ b/app/scripts/controllers/bridge-status/mocks.ts @@ -0,0 +1,292 @@ +import { + BridgeId, + StatusResponse, + StatusTypes, + ActionTypes, +} from '../../../../shared/types/bridge-status'; + +export const MockStatusResponse = { + getPending: ({ + srcTxHash = '0xsrcTxHash1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + status: 'PENDING' as StatusTypes, + srcChain: { + chainId: srcChainId, + txHash: srcTxHash, + amount: '991250000000000', + token: { + address: '0x0000000000000000000000000000000000000000', + chainId: srcChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2518.47', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + }, + destChain: { + chainId: destChainId, + token: {}, + }, + }), + getComplete: ({ + srcTxHash = '0xsrcTxHash1', + destTxHash = '0xdestTxHash1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + status: 'COMPLETE' as StatusTypes, + isExpectedToken: true, + bridge: 'across' as BridgeId, + srcChain: { + chainId: srcChainId, + txHash: srcTxHash, + amount: '991250000000000', + token: { + address: '0x0000000000000000000000000000000000000000', + chainId: srcChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.7', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + }, + destChain: { + chainId: destChainId, + txHash: destTxHash, + amount: '990654755978611', + token: { + address: '0x0000000000000000000000000000000000000000', + chainId: destChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.63', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + }, + }), +}; + +let mockFetchBridgeTxStatusCount = 0; +export const mockFetchBridgeTxStatus: () => Promise = () => { + return new Promise((resolve) => { + console.log('HELLO mockFetchBridgeTxStatus', mockFetchBridgeTxStatusCount); + setTimeout(() => { + if (mockFetchBridgeTxStatusCount === 0) { + resolve( + MockStatusResponse.getPending({ + srcTxHash: + '0xe3e223b9725765a7de557effdb2b507ace3534bcff2c1fe3a857e0791e56a518', + srcChainId: 1, + destChainId: 42161, + }), + ); + } else { + resolve( + MockStatusResponse.getComplete({ + srcTxHash: + '0xe3e223b9725765a7de557effdb2b507ace3534bcff2c1fe3a857e0791e56a518', + destTxHash: + '0x010e1bffe8288956012e6b6132d7eb3eaf9d0bbf066bd13aae13b973c678508f', + srcChainId: 1, + destChainId: 42161, + }), + ); + } + mockFetchBridgeTxStatusCount += 1; + }, 2000); + }); +}; + +export const getMockQuote = ({ + srcChainId = 42161, + destChainId = 10, +} = {}) => ({ + requestId: '197c402f-cb96-4096-9f8c-54aed84ca776', + srcChainId, + srcTokenAmount: '991250000000000', + srcAsset: { + address: '0x0000000000000000000000000000000000000000', + chainId: srcChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.7', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + destChainId, + destTokenAmount: '990654755978612', + destAsset: { + address: '0x0000000000000000000000000000000000000000', + chainId: destChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.63', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + feeData: { + metabridge: { + amount: '8750000000000', + asset: { + address: '0x0000000000000000000000000000000000000000', + chainId: srcChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.7', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + }, + }, + bridgeId: 'lifi', + bridges: ['across'], + steps: [ + { + action: 'bridge' as ActionTypes, + srcChainId, + destChainId, + protocol: { + name: 'across', + displayName: 'Across', + icon: 'https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png', + }, + srcAsset: { + address: '0x0000000000000000000000000000000000000000', + chainId: srcChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.7', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + destAsset: { + address: '0x0000000000000000000000000000000000000000', + chainId: destChainId, + symbol: 'ETH', + decimals: 18, + name: 'ETH', + coinKey: 'ETH', + logoURI: + 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + priceUSD: '2478.63', + icon: 'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png', + }, + srcAmount: '991250000000000', + destAmount: '990654755978612', + }, + ], +}); + +export const getMockStartPollingForBridgeTxStatusArgs = ({ + srcTxHash = '0xsrcTxHash1', + account = '0xaccount1', + srcChainId = 42161, + destChainId = 10, +} = {}) => ({ + statusRequest: { + bridgeId: 'lifi', + srcTxHash, + bridge: 'across', + srcChainId, + destChainId, + quote: getMockQuote({ srcChainId, destChainId }), + refuel: false, + }, + quoteResponse: { + quote: getMockQuote({ srcChainId, destChainId }), + trade: { + chainId: srcChainId, + to: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + from: account, + value: '0x038d7ea4c68000', + data: '0x3ce33bff0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d6c6966694164617074657256320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000038589602234000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000007f544a44c0000000000000000000000000056ca675c3633cc16bd6849e2b431d4e8de5e23bf000000000000000000000000000000000000000000000000000000000000006c5a39b10a4f4f0747826140d2c5fe6ef47965741f6f7a4734bf784bf3ae3f24520000000a000222266cc2dca0671d2a17ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00dfeeddeadbeef8932eb23bad9bddb5cf81426f78279a53c6c3b7100000000000000000000000000000000000000009ce3c510b3f58edc8d53ae708056e30926f62d0b42d5c9b61c391bb4e8a2c1917f8ed995169ffad0d79af2590303e83c57e15a9e0b248679849556c2e03a1c811b', + gasLimit: 282915, + }, + approval: null, + estimatedProcessingTimeInSeconds: 15, + }, + startTime: 1729964825189, + slippagePercentage: 0, + pricingData: undefined, + initialDestAssetBalance: undefined, + targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', +}); + +export const MockTxHistory = { + getInit: ({ + srcTxHash = '0xsrcTxHash1', + account = '0xaccount1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + [srcTxHash]: { + quote: getMockQuote({ srcChainId, destChainId }), + startTime: 1729964825189, + estimatedProcessingTimeInSeconds: 15, + slippagePercentage: 0, + account, + targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + }, + }), + getPending: ({ + srcTxHash = '0xsrcTxHash1', + account = '0xaccount1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + [srcTxHash]: { + quote: getMockQuote({ srcChainId, destChainId }), + startTime: 1729964825189, + estimatedProcessingTimeInSeconds: 15, + slippagePercentage: 0, + account, + status: MockStatusResponse.getPending({ + srcTxHash, + srcChainId, + }), + targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + }, + }), + getComplete: ({ + srcTxHash = '0xsrcTxHash1', + account = '0xaccount1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + [srcTxHash]: { + quote: getMockQuote({ srcChainId, destChainId }), + startTime: 1729964825189, + estimatedProcessingTimeInSeconds: 15, + slippagePercentage: 0, + account, + status: MockStatusResponse.getComplete({ srcTxHash }), + targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + }, + }), +}; From 9cedb91457ed3603c0146549e9e5d688aa788c70 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:06:11 -0500 Subject: [PATCH 02/48] fix: show segments for bridge txs always --- .../transaction-list-item.component.js | 2 +- .../bridge-activity-item-tx-segments.tsx | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js index 060bc8158e92..60f3b46d31d1 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js @@ -355,7 +355,7 @@ function TransactionListItemInner({ ///: END:ONLY_INCLUDE_IF } subtitle={ - isBridgeTx && isBridgeComplete === false ? ( + isBridgeTx && !isBridgeComplete ? ( { : StatusTypes.PENDING; }; -const getDestTxStatus = ( - bridgeTxHistoryItem: BridgeHistoryItem, - srcTxStatus: StatusTypes, -) => { +const getDestTxStatus = ({ + bridgeTxHistoryItem, + srcTxStatus, +}: { + bridgeTxHistoryItem?: BridgeHistoryItem; + srcTxStatus: StatusTypes; +}) => { if (srcTxStatus !== StatusTypes.COMPLETE) { return null; } @@ -60,12 +63,12 @@ export default function BridgeActivityItemTxSegments({ bridgeTxHistoryItem, transactionGroup, }: { - bridgeTxHistoryItem: BridgeHistoryItem; + bridgeTxHistoryItem?: BridgeHistoryItem; transactionGroup: UseBridgeDataProps['transactionGroup']; }) { const { initialTransaction } = transactionGroup; const srcTxStatus = getSrcTxStatus(initialTransaction); - const destTxStatus = getDestTxStatus(bridgeTxHistoryItem, srcTxStatus); + const destTxStatus = getDestTxStatus({ bridgeTxHistoryItem, srcTxStatus }); const txIndex = getTxIndex(srcTxStatus); return ( From edafc9615d5d4c1cc6a19e817212d5c5d8634639 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:09:51 -0500 Subject: [PATCH 03/48] chore: add comment --- ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts index fcdf8569bdd0..143d239120f2 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts @@ -21,6 +21,7 @@ export default function useSubmitBridgeTransaction() { // Execute transaction(s) let approvalTxMeta: TransactionMeta | undefined; if (quoteResponse?.approval) { + // This will never be an STX approvalTxMeta = await handleApprovalTx({ approval: quoteResponse.approval, quoteResponse, From 53018e152068e234d6594ce64d0910bcb2d195af Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:16:50 -0500 Subject: [PATCH 04/48] chore: ignore yalc files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 074f4076a7cc..6ee150dd8653 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.yalc +yalc.lock + npm-debug.log yarn-error.log node_modules From e33258f350d586ec00808feeadcbaf617ad0e321 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:27:14 -0500 Subject: [PATCH 05/48] chore: refactor --- .../bridge-status/bridge-status-controller.ts | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 86e87d9d0622..268250df6e74 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -182,31 +182,33 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // Write all non-status fields to state so we can reference the quote in Activity list without the Bridge API // We know it's in progress but not the exact status yet + const txHistoryItem = { + quote: quoteResponse.quote, + startTime, + estimatedProcessingTimeInSeconds: + quoteResponse.estimatedProcessingTimeInSeconds, + slippagePercentage, + pricingData, + initialDestAssetBalance, + targetContractAddress, + account, + status: { + // We always have a PENDING status when we start polling for a tx, don't need the Bridge API for that + // Also we know the bare minimum fields for status at this point in time + status: StatusTypes.PENDING, + srcChain: { + chainId: statusRequest.srcChainId, + txHash: statusRequest.srcTxHash, + }, + }, + }; + this.update((_state) => { _state.bridgeStatusState = { ...bridgeStatusState, txHistory: { ...bridgeStatusState.txHistory, - [statusRequest.srcTxHash]: { - quote: quoteResponse.quote, - startTime, - estimatedProcessingTimeInSeconds: - quoteResponse.estimatedProcessingTimeInSeconds, - slippagePercentage, - pricingData, - initialDestAssetBalance, - targetContractAddress, - account, - status: { - // We always have a PENDING status when we start polling for a tx, don't need the Bridge API for that - // Also we know the bare minimum fields for status at this point in time - status: StatusTypes.PENDING, - srcChain: { - chainId: statusRequest.srcChainId, - txHash: statusRequest.srcTxHash, - }, - }, - }, + [statusRequest.srcTxHash]: txHistoryItem, }, }; }); From fe52cb65ab7037c271f9e239f232ba66d0ece6ac Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:28:27 -0500 Subject: [PATCH 06/48] chore: bump packages so we can use tx controller 39 --- yarn.lock | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index c88156929c17..48ea08317fa9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6441,13 +6441,17 @@ __metadata: "@ethersproject/abi": "npm:^5.7.0" "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" - "@metamask/base-controller": "npm:^7.0.2" - "@metamask/controller-utils": "npm:^11.4.3" + "@metamask/accounts-controller": "npm:^17.2.0" + "@metamask/approval-controller": "npm:^7.0.0" + "@metamask/base-controller": "npm:^6.0.0" + "@metamask/controller-utils": "npm:^11.0.0" "@metamask/eth-query": "npm:^4.0.0" + "@metamask/gas-fee-controller": "npm:^18.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/nonce-tracker": "npm:^6.0.0" - "@metamask/rpc-errors": "npm:^7.0.1" - "@metamask/utils": "npm:^10.0.0" + "@metamask/network-controller": "npm:^19.0.0" + "@metamask/nonce-tracker": "npm:^5.0.0" + "@metamask/rpc-errors": "npm:^6.2.1" + "@metamask/utils": "npm:^8.3.0" async-mutex: "npm:^0.5.0" bn.js: "npm:^5.2.1" eth-method-registry: "npm:^4.0.0" @@ -6456,7 +6460,7 @@ __metadata: uuid: "npm:^8.3.2" peerDependencies: "@babel/runtime": ^7.23.9 - "@metamask/accounts-controller": ^20.0.0 + "@metamask/accounts-controller": ^17.0.0 "@metamask/approval-controller": ^7.0.0 "@metamask/gas-fee-controller": ^22.0.0 "@metamask/network-controller": ^22.0.0 From 09dfc0764c605845db5d085d5445d97d0e862173 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:31:47 -0500 Subject: [PATCH 07/48] feat: hide stx status screen if it's a bridge tx and route immediately to activity --- .../lib/transaction/smart-transactions.ts | 28 ++++++++++++------- ui/pages/bridge/hooks/useHandleTx.ts | 2 +- .../hooks/useSubmitBridgeTransaction.ts | 9 +++--- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/scripts/lib/transaction/smart-transactions.ts b/app/scripts/lib/transaction/smart-transactions.ts index c80cd8373d2a..c045aa15f937 100644 --- a/app/scripts/lib/transaction/smart-transactions.ts +++ b/app/scripts/lib/transaction/smart-transactions.ts @@ -126,6 +126,8 @@ class SmartTransactionHook { } async submit() { + const shouldShowStatusPage = + this.#transactionMeta.type !== TransactionType.bridge; const isUnsupportedTransactionTypeForSmartTransaction = this .#transactionMeta?.type ? [TransactionType.swapAndSend, TransactionType.swapApproval].includes( @@ -141,10 +143,13 @@ class SmartTransactionHook { ) { return useRegularTransactionSubmit; } - const { id: approvalFlowId } = await this.#controllerMessenger.call( - 'ApprovalController:startFlow', - ); - this.#approvalFlowId = approvalFlowId; + + if (shouldShowStatusPage) { + const { id: approvalFlowId } = await this.#controllerMessenger.call( + 'ApprovalController:startFlow', + ); + this.#approvalFlowId = approvalFlowId; + } let getFeesResponse; try { getFeesResponse = await this.#smartTransactionsController.getFees( @@ -169,12 +174,15 @@ class SmartTransactionHook { } const extensionReturnTxHashAsap = this.#featureFlags?.smartTransactions?.extensionReturnTxHashAsap; - this.#addApprovalRequest({ - uuid, - }); - this.#addListenerToUpdateStatusPage({ - uuid, - }); + + if (shouldShowStatusPage) { + this.#addApprovalRequest({ + uuid, + }); + this.#addListenerToUpdateStatusPage({ + uuid, + }); + } let transactionHash: string | undefined | null; if (extensionReturnTxHashAsap && submitTransactionResponse?.txHash) { transactionHash = submitTransactionResponse.txHash; diff --git a/ui/pages/bridge/hooks/useHandleTx.ts b/ui/pages/bridge/hooks/useHandleTx.ts index ffd378449bc7..d89cc90a478f 100644 --- a/ui/pages/bridge/hooks/useHandleTx.ts +++ b/ui/pages/bridge/hooks/useHandleTx.ts @@ -39,7 +39,7 @@ export default function useHandleTx() { }; swapsOptions: { hasApproveTx: boolean; - meta: Partial; + meta: Partial; // all fields in meta are merged with TransactionMeta downstream in TransactionController }; }) => { const hexChainId = decimalToPrefixedHex(txParams.chainId); diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts index 143d239120f2..b7ec8b31b2ec 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts @@ -28,6 +28,11 @@ export default function useSubmitBridgeTransaction() { }); } + // Route user to activity tab on Home page + // Do it ahead of time because otherwise STX waits for a txHash on TransactionType.bridge and that can take a while + await dispatch(setDefaultHomeActiveTabName('activity')); + history.push(DEFAULT_ROUTE); + const bridgeTxMeta = await handleBridgeTx({ quoteResponse, approvalTxId: approvalTxMeta?.id, @@ -61,10 +66,6 @@ export default function useSubmitBridgeTransaction() { if (quoteResponse.quote.destAsset.address !== zeroAddress()) { await addDestToken(quoteResponse); } - - // Route user to activity tab on Home page - await dispatch(setDefaultHomeActiveTabName('activity')); - history.push(DEFAULT_ROUTE); }; return { From 07294ab09e36066e32fb390f4821b212445aa1c0 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:35:21 -0500 Subject: [PATCH 08/48] chore: use txMeta.id to identify txs if no txHash, add destChainId to txMeta so we can read it from just txMeta if no bridgeHistoryItem --- ui/hooks/bridge/useBridgeChainInfo.ts | 42 +++++++++++--- ui/hooks/bridge/useBridgeTxHistoryData.ts | 15 +++-- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 3 + .../transaction-details.tsx | 58 +++++++++++-------- ui/pages/routes/routes.component.js | 2 +- 5 files changed, 80 insertions(+), 40 deletions(-) diff --git a/ui/hooks/bridge/useBridgeChainInfo.ts b/ui/hooks/bridge/useBridgeChainInfo.ts index f5229391ff44..279a72e5f377 100644 --- a/ui/hooks/bridge/useBridgeChainInfo.ts +++ b/ui/hooks/bridge/useBridgeChainInfo.ts @@ -1,4 +1,5 @@ import { useSelector } from 'react-redux'; +import { TransactionMeta } from '@metamask/transaction-controller'; import { Hex } from '@metamask/utils'; import { NetworkConfiguration } from '@metamask/network-controller'; import { Numeric } from '../../../shared/modules/Numeric'; @@ -10,26 +11,49 @@ import { } from '../../../shared/constants/network'; import { CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../shared/constants/common'; +const getSourceAndDestChainIds = ({ + bridgeHistoryItem, + srcTxMeta, +}: UseBridgeChainInfoProps) => { + const hexSrcChainId = bridgeHistoryItem + ? (new Numeric( + bridgeHistoryItem.quote.srcChainId, + 10, + ).toPrefixedHexString() as Hex) + : srcTxMeta?.chainId; + const hexDestChainId = bridgeHistoryItem + ? (new Numeric( + bridgeHistoryItem.quote.destChainId, + 10, + ).toPrefixedHexString() as Hex) + : srcTxMeta?.destinationChainId; + + return { + hexSrcChainId, + hexDestChainId, + }; +}; + +/** + * Can use either a bridgeHistoryItem or a transactionGroup to get the chain info + */ export type UseBridgeChainInfoProps = { bridgeHistoryItem?: BridgeHistoryItem; + srcTxMeta?: TransactionMeta; }; export default function useBridgeChainInfo({ bridgeHistoryItem, + srcTxMeta, }: UseBridgeChainInfoProps) { const networkConfigurationsByChainId = useSelector( getNetworkConfigurationsByChainId, ); - const decSrcChainId = bridgeHistoryItem?.quote.srcChainId; - const hexSrcChainId = decSrcChainId - ? (new Numeric(decSrcChainId, 10).toPrefixedHexString() as Hex) - : undefined; - - const decDestChainId = bridgeHistoryItem?.quote.destChainId; - const hexDestChainId = decDestChainId - ? (new Numeric(decDestChainId, 10).toPrefixedHexString() as Hex) - : undefined; + const { hexSrcChainId, hexDestChainId } = getSourceAndDestChainIds({ + bridgeHistoryItem, + srcTxMeta, + }); if (!bridgeHistoryItem || !hexSrcChainId || !hexDestChainId) { return { diff --git a/ui/hooks/bridge/useBridgeTxHistoryData.ts b/ui/hooks/bridge/useBridgeTxHistoryData.ts index 97cc7f5675a3..a36821ad2e38 100644 --- a/ui/hooks/bridge/useBridgeTxHistoryData.ts +++ b/ui/hooks/bridge/useBridgeTxHistoryData.ts @@ -27,11 +27,12 @@ export default function useBridgeTxHistoryData({ const srcTxHash = transactionGroup.initialTransaction.hash; - // If this tx is a bridge tx, it will have a bridgeHistoryItem + // If this tx is a bridge tx and not a smart transaction, it will always have a bridgeHistoryItem const bridgeHistoryItem = srcTxHash ? bridgeHistory[srcTxHash] : undefined; const { destNetwork } = useBridgeChainInfo({ bridgeHistoryItem, + srcTxMeta: transactionGroup.initialTransaction, }); const destChainName = destNetwork?.name; @@ -47,11 +48,13 @@ export default function useBridgeTxHistoryData({ ) : null; - const showBridgeTxDetails = srcTxHash - ? () => { - history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxHash}`); - } - : null; + // We should be able to use a srcTxHash or a txMeta.id, STX won't have txHash right away + // the txMeta.id is just a fallback, we prefer using srcTxHash + const srcTxHashOrTxId = srcTxHash || transactionGroup.initialTransaction.id; + + const showBridgeTxDetails = () => { + history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxHashOrTxId}`); + }; return { bridgeTitleSuffix, diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index f8135a1bbc08..e8c782b1595f 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -27,6 +27,9 @@ export default function useHandleBridgeTx() { swapsOptions: { hasApproveTx: Boolean(quoteResponse?.approval), meta: { + destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, // estimatedBaseFee: decEstimatedBaseFee, // swapMetaData, type: TransactionType.bridge, diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index 780186a671e0..6865c968a2a4 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -71,28 +71,34 @@ const CrossChainSwapTxDetails = () => { const t = useI18nContext(); const rootState = useSelector((state) => state); const history = useHistory(); - const { srcTxHash } = useParams<{ srcTxHash: string }>(); + // we should be able to use a srcTxHash or a txMeta.id, STX won't have txHash right away + const { srcTxHashOrTxId } = useParams<{ srcTxHashOrTxId: string }>(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); const selectedAddressTxList = useSelector( selectedAddressTxListSelector, ) as TransactionMeta[]; - const bridgeHistoryItem = srcTxHash ? bridgeHistory[srcTxHash] : undefined; + + const bridgeHistoryItem = srcTxHashOrTxId + ? bridgeHistory[srcTxHashOrTxId] + : undefined; const networkConfigurationsByChainId = useSelector( getNetworkConfigurationsByChainId, ); + + const srcChainTxMeta = selectedAddressTxList.find( + (tx) => tx.hash === srcTxHashOrTxId || tx.id === srcTxHashOrTxId, + ); + const { srcNetwork, destNetwork } = useBridgeChainInfo({ bridgeHistoryItem, + srcTxMeta: srcChainTxMeta, }); - const srcBlockExplorerUrl = getBlockExplorerUrl(srcNetwork, srcTxHash); + const srcBlockExplorerUrl = getBlockExplorerUrl(srcNetwork, srcTxHashOrTxId); const destTxHash = bridgeHistoryItem?.status.destChain?.txHash; const destBlockExplorerUrl = getBlockExplorerUrl(destNetwork, destTxHash); - const srcChainTxMeta = selectedAddressTxList.find( - (tx) => tx.hash === srcTxHash, - ); - const status = bridgeHistoryItem?.status.status; const destChainIconUrl = destNetwork @@ -141,24 +147,28 @@ const CrossChainSwapTxDetails = () => { flexDirection={FlexDirection.Column} gap={4} > - {status !== StatusTypes.COMPLETE && bridgeHistoryItem && ( - - )} - - {/* Links to block explorers */} - - - + {bridgeHistoryItem && ( + <> + {status !== StatusTypes.COMPLETE && bridgeHistoryItem && ( + + )} + {/* Links to block explorers */} + + + + )} {/* General tx details */} From 23eb6eb9b2ec1c05fdf6b038bc1aa083ba0b5ffb Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:39:27 -0500 Subject: [PATCH 09/48] fix: error with i18n when no dest explorer ready --- .../bridge/transaction-details/bridge-explorer-links.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/pages/bridge/transaction-details/bridge-explorer-links.tsx b/ui/pages/bridge/transaction-details/bridge-explorer-links.tsx index 570680f416d2..6c7c06338567 100644 --- a/ui/pages/bridge/transaction-details/bridge-explorer-links.tsx +++ b/ui/pages/bridge/transaction-details/bridge-explorer-links.tsx @@ -73,9 +73,11 @@ export default function BridgeExplorerLinks({ const srcButtonText = t('bridgeExplorerLinkViewOn', [ getBlockExplorerName(srcChainId, srcBlockExplorerUrl), ]); - const destButtonText = t('bridgeExplorerLinkViewOn', [ - getBlockExplorerName(destChainId, destBlockExplorerUrl), - ]); + const destButtonText = destBlockExplorerUrl + ? t('bridgeExplorerLinkViewOn', [ + getBlockExplorerName(destChainId, destBlockExplorerUrl), + ]) + : undefined; return ( From 119373cacac63cb3582211051b1396562197ba80 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:42:55 -0500 Subject: [PATCH 10/48] chore: add bridgeSteps to txMeta during bridge tx --- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index e8c782b1595f..e7795c2d2ce5 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -40,6 +40,32 @@ export default function useHandleBridgeTx() { approvalTxId, // this is the decimal (non atomic) amount (not USD value) of source token to swap swapTokenValue: sentAmountDec, + // Convert chainIds to hex + bridgeSteps: quoteResponse.quote.steps.map((step) => ({ + ...step, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + action: step.action as any, + srcChainId: new Numeric(step.srcChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + destChainId: step.destChainId + ? (new Numeric(step.destChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`) + : undefined, + srcAsset: { + ...step.srcAsset, + chainId: new Numeric(step.srcAsset.chainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + }, + destAsset: { + ...step.destAsset, + chainId: new Numeric(step.destAsset.chainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + }, + })), }, }, }); From 6d137f0ce53b3afd881cb46bedf3d2fb029f5958 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:02:04 -0500 Subject: [PATCH 11/48] chore: display steps even though we only have txMeta.id --- .../bridge-step-description.tsx | 18 ++++++--- .../transaction-details/bridge-step-list.tsx | 34 +++++++++++++--- .../step-progress-bar-item.tsx | 2 +- .../transaction-details.tsx | 40 +++++++++---------- 4 files changed, 61 insertions(+), 33 deletions(-) diff --git a/ui/pages/bridge/transaction-details/bridge-step-description.tsx b/ui/pages/bridge/transaction-details/bridge-step-description.tsx index ef69ea4ed116..03abb77a32e2 100644 --- a/ui/pages/bridge/transaction-details/bridge-step-description.tsx +++ b/ui/pages/bridge/transaction-details/bridge-step-description.tsx @@ -117,11 +117,19 @@ const getSwapActionText = ( ]); }; -export const getStepStatus = ( - bridgeHistoryItem: BridgeHistoryItem, - step: Step, - srcChainTxMeta?: TransactionMeta, -) => { +export const getStepStatus = ({ + bridgeHistoryItem, + step, + srcChainTxMeta, +}: { + bridgeHistoryItem?: BridgeHistoryItem; + step: Step; + srcChainTxMeta?: TransactionMeta; +}) => { + if (!bridgeHistoryItem) { + return StatusTypes.UNKNOWN; + } + if (step.action === ActionTypes.SWAP) { return getSwapActionStatus(bridgeHistoryItem, step, srcChainTxMeta); } else if (step.action === ActionTypes.BRIDGE) { diff --git a/ui/pages/bridge/transaction-details/bridge-step-list.tsx b/ui/pages/bridge/transaction-details/bridge-step-list.tsx index b715c29e1f0e..112cc05243a0 100644 --- a/ui/pages/bridge/transaction-details/bridge-step-list.tsx +++ b/ui/pages/bridge/transaction-details/bridge-step-list.tsx @@ -6,8 +6,10 @@ import { Box } from '../../../components/component-library'; import { BridgeHistoryItem, StatusTypes, + Step, } from '../../../../shared/types/bridge-status'; import { formatDate } from '../../../helpers/utils/util'; +import { Numeric } from '../../../../shared/modules/Numeric'; import BridgeStepDescription, { getStepStatus, } from './bridge-step-description'; @@ -29,7 +31,7 @@ const getTime = ( }; type BridgeStepsProps = { - bridgeHistoryItem: BridgeHistoryItem; + bridgeHistoryItem?: BridgeHistoryItem; srcChainTxMeta?: TransactionMeta; networkConfigurationsByChainId: Record; }; @@ -39,9 +41,29 @@ export default function BridgeStepList({ srcChainTxMeta, networkConfigurationsByChainId, }: BridgeStepsProps) { - const { steps } = bridgeHistoryItem.quote; + const steps = + bridgeHistoryItem?.quote.steps || + srcChainTxMeta?.bridgeSteps?.map((step) => ({ + // Convert hex to numbers + ...step, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + action: step.action as any, + srcChainId: new Numeric(step.srcChainId, 16).toBase(10).toNumber(), + destChainId: step.destChainId + ? new Numeric(step.destChainId, 16).toBase(10).toNumber() + : undefined, + srcAsset: { + ...step.srcAsset, + chainId: new Numeric(step.srcAsset.chainId, 16).toBase(10).toNumber(), + }, + destAsset: { + ...step.destAsset, + chainId: new Numeric(step.destAsset.chainId, 16).toBase(10).toNumber(), + }, + })) || + []; const stepStatuses = steps.map((step) => - getStepStatus(bridgeHistoryItem, step, srcChainTxMeta), + getStepStatus({ bridgeHistoryItem, step: step as Step, srcChainTxMeta }), ); return ( @@ -71,8 +93,8 @@ export default function BridgeStepList({ getTime( i, i === steps.length - 1, - bridgeHistoryItem.startTime, - bridgeHistoryItem.estimatedProcessingTimeInSeconds, + bridgeHistoryItem?.startTime || srcChainTxMeta?.time, + bridgeHistoryItem?.estimatedProcessingTimeInSeconds || 0, ), 'hh:mm a', ); @@ -85,7 +107,7 @@ export default function BridgeStepList({ isEdgeComplete={isEdgeComplete} > {/* Indicator dots */} - {stepStatus === null && ( + {(stepStatus === null || stepStatus === StatusTypes.UNKNOWN) && ( )} {stepStatus === StatusTypes.PENDING && ( diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index 6865c968a2a4..f3a7d505b86f 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -147,28 +147,26 @@ const CrossChainSwapTxDetails = () => { flexDirection={FlexDirection.Column} gap={4} > - {bridgeHistoryItem && ( - <> - {status !== StatusTypes.COMPLETE && bridgeHistoryItem && ( - - )} - - {/* Links to block explorers */} - - - - )} + )} + + {/* Links to block explorers */} + + + {/* General tx details */} Date: Thu, 14 Nov 2024 15:56:51 -0500 Subject: [PATCH 12/48] chore: show pending and bridge amount for STX in tx details --- ui/helpers/utils/token-util.js | 3 +- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 7 ++ .../transaction-details.tsx | 70 ++++++++++++++++--- 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js index f64f74f62fa6..10beae68b39e 100644 --- a/ui/helpers/utils/token-util.js +++ b/ui/helpers/utils/token-util.js @@ -217,7 +217,8 @@ export function getTokenFiatAmount( if ( conversionRate <= 0 || !contractExchangeRate || - tokenAmount === undefined + tokenAmount === undefined || + tokenAmount === false ) { return undefined; } diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index e7795c2d2ce5..0d33b8248e13 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -33,10 +33,17 @@ export default function useHandleBridgeTx() { // estimatedBaseFee: decEstimatedBaseFee, // swapMetaData, type: TransactionType.bridge, + + sourceTokenAmount: quoteResponse.quote.srcTokenAmount, sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, + sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals, + sourceTokenAddress: quoteResponse.quote.srcAsset.address, + + destinationTokenAmount: quoteResponse.quote.destTokenAmount, destinationTokenSymbol: quoteResponse.quote.destAsset.symbol, destinationTokenDecimals: quoteResponse.quote.destAsset.decimals, destinationTokenAddress: quoteResponse.quote.destAsset.address, + approvalTxId, // this is the decimal (non atomic) amount (not USD value) of source token to swap swapTokenValue: sentAmountDec, diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index f3a7d505b86f..c4957863f0db 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -23,7 +23,10 @@ import UserPreferencedCurrencyDisplay from '../../../components/app/user-prefere import { EtherDenomination } from '../../../../shared/constants/common'; import { PRIMARY } from '../../../helpers/constants/common'; import CurrencyDisplay from '../../../components/ui/currency-display/currency-display.component'; -import { StatusTypes } from '../../../../shared/types/bridge-status'; +import { + BridgeHistoryItem, + StatusTypes, +} from '../../../../shared/types/bridge-status'; import { AlignItems, Display, @@ -60,6 +63,51 @@ const getBlockExplorerUrl = ( return `${rootUrl}/tx/${txHash}`; }; +const getBridgeHistoryItem = ( + srcTxHashOrTxId: string | undefined, + bridgeHistory: Record, + srcChainTxMeta: TransactionMeta | undefined, +) => { + if (!srcTxHashOrTxId) { + return undefined; + } + + const historyItemFromUrlParamHash = bridgeHistory[srcTxHashOrTxId]; + const historyItemFromTxMetaHash = srcChainTxMeta?.hash + ? bridgeHistory[srcChainTxMeta?.hash] + : undefined; + + return historyItemFromUrlParamHash || historyItemFromTxMetaHash; +}; + +const getBridgeAmount = ({ + bridgeHistoryItem, + srcChainTxMeta, +}: { + bridgeHistoryItem?: BridgeHistoryItem; + srcChainTxMeta?: TransactionMeta; +}) => { + if (bridgeHistoryItem) { + return `${calcTokenAmount( + bridgeHistoryItem.quote.srcTokenAmount, + bridgeHistoryItem.quote.srcAsset.decimals, + ).toFixed()} ${bridgeHistoryItem.quote.srcAsset.symbol}`; + } + + if ( + srcChainTxMeta && + srcChainTxMeta.sourceTokenAmount && + srcChainTxMeta.sourceTokenDecimals + ) { + return `${calcTokenAmount( + srcChainTxMeta.sourceTokenAmount, + srcChainTxMeta.sourceTokenDecimals, + ).toFixed()} ${srcChainTxMeta.sourceTokenSymbol}`; + } + + return undefined; +}; + const StatusToColorMap: Record = { [StatusTypes.PENDING]: TextColor.warningDefault, [StatusTypes.COMPLETE]: TextColor.successDefault, @@ -78,9 +126,6 @@ const CrossChainSwapTxDetails = () => { selectedAddressTxListSelector, ) as TransactionMeta[]; - const bridgeHistoryItem = srcTxHashOrTxId - ? bridgeHistory[srcTxHashOrTxId] - : undefined; const networkConfigurationsByChainId = useSelector( getNetworkConfigurationsByChainId, ); @@ -88,6 +133,12 @@ const CrossChainSwapTxDetails = () => { const srcChainTxMeta = selectedAddressTxList.find( (tx) => tx.hash === srcTxHashOrTxId || tx.id === srcTxHashOrTxId, ); + // Even if user is still on /tx-details/txMetaId, we want to be able to show the bridge history item + const bridgeHistoryItem = getBridgeHistoryItem( + srcTxHashOrTxId, + bridgeHistory, + srcChainTxMeta, + ); const { srcNetwork, destNetwork } = useBridgeChainInfo({ bridgeHistoryItem, @@ -99,7 +150,9 @@ const CrossChainSwapTxDetails = () => { const destTxHash = bridgeHistoryItem?.status.destChain?.txHash; const destBlockExplorerUrl = getBlockExplorerUrl(destNetwork, destTxHash); - const status = bridgeHistoryItem?.status.status; + const status = bridgeHistoryItem + ? bridgeHistoryItem?.status.status + : StatusTypes.PENDING; const destChainIconUrl = destNetwork ? CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[ @@ -118,12 +171,7 @@ const CrossChainSwapTxDetails = () => { }) : undefined; - const bridgeAmount = bridgeHistoryItem - ? `${calcTokenAmount( - bridgeHistoryItem.quote.srcTokenAmount, - bridgeHistoryItem.quote.srcAsset.decimals, - ).toFixed()} ${bridgeHistoryItem.quote.srcAsset.symbol}` - : undefined; + const bridgeAmount = getBridgeAmount({ bridgeHistoryItem, srcChainTxMeta }); return (
From 71d997db83b3bdb91b48072dd77d1f807fce66ea Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:38:51 -0500 Subject: [PATCH 13/48] fix: bridge type missing dest network name --- ui/hooks/bridge/useBridgeChainInfo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/hooks/bridge/useBridgeChainInfo.ts b/ui/hooks/bridge/useBridgeChainInfo.ts index 279a72e5f377..6073ccb57e8f 100644 --- a/ui/hooks/bridge/useBridgeChainInfo.ts +++ b/ui/hooks/bridge/useBridgeChainInfo.ts @@ -55,7 +55,7 @@ export default function useBridgeChainInfo({ srcTxMeta, }); - if (!bridgeHistoryItem || !hexSrcChainId || !hexDestChainId) { + if (!hexSrcChainId || !hexDestChainId) { return { srcNetwork: undefined, destNetwork: undefined, From 01353f741e5d80c2eed9cbb9495023561fa0b4e2 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:40:54 -0500 Subject: [PATCH 14/48] fix: source explorer incorrectly using txMeta.id as hash --- ui/pages/bridge/transaction-details/transaction-details.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index c4957863f0db..0348c503a491 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -145,7 +145,8 @@ const CrossChainSwapTxDetails = () => { srcTxMeta: srcChainTxMeta, }); - const srcBlockExplorerUrl = getBlockExplorerUrl(srcNetwork, srcTxHashOrTxId); + const srcTxHash = srcChainTxMeta?.hash; + const srcBlockExplorerUrl = getBlockExplorerUrl(srcNetwork, srcTxHash); const destTxHash = bridgeHistoryItem?.status.destChain?.txHash; const destBlockExplorerUrl = getBlockExplorerUrl(destNetwork, destTxHash); From d7acd8d74185c59657b320e7b561efb803aa7bf5 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:45:05 -0500 Subject: [PATCH 15/48] chore: add stx mocks --- .../transaction/smart-transactions-mocks.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 app/scripts/lib/transaction/smart-transactions-mocks.ts diff --git a/app/scripts/lib/transaction/smart-transactions-mocks.ts b/app/scripts/lib/transaction/smart-transactions-mocks.ts new file mode 100644 index 000000000000..a54cbf540ec1 --- /dev/null +++ b/app/scripts/lib/transaction/smart-transactions-mocks.ts @@ -0,0 +1,23 @@ +export const mockWaitForTransactionHash: () => Promise = () => { + return new Promise((resolve) => { + setTimeout(() => { + // Need a real tx hash to pass some downstream validation + resolve( + '0xe3e223b9725765a7de557effdb2b507ace3534bcff2c1fe3a857e0791e56a518', + ); + }, 20_000_000); + }); +}; + +export const mockSignAndSubmitTransactions: () => Promise<{ + uuid: string; + txHash?: string; +}> = () => { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ + uuid: 'uuid123456789', + }); + }, 100); + }); +}; From 4944e3a9fdd13679f44b83a88aededc0dc0b0218 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:05:52 -0500 Subject: [PATCH 16/48] fix: trying to dispatch something that isnt an action --- ui/pages/bridge/prepare/bridge-cta-button.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/pages/bridge/prepare/bridge-cta-button.tsx b/ui/pages/bridge/prepare/bridge-cta-button.tsx index 7355e6579dfa..b148b71c67f0 100644 --- a/ui/pages/bridge/prepare/bridge-cta-button.tsx +++ b/ui/pages/bridge/prepare/bridge-cta-button.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import { Button } from '../../../components/component-library'; import { getFromAmount, @@ -13,7 +13,6 @@ import { useI18nContext } from '../../../hooks/useI18nContext'; import useSubmitBridgeTransaction from '../hooks/useSubmitBridgeTransaction'; export const BridgeCTAButton = () => { - const dispatch = useDispatch(); const t = useI18nContext(); const fromToken = useSelector(getFromToken); @@ -55,7 +54,7 @@ export const BridgeCTAButton = () => { data-testid="bridge-cta-button" onClick={() => { if (isTxSubmittable) { - dispatch(submitBridgeTransaction(activeQuote)); + submitBridgeTransaction(activeQuote); } }} disabled={!isTxSubmittable} From fc1fdf4b010b9f4d1815790762e0dd5536fa99d2 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:06:15 -0500 Subject: [PATCH 17/48] chore: add way to call addTransaction background fn from ui --- ui/store/actions.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 01b8b9e458d9..5ed6fe7ddf62 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -1066,6 +1066,44 @@ export async function addTransactionAndWaitForPublish( ); } +/** + * Wrapper around the promisifedBackground to create a new unapproved + * transaction in the background and return the newly created txMeta. + * This method does not show errors or route to a confirmation page + * + * @param txParams - the transaction parameters + * @param options - Additional options for the transaction. + * @param options.method + * @param options.requireApproval - Whether the transaction requires approval. + * @param options.swaps - Options specific to swaps transactions. + * @param options.swaps.hasApproveTx - Whether the swap required an approval transaction. + * @param options.swaps.meta - Additional transaction metadata required by swaps. + * @param options.type + * @returns + */ +export async function addTransaction( + txParams: TransactionParams, + options: { + method?: string; + requireApproval?: boolean; + swaps?: { hasApproveTx?: boolean; meta?: Record }; + type?: TransactionType; + }, +): Promise { + log.debug('background.addTransaction'); + + const actionId = generateActionId(); + + return await submitRequestToBackground('addTransaction', [ + txParams, + { + ...options, + origin: ORIGIN_METAMASK, + actionId, + }, + ]); +} + export function updateAndApproveTx( txMeta: TransactionMeta, dontShowLoadingIndicator: boolean, From 165b7c760ecb397dee8e304f83861204b71d5888 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:51:41 -0500 Subject: [PATCH 18/48] chore: make sure to restart polling for anything not complete or failed --- .../controllers/bridge-status/bridge-status-controller.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 268250df6e74..9a1e62eb1e39 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -130,7 +130,9 @@ export default class BridgeStatusController extends StaticIntervalPollingControl const historyItems = Object.values(bridgeStatusState.txHistory); const incompleteHistoryItems = historyItems .filter( - (historyItem) => historyItem.status.status !== StatusTypes.COMPLETE, + (historyItem) => + historyItem.status.status === StatusTypes.PENDING || + historyItem.status.status === StatusTypes.UNKNOWN, ) .filter((historyItem) => { // Check if we are already polling this tx, if so, skip restarting polling for that From d4a3fc2346a738e1c50479641617a60d6380d77a Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:09:42 -0500 Subject: [PATCH 19/48] chore: refactor BridgeStatusController to work with txMetaId rather than srcTxHash --- .../bridge-status/bridge-status-controller.ts | 136 ++++++++++-------- .../controllers/bridge-status/types.ts | 11 +- .../controllers/bridge-status/utils.ts | 11 +- app/scripts/metamask-controller.js | 1 + shared/types/bridge-status.ts | 13 +- ui/hooks/bridge/useBridgeTxHistoryData.ts | 13 +- ui/pages/bridge/hooks/useHandleApprovalTx.ts | 28 ++-- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 100 ++++++------- ui/pages/bridge/hooks/useHandleTx.ts | 22 +-- .../hooks/useSubmitBridgeTransaction.ts | 37 +++-- .../transaction-details.tsx | 30 +--- ui/pages/routes/routes.component.js | 2 +- 12 files changed, 224 insertions(+), 180 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 9a1e62eb1e39..3f245d398a4d 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -1,13 +1,12 @@ import { StateMetadata } from '@metamask/base-controller'; -import type { NetworkClientId } from '@metamask/network-controller'; import { StaticIntervalPollingController } from '@metamask/polling-controller'; import { Hex } from '@metamask/utils'; // eslint-disable-next-line import/no-restricted-paths import { StartPollingForBridgeTxStatusArgs, - StatusRequest, StatusTypes, BridgeStatusControllerState, + StatusRequestWithSrcTxHash, } from '../../../../shared/types/bridge-status'; import { decimalToPrefixedHex } from '../../../../shared/modules/conversion.utils'; import { @@ -16,7 +15,7 @@ import { REFRESH_INTERVAL_MS, } from './constants'; import { BridgeStatusControllerMessenger } from './types'; -import { fetchBridgeTxStatus } from './utils'; +import { fetchBridgeTxStatus, isStatusRequestWithSrcTxHash } from './utils'; const metadata: StateMetadata<{ bridgeStatusState: BridgeStatusControllerState; @@ -30,21 +29,18 @@ const metadata: StateMetadata<{ }; /** The input to start polling for the {@link BridgeStatusController} */ -type BridgeStatusPollingInput = { - networkClientId: NetworkClientId; - statusRequest: StatusRequest; -}; +type BridgeStatusPollingInput = FetchBridgeTxStatusArgs; -type SrcTxHash = string; +type SrcTxMetaId = string; export type FetchBridgeTxStatusArgs = { - statusRequest: StatusRequest; + bridgeTxMetaId: string; }; export default class BridgeStatusController extends StaticIntervalPollingController()< typeof BRIDGE_STATUS_CONTROLLER_NAME, { bridgeStatusState: BridgeStatusControllerState }, BridgeStatusControllerMessenger > { - #pollingTokensBySrcTxHash: Record = {}; + #pollingTokensByTxMetaId: Record = {}; constructor({ messenger, @@ -136,32 +132,19 @@ export default class BridgeStatusController extends StaticIntervalPollingControl ) .filter((historyItem) => { // Check if we are already polling this tx, if so, skip restarting polling for that - const srcTxHash = historyItem.status.srcChain.txHash; - const pollingToken = this.#pollingTokensBySrcTxHash[srcTxHash]; + const srcTxMetaId = historyItem.txMetaId; + const pollingToken = this.#pollingTokensByTxMetaId[srcTxMetaId]; return !pollingToken; }); incompleteHistoryItems.forEach((historyItem) => { - const statusRequest = { - bridgeId: historyItem.quote.bridgeId, - srcTxHash: historyItem.status.srcChain.txHash, - bridge: historyItem.quote.bridges[0], - srcChainId: historyItem.quote.srcChainId, - destChainId: historyItem.quote.destChainId, - quote: historyItem.quote, - refuel: Boolean(historyItem.quote.refuel), - }; - - const hexSourceChainId = decimalToPrefixedHex(statusRequest.srcChainId); - const networkClientId = this.messagingSystem.call( - 'NetworkController:findNetworkClientIdByChainId', - hexSourceChainId, - ); + const bridgeTxMetaId = historyItem.txMetaId; // We manually call startPolling() here rather than go through startPollingForBridgeTxStatus() // because we don't want to overwrite the existing historyItem in state - this.#pollingTokensBySrcTxHash[statusRequest.srcTxHash] = - this.startPolling({ networkClientId, statusRequest }); + this.#pollingTokensByTxMetaId[bridgeTxMetaId] = this.startPolling({ + bridgeTxMetaId, + }); }); }; @@ -169,6 +152,7 @@ export default class BridgeStatusController extends StaticIntervalPollingControl startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgs, ) => { const { + bridgeTxMeta, statusRequest, quoteResponse, startTime, @@ -177,14 +161,14 @@ export default class BridgeStatusController extends StaticIntervalPollingControl initialDestAssetBalance, targetContractAddress, } = startPollingForBridgeTxStatusArgs; - const hexSourceChainId = decimalToPrefixedHex(statusRequest.srcChainId); - const { bridgeStatusState } = this.state; const { address: account } = this.#getSelectedAccount(); // Write all non-status fields to state so we can reference the quote in Activity list without the Bridge API // We know it's in progress but not the exact status yet const txHistoryItem = { + txMetaId: bridgeTxMeta.id, + statusRequest, // Attach the statusRequest to the historyItem so we can look it up and update it when srcTxHash is available quote: quoteResponse.quote, startTime, estimatedProcessingTimeInSeconds: @@ -204,24 +188,20 @@ export default class BridgeStatusController extends StaticIntervalPollingControl }, }, }; - this.update((_state) => { _state.bridgeStatusState = { ...bridgeStatusState, txHistory: { ...bridgeStatusState.txHistory, - [statusRequest.srcTxHash]: txHistoryItem, + // Use the txMeta.id as the key so we can reference the txMeta in TransactionController + [bridgeTxMeta.id]: txHistoryItem, }, }; }); - const networkClientId = this.messagingSystem.call( - 'NetworkController:findNetworkClientIdByChainId', - hexSourceChainId, - ); - this.#pollingTokensBySrcTxHash[statusRequest.srcTxHash] = this.startPolling( - { networkClientId, statusRequest }, - ); + this.#pollingTokensByTxMetaId[bridgeTxMeta.id] = this.startPolling({ + bridgeTxMetaId: bridgeTxMeta.id, + }); }; // This will be called after you call this.startPolling() @@ -235,14 +215,15 @@ export default class BridgeStatusController extends StaticIntervalPollingControl } #fetchBridgeTxStatus = async ({ - networkClientId: _networkClientId, - statusRequest, - }: BridgeStatusPollingInput) => { + bridgeTxMetaId, + }: FetchBridgeTxStatusArgs) => { const { bridgeStatusState } = this.state; try { // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly. + // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases + const statusRequest = this.#getStatusRequestWithSrcTxHash(bridgeTxMetaId); const status = await fetchBridgeTxStatus(statusRequest); // No need to purge these on network change or account change, TransactionController does not purge either. @@ -251,13 +232,13 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // First stab at this will not stop polling when you are on a different account this.update((_state) => { const bridgeHistoryItem = - _state.bridgeStatusState.txHistory[statusRequest.srcTxHash]; + _state.bridgeStatusState.txHistory[bridgeTxMetaId]; _state.bridgeStatusState = { ...bridgeStatusState, txHistory: { ...bridgeStatusState.txHistory, - [statusRequest.srcTxHash]: { + [bridgeTxMetaId]: { ...bridgeHistoryItem, status, }, @@ -265,8 +246,7 @@ export default class BridgeStatusController extends StaticIntervalPollingControl }; }); - const pollingToken = - this.#pollingTokensBySrcTxHash[statusRequest.srcTxHash]; + const pollingToken = this.#pollingTokensByTxMetaId[bridgeTxMetaId]; if (status.status === StatusTypes.COMPLETE && pollingToken) { this.stopPollingByPollingToken(pollingToken); } @@ -275,14 +255,58 @@ export default class BridgeStatusController extends StaticIntervalPollingControl } }; + #getStatusRequestWithSrcTxHash = ( + bridgeTxMetaId: string, + ): StatusRequestWithSrcTxHash => { + const { bridgeStatusState } = this.state; + const { statusRequest } = bridgeStatusState.txHistory[bridgeTxMetaId]; + + if (isStatusRequestWithSrcTxHash(statusRequest)) { + return statusRequest; + } + + // Look up in TransactionController if txMeta has been updated with the srcTxHash, if so, update it here as well + const txControllerState = this.messagingSystem.call( + 'TransactionController:getState', + ); + const txMeta = txControllerState.transactions.find( + (tx) => tx.id === bridgeTxMetaId, + ); + + const { nextState } = this.update((_state) => { + _state.bridgeStatusState = { + ...bridgeStatusState, + txHistory: { + ...bridgeStatusState.txHistory, + [bridgeTxMetaId]: { + ...bridgeStatusState.txHistory[bridgeTxMetaId], + statusRequest: { + ...bridgeStatusState.txHistory[bridgeTxMetaId].statusRequest, + srcTxHash: txMeta?.hash, + }, + }, + }, + }; + }); + + const nextStatusRequest = + nextState.bridgeStatusState.txHistory[bridgeTxMetaId].statusRequest; + + if (isStatusRequestWithSrcTxHash(nextStatusRequest)) { + return nextStatusRequest; + } + + throw new Error('StatusRequestWithSrcTxHash missing srcTxHash'); + }; + // Wipes the bridge status for the given address and chainId // Will match either source or destination chainId to the selectedChainId #wipeBridgeStatusByChainId = (address: string, selectedChainId: Hex) => { - const sourceTxHashesToDelete = Object.keys( + const sourceTxMetaIdsToDelete = Object.keys( this.state.bridgeStatusState.txHistory, - ).filter((sourceTxHash) => { + ).filter((txMetaId) => { const bridgeHistoryItem = - this.state.bridgeStatusState.txHistory[sourceTxHash]; + this.state.bridgeStatusState.txHistory[txMetaId]; const hexSourceChainId = decimalToPrefixedHex( bridgeHistoryItem.quote.srcChainId, @@ -298,20 +322,20 @@ export default class BridgeStatusController extends StaticIntervalPollingControl ); }); - sourceTxHashesToDelete.forEach((sourceTxHash) => { - const pollingToken = this.#pollingTokensBySrcTxHash[sourceTxHash]; + sourceTxMetaIdsToDelete.forEach((sourceTxMetaId) => { + const pollingToken = this.#pollingTokensByTxMetaId[sourceTxMetaId]; if (pollingToken) { this.stopPollingByPollingToken( - this.#pollingTokensBySrcTxHash[sourceTxHash], + this.#pollingTokensByTxMetaId[sourceTxMetaId], ); } }); this.update((_state) => { - _state.bridgeStatusState.txHistory = sourceTxHashesToDelete.reduce( - (acc, sourceTxHash) => { - delete acc[sourceTxHash]; + _state.bridgeStatusState.txHistory = sourceTxMetaIdsToDelete.reduce( + (acc, sourceTxMetaId) => { + delete acc[sourceTxMetaId]; return acc; }, _state.bridgeStatusState.txHistory, diff --git a/app/scripts/controllers/bridge-status/types.ts b/app/scripts/controllers/bridge-status/types.ts index 040cd1e0c9bd..d06c3c40b3d8 100644 --- a/app/scripts/controllers/bridge-status/types.ts +++ b/app/scripts/controllers/bridge-status/types.ts @@ -9,6 +9,7 @@ import { NetworkControllerGetStateAction, } from '@metamask/network-controller'; import { AccountsControllerGetSelectedAccountAction } from '@metamask/accounts-controller'; +import { TransactionControllerGetStateAction } from '@metamask/transaction-controller'; import { BridgeStatusAction, BridgeStatusControllerState, @@ -37,11 +38,19 @@ type BridgeStatusControllerEvents = ControllerStateChangeEvent< BridgeStatusControllerState >; +/** + * The external actions available to the BridgeStatusController. + */ type AllowedActions = | NetworkControllerFindNetworkClientIdByChainIdAction | NetworkControllerGetStateAction | NetworkControllerGetNetworkClientByIdAction - | AccountsControllerGetSelectedAccountAction; + | AccountsControllerGetSelectedAccountAction + | TransactionControllerGetStateAction; + +/** + * The external events available to the BridgeStatusController. + */ type AllowedEvents = never; /** diff --git a/app/scripts/controllers/bridge-status/utils.ts b/app/scripts/controllers/bridge-status/utils.ts index 323e7e2faeab..0d6cdd0cf296 100644 --- a/app/scripts/controllers/bridge-status/utils.ts +++ b/app/scripts/controllers/bridge-status/utils.ts @@ -5,6 +5,7 @@ import { import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; import { StatusResponse, + StatusRequestWithSrcTxHash, StatusRequest, } from '../../../../shared/types/bridge-status'; import { validateResponse, validators } from './validators'; @@ -13,7 +14,9 @@ const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID }; export const BRIDGE_STATUS_BASE_URL = `${BRIDGE_API_BASE_URL}/getTxStatus`; -export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => { +export const fetchBridgeTxStatus = async ( + statusRequest: StatusRequestWithSrcTxHash, +) => { // Assemble params const { quote, ...statusRequestNoQuote } = statusRequest; const statusRequestNoQuoteFormatted = Object.fromEntries( @@ -47,3 +50,9 @@ export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => { // Return return rawTxStatus; }; + +export const isStatusRequestWithSrcTxHash = ( + statusRequest: StatusRequest, +): statusRequest is StatusRequestWithSrcTxHash => { + return statusRequest.srcTxHash !== undefined; +}; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 1eaf354b4caf..95dc69636a40 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -2192,6 +2192,7 @@ export default class MetamaskController extends EventEmitter { 'NetworkController:getNetworkClientById', 'NetworkController:findNetworkClientIdByChainId', 'NetworkController:getState', + 'TransactionController:getState', ], allowedEvents: [], }); diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index f4c5c82d26eb..a108d7fbcd43 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -1,4 +1,6 @@ // eslint-disable-next-line import/no-restricted-paths +import { TransactionMeta } from '@metamask/transaction-controller'; +// TODO fix this import { ChainId, Quote, QuoteResponse } from '../../ui/pages/bridge/types'; // All fields need to be types not interfaces, same with their children fields @@ -13,7 +15,7 @@ export enum StatusTypes { export type StatusRequest = { bridgeId: string; // lifi, socket, squid - srcTxHash: string; // lifi, socket, squid + srcTxHash?: string; // lifi, socket, squid, this might be undefined if this is a smart transaction (STX) bridge: string; // lifi, socket, squid srcChainId: ChainId; // lifi, socket, squid destChainId: ChainId; // lifi, socket, squid @@ -21,6 +23,10 @@ export type StatusRequest = { refuel?: boolean; // lifi }; +export type StatusRequestWithSrcTxHash = StatusRequest & { + srcTxHash: string; +}; + export type Asset = { chainId: ChainId; address: string; @@ -32,7 +38,7 @@ export type Asset = { export type SrcChainStatus = { chainId: ChainId; - txHash: string; + txHash?: string; // might be undefined if this is a smart transaction (STX) amount?: string; token?: Asset; }; @@ -105,6 +111,8 @@ export type RefuelStatusResponse = object & StatusResponse; export type RefuelData = object & Step; export type BridgeHistoryItem = { + txMetaId: string; // Need this to handle STX that might not have a txHash immediately + statusRequest: StatusRequest; quote: Quote; status: StatusResponse; startTime?: number; @@ -130,6 +138,7 @@ export enum BridgeStatusAction { } export type StartPollingForBridgeTxStatusArgs = { + bridgeTxMeta: TransactionMeta; statusRequest: StatusRequest; quoteResponse: QuoteResponse; startTime?: BridgeHistoryItem['startTime']; diff --git a/ui/hooks/bridge/useBridgeTxHistoryData.ts b/ui/hooks/bridge/useBridgeTxHistoryData.ts index a36821ad2e38..bfa97ee22d7c 100644 --- a/ui/hooks/bridge/useBridgeTxHistoryData.ts +++ b/ui/hooks/bridge/useBridgeTxHistoryData.ts @@ -25,10 +25,13 @@ export default function useBridgeTxHistoryData({ const history = useHistory(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); - const srcTxHash = transactionGroup.initialTransaction.hash; + // TODO use the txMetaId instead of srcTxHash + const srcTxMetaId = transactionGroup.initialTransaction.id; // If this tx is a bridge tx and not a smart transaction, it will always have a bridgeHistoryItem - const bridgeHistoryItem = srcTxHash ? bridgeHistory[srcTxHash] : undefined; + const bridgeHistoryItem = srcTxMetaId + ? bridgeHistory[srcTxMetaId] + : undefined; const { destNetwork } = useBridgeChainInfo({ bridgeHistoryItem, @@ -48,12 +51,8 @@ export default function useBridgeTxHistoryData({ ) : null; - // We should be able to use a srcTxHash or a txMeta.id, STX won't have txHash right away - // the txMeta.id is just a fallback, we prefer using srcTxHash - const srcTxHashOrTxId = srcTxHash || transactionGroup.initialTransaction.id; - const showBridgeTxDetails = () => { - history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxHashOrTxId}`); + history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxMetaId}`); }; return { diff --git a/ui/pages/bridge/hooks/useHandleApprovalTx.ts b/ui/pages/bridge/hooks/useHandleApprovalTx.ts index c0fb7812cce0..2b88db87b48e 100644 --- a/ui/pages/bridge/hooks/useHandleApprovalTx.ts +++ b/ui/pages/bridge/hooks/useHandleApprovalTx.ts @@ -42,12 +42,15 @@ export default function useHandleApprovalTx() { await handleTx({ txType: TransactionType.bridgeApproval, txParams, - swapsOptions: { - hasApproveTx: true, - meta: { - type: TransactionType.bridgeApproval, - }, + fieldsToAddToTxMeta: { + sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, }, + // swapsOptions: { + // hasApproveTx: true, + // meta: { + // type: TransactionType.bridgeApproval, + // }, + // }, }); } }; @@ -74,13 +77,16 @@ export default function useHandleApprovalTx() { const txMeta = await handleTx({ txType: TransactionType.bridgeApproval, txParams: approval, - swapsOptions: { - hasApproveTx: true, - meta: { - type: TransactionType.bridgeApproval, - sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, - }, + fieldsToAddToTxMeta: { + sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, }, + // swapsOptions: { + // hasApproveTx: true, + // meta: { + // type: TransactionType.bridgeApproval, + // sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, + // }, + // }, }); return txMeta; diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index 0d33b8248e13..1f0ed980a587 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -21,60 +21,62 @@ export default function useHandleBridgeTx() { .shiftedBy(quoteResponse.quote.srcAsset.decimals) .toString(); - const txMeta = await handleTx({ - txType: TransactionType.bridge, - txParams: quoteResponse.trade, - swapsOptions: { - hasApproveTx: Boolean(quoteResponse?.approval), - meta: { - destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - // estimatedBaseFee: decEstimatedBaseFee, - // swapMetaData, - type: TransactionType.bridge, + const fieldsToAddToTxMeta = { + destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + // estimatedBaseFee: decEstimatedBaseFee, + // swapMetaData, + type: TransactionType.bridge, - sourceTokenAmount: quoteResponse.quote.srcTokenAmount, - sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, - sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals, - sourceTokenAddress: quoteResponse.quote.srcAsset.address, + sourceTokenAmount: quoteResponse.quote.srcTokenAmount, + sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, + sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals, + sourceTokenAddress: quoteResponse.quote.srcAsset.address, - destinationTokenAmount: quoteResponse.quote.destTokenAmount, - destinationTokenSymbol: quoteResponse.quote.destAsset.symbol, - destinationTokenDecimals: quoteResponse.quote.destAsset.decimals, - destinationTokenAddress: quoteResponse.quote.destAsset.address, + destinationTokenAmount: quoteResponse.quote.destTokenAmount, + destinationTokenSymbol: quoteResponse.quote.destAsset.symbol, + destinationTokenDecimals: quoteResponse.quote.destAsset.decimals, + destinationTokenAddress: quoteResponse.quote.destAsset.address, - approvalTxId, - // this is the decimal (non atomic) amount (not USD value) of source token to swap - swapTokenValue: sentAmountDec, - // Convert chainIds to hex - bridgeSteps: quoteResponse.quote.steps.map((step) => ({ - ...step, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - action: step.action as any, - srcChainId: new Numeric(step.srcChainId, 10) + approvalTxId, + // this is the decimal (non atomic) amount (not USD value) of source token to swap + swapTokenValue: sentAmountDec, + // Convert chainIds to hex + bridgeSteps: quoteResponse.quote.steps.map((step) => ({ + ...step, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + action: step.action as any, + srcChainId: new Numeric(step.srcChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + destChainId: step.destChainId + ? (new Numeric(step.destChainId, 10) .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - destChainId: step.destChainId - ? (new Numeric(step.destChainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`) - : undefined, - srcAsset: { - ...step.srcAsset, - chainId: new Numeric(step.srcAsset.chainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - }, - destAsset: { - ...step.destAsset, - chainId: new Numeric(step.destAsset.chainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - }, - })), + .toLowerCase() as `0x${string}`) + : undefined, + srcAsset: { + ...step.srcAsset, + chainId: new Numeric(step.srcAsset.chainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, }, - }, + destAsset: { + ...step.destAsset, + chainId: new Numeric(step.destAsset.chainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + }, + })), + }; + + const txMeta = await handleTx({ + txType: TransactionType.bridge, + txParams: quoteResponse.trade, + fieldsToAddToTxMeta, + // swapsOptions: { + // hasApproveTx: Boolean(quoteResponse?.approval), + // }, }); return txMeta; diff --git a/ui/pages/bridge/hooks/useHandleTx.ts b/ui/pages/bridge/hooks/useHandleTx.ts index d89cc90a478f..944eb8a47451 100644 --- a/ui/pages/bridge/hooks/useHandleTx.ts +++ b/ui/pages/bridge/hooks/useHandleTx.ts @@ -5,7 +5,8 @@ import { import { useDispatch, useSelector } from 'react-redux'; import { forceUpdateMetamaskState, - addTransactionAndWaitForPublish, + addTransaction, + updateTransaction, } from '../../../store/actions'; import { getHexMaxGasLimit, @@ -26,7 +27,8 @@ export default function useHandleTx() { const handleTx = async ({ txType, txParams, - swapsOptions, + // swapsOptions, + fieldsToAddToTxMeta, }: { txType: TransactionType.bridgeApproval | TransactionType.bridge; txParams: { @@ -37,10 +39,11 @@ export default function useHandleTx() { data: string; gasLimit: number | null; }; - swapsOptions: { - hasApproveTx: boolean; - meta: Partial; // all fields in meta are merged with TransactionMeta downstream in TransactionController - }; + // swapsOptions: { + // hasApproveTx: boolean; + // meta: Partial; // all fields in meta are merged with TransactionMeta downstream in TransactionController + // }; + fieldsToAddToTxMeta: Omit, 'status'>; // We don't add status, so omit it to fix the type error }) => { const hexChainId = decimalToPrefixedHex(txParams.chainId); @@ -61,12 +64,15 @@ export default function useHandleTx() { maxPriorityFeePerGas, }; - const txMeta = await addTransactionAndWaitForPublish(finalTxParams, { + // Need access to the txMeta.id, so we call addTransaction instead of addTransactionAndWaitForPublish + const txMeta = await addTransaction(finalTxParams, { requireApproval: false, type: txType, - swaps: swapsOptions, + // swaps: swapsOptions, }); + dispatch(updateTransaction({ ...txMeta, ...fieldsToAddToTxMeta }, true)); + await forceUpdateMetamaskState(dispatch); return txMeta; diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts index b7ec8b31b2ec..d6a5563ae170 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts @@ -39,25 +39,24 @@ export default function useSubmitBridgeTransaction() { }); // Get bridge tx status - if (bridgeTxMeta.hash) { - const statusRequest = { - bridgeId: quoteResponse.quote.bridgeId, - srcTxHash: bridgeTxMeta.hash, - bridge: quoteResponse.quote.bridges[0], - srcChainId: quoteResponse.quote.srcChainId, - destChainId: quoteResponse.quote.destChainId, - quote: quoteResponse.quote, - refuel: Boolean(quoteResponse.quote.refuel), - }; - dispatch( - startPollingForBridgeTxStatus({ - statusRequest, - quoteResponse, - slippagePercentage: 0, // TODO pull this from redux/bridgecontroller once it's implemented. currently hardcoded in quoteRequest.slippage right now - startTime: bridgeTxMeta.time, - }), - ); - } + const statusRequest = { + bridgeId: quoteResponse.quote.bridgeId, + srcTxHash: bridgeTxMeta.hash, + bridge: quoteResponse.quote.bridges[0], + srcChainId: quoteResponse.quote.srcChainId, + destChainId: quoteResponse.quote.destChainId, + quote: quoteResponse.quote, + refuel: Boolean(quoteResponse.quote.refuel), + }; + dispatch( + startPollingForBridgeTxStatus({ + bridgeTxMeta, + statusRequest, + quoteResponse, + slippagePercentage: 0, // TODO pull this from redux/bridgecontroller once it's implemented. currently hardcoded in quoteRequest.slippage right now + startTime: bridgeTxMeta.time, + }), + ); // Add tokens if not the native gas token if (quoteResponse.quote.srcAsset.address !== zeroAddress()) { diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index 0348c503a491..e4677e94d11b 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -63,23 +63,6 @@ const getBlockExplorerUrl = ( return `${rootUrl}/tx/${txHash}`; }; -const getBridgeHistoryItem = ( - srcTxHashOrTxId: string | undefined, - bridgeHistory: Record, - srcChainTxMeta: TransactionMeta | undefined, -) => { - if (!srcTxHashOrTxId) { - return undefined; - } - - const historyItemFromUrlParamHash = bridgeHistory[srcTxHashOrTxId]; - const historyItemFromTxMetaHash = srcChainTxMeta?.hash - ? bridgeHistory[srcChainTxMeta?.hash] - : undefined; - - return historyItemFromUrlParamHash || historyItemFromTxMetaHash; -}; - const getBridgeAmount = ({ bridgeHistoryItem, srcChainTxMeta, @@ -119,8 +102,7 @@ const CrossChainSwapTxDetails = () => { const t = useI18nContext(); const rootState = useSelector((state) => state); const history = useHistory(); - // we should be able to use a srcTxHash or a txMeta.id, STX won't have txHash right away - const { srcTxHashOrTxId } = useParams<{ srcTxHashOrTxId: string }>(); + const { srcTxMetaId } = useParams<{ srcTxMetaId: string }>(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); const selectedAddressTxList = useSelector( selectedAddressTxListSelector, @@ -131,14 +113,12 @@ const CrossChainSwapTxDetails = () => { ); const srcChainTxMeta = selectedAddressTxList.find( - (tx) => tx.hash === srcTxHashOrTxId || tx.id === srcTxHashOrTxId, + (tx) => tx.id === srcTxMetaId, ); // Even if user is still on /tx-details/txMetaId, we want to be able to show the bridge history item - const bridgeHistoryItem = getBridgeHistoryItem( - srcTxHashOrTxId, - bridgeHistory, - srcChainTxMeta, - ); + const bridgeHistoryItem = srcTxMetaId + ? bridgeHistory[srcTxMetaId] + : undefined; const { srcNetwork, destNetwork } = useBridgeChainInfo({ bridgeHistoryItem, diff --git a/ui/pages/routes/routes.component.js b/ui/pages/routes/routes.component.js index 69c404445dce..6212ed4258f3 100644 --- a/ui/pages/routes/routes.component.js +++ b/ui/pages/routes/routes.component.js @@ -320,7 +320,7 @@ export default class Routes extends Component { From bf220307798a859070f84e4559bc01feab721703 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:34:47 -0500 Subject: [PATCH 20/48] chore: remove unnecessary fields being added to txMeta --- ui/hooks/bridge/useBridgeTxHistoryData.ts | 2 -- ui/pages/bridge/hooks/useHandleApprovalTx.ts | 13 -------- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 31 -------------------- ui/pages/bridge/hooks/useHandleTx.ts | 6 ---- 4 files changed, 52 deletions(-) diff --git a/ui/hooks/bridge/useBridgeTxHistoryData.ts b/ui/hooks/bridge/useBridgeTxHistoryData.ts index bfa97ee22d7c..0556caed88fb 100644 --- a/ui/hooks/bridge/useBridgeTxHistoryData.ts +++ b/ui/hooks/bridge/useBridgeTxHistoryData.ts @@ -24,8 +24,6 @@ export default function useBridgeTxHistoryData({ const t = useI18nContext(); const history = useHistory(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); - - // TODO use the txMetaId instead of srcTxHash const srcTxMetaId = transactionGroup.initialTransaction.id; // If this tx is a bridge tx and not a smart transaction, it will always have a bridgeHistoryItem diff --git a/ui/pages/bridge/hooks/useHandleApprovalTx.ts b/ui/pages/bridge/hooks/useHandleApprovalTx.ts index 2b88db87b48e..23f4b19cf2b8 100644 --- a/ui/pages/bridge/hooks/useHandleApprovalTx.ts +++ b/ui/pages/bridge/hooks/useHandleApprovalTx.ts @@ -45,12 +45,6 @@ export default function useHandleApprovalTx() { fieldsToAddToTxMeta: { sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, }, - // swapsOptions: { - // hasApproveTx: true, - // meta: { - // type: TransactionType.bridgeApproval, - // }, - // }, }); } }; @@ -80,13 +74,6 @@ export default function useHandleApprovalTx() { fieldsToAddToTxMeta: { sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, }, - // swapsOptions: { - // hasApproveTx: true, - // meta: { - // type: TransactionType.bridgeApproval, - // sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, - // }, - // }, }); return txMeta; diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index 1f0ed980a587..fe2235e5489c 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -26,8 +26,6 @@ export default function useHandleBridgeTx() { .toPrefixedHexString() .toLowerCase() as `0x${string}`, // estimatedBaseFee: decEstimatedBaseFee, - // swapMetaData, - type: TransactionType.bridge, sourceTokenAmount: quoteResponse.quote.srcTokenAmount, sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, @@ -42,41 +40,12 @@ export default function useHandleBridgeTx() { approvalTxId, // this is the decimal (non atomic) amount (not USD value) of source token to swap swapTokenValue: sentAmountDec, - // Convert chainIds to hex - bridgeSteps: quoteResponse.quote.steps.map((step) => ({ - ...step, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - action: step.action as any, - srcChainId: new Numeric(step.srcChainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - destChainId: step.destChainId - ? (new Numeric(step.destChainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`) - : undefined, - srcAsset: { - ...step.srcAsset, - chainId: new Numeric(step.srcAsset.chainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - }, - destAsset: { - ...step.destAsset, - chainId: new Numeric(step.destAsset.chainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - }, - })), }; const txMeta = await handleTx({ txType: TransactionType.bridge, txParams: quoteResponse.trade, fieldsToAddToTxMeta, - // swapsOptions: { - // hasApproveTx: Boolean(quoteResponse?.approval), - // }, }); return txMeta; diff --git a/ui/pages/bridge/hooks/useHandleTx.ts b/ui/pages/bridge/hooks/useHandleTx.ts index 944eb8a47451..dd008c0bf9e8 100644 --- a/ui/pages/bridge/hooks/useHandleTx.ts +++ b/ui/pages/bridge/hooks/useHandleTx.ts @@ -27,7 +27,6 @@ export default function useHandleTx() { const handleTx = async ({ txType, txParams, - // swapsOptions, fieldsToAddToTxMeta, }: { txType: TransactionType.bridgeApproval | TransactionType.bridge; @@ -39,10 +38,6 @@ export default function useHandleTx() { data: string; gasLimit: number | null; }; - // swapsOptions: { - // hasApproveTx: boolean; - // meta: Partial; // all fields in meta are merged with TransactionMeta downstream in TransactionController - // }; fieldsToAddToTxMeta: Omit, 'status'>; // We don't add status, so omit it to fix the type error }) => { const hexChainId = decimalToPrefixedHex(txParams.chainId); @@ -68,7 +63,6 @@ export default function useHandleTx() { const txMeta = await addTransaction(finalTxParams, { requireApproval: false, type: txType, - // swaps: swapsOptions, }); dispatch(updateTransaction({ ...txMeta, ...fieldsToAddToTxMeta }, true)); From bc1a9137504af6874abd84f4abc19cbf0fb48225 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:36:41 -0500 Subject: [PATCH 21/48] chore: remove yalc package and use npm one --- yarn.lock | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 48ea08317fa9..060ccc7c69fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26606,11 +26606,7 @@ __metadata: "@storybook/react-webpack5": "npm:^7.6.20" "@storybook/storybook-deployer": "npm:^2.8.16" "@storybook/test-runner": "npm:^0.14.1" - "@storybook/theming": "npm:^7.6.20" "@swc/core": "npm:1.4.11" - "@swc/helpers": "npm:^0.5.7" - "@testing-library/dom": "npm:^7.31.2" - "@testing-library/jest-dom": "npm:^5.11.10" "@testing-library/react": "npm:^10.4.8" "@testing-library/react-hooks": "npm:^8.0.1" "@testing-library/user-event": "npm:^14.4.3" From 45036405cd857ce417079dd39c948241403ab696 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:49:59 -0500 Subject: [PATCH 22/48] chore: move bridgeTitle fully into useTransactionDisplayData --- app/_locales/en/messages.json | 2 +- .../transaction-list-item.component.js | 14 +++++-------- ui/hooks/bridge/useBridgeTxHistoryData.ts | 20 +------------------ ui/hooks/useTransactionDisplayData.js | 18 +++++++++++++++-- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f125126e878c..07dc59d6c895 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -917,7 +917,7 @@ "message": "Bridge to" }, "bridgeToChain": { - "message": " to $1" + "message": "Bridge to $1" }, "bridgeTotalFeesTooltipText": { "message": "This includes gas fees (paid to crypto miners) and relayer fees (paid to power complex services like bridging).\nFees are based on network traffic and transaction complexity. MetaMask does not profit from either fee." diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js index 60f3b46d31d1..1789b4d4b0dd 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js @@ -93,14 +93,10 @@ function TransactionListItemInner({ // Bridge transactions const isBridgeTx = transactionGroup.initialTransaction.type === TransactionType.bridge; - const { - bridgeTitleSuffix, - bridgeTxHistoryItem, - isBridgeComplete, - showBridgeTxDetails, - } = useBridgeTxHistoryData({ - transactionGroup, - }); + const { bridgeTxHistoryItem, isBridgeComplete, showBridgeTxDetails } = + useBridgeTxHistoryData({ + transactionGroup, + }); const { initialTransaction: { id }, @@ -309,7 +305,7 @@ function TransactionListItemInner({ : toggleShowDetails } className={className} - title={`${title}${bridgeTitleSuffix}`} + title={title} icon={ ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) isCustodian ? ( diff --git a/ui/hooks/bridge/useBridgeTxHistoryData.ts b/ui/hooks/bridge/useBridgeTxHistoryData.ts index 0556caed88fb..dd20835c411c 100644 --- a/ui/hooks/bridge/useBridgeTxHistoryData.ts +++ b/ui/hooks/bridge/useBridgeTxHistoryData.ts @@ -4,8 +4,6 @@ import { TransactionMeta } from '@metamask/transaction-controller'; import { useHistory } from 'react-router-dom'; import { selectBridgeHistoryForAccount } from '../../ducks/bridge-status/selectors'; import { CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE } from '../../helpers/constants/routes'; -import { useI18nContext } from '../useI18nContext'; -import useBridgeChainInfo from './useBridgeChainInfo'; export type UseBridgeDataProps = { transactionGroup: { @@ -21,25 +19,10 @@ export type UseBridgeDataProps = { export default function useBridgeTxHistoryData({ transactionGroup, }: UseBridgeDataProps) { - const t = useI18nContext(); const history = useHistory(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); const srcTxMetaId = transactionGroup.initialTransaction.id; - - // If this tx is a bridge tx and not a smart transaction, it will always have a bridgeHistoryItem - const bridgeHistoryItem = srcTxMetaId - ? bridgeHistory[srcTxMetaId] - : undefined; - - const { destNetwork } = useBridgeChainInfo({ - bridgeHistoryItem, - srcTxMeta: transactionGroup.initialTransaction, - }); - - const destChainName = destNetwork?.name; - const bridgeTitleSuffix = destChainName - ? t('bridgeToChain', [destChainName]) - : ''; + const bridgeHistoryItem = bridgeHistory[srcTxMetaId]; // By complete, this means BOTH source and dest tx are confirmed const isBridgeComplete = bridgeHistoryItem @@ -54,7 +37,6 @@ export default function useBridgeTxHistoryData({ }; return { - bridgeTitleSuffix, bridgeTxHistoryItem: bridgeHistoryItem, isBridgeComplete, showBridgeTxDetails, diff --git a/ui/hooks/useTransactionDisplayData.js b/ui/hooks/useTransactionDisplayData.js index 2adcdd03e760..4b97438195e6 100644 --- a/ui/hooks/useTransactionDisplayData.js +++ b/ui/hooks/useTransactionDisplayData.js @@ -35,6 +35,7 @@ import { TransactionGroupCategory } from '../../shared/constants/transaction'; import { captureSingleException } from '../store/actions'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils'; +import { selectBridgeHistoryForAccount } from '../ducks/bridge-status/selectors'; import { useI18nContext } from './useI18nContext'; import { useTokenFiatAmount } from './useTokenFiatAmount'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; @@ -43,6 +44,7 @@ import { useTokenDisplayValue } from './useTokenDisplayValue'; import { useTokenData } from './useTokenData'; import { useSwappedTokenValue } from './useSwappedTokenValue'; import { useCurrentAsset } from './useCurrentAsset'; +import useBridgeChainInfo from './bridge/useBridgeChainInfo'; /** * There are seven types of transaction entries that are currently differentiated in the design: @@ -107,6 +109,16 @@ export function useTransactionDisplayData(transactionGroup) { const tokenList = useSelector(getTokenList); const t = useI18nContext(); + // Bridge data + const bridgeHistory = useSelector(selectBridgeHistoryForAccount); + const srcTxMetaId = transactionGroup.initialTransaction.id; + const bridgeHistoryItem = bridgeHistory[srcTxMetaId]; + const { destNetwork } = useBridgeChainInfo({ + bridgeHistoryItem, + srcTxMeta: transactionGroup.initialTransaction, + }); + const destChainName = destNetwork?.name; + const { initialTransaction, primaryTransaction } = transactionGroup; // initialTransaction contains the data we need to derive the primary purpose of this transaction group const { type } = initialTransaction; @@ -367,10 +379,12 @@ export function useTransactionDisplayData(transactionGroup) { title = t('bridgeApproval', [primaryTransaction.sourceTokenSymbol]); subtitle = origin; subtitleContainsOrigin = true; - primarySuffix = primaryTransaction.sourceTokenSymbol; // TODO this will be undefined right now + primarySuffix = primaryTransaction.sourceTokenSymbol; } else if (type === TransactionType.bridge) { - title = t('bridge'); + title = t('bridgeToChain', [destChainName]); category = TransactionGroupCategory.bridge; + // TODO add primaryDisplayValue,primarySuffix + // also secondaryDisplayValue, secondarySuffix } else { dispatch( captureSingleException( From 13f9804c6322c0b8f3c0b4007ac0cf45e773afe6 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:51:05 -0500 Subject: [PATCH 23/48] fix: lint error --- shared/types/bridge-status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index a108d7fbcd43..5ac5190003e1 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -1,6 +1,6 @@ -// eslint-disable-next-line import/no-restricted-paths import { TransactionMeta } from '@metamask/transaction-controller'; // TODO fix this +// eslint-disable-next-line import/no-restricted-paths import { ChainId, Quote, QuoteResponse } from '../../ui/pages/bridge/types'; // All fields need to be types not interfaces, same with their children fields From cb1f4fcd21655816f505d6da1109d83564607699 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:16:47 -0500 Subject: [PATCH 24/48] chore: update comment --- ui/pages/bridge/hooks/useHandleTx.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/pages/bridge/hooks/useHandleTx.ts b/ui/pages/bridge/hooks/useHandleTx.ts index dd008c0bf9e8..e4b7ef961ea8 100644 --- a/ui/pages/bridge/hooks/useHandleTx.ts +++ b/ui/pages/bridge/hooks/useHandleTx.ts @@ -59,7 +59,9 @@ export default function useHandleTx() { maxPriorityFeePerGas, }; - // Need access to the txMeta.id, so we call addTransaction instead of addTransactionAndWaitForPublish + // Need access to the txMeta.id right away so we can track it in BridgeStatusController, + // so we call addTransaction instead of addTransactionAndWaitForPublish + // if it's an STX, addTransactionAndWaitForPublish blocks until there is a txHash const txMeta = await addTransaction(finalTxParams, { requireApproval: false, type: txType, From 462da0de379ba292fec09829c5310309b6629ead Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:22:05 -0500 Subject: [PATCH 25/48] fix: broken tests and lint errors --- .../controllers/bridge-status/mocks.ts | 60 ++++++++++++++++++- ui/hooks/bridge/useBridgeChainInfo.ts | 18 ++++-- .../transaction-details/bridge-step-list.tsx | 23 +------ 3 files changed, 71 insertions(+), 30 deletions(-) diff --git a/app/scripts/controllers/bridge-status/mocks.ts b/app/scripts/controllers/bridge-status/mocks.ts index c631fe84ab3a..7ccce65e8474 100644 --- a/app/scripts/controllers/bridge-status/mocks.ts +++ b/app/scripts/controllers/bridge-status/mocks.ts @@ -1,3 +1,4 @@ +import { TransactionMeta } from '@metamask/transaction-controller'; import { BridgeId, StatusResponse, @@ -204,11 +205,15 @@ export const getMockQuote = ({ }); export const getMockStartPollingForBridgeTxStatusArgs = ({ + txMetaId = 'bridgeTxMetaId1', srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}) => ({ + bridgeTxMeta: { + id: txMetaId, + } as TransactionMeta, statusRequest: { bridgeId: 'lifi', srcTxHash, @@ -239,13 +244,44 @@ export const getMockStartPollingForBridgeTxStatusArgs = ({ }); export const MockTxHistory = { + getInitNoSrcTxHash: ({ + txMetaId = 'bridgeTxMetaId1', + account = '0xaccount1', + srcChainId = 42161, + destChainId = 10, + } = {}) => ({ + [txMetaId]: { + txMetaId, + statusRequest: { + bridgeId: 'lifi', + bridge: 'across', + srcChainId, + destChainId, + }, + quote: getMockQuote({ srcChainId, destChainId }), + startTime: 1729964825189, + estimatedProcessingTimeInSeconds: 15, + slippagePercentage: 0, + account, + targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + }, + }), getInit: ({ + txMetaId = 'bridgeTxMetaId1', srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}) => ({ - [srcTxHash]: { + [txMetaId]: { + txMetaId, + statusRequest: { + bridgeId: 'lifi', + srcTxHash, + bridge: 'across', + srcChainId, + destChainId, + }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, @@ -255,12 +291,21 @@ export const MockTxHistory = { }, }), getPending: ({ + txMetaId = 'bridgeTxMetaId1', srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}) => ({ - [srcTxHash]: { + [txMetaId]: { + txMetaId, + statusRequest: { + bridgeId: 'lifi', + srcTxHash, + bridge: 'across', + srcChainId, + destChainId, + }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, @@ -274,12 +319,21 @@ export const MockTxHistory = { }, }), getComplete: ({ + txMetaId = 'bridgeTxMetaId1', srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}) => ({ - [srcTxHash]: { + [txMetaId]: { + txMetaId, + statusRequest: { + bridgeId: 'lifi', + srcTxHash, + bridge: 'across', + srcChainId, + destChainId, + }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, diff --git a/ui/hooks/bridge/useBridgeChainInfo.ts b/ui/hooks/bridge/useBridgeChainInfo.ts index 6073ccb57e8f..2f7fda1c1f1b 100644 --- a/ui/hooks/bridge/useBridgeChainInfo.ts +++ b/ui/hooks/bridge/useBridgeChainInfo.ts @@ -1,5 +1,8 @@ import { useSelector } from 'react-redux'; -import { TransactionMeta } from '@metamask/transaction-controller'; +import { + TransactionMeta, + TransactionType, +} from '@metamask/transaction-controller'; import { Hex } from '@metamask/utils'; import { NetworkConfiguration } from '@metamask/network-controller'; import { Numeric } from '../../../shared/modules/Numeric'; @@ -13,20 +16,19 @@ import { CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../shared/constant const getSourceAndDestChainIds = ({ bridgeHistoryItem, - srcTxMeta, }: UseBridgeChainInfoProps) => { const hexSrcChainId = bridgeHistoryItem ? (new Numeric( bridgeHistoryItem.quote.srcChainId, 10, ).toPrefixedHexString() as Hex) - : srcTxMeta?.chainId; + : undefined; const hexDestChainId = bridgeHistoryItem ? (new Numeric( bridgeHistoryItem.quote.destChainId, 10, ).toPrefixedHexString() as Hex) - : srcTxMeta?.destinationChainId; + : undefined; return { hexSrcChainId, @@ -50,9 +52,15 @@ export default function useBridgeChainInfo({ getNetworkConfigurationsByChainId, ); + if (srcTxMeta?.type !== TransactionType.bridge) { + return { + srcNetwork: undefined, + destNetwork: undefined, + }; + } + const { hexSrcChainId, hexDestChainId } = getSourceAndDestChainIds({ bridgeHistoryItem, - srcTxMeta, }); if (!hexSrcChainId || !hexDestChainId) { diff --git a/ui/pages/bridge/transaction-details/bridge-step-list.tsx b/ui/pages/bridge/transaction-details/bridge-step-list.tsx index 112cc05243a0..808a3e7d40f8 100644 --- a/ui/pages/bridge/transaction-details/bridge-step-list.tsx +++ b/ui/pages/bridge/transaction-details/bridge-step-list.tsx @@ -9,7 +9,6 @@ import { Step, } from '../../../../shared/types/bridge-status'; import { formatDate } from '../../../helpers/utils/util'; -import { Numeric } from '../../../../shared/modules/Numeric'; import BridgeStepDescription, { getStepStatus, } from './bridge-step-description'; @@ -41,27 +40,7 @@ export default function BridgeStepList({ srcChainTxMeta, networkConfigurationsByChainId, }: BridgeStepsProps) { - const steps = - bridgeHistoryItem?.quote.steps || - srcChainTxMeta?.bridgeSteps?.map((step) => ({ - // Convert hex to numbers - ...step, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - action: step.action as any, - srcChainId: new Numeric(step.srcChainId, 16).toBase(10).toNumber(), - destChainId: step.destChainId - ? new Numeric(step.destChainId, 16).toBase(10).toNumber() - : undefined, - srcAsset: { - ...step.srcAsset, - chainId: new Numeric(step.srcAsset.chainId, 16).toBase(10).toNumber(), - }, - destAsset: { - ...step.destAsset, - chainId: new Numeric(step.destAsset.chainId, 16).toBase(10).toNumber(), - }, - })) || - []; + const steps = bridgeHistoryItem?.quote.steps || []; const stepStatuses = steps.map((step) => getStepStatus({ bridgeHistoryItem, step: step as Step, srcChainTxMeta }), ); From a1935a95dbc97adb466bfe88a83617e0d7568bb8 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:37:50 -0500 Subject: [PATCH 26/48] fix: show standard non-confirmed final statuses instead of bridge segments and bridge tx details --- .../transaction-list-item.component.js | 8 +++++-- ui/hooks/bridge/useBridgeTxHistoryData.ts | 24 +++++++++++++++---- ui/hooks/useTransactionDisplayData.js | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js index 1789b4d4b0dd..abe14c14258e 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js @@ -69,7 +69,9 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { ActivityListItem } from '../../multichain'; import { abortTransactionSigning } from '../../../store/actions'; import { getIsSmartTransaction } from '../../../../shared/modules/selectors'; -import useBridgeTxHistoryData from '../../../hooks/bridge/useBridgeTxHistoryData'; +import useBridgeTxHistoryData, { + FINAL_NON_CONFIRMED_STATUSES, +} from '../../../hooks/bridge/useBridgeTxHistoryData'; import BridgeActivityItemTxSegments from '../../../pages/bridge/transaction-details/bridge-activity-item-tx-segments'; function TransactionListItemInner({ @@ -351,7 +353,9 @@ function TransactionListItemInner({ ///: END:ONLY_INCLUDE_IF } subtitle={ - isBridgeTx && !isBridgeComplete ? ( + !FINAL_NON_CONFIRMED_STATUSES.includes(status) && + isBridgeTx && + !isBridgeComplete ? ( { - history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxMetaId}`); - }; + const showBridgeTxDetails = FINAL_NON_CONFIRMED_STATUSES.includes( + txMeta.status, + ) + ? undefined + : () => { + history.push(`${CROSS_CHAIN_SWAP_TX_DETAILS_ROUTE}/${srcTxMetaId}`); + }; return { bridgeTxHistoryItem: bridgeHistoryItem, diff --git a/ui/hooks/useTransactionDisplayData.js b/ui/hooks/useTransactionDisplayData.js index 4b97438195e6..66b9102d5c29 100644 --- a/ui/hooks/useTransactionDisplayData.js +++ b/ui/hooks/useTransactionDisplayData.js @@ -381,7 +381,7 @@ export function useTransactionDisplayData(transactionGroup) { subtitleContainsOrigin = true; primarySuffix = primaryTransaction.sourceTokenSymbol; } else if (type === TransactionType.bridge) { - title = t('bridgeToChain', [destChainName]); + title = t('bridgeToChain', [destChainName || '']); category = TransactionGroupCategory.bridge; // TODO add primaryDisplayValue,primarySuffix // also secondaryDisplayValue, secondarySuffix From 9fa31f6008c1be42689f00e742e41facd5ed26ad Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:39:49 -0500 Subject: [PATCH 27/48] fix: update snapshots --- .../bridge-status-controller.test.ts.snap | 192 +++++++++++++++++- 1 file changed, 190 insertions(+), 2 deletions(-) diff --git a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap index ebd3a938822e..9c0345295317 100644 --- a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap +++ b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap @@ -2,7 +2,7 @@ exports[`BridgeStatusController constructor rehydrates the tx history state 1`] = ` { - "0xsrcTxHash1": { + "bridgeTxMetaId1": { "account": "0xaccount1", "estimatedProcessingTimeInSeconds": 15, "initialDestAssetBalance": undefined, @@ -101,14 +101,108 @@ exports[`BridgeStatusController constructor rehydrates the tx history state 1`] }, "status": "PENDING", }, + "statusRequest": { + "bridge": "across", + "bridgeId": "lifi", + "destChainId": 10, + "quote": { + "bridgeId": "lifi", + "bridges": [ + "across", + ], + "destAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 10, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.63", + "symbol": "ETH", + }, + "destChainId": 10, + "destTokenAmount": "990654755978612", + "feeData": { + "metabridge": { + "amount": "8750000000000", + "asset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + }, + }, + "requestId": "197c402f-cb96-4096-9f8c-54aed84ca776", + "srcAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + "srcChainId": 42161, + "srcTokenAmount": "991250000000000", + "steps": [ + { + "action": "bridge", + "destAmount": "990654755978612", + "destAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 10, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.63", + "symbol": "ETH", + }, + "destChainId": 10, + "protocol": { + "displayName": "Across", + "icon": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png", + "name": "across", + }, + "srcAmount": "991250000000000", + "srcAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + "srcChainId": 42161, + }, + ], + }, + "refuel": false, + "srcChainId": 42161, + "srcTxHash": "0xsrcTxHash1", + }, "targetContractAddress": "0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC", + "txMetaId": "bridgeTxMetaId1", }, } `; exports[`BridgeStatusController startPollingForBridgeTxStatus sets the inital tx history state 1`] = ` { - "0xsrcTxHash1": { + "bridgeTxMetaId1": { "account": "0xaccount1", "estimatedProcessingTimeInSeconds": 15, "initialDestAssetBalance": undefined, @@ -207,7 +301,101 @@ exports[`BridgeStatusController startPollingForBridgeTxStatus sets the inital tx }, "status": "PENDING", }, + "statusRequest": { + "bridge": "across", + "bridgeId": "lifi", + "destChainId": 10, + "quote": { + "bridgeId": "lifi", + "bridges": [ + "across", + ], + "destAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 10, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.63", + "symbol": "ETH", + }, + "destChainId": 10, + "destTokenAmount": "990654755978612", + "feeData": { + "metabridge": { + "amount": "8750000000000", + "asset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + }, + }, + "requestId": "197c402f-cb96-4096-9f8c-54aed84ca776", + "srcAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + "srcChainId": 42161, + "srcTokenAmount": "991250000000000", + "steps": [ + { + "action": "bridge", + "destAmount": "990654755978612", + "destAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 10, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.63", + "symbol": "ETH", + }, + "destChainId": 10, + "protocol": { + "displayName": "Across", + "icon": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png", + "name": "across", + }, + "srcAmount": "991250000000000", + "srcAsset": { + "address": "0x0000000000000000000000000000000000000000", + "chainId": 42161, + "coinKey": "ETH", + "decimals": 18, + "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", + "name": "ETH", + "priceUSD": "2478.7", + "symbol": "ETH", + }, + "srcChainId": 42161, + }, + ], + }, + "refuel": false, + "srcChainId": 42161, + "srcTxHash": "0xsrcTxHash1", + }, "targetContractAddress": "0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC", + "txMetaId": "bridgeTxMetaId1", }, } `; From 298e2ae64c16f3f751561cf94a1db75f46fea862 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:00:49 -0500 Subject: [PATCH 28/48] chore: remove statusRequest from txHistory --- .../bridge-status-controller.test.ts.snap | 186 ------------------ .../bridge-status/bridge-status-controller.ts | 39 ++-- .../controllers/bridge-status/mocks.ts | 28 --- .../controllers/bridge-status/utils.ts | 25 ++- shared/types/bridge-status.ts | 7 +- 5 files changed, 39 insertions(+), 246 deletions(-) diff --git a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap index 9c0345295317..2dbcf3f1b027 100644 --- a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap +++ b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap @@ -101,99 +101,6 @@ exports[`BridgeStatusController constructor rehydrates the tx history state 1`] }, "status": "PENDING", }, - "statusRequest": { - "bridge": "across", - "bridgeId": "lifi", - "destChainId": 10, - "quote": { - "bridgeId": "lifi", - "bridges": [ - "across", - ], - "destAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 10, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.63", - "symbol": "ETH", - }, - "destChainId": 10, - "destTokenAmount": "990654755978612", - "feeData": { - "metabridge": { - "amount": "8750000000000", - "asset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - }, - }, - "requestId": "197c402f-cb96-4096-9f8c-54aed84ca776", - "srcAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - "srcChainId": 42161, - "srcTokenAmount": "991250000000000", - "steps": [ - { - "action": "bridge", - "destAmount": "990654755978612", - "destAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 10, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.63", - "symbol": "ETH", - }, - "destChainId": 10, - "protocol": { - "displayName": "Across", - "icon": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png", - "name": "across", - }, - "srcAmount": "991250000000000", - "srcAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - "srcChainId": 42161, - }, - ], - }, - "refuel": false, - "srcChainId": 42161, - "srcTxHash": "0xsrcTxHash1", - }, "targetContractAddress": "0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC", "txMetaId": "bridgeTxMetaId1", }, @@ -301,99 +208,6 @@ exports[`BridgeStatusController startPollingForBridgeTxStatus sets the inital tx }, "status": "PENDING", }, - "statusRequest": { - "bridge": "across", - "bridgeId": "lifi", - "destChainId": 10, - "quote": { - "bridgeId": "lifi", - "bridges": [ - "across", - ], - "destAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 10, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.63", - "symbol": "ETH", - }, - "destChainId": 10, - "destTokenAmount": "990654755978612", - "feeData": { - "metabridge": { - "amount": "8750000000000", - "asset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - }, - }, - "requestId": "197c402f-cb96-4096-9f8c-54aed84ca776", - "srcAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - "srcChainId": 42161, - "srcTokenAmount": "991250000000000", - "steps": [ - { - "action": "bridge", - "destAmount": "990654755978612", - "destAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 10, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.63", - "symbol": "ETH", - }, - "destChainId": 10, - "protocol": { - "displayName": "Across", - "icon": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/bridges/acrossv2.png", - "name": "across", - }, - "srcAmount": "991250000000000", - "srcAsset": { - "address": "0x0000000000000000000000000000000000000000", - "chainId": 42161, - "coinKey": "ETH", - "decimals": 18, - "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png", - "name": "ETH", - "priceUSD": "2478.7", - "symbol": "ETH", - }, - "srcChainId": 42161, - }, - ], - }, - "refuel": false, - "srcChainId": 42161, - "srcTxHash": "0xsrcTxHash1", - }, "targetContractAddress": "0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC", "txMetaId": "bridgeTxMetaId1", }, diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 3f245d398a4d..4578eb3435dc 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -6,7 +6,6 @@ import { StartPollingForBridgeTxStatusArgs, StatusTypes, BridgeStatusControllerState, - StatusRequestWithSrcTxHash, } from '../../../../shared/types/bridge-status'; import { decimalToPrefixedHex } from '../../../../shared/modules/conversion.utils'; import { @@ -15,7 +14,7 @@ import { REFRESH_INTERVAL_MS, } from './constants'; import { BridgeStatusControllerMessenger } from './types'; -import { fetchBridgeTxStatus, isStatusRequestWithSrcTxHash } from './utils'; +import { fetchBridgeTxStatus, getStatusRequest } from './utils'; const metadata: StateMetadata<{ bridgeStatusState: BridgeStatusControllerState; @@ -168,7 +167,6 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // We know it's in progress but not the exact status yet const txHistoryItem = { txMetaId: bridgeTxMeta.id, - statusRequest, // Attach the statusRequest to the historyItem so we can look it up and update it when srcTxHash is available quote: quoteResponse.quote, startTime, estimatedProcessingTimeInSeconds: @@ -223,7 +221,9 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // We try here because we receive 500 errors from Bridge API if we try to fetch immediately after submitting the source tx // Oddly mostly happens on Optimism, never on Arbitrum. By the 2nd fetch, the Bridge API responds properly. // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases - const statusRequest = this.#getStatusRequestWithSrcTxHash(bridgeTxMetaId); + const historyItem = bridgeStatusState.txHistory[bridgeTxMetaId]; + const srcTxHash = this.#getSrcTxHash(bridgeTxMetaId); + const statusRequest = getStatusRequest(historyItem.quote, srcTxHash); const status = await fetchBridgeTxStatus(statusRequest); // No need to purge these on network change or account change, TransactionController does not purge either. @@ -255,14 +255,13 @@ export default class BridgeStatusController extends StaticIntervalPollingControl } }; - #getStatusRequestWithSrcTxHash = ( - bridgeTxMetaId: string, - ): StatusRequestWithSrcTxHash => { + #getSrcTxHash = (bridgeTxMetaId: string): string => { const { bridgeStatusState } = this.state; - const { statusRequest } = bridgeStatusState.txHistory[bridgeTxMetaId]; + const srcTxHash = + bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash; - if (isStatusRequestWithSrcTxHash(statusRequest)) { - return statusRequest; + if (srcTxHash) { + return srcTxHash; } // Look up in TransactionController if txMeta has been updated with the srcTxHash, if so, update it here as well @@ -280,23 +279,27 @@ export default class BridgeStatusController extends StaticIntervalPollingControl ...bridgeStatusState.txHistory, [bridgeTxMetaId]: { ...bridgeStatusState.txHistory[bridgeTxMetaId], - statusRequest: { - ...bridgeStatusState.txHistory[bridgeTxMetaId].statusRequest, - srcTxHash: txMeta?.hash, + status: { + ...bridgeStatusState.txHistory[bridgeTxMetaId].status, + srcChain: { + ...bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain, + txHash: txMeta?.hash, + }, }, }, }, }; }); - const nextStatusRequest = - nextState.bridgeStatusState.txHistory[bridgeTxMetaId].statusRequest; + const nextSrcTxHash = + nextState.bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain + .txHash; - if (isStatusRequestWithSrcTxHash(nextStatusRequest)) { - return nextStatusRequest; + if (nextSrcTxHash) { + return nextSrcTxHash; } - throw new Error('StatusRequestWithSrcTxHash missing srcTxHash'); + throw new Error('srcTxHash missing'); }; // Wipes the bridge status for the given address and chainId diff --git a/app/scripts/controllers/bridge-status/mocks.ts b/app/scripts/controllers/bridge-status/mocks.ts index 7ccce65e8474..d46157d3433b 100644 --- a/app/scripts/controllers/bridge-status/mocks.ts +++ b/app/scripts/controllers/bridge-status/mocks.ts @@ -252,12 +252,6 @@ export const MockTxHistory = { } = {}) => ({ [txMetaId]: { txMetaId, - statusRequest: { - bridgeId: 'lifi', - bridge: 'across', - srcChainId, - destChainId, - }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, @@ -268,20 +262,12 @@ export const MockTxHistory = { }), getInit: ({ txMetaId = 'bridgeTxMetaId1', - srcTxHash = '0xsrcTxHash1', account = '0xaccount1', srcChainId = 42161, destChainId = 10, } = {}) => ({ [txMetaId]: { txMetaId, - statusRequest: { - bridgeId: 'lifi', - srcTxHash, - bridge: 'across', - srcChainId, - destChainId, - }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, @@ -299,13 +285,6 @@ export const MockTxHistory = { } = {}) => ({ [txMetaId]: { txMetaId, - statusRequest: { - bridgeId: 'lifi', - srcTxHash, - bridge: 'across', - srcChainId, - destChainId, - }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, @@ -327,13 +306,6 @@ export const MockTxHistory = { } = {}) => ({ [txMetaId]: { txMetaId, - statusRequest: { - bridgeId: 'lifi', - srcTxHash, - bridge: 'across', - srcChainId, - destChainId, - }, quote: getMockQuote({ srcChainId, destChainId }), startTime: 1729964825189, estimatedProcessingTimeInSeconds: 15, diff --git a/app/scripts/controllers/bridge-status/utils.ts b/app/scripts/controllers/bridge-status/utils.ts index 0d6cdd0cf296..1792c8f00b42 100644 --- a/app/scripts/controllers/bridge-status/utils.ts +++ b/app/scripts/controllers/bridge-status/utils.ts @@ -5,18 +5,18 @@ import { import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; import { StatusResponse, - StatusRequestWithSrcTxHash, StatusRequest, } from '../../../../shared/types/bridge-status'; +// TODO fix this +// eslint-disable-next-line import/no-restricted-paths +import { Quote } from '../../../../ui/pages/bridge/types'; import { validateResponse, validators } from './validators'; const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID }; export const BRIDGE_STATUS_BASE_URL = `${BRIDGE_API_BASE_URL}/getTxStatus`; -export const fetchBridgeTxStatus = async ( - statusRequest: StatusRequestWithSrcTxHash, -) => { +export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => { // Assemble params const { quote, ...statusRequestNoQuote } = statusRequest; const statusRequestNoQuoteFormatted = Object.fromEntries( @@ -51,8 +51,17 @@ export const fetchBridgeTxStatus = async ( return rawTxStatus; }; -export const isStatusRequestWithSrcTxHash = ( - statusRequest: StatusRequest, -): statusRequest is StatusRequestWithSrcTxHash => { - return statusRequest.srcTxHash !== undefined; +export const getStatusRequest = ( + quote: Quote, + srcTxHash: string, +): StatusRequest => { + return { + bridgeId: quote.bridgeId, + srcTxHash, + bridge: quote.bridges[0], + srcChainId: quote.srcChainId, + destChainId: quote.destChainId, + quote, + refuel: Boolean(quote.refuel), + }; }; diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index 5ac5190003e1..e873141fa981 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -15,7 +15,7 @@ export enum StatusTypes { export type StatusRequest = { bridgeId: string; // lifi, socket, squid - srcTxHash?: string; // lifi, socket, squid, this might be undefined if this is a smart transaction (STX) + srcTxHash: string; // lifi, socket, squid bridge: string; // lifi, socket, squid srcChainId: ChainId; // lifi, socket, squid destChainId: ChainId; // lifi, socket, squid @@ -23,10 +23,6 @@ export type StatusRequest = { refuel?: boolean; // lifi }; -export type StatusRequestWithSrcTxHash = StatusRequest & { - srcTxHash: string; -}; - export type Asset = { chainId: ChainId; address: string; @@ -112,7 +108,6 @@ export type RefuelData = object & Step; export type BridgeHistoryItem = { txMetaId: string; // Need this to handle STX that might not have a txHash immediately - statusRequest: StatusRequest; quote: Quote; status: StatusResponse; startTime?: number; From ad13ba698a7c6680d40ed86fd75020de4a944805 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:06:02 -0500 Subject: [PATCH 29/48] fix: broken tests --- .../bridge-status-controller.test.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts index e10df4a6f521..0a8e331b2767 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.test.ts @@ -261,6 +261,7 @@ describe('BridgeStatusController', () => { // Start polling for 0xaccount2 bridgeStatusController.startPollingForBridgeTxStatus( getMockStartPollingForBridgeTxStatusArgs({ + txMetaId: 'bridgeTxMetaId2', srcTxHash: '0xsrcTxHash2', account: '0xaccount2', }), @@ -271,10 +272,10 @@ describe('BridgeStatusController', () => { // Check that both accounts have a tx history entry expect( bridgeStatusController.state.bridgeStatusState.txHistory, - ).toHaveProperty('0xsrcTxHash1'); + ).toHaveProperty('bridgeTxMetaId1'); expect( bridgeStatusController.state.bridgeStatusState.txHistory, - ).toHaveProperty('0xsrcTxHash2'); + ).toHaveProperty('bridgeTxMetaId2'); // Wipe the status for 1 account only bridgeStatusController.wipeBridgeStatus({ @@ -334,6 +335,7 @@ describe('BridgeStatusController', () => { getMockStartPollingForBridgeTxStatusArgs({ account: '0xaccount1', srcTxHash: '0xsrcTxHash1', + txMetaId: 'bridgeTxMetaId1', srcChainId: 42161, destChainId: 1, }), @@ -346,6 +348,7 @@ describe('BridgeStatusController', () => { getMockStartPollingForBridgeTxStatusArgs({ account: '0xaccount1', srcTxHash: '0xsrcTxHash2', + txMetaId: 'bridgeTxMetaId2', srcChainId: 10, destChainId: 123, }), @@ -355,20 +358,20 @@ describe('BridgeStatusController', () => { // Check we have a tx history entry for each chainId expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash1'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId1 .quote.srcChainId, ).toEqual(42161); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash1'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId1 .quote.destChainId, ).toEqual(1); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash2'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId2 .quote.srcChainId, ).toEqual(10); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash2'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId2 .quote.destChainId, ).toEqual(123); @@ -429,6 +432,7 @@ describe('BridgeStatusController', () => { getMockStartPollingForBridgeTxStatusArgs({ account: '0xaccount1', srcTxHash: '0xsrcTxHash1', + txMetaId: 'bridgeTxMetaId1', srcChainId: 42161, destChainId: 1, }), @@ -441,6 +445,7 @@ describe('BridgeStatusController', () => { getMockStartPollingForBridgeTxStatusArgs({ account: '0xaccount1', srcTxHash: '0xsrcTxHash2', + txMetaId: 'bridgeTxMetaId2', srcChainId: 10, destChainId: 123, }), @@ -450,20 +455,20 @@ describe('BridgeStatusController', () => { // Check we have a tx history entry for each chainId expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash1'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId1 .quote.srcChainId, ).toEqual(42161); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash1'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId1 .quote.destChainId, ).toEqual(1); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash2'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId2 .quote.srcChainId, ).toEqual(10); expect( - bridgeStatusController.state.bridgeStatusState.txHistory['0xsrcTxHash2'] + bridgeStatusController.state.bridgeStatusState.txHistory.bridgeTxMetaId2 .quote.destChainId, ).toEqual(123); From bbadf029694269a677d03db594d65ec3809957d4 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:23:17 -0500 Subject: [PATCH 30/48] fix: possible to have statusRequest with undefined srcTxHash --- .../bridge-status/bridge-status-controller.ts | 7 +++++-- app/scripts/controllers/bridge-status/utils.ts | 10 ++++++---- shared/types/bridge-status.ts | 6 +++++- ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 4578eb3435dc..d79f751df55c 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -14,7 +14,7 @@ import { REFRESH_INTERVAL_MS, } from './constants'; import { BridgeStatusControllerMessenger } from './types'; -import { fetchBridgeTxStatus, getStatusRequest } from './utils'; +import { fetchBridgeTxStatus, getStatusRequestWithSrcTxHash } from './utils'; const metadata: StateMetadata<{ bridgeStatusState: BridgeStatusControllerState; @@ -223,7 +223,10 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases const historyItem = bridgeStatusState.txHistory[bridgeTxMetaId]; const srcTxHash = this.#getSrcTxHash(bridgeTxMetaId); - const statusRequest = getStatusRequest(historyItem.quote, srcTxHash); + const statusRequest = getStatusRequestWithSrcTxHash( + historyItem.quote, + srcTxHash, + ); const status = await fetchBridgeTxStatus(statusRequest); // No need to purge these on network change or account change, TransactionController does not purge either. diff --git a/app/scripts/controllers/bridge-status/utils.ts b/app/scripts/controllers/bridge-status/utils.ts index 1792c8f00b42..33af3c09cb03 100644 --- a/app/scripts/controllers/bridge-status/utils.ts +++ b/app/scripts/controllers/bridge-status/utils.ts @@ -5,7 +5,7 @@ import { import fetchWithCache from '../../../../shared/lib/fetch-with-cache'; import { StatusResponse, - StatusRequest, + StatusRequestWithSrcTxHash, } from '../../../../shared/types/bridge-status'; // TODO fix this // eslint-disable-next-line import/no-restricted-paths @@ -16,7 +16,9 @@ const CLIENT_ID_HEADER = { 'X-Client-Id': BRIDGE_CLIENT_ID }; export const BRIDGE_STATUS_BASE_URL = `${BRIDGE_API_BASE_URL}/getTxStatus`; -export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => { +export const fetchBridgeTxStatus = async ( + statusRequest: StatusRequestWithSrcTxHash, +) => { // Assemble params const { quote, ...statusRequestNoQuote } = statusRequest; const statusRequestNoQuoteFormatted = Object.fromEntries( @@ -51,10 +53,10 @@ export const fetchBridgeTxStatus = async (statusRequest: StatusRequest) => { return rawTxStatus; }; -export const getStatusRequest = ( +export const getStatusRequestWithSrcTxHash = ( quote: Quote, srcTxHash: string, -): StatusRequest => { +): StatusRequestWithSrcTxHash => { return { bridgeId: quote.bridgeId, srcTxHash, diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index e873141fa981..e489daa90ccb 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -15,7 +15,7 @@ export enum StatusTypes { export type StatusRequest = { bridgeId: string; // lifi, socket, squid - srcTxHash: string; // lifi, socket, squid + srcTxHash?: string; // lifi, socket, squid, might be undefined for STX bridge: string; // lifi, socket, squid srcChainId: ChainId; // lifi, socket, squid destChainId: ChainId; // lifi, socket, squid @@ -23,6 +23,10 @@ export type StatusRequest = { refuel?: boolean; // lifi }; +export type StatusRequestWithSrcTxHash = StatusRequest & { + srcTxHash: string; +}; + export type Asset = { chainId: ChainId; address: string; diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts index d6a5563ae170..3632be62bb87 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts @@ -41,7 +41,7 @@ export default function useSubmitBridgeTransaction() { // Get bridge tx status const statusRequest = { bridgeId: quoteResponse.quote.bridgeId, - srcTxHash: bridgeTxMeta.hash, + srcTxHash: bridgeTxMeta.hash, // This might be undefined for STX bridge: quoteResponse.quote.bridges[0], srcChainId: quoteResponse.quote.srcChainId, destChainId: quoteResponse.quote.destChainId, From cfd6ed6317b60110b5ba044a50be9fd2e073f941 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:39:09 -0500 Subject: [PATCH 31/48] fix: broken tests --- .../hooks/useSubmitBridgeTransaction.test.tsx | 68 ++++++------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.test.tsx b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.test.tsx index 55c19821777b..a84dc7aae443 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.test.tsx +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.test.tsx @@ -35,7 +35,7 @@ jest.mock('../../../store/actions', () => { const original = jest.requireActual('../../../store/actions'); return { ...original, - addTransactionAndWaitForPublish: jest.fn(), + addTransaction: jest.fn(), addToken: jest.fn().mockImplementation(original.addToken), addNetwork: jest.fn().mockImplementation(original.addNetwork), }; @@ -132,15 +132,15 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { it('executes bridge transaction', async () => { // Setup - const mockAddTransactionAndWaitForPublish = jest.fn(() => { + const mockAddTransaction = jest.fn(() => { return { id: 'txMetaId-01', }; }); // For some reason, setBackgroundConnection does not work, gets hung up on the promise, so mock this way instead - (actions.addTransactionAndWaitForPublish as jest.Mock).mockImplementation( - mockAddTransactionAndWaitForPublish, + (actions.addTransaction as jest.Mock).mockImplementation( + mockAddTransaction, ); const store = makeMockStore(); const { result } = renderHook(() => useSubmitBridgeTransaction(), { @@ -153,7 +153,7 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { ); // Assert - expect(mockAddTransactionAndWaitForPublish).toHaveBeenLastCalledWith( + expect(mockAddTransaction).toHaveBeenLastCalledWith( { chainId: '0x1', data: '0x3ce33bff0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000a7d8c000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d6c6966694164617074657256320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000e397c4883ec89ed4fc9d258f00c689708b2799c9000000000000000000000000000000000000000000000000000000000000a4b1000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000000000000000000000000000000000000000a660c6000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000177fa000000000000000000000000e6b738da243e8fa2a0ed5915645789add5de515200000000000000000000000000000000000000000000000000000000000000902340ab8fc3119af1d016a0eec5fe6ef47965741f6f7a4734bf784bf3ae3f2452a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000a660c60000a4b10008df3abdeb853d66fefedfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00dfeeddeadbeef8932eb23bad9bddb5cf81426f78279a53c6c3b7100000000000000000000000000000000740cfc1bc02079862368cb4eea1332bd9f2dfa925fc757fd51e40919859b87ca031a2a12d67e4ca4ba67d52b59114b3e18c1e8c839ae015112af82e92251db701b', @@ -167,34 +167,21 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { }, { requireApproval: false, - swaps: { - hasApproveTx: true, - meta: { - approvalTxId: 'txMetaId-01', - destinationTokenAddress: - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - destinationTokenDecimals: 6, - destinationTokenSymbol: 'USDC', - sourceTokenSymbol: 'USDC', - swapTokenValue: '11', - type: 'bridge', - }, - }, type: 'bridge', }, ); }); it('executes approval transaction if it exists', async () => { // Setup - const mockAddTransactionAndWaitForPublish = jest.fn(() => { + const mockAddTransaction = jest.fn(() => { return { id: 'txMetaId-01', }; }); // For some reason, setBackgroundConnection does not work, gets hung up on the promise, so mock this way instead - (actions.addTransactionAndWaitForPublish as jest.Mock).mockImplementation( - mockAddTransactionAndWaitForPublish, + (actions.addTransaction as jest.Mock).mockImplementation( + mockAddTransaction, ); const store = makeMockStore(); const { result } = renderHook(() => useSubmitBridgeTransaction(), { @@ -207,7 +194,7 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { ); // Assert - expect(mockAddTransactionAndWaitForPublish).toHaveBeenNthCalledWith( + expect(mockAddTransaction).toHaveBeenNthCalledWith( 1, { chainId: '0x1', @@ -222,14 +209,10 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { }, { requireApproval: false, - swaps: { - hasApproveTx: true, - meta: { sourceTokenSymbol: 'USDC', type: 'bridgeApproval' }, - }, type: 'bridgeApproval', }, ); - expect(mockAddTransactionAndWaitForPublish).toHaveBeenNthCalledWith( + expect(mockAddTransaction).toHaveBeenNthCalledWith( 2, { chainId: '0x1', @@ -244,19 +227,6 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { }, { requireApproval: false, - swaps: { - hasApproveTx: true, - meta: { - approvalTxId: 'txMetaId-01', - destinationTokenAddress: - '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', - destinationTokenDecimals: 6, - destinationTokenSymbol: 'USDC', - sourceTokenSymbol: 'USDC', - swapTokenValue: '11', - type: 'bridge', - }, - }, type: 'bridge', }, ); @@ -300,14 +270,14 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { wrapper: makeWrapper(store), }); - const mockAddTransactionAndWaitForPublish = jest.fn(() => { + const mockAddTransaction = jest.fn(() => { return { id: 'txMetaId-01', }; }); // For some reason, setBackgroundConnection does not work, gets hung up on the promise, so mock this way instead - (actions.addTransactionAndWaitForPublish as jest.Mock).mockImplementation( - mockAddTransactionAndWaitForPublish, + (actions.addTransaction as jest.Mock).mockImplementation( + mockAddTransaction, ); (actions.addToken as jest.Mock).mockImplementation( () => async () => ({}), @@ -366,14 +336,14 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { wrapper: makeWrapper(store), }); - const mockAddTransactionAndWaitForPublish = jest.fn(() => { + const mockAddTransaction = jest.fn(() => { return { id: 'txMetaId-01', }; }); // For some reason, setBackgroundConnection does not work, gets hung up on the promise, so mock this way instead - (actions.addTransactionAndWaitForPublish as jest.Mock).mockImplementation( - mockAddTransactionAndWaitForPublish, + (actions.addTransaction as jest.Mock).mockImplementation( + mockAddTransaction, ); (actions.addToken as jest.Mock).mockImplementation( () => async () => ({}), @@ -397,14 +367,14 @@ describe('ui/pages/bridge/hooks/useSubmitBridgeTransaction', () => { // Setup const store = makeMockStore(); - const mockAddTransactionAndWaitForPublish = jest.fn(() => { + const mockAddTransaction = jest.fn(() => { return { id: 'txMetaId-01', }; }); // For some reason, setBackgroundConnection does not work, gets hung up on the promise, so mock this way instead - (actions.addTransactionAndWaitForPublish as jest.Mock).mockImplementation( - mockAddTransactionAndWaitForPublish, + (actions.addTransaction as jest.Mock).mockImplementation( + mockAddTransaction, ); const mockedGetNetworkConfigurationsByChainId = // @ts-expect-error this is a jest mock From 215dceb859bd0698ff9fd221cb52c73c94bcc741 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:19:01 -0500 Subject: [PATCH 32/48] fix: broken tests --- .../transaction-list-item.component.js | 3 ++- .../transaction-list-item.component.test.js | 14 ++++++++++++++ ui/hooks/bridge/useBridgeTxHistoryData.ts | 6 +++--- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 1 + .../bridge-activity-item-tx-segments.tsx | 4 ++-- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js index abe14c14258e..5ffcb1405deb 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js @@ -69,7 +69,8 @@ import { MetaMetricsContext } from '../../../contexts/metametrics'; import { ActivityListItem } from '../../multichain'; import { abortTransactionSigning } from '../../../store/actions'; import { getIsSmartTransaction } from '../../../../shared/modules/selectors'; -import useBridgeTxHistoryData, { +import { + useBridgeTxHistoryData, FINAL_NON_CONFIRMED_STATUSES, } from '../../../hooks/bridge/useBridgeTxHistoryData'; import BridgeActivityItemTxSegments from '../../../pages/bridge/transaction-details/bridge-activity-item-tx-segments'; diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.test.js b/ui/components/app/transaction-list-item/transaction-list-item.component.test.js index e305040bf969..cc6e9ff63c70 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.test.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.test.js @@ -24,6 +24,7 @@ import { } from '../../../../shared/constants/metametrics'; import { MetaMetricsContext } from '../../../contexts/metametrics'; import { abortTransactionSigning } from '../../../store/actions'; +import { selectBridgeHistoryForAccount } from '../../../ducks/bridge-status/selectors'; import TransactionListItem from '.'; const FEE_MARKET_ESTIMATE_RETURN_VALUE = { @@ -62,6 +63,17 @@ jest.mock('react-redux', () => { }; }); +jest.mock('../../../hooks/bridge/useBridgeTxHistoryData', () => { + return { + ...jest.requireActual('../../../hooks/bridge/useBridgeTxHistoryData'), + useBridgeTxHistoryData: jest.fn(() => ({ + bridgeTxHistoryItem: undefined, + isBridgeComplete: false, + showBridgeTxDetails: false, + })), + }; +}); + jest.mock('../../../hooks/useGasFeeEstimates', () => ({ useGasFeeEstimates: jest.fn(), })); @@ -110,6 +122,8 @@ const generateUseSelectorRouter = (opts) => (selector) => { return opts.shouldShowFiat ?? false; } else if (selector === getTokens) { return opts.tokens ?? []; + } else if (selector === selectBridgeHistoryForAccount) { + return opts.bridgeHistory ?? {}; } return undefined; }; diff --git a/ui/hooks/bridge/useBridgeTxHistoryData.ts b/ui/hooks/bridge/useBridgeTxHistoryData.ts index 16d4144b87aa..6de4f7ef75b5 100644 --- a/ui/hooks/bridge/useBridgeTxHistoryData.ts +++ b/ui/hooks/bridge/useBridgeTxHistoryData.ts @@ -14,7 +14,7 @@ export const FINAL_NON_CONFIRMED_STATUSES = [ TransactionStatus.rejected, ]; -export type UseBridgeDataProps = { +export type UseBridgeTxHistoryDataProps = { transactionGroup: { hasCancelled: boolean; hasRetried: boolean; @@ -25,9 +25,9 @@ export type UseBridgeDataProps = { }; }; -export default function useBridgeTxHistoryData({ +export function useBridgeTxHistoryData({ transactionGroup, -}: UseBridgeDataProps) { +}: UseBridgeTxHistoryDataProps) { const history = useHistory(); const bridgeHistory = useSelector(selectBridgeHistoryForAccount); const txMeta = transactionGroup.initialTransaction; diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index fe2235e5489c..84fd280ae8eb 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -22,6 +22,7 @@ export default function useHandleBridgeTx() { .toString(); const fieldsToAddToTxMeta = { + // TODO get this added to TxMeta type destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) .toPrefixedHexString() .toLowerCase() as `0x${string}`, diff --git a/ui/pages/bridge/transaction-details/bridge-activity-item-tx-segments.tsx b/ui/pages/bridge/transaction-details/bridge-activity-item-tx-segments.tsx index 93fed4322205..a757eddffb79 100644 --- a/ui/pages/bridge/transaction-details/bridge-activity-item-tx-segments.tsx +++ b/ui/pages/bridge/transaction-details/bridge-activity-item-tx-segments.tsx @@ -14,7 +14,7 @@ import { FlexDirection, TextColor, } from '../../../helpers/constants/design-system'; -import { UseBridgeDataProps } from '../../../hooks/bridge/useBridgeTxHistoryData'; +import { UseBridgeTxHistoryDataProps } from '../../../hooks/bridge/useBridgeTxHistoryData'; import Segment from './segment'; const getTxIndex = (srcTxStatus: StatusTypes) => { @@ -64,7 +64,7 @@ export default function BridgeActivityItemTxSegments({ transactionGroup, }: { bridgeTxHistoryItem?: BridgeHistoryItem; - transactionGroup: UseBridgeDataProps['transactionGroup']; + transactionGroup: UseBridgeTxHistoryDataProps['transactionGroup']; }) { const { initialTransaction } = transactionGroup; const srcTxStatus = getSrcTxStatus(initialTransaction); From fdb119f5384fbfca5395b4b0e2d77e5555b07c2c Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:25:41 -0500 Subject: [PATCH 33/48] chore: add comment --- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 43 +++++++++++----------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index 84fd280ae8eb..dac86d16d092 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -21,32 +21,31 @@ export default function useHandleBridgeTx() { .shiftedBy(quoteResponse.quote.srcAsset.decimals) .toString(); - const fieldsToAddToTxMeta = { - // TODO get this added to TxMeta type - destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) - .toPrefixedHexString() - .toLowerCase() as `0x${string}`, - // estimatedBaseFee: decEstimatedBaseFee, - - sourceTokenAmount: quoteResponse.quote.srcTokenAmount, - sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, - sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals, - sourceTokenAddress: quoteResponse.quote.srcAsset.address, - - destinationTokenAmount: quoteResponse.quote.destTokenAmount, - destinationTokenSymbol: quoteResponse.quote.destAsset.symbol, - destinationTokenDecimals: quoteResponse.quote.destAsset.decimals, - destinationTokenAddress: quoteResponse.quote.destAsset.address, - - approvalTxId, - // this is the decimal (non atomic) amount (not USD value) of source token to swap - swapTokenValue: sentAmountDec, - }; const txMeta = await handleTx({ txType: TransactionType.bridge, txParams: quoteResponse.trade, - fieldsToAddToTxMeta, + fieldsToAddToTxMeta: { + // @ts-expect-error TODO get this added to TxMeta type + destinationChainId: new Numeric(quoteResponse.quote.destChainId, 10) + .toPrefixedHexString() + .toLowerCase() as `0x${string}`, + // estimatedBaseFee: decEstimatedBaseFee, + + sourceTokenAmount: quoteResponse.quote.srcTokenAmount, + sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol, + sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals, + sourceTokenAddress: quoteResponse.quote.srcAsset.address, + + destinationTokenAmount: quoteResponse.quote.destTokenAmount, + destinationTokenSymbol: quoteResponse.quote.destAsset.symbol, + destinationTokenDecimals: quoteResponse.quote.destAsset.decimals, + destinationTokenAddress: quoteResponse.quote.destAsset.address, + + approvalTxId, + // this is the decimal (non atomic) amount (not USD value) of source token to swap + swapTokenValue: sentAmountDec, + };, }); return txMeta; From 821f5f0e070121063c54c51e26473bb902aeb2b7 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:25:52 -0500 Subject: [PATCH 34/48] fix: broken syntax --- ui/pages/bridge/hooks/useHandleBridgeTx.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/pages/bridge/hooks/useHandleBridgeTx.ts b/ui/pages/bridge/hooks/useHandleBridgeTx.ts index dac86d16d092..6d35cc931b77 100644 --- a/ui/pages/bridge/hooks/useHandleBridgeTx.ts +++ b/ui/pages/bridge/hooks/useHandleBridgeTx.ts @@ -21,7 +21,6 @@ export default function useHandleBridgeTx() { .shiftedBy(quoteResponse.quote.srcAsset.decimals) .toString(); - const txMeta = await handleTx({ txType: TransactionType.bridge, txParams: quoteResponse.trade, @@ -45,7 +44,7 @@ export default function useHandleBridgeTx() { approvalTxId, // this is the decimal (non atomic) amount (not USD value) of source token to swap swapTokenValue: sentAmountDec, - };, + }, }); return txMeta; From d01d7319a2bafedf9555785cb3f52fbe39bcbc4c Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:33:05 -0500 Subject: [PATCH 35/48] chore: add comments --- .../lib/transaction/smart-transactions-mocks.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/scripts/lib/transaction/smart-transactions-mocks.ts b/app/scripts/lib/transaction/smart-transactions-mocks.ts index a54cbf540ec1..4d819aa1b7c5 100644 --- a/app/scripts/lib/transaction/smart-transactions-mocks.ts +++ b/app/scripts/lib/transaction/smart-transactions-mocks.ts @@ -1,3 +1,9 @@ +/** + * Mock for waitForTransactionHash. Simply replace the waitForTransactionHash + * with this mock so that we can debug locally without spending gas on mainnet. + * + * @returns Promise + */ export const mockWaitForTransactionHash: () => Promise = () => { return new Promise((resolve) => { setTimeout(() => { @@ -9,6 +15,12 @@ export const mockWaitForTransactionHash: () => Promise = () => { }); }; +/** + * Mock for signAndSubmitTransactions. Simply replace the signAndSubmitTransactions + * with this mock so that we can debug locally without spending gas on mainnet. + * + * @returns Promise<{ uuid: string; txHash?: string }> + */ export const mockSignAndSubmitTransactions: () => Promise<{ uuid: string; txHash?: string; From ff666135cb1212766d82580b7b02b05f6944873e Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:34:02 -0500 Subject: [PATCH 36/48] chore: remove unneeded comment --- ui/hooks/bridge/useBridgeChainInfo.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/hooks/bridge/useBridgeChainInfo.ts b/ui/hooks/bridge/useBridgeChainInfo.ts index 2f7fda1c1f1b..b2027af8eb71 100644 --- a/ui/hooks/bridge/useBridgeChainInfo.ts +++ b/ui/hooks/bridge/useBridgeChainInfo.ts @@ -36,9 +36,6 @@ const getSourceAndDestChainIds = ({ }; }; -/** - * Can use either a bridgeHistoryItem or a transactionGroup to get the chain info - */ export type UseBridgeChainInfoProps = { bridgeHistoryItem?: BridgeHistoryItem; srcTxMeta?: TransactionMeta; From e9a311912101306eb34d280d61df4f73f5ad410d Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:37:16 -0500 Subject: [PATCH 37/48] chore: add comment --- ui/pages/bridge/hooks/useHandleTx.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/pages/bridge/hooks/useHandleTx.ts b/ui/pages/bridge/hooks/useHandleTx.ts index e4b7ef961ea8..70deb084c87c 100644 --- a/ui/pages/bridge/hooks/useHandleTx.ts +++ b/ui/pages/bridge/hooks/useHandleTx.ts @@ -67,6 +67,8 @@ export default function useHandleTx() { type: txType, }); + // Note that updateTransaction doesn't actually error if you add fields that don't conform the to the txMeta type + // they will be there at runtime, but you just don't get any type safety checks on them dispatch(updateTransaction({ ...txMeta, ...fieldsToAddToTxMeta }, true)); await forceUpdateMetamaskState(dispatch); From c33aeb4e4bbfcf331286d94054e85fc5cf3edcc1 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:33:39 -0500 Subject: [PATCH 38/48] chore: remove unneeded type assertion --- ui/pages/bridge/transaction-details/bridge-step-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/bridge/transaction-details/bridge-step-list.tsx b/ui/pages/bridge/transaction-details/bridge-step-list.tsx index 808a3e7d40f8..5c2dcc4453f4 100644 --- a/ui/pages/bridge/transaction-details/bridge-step-list.tsx +++ b/ui/pages/bridge/transaction-details/bridge-step-list.tsx @@ -86,7 +86,7 @@ export default function BridgeStepList({ isEdgeComplete={isEdgeComplete} > Date: Wed, 27 Nov 2024 15:46:55 -0500 Subject: [PATCH 39/48] chore: don't throw an error if srcTxHash missing, simplify #getSrcTxHash, move state update outside of it --- .../bridge-status/bridge-status-controller.ts | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index d79f751df55c..58fee18e97f4 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -223,6 +223,12 @@ export default class BridgeStatusController extends StaticIntervalPollingControl // Also srcTxHash may not be available immediately for STX, so we don't want to fetch in those cases const historyItem = bridgeStatusState.txHistory[bridgeTxMetaId]; const srcTxHash = this.#getSrcTxHash(bridgeTxMetaId); + if (!srcTxHash) { + return; + } + + this.#updateSrcTxHash(bridgeTxMetaId, srcTxHash); + const statusRequest = getStatusRequestWithSrcTxHash( historyItem.quote, srcTxHash, @@ -258,8 +264,10 @@ export default class BridgeStatusController extends StaticIntervalPollingControl } }; - #getSrcTxHash = (bridgeTxMetaId: string): string => { + #getSrcTxHash = (bridgeTxMetaId: string): string | undefined => { const { bridgeStatusState } = this.state; + // Prefer the srcTxHash from bridgeStatusState so we don't have to look up in TransactionController + // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX const srcTxHash = bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash; @@ -267,15 +275,23 @@ export default class BridgeStatusController extends StaticIntervalPollingControl return srcTxHash; } - // Look up in TransactionController if txMeta has been updated with the srcTxHash, if so, update it here as well + // Look up in TransactionController if txMeta has been updated with the srcTxHash const txControllerState = this.messagingSystem.call( 'TransactionController:getState', ); const txMeta = txControllerState.transactions.find( (tx) => tx.id === bridgeTxMetaId, ); + return txMeta?.hash; + }; + + #updateSrcTxHash = (bridgeTxMetaId: string, srcTxHash: string) => { + const { bridgeStatusState } = this.state; + if (bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash) { + return; + } - const { nextState } = this.update((_state) => { + this.update((_state) => { _state.bridgeStatusState = { ...bridgeStatusState, txHistory: { @@ -286,23 +302,13 @@ export default class BridgeStatusController extends StaticIntervalPollingControl ...bridgeStatusState.txHistory[bridgeTxMetaId].status, srcChain: { ...bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain, - txHash: txMeta?.hash, + txHash: srcTxHash, }, }, }, }, }; }); - - const nextSrcTxHash = - nextState.bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain - .txHash; - - if (nextSrcTxHash) { - return nextSrcTxHash; - } - - throw new Error('srcTxHash missing'); }; // Wipes the bridge status for the given address and chainId From 03e3f69feadeaf95d06662c7eca84bbfea1290fe Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:25:49 -0500 Subject: [PATCH 40/48] fix: broken yarn lock --- yarn.lock | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 060ccc7c69fc..88ed1e138bbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6468,6 +6468,39 @@ __metadata: languageName: node linkType: hard +"@metamask/transaction-controller@npm:^40.1.0": + version: 40.1.0 + resolution: "@metamask/transaction-controller@npm:40.1.0" + dependencies: + "@ethereumjs/common": "npm:^3.2.0" + "@ethereumjs/tx": "npm:^4.2.0" + "@ethereumjs/util": "npm:^8.1.0" + "@ethersproject/abi": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@ethersproject/providers": "npm:^5.7.0" + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/controller-utils": "npm:^11.4.3" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/nonce-tracker": "npm:^6.0.0" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/utils": "npm:^10.0.0" + async-mutex: "npm:^0.5.0" + bn.js: "npm:^5.2.1" + eth-method-registry: "npm:^4.0.0" + fast-json-patch: "npm:^3.1.1" + lodash: "npm:^4.17.21" + uuid: "npm:^8.3.2" + peerDependencies: + "@babel/runtime": ^7.23.9 + "@metamask/accounts-controller": ^20.0.0 + "@metamask/approval-controller": ^7.0.0 + "@metamask/gas-fee-controller": ^22.0.0 + "@metamask/network-controller": ^22.0.0 + checksum: 10/1057af5b0da2d51e46e7568fc0e7fdbe6aed34a013cf56a5a35ad694cbedcb726a5823bbe70b980d1dc9560138acf9d82ac5f0e06f7d17e11b46abacd466dc42 + languageName: node + linkType: hard + "@metamask/user-operation-controller@npm:^16.0.0": version: 16.0.0 resolution: "@metamask/user-operation-controller@npm:16.0.0" @@ -26606,7 +26639,11 @@ __metadata: "@storybook/react-webpack5": "npm:^7.6.20" "@storybook/storybook-deployer": "npm:^2.8.16" "@storybook/test-runner": "npm:^0.14.1" + "@storybook/theming": "npm:^7.6.20" "@swc/core": "npm:1.4.11" + "@swc/helpers": "npm:^0.5.7" + "@testing-library/dom": "npm:^7.31.2" + "@testing-library/jest-dom": "npm:^5.11.10" "@testing-library/react": "npm:^10.4.8" "@testing-library/react-hooks": "npm:^8.0.1" "@testing-library/user-event": "npm:^14.4.3" @@ -38225,4 +38262,4 @@ __metadata: resolution: "zxcvbn@patch:zxcvbn@npm%3A4.4.2#./.yarn/patches/zxcvbn-npm-4.4.2-6527983856.patch::version=4.4.2&hash=e3565a&locator=metamask-crx%40workspace%3A." checksum: 10/4413b5c71428a92b08fa11ef238987609af399447f33a76b6aa66ab2f964c88037a3d7692bf3d644bc68dd386e4ebe0476bae041fe6b515963f497879c04fbff languageName: node - linkType: hard + linkType: hard \ No newline at end of file From 77038790af72e45b2976169b8d282b844c8acfe1 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:27:02 -0500 Subject: [PATCH 41/48] chore: one more fix for yarn lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 88ed1e138bbe..baa7f35b897c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38262,4 +38262,4 @@ __metadata: resolution: "zxcvbn@patch:zxcvbn@npm%3A4.4.2#./.yarn/patches/zxcvbn-npm-4.4.2-6527983856.patch::version=4.4.2&hash=e3565a&locator=metamask-crx%40workspace%3A." checksum: 10/4413b5c71428a92b08fa11ef238987609af399447f33a76b6aa66ab2f964c88037a3d7692bf3d644bc68dd386e4ebe0476bae041fe6b515963f497879c04fbff languageName: node - linkType: hard \ No newline at end of file + linkType: hard From 6c994d641087e267dad0ebb67f34422487b7b0fa Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:08:16 -0500 Subject: [PATCH 42/48] chore: improve typing for active quote to have better pricing data --- ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts index 3632be62bb87..9985757d7fa3 100644 --- a/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts +++ b/ui/pages/bridge/hooks/useSubmitBridgeTransaction.ts @@ -2,7 +2,7 @@ import { useDispatch } from 'react-redux'; import { zeroAddress } from 'ethereumjs-util'; import { useHistory } from 'react-router-dom'; import { TransactionMeta } from '@metamask/transaction-controller'; -import { QuoteResponse } from '../types'; +import { QuoteMetadata, QuoteResponse } from '../types'; import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; import { setDefaultHomeActiveTabName } from '../../../store/actions'; import { startPollingForBridgeTxStatus } from '../../../ducks/bridge-status/actions'; @@ -17,7 +17,9 @@ export default function useSubmitBridgeTransaction() { const { handleApprovalTx } = useHandleApprovalTx(); const { handleBridgeTx } = useHandleBridgeTx(); - const submitBridgeTransaction = async (quoteResponse: QuoteResponse) => { + const submitBridgeTransaction = async ( + quoteResponse: QuoteResponse & QuoteMetadata, + ) => { // Execute transaction(s) let approvalTxMeta: TransactionMeta | undefined; if (quoteResponse?.approval) { From fb1792efca32f0f30c6cee039172890f662f687e Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Fri, 29 Nov 2024 16:32:19 -0500 Subject: [PATCH 43/48] chore: add sentAmount to historyItem.pricingData --- .../bridge-status/bridge-status-controller.ts | 11 +++--- shared/types/bridge-status.ts | 36 +++++++++++++------ .../transaction-details.tsx | 6 ++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/app/scripts/controllers/bridge-status/bridge-status-controller.ts b/app/scripts/controllers/bridge-status/bridge-status-controller.ts index 58fee18e97f4..1763e47feb77 100644 --- a/app/scripts/controllers/bridge-status/bridge-status-controller.ts +++ b/app/scripts/controllers/bridge-status/bridge-status-controller.ts @@ -3,9 +3,9 @@ import { StaticIntervalPollingController } from '@metamask/polling-controller'; import { Hex } from '@metamask/utils'; // eslint-disable-next-line import/no-restricted-paths import { - StartPollingForBridgeTxStatusArgs, StatusTypes, BridgeStatusControllerState, + StartPollingForBridgeTxStatusArgsSerialized, } from '../../../../shared/types/bridge-status'; import { decimalToPrefixedHex } from '../../../../shared/modules/conversion.utils'; import { @@ -148,7 +148,7 @@ export default class BridgeStatusController extends StaticIntervalPollingControl }; startPollingForBridgeTxStatus = ( - startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgs, + startPollingForBridgeTxStatusArgs: StartPollingForBridgeTxStatusArgsSerialized, ) => { const { bridgeTxMeta, @@ -156,7 +156,6 @@ export default class BridgeStatusController extends StaticIntervalPollingControl quoteResponse, startTime, slippagePercentage, - pricingData, initialDestAssetBalance, targetContractAddress, } = startPollingForBridgeTxStatusArgs; @@ -172,7 +171,9 @@ export default class BridgeStatusController extends StaticIntervalPollingControl estimatedProcessingTimeInSeconds: quoteResponse.estimatedProcessingTimeInSeconds, slippagePercentage, - pricingData, + pricingData: { + amountSent: quoteResponse.sentAmount.amount, + }, initialDestAssetBalance, targetContractAddress, account, @@ -266,7 +267,7 @@ export default class BridgeStatusController extends StaticIntervalPollingControl #getSrcTxHash = (bridgeTxMetaId: string): string | undefined => { const { bridgeStatusState } = this.state; - // Prefer the srcTxHash from bridgeStatusState so we don't have to look up in TransactionController + // Prefer the srcTxHash from bridgeStatusState so we don't have to l ook up in TransactionController // But it is possible to have bridgeHistoryItem in state without the srcTxHash yet when it is an STX const srcTxHash = bridgeStatusState.txHistory[bridgeTxMetaId].status.srcChain.txHash; diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index e489daa90ccb..6a36411b52ee 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -1,7 +1,12 @@ import { TransactionMeta } from '@metamask/transaction-controller'; // TODO fix this -// eslint-disable-next-line import/no-restricted-paths -import { ChainId, Quote, QuoteResponse } from '../../ui/pages/bridge/types'; +import { + ChainId, + Quote, + QuoteMetadata, + QuoteResponse, + // eslint-disable-next-line import/no-restricted-paths +} from '../../ui/pages/bridge/types'; // All fields need to be types not interfaces, same with their children fields // o/w you get a type error @@ -119,13 +124,15 @@ export type BridgeHistoryItem = { slippagePercentage: number; completionTime?: number; pricingData?: { - quotedGasInUsd: number; - quotedReturnInUsd: number; - amountSentInUsd: number; - quotedRefuelSrcAmountInUsd?: number; - quotedRefuelDestAmountInUsd?: number; + amountSent: string; // This is from QuoteMetadata.sentAmount.amount, accounts for the MM fees + + quotedGasInUsd?: string; + quotedReturnInUsd?: string; + amountSentInUsd?: string; + quotedRefuelSrcAmountInUsd?: string; + quotedRefuelDestAmountInUsd?: string; }; - initialDestAssetBalance?: number; + initialDestAssetBalance?: string; targetContractAddress?: string; account: string; }; @@ -136,17 +143,26 @@ export enum BridgeStatusAction { GET_STATE = 'getState', } +// The BigNumber values are serialized to strings +export type QuoteMetadataSerialized = { + sentAmount: { amount: string; fiat: string | null }; +}; + export type StartPollingForBridgeTxStatusArgs = { bridgeTxMeta: TransactionMeta; statusRequest: StatusRequest; - quoteResponse: QuoteResponse; + quoteResponse: QuoteResponse & QuoteMetadata; startTime?: BridgeHistoryItem['startTime']; slippagePercentage: BridgeHistoryItem['slippagePercentage']; - pricingData?: BridgeHistoryItem['pricingData']; initialDestAssetBalance?: BridgeHistoryItem['initialDestAssetBalance']; targetContractAddress?: BridgeHistoryItem['targetContractAddress']; }; +export type StartPollingForBridgeTxStatusArgsSerialized = + StartPollingForBridgeTxStatusArgs & { + quoteResponse: QuoteResponse & QuoteMetadataSerialized; + }; + export type SourceChainTxMetaId = string; export type BridgeStatusControllerState = { diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index e4677e94d11b..46ad88d3d83f 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -63,6 +63,12 @@ const getBlockExplorerUrl = ( return `${rootUrl}/tx/${txHash}`; }; +/** + * @param options0 + * @param options0.bridgeHistoryItem + * @param options0.srcChainTxMeta + * @returns A string representing the bridge amount in decimal form + */ const getBridgeAmount = ({ bridgeHistoryItem, srcChainTxMeta, From 12b61dbce728822a6bdb6130e52e0fa73ff08cac Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Fri, 29 Nov 2024 16:34:01 -0500 Subject: [PATCH 44/48] fix: use correct sent amount --- .../transaction-details.tsx | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/ui/pages/bridge/transaction-details/transaction-details.tsx b/ui/pages/bridge/transaction-details/transaction-details.tsx index 46ad88d3d83f..20ee8fb48754 100644 --- a/ui/pages/bridge/transaction-details/transaction-details.tsx +++ b/ui/pages/bridge/transaction-details/transaction-details.tsx @@ -36,7 +36,6 @@ import { } from '../../../helpers/constants/design-system'; import { formatDate } from '../../../helpers/utils/util'; import { ConfirmInfoRowDivider as Divider } from '../../../components/app/confirm/info/row'; -import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP } from '../../../../shared/constants/network'; import { selectedAddressTxListSelector } from '../../../selectors'; @@ -66,32 +65,15 @@ const getBlockExplorerUrl = ( /** * @param options0 * @param options0.bridgeHistoryItem - * @param options0.srcChainTxMeta * @returns A string representing the bridge amount in decimal form */ const getBridgeAmount = ({ bridgeHistoryItem, - srcChainTxMeta, }: { bridgeHistoryItem?: BridgeHistoryItem; - srcChainTxMeta?: TransactionMeta; }) => { if (bridgeHistoryItem) { - return `${calcTokenAmount( - bridgeHistoryItem.quote.srcTokenAmount, - bridgeHistoryItem.quote.srcAsset.decimals, - ).toFixed()} ${bridgeHistoryItem.quote.srcAsset.symbol}`; - } - - if ( - srcChainTxMeta && - srcChainTxMeta.sourceTokenAmount && - srcChainTxMeta.sourceTokenDecimals - ) { - return `${calcTokenAmount( - srcChainTxMeta.sourceTokenAmount, - srcChainTxMeta.sourceTokenDecimals, - ).toFixed()} ${srcChainTxMeta.sourceTokenSymbol}`; + return bridgeHistoryItem.pricingData?.amountSent; } return undefined; @@ -158,7 +140,7 @@ const CrossChainSwapTxDetails = () => { }) : undefined; - const bridgeAmount = getBridgeAmount({ bridgeHistoryItem, srcChainTxMeta }); + const bridgeAmount = getBridgeAmount({ bridgeHistoryItem }); return (
From 86e12025495183be197e57e95aef5908e4a6b44d Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:18:40 -0500 Subject: [PATCH 45/48] fix: broken tests --- .../bridge-status-controller.test.ts.snap | 8 ++++++-- app/scripts/controllers/bridge-status/mocks.ts | 13 +++++++++++-- shared/types/bridge-status.ts | 10 ++++++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap index 2dbcf3f1b027..8bbeab6356d9 100644 --- a/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap +++ b/app/scripts/controllers/bridge-status/__snapshots__/bridge-status-controller.test.ts.snap @@ -6,7 +6,9 @@ exports[`BridgeStatusController constructor rehydrates the tx history state 1`] "account": "0xaccount1", "estimatedProcessingTimeInSeconds": 15, "initialDestAssetBalance": undefined, - "pricingData": undefined, + "pricingData": { + "amountSent": "1.234", + }, "quote": { "bridgeId": "lifi", "bridges": [ @@ -113,7 +115,9 @@ exports[`BridgeStatusController startPollingForBridgeTxStatus sets the inital tx "account": "0xaccount1", "estimatedProcessingTimeInSeconds": 15, "initialDestAssetBalance": undefined, - "pricingData": undefined, + "pricingData": { + "amountSent": "1.234", + }, "quote": { "bridgeId": "lifi", "bridges": [ diff --git a/app/scripts/controllers/bridge-status/mocks.ts b/app/scripts/controllers/bridge-status/mocks.ts index d46157d3433b..1507af5830ec 100644 --- a/app/scripts/controllers/bridge-status/mocks.ts +++ b/app/scripts/controllers/bridge-status/mocks.ts @@ -4,6 +4,7 @@ import { StatusResponse, StatusTypes, ActionTypes, + StartPollingForBridgeTxStatusArgsSerialized, } from '../../../../shared/types/bridge-status'; export const MockStatusResponse = { @@ -210,7 +211,7 @@ export const getMockStartPollingForBridgeTxStatusArgs = ({ account = '0xaccount1', srcChainId = 42161, destChainId = 10, -} = {}) => ({ +} = {}): StartPollingForBridgeTxStatusArgsSerialized => ({ bridgeTxMeta: { id: txMetaId, } as TransactionMeta, @@ -235,10 +236,10 @@ export const getMockStartPollingForBridgeTxStatusArgs = ({ }, approval: null, estimatedProcessingTimeInSeconds: 15, + sentAmount: { amount: '1.234', fiat: null }, }, startTime: 1729964825189, slippagePercentage: 0, - pricingData: undefined, initialDestAssetBalance: undefined, targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', }); @@ -258,6 +259,8 @@ export const MockTxHistory = { slippagePercentage: 0, account, targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + initialDestAssetBalance: undefined, + pricingData: { amountSent: '1.234' }, }, }), getInit: ({ @@ -274,6 +277,8 @@ export const MockTxHistory = { slippagePercentage: 0, account, targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + initialDestAssetBalance: undefined, + pricingData: { amountSent: '1.234' }, }, }), getPending: ({ @@ -295,6 +300,8 @@ export const MockTxHistory = { srcChainId, }), targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + initialDestAssetBalance: undefined, + pricingData: { amountSent: '1.234' }, }, }), getComplete: ({ @@ -313,6 +320,8 @@ export const MockTxHistory = { account, status: MockStatusResponse.getComplete({ srcTxHash }), targetContractAddress: '0x23981fC34e69eeDFE2BD9a0a9fCb0719Fe09DbFC', + initialDestAssetBalance: undefined, + pricingData: { amountSent: '1.234' }, }, }), }; diff --git a/shared/types/bridge-status.ts b/shared/types/bridge-status.ts index 6a36411b52ee..845a3c7cec65 100644 --- a/shared/types/bridge-status.ts +++ b/shared/types/bridge-status.ts @@ -158,10 +158,12 @@ export type StartPollingForBridgeTxStatusArgs = { targetContractAddress?: BridgeHistoryItem['targetContractAddress']; }; -export type StartPollingForBridgeTxStatusArgsSerialized = - StartPollingForBridgeTxStatusArgs & { - quoteResponse: QuoteResponse & QuoteMetadataSerialized; - }; +export type StartPollingForBridgeTxStatusArgsSerialized = Omit< + StartPollingForBridgeTxStatusArgs, + 'quoteResponse' +> & { + quoteResponse: QuoteResponse & QuoteMetadataSerialized; +}; export type SourceChainTxMetaId = string; From 42c555921e7633d84fa88d542532f6ee23d0bf82 Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:16:56 -0500 Subject: [PATCH 46/48] chore: bump yarn --- yarn.lock | 39 +-------------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/yarn.lock b/yarn.lock index baa7f35b897c..c88156929c17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6434,43 +6434,6 @@ __metadata: "@metamask/transaction-controller@npm:^41.0.0": version: 41.0.0 resolution: "@metamask/transaction-controller@npm:41.0.0" - dependencies: - "@ethereumjs/common": "npm:^3.2.0" - "@ethereumjs/tx": "npm:^4.2.0" - "@ethereumjs/util": "npm:^8.1.0" - "@ethersproject/abi": "npm:^5.7.0" - "@ethersproject/contracts": "npm:^5.7.0" - "@ethersproject/providers": "npm:^5.7.0" - "@metamask/accounts-controller": "npm:^17.2.0" - "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/base-controller": "npm:^6.0.0" - "@metamask/controller-utils": "npm:^11.0.0" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/gas-fee-controller": "npm:^18.0.0" - "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/network-controller": "npm:^19.0.0" - "@metamask/nonce-tracker": "npm:^5.0.0" - "@metamask/rpc-errors": "npm:^6.2.1" - "@metamask/utils": "npm:^8.3.0" - async-mutex: "npm:^0.5.0" - bn.js: "npm:^5.2.1" - eth-method-registry: "npm:^4.0.0" - fast-json-patch: "npm:^3.1.1" - lodash: "npm:^4.17.21" - uuid: "npm:^8.3.2" - peerDependencies: - "@babel/runtime": ^7.23.9 - "@metamask/accounts-controller": ^17.0.0 - "@metamask/approval-controller": ^7.0.0 - "@metamask/gas-fee-controller": ^22.0.0 - "@metamask/network-controller": ^22.0.0 - checksum: 10/67a00b2eade35fc4e635a6bcbbcd847b3986b3bdcc9730ff2c8f81234df18ed11149203c13d6bad616e859f7e25879efab36b6dc4be05a4e747b4280ae2f300d - languageName: node - linkType: hard - -"@metamask/transaction-controller@npm:^40.1.0": - version: 40.1.0 - resolution: "@metamask/transaction-controller@npm:40.1.0" dependencies: "@ethereumjs/common": "npm:^3.2.0" "@ethereumjs/tx": "npm:^4.2.0" @@ -6497,7 +6460,7 @@ __metadata: "@metamask/approval-controller": ^7.0.0 "@metamask/gas-fee-controller": ^22.0.0 "@metamask/network-controller": ^22.0.0 - checksum: 10/1057af5b0da2d51e46e7568fc0e7fdbe6aed34a013cf56a5a35ad694cbedcb726a5823bbe70b980d1dc9560138acf9d82ac5f0e06f7d17e11b46abacd466dc42 + checksum: 10/67a00b2eade35fc4e635a6bcbbcd847b3986b3bdcc9730ff2c8f81234df18ed11149203c13d6bad616e859f7e25879efab36b6dc4be05a4e747b4280ae2f300d languageName: node linkType: hard From 03c74fd891bbda3ff3e0ad92e0f2aa00b644c19a Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:28:04 -0500 Subject: [PATCH 47/48] chore: add check for showing status page in onApproveOrReject --- app/scripts/lib/transaction/smart-transactions.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/transaction/smart-transactions.ts b/app/scripts/lib/transaction/smart-transactions.ts index c045aa15f937..0c64c6796019 100644 --- a/app/scripts/lib/transaction/smart-transactions.ts +++ b/app/scripts/lib/transaction/smart-transactions.ts @@ -101,6 +101,8 @@ class SmartTransactionHook { #txParams: TransactionParams; + #shouldShowStatusPage: boolean; + constructor(request: SubmitSmartTransactionRequest) { const { transactionMeta, @@ -123,11 +125,11 @@ class SmartTransactionHook { this.#isDapp = transactionMeta.origin !== ORIGIN_METAMASK; this.#chainId = transactionMeta.chainId; this.#txParams = transactionMeta.txParams; + this.#shouldShowStatusPage = + transactionMeta.type !== TransactionType.bridge; } async submit() { - const shouldShowStatusPage = - this.#transactionMeta.type !== TransactionType.bridge; const isUnsupportedTransactionTypeForSmartTransaction = this .#transactionMeta?.type ? [TransactionType.swapAndSend, TransactionType.swapApproval].includes( @@ -144,7 +146,7 @@ class SmartTransactionHook { return useRegularTransactionSubmit; } - if (shouldShowStatusPage) { + if (this.#shouldShowStatusPage) { const { id: approvalFlowId } = await this.#controllerMessenger.call( 'ApprovalController:startFlow', ); @@ -175,7 +177,7 @@ class SmartTransactionHook { const extensionReturnTxHashAsap = this.#featureFlags?.smartTransactions?.extensionReturnTxHashAsap; - if (shouldShowStatusPage) { + if (this.#shouldShowStatusPage) { this.#addApprovalRequest({ uuid, }); @@ -205,7 +207,7 @@ class SmartTransactionHook { } #onApproveOrReject() { - if (this.#approvalFlowEnded) { + if (!this.#shouldShowStatusPage || this.#approvalFlowEnded) { return; } this.#approvalFlowEnded = true; From c57704325b369653ed235ffe2df2f384549bf43c Mon Sep 17 00:00:00 2001 From: IF <139582705+infiniteflower@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:11:54 -0500 Subject: [PATCH 48/48] chore: skip STX if bridge approval --- app/scripts/lib/transaction/smart-transactions.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/transaction/smart-transactions.ts b/app/scripts/lib/transaction/smart-transactions.ts index 0c64c6796019..6f9acac1ce74 100644 --- a/app/scripts/lib/transaction/smart-transactions.ts +++ b/app/scripts/lib/transaction/smart-transactions.ts @@ -132,9 +132,11 @@ class SmartTransactionHook { async submit() { const isUnsupportedTransactionTypeForSmartTransaction = this .#transactionMeta?.type - ? [TransactionType.swapAndSend, TransactionType.swapApproval].includes( - this.#transactionMeta.type, - ) + ? [ + TransactionType.swapAndSend, + TransactionType.swapApproval, + TransactionType.bridgeApproval, + ].includes(this.#transactionMeta.type) : false; // Will cause TransactionController to publish to the RPC provider as normal.