Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/bridge-status-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Refactor `handleLineaDelay` to `handleApprovalDelay` for improved abstraction and add support for Base chain by using an array and `includes` for chain ID checks ([#6674](https://github.com/MetaMask/core/pull/6674))

## [43.1.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2464,7 +2464,7 @@ describe('BridgeStatusController', () => {

it('should delay after submitting linea approval', async () => {
const handleLineaDelaySpy = jest
.spyOn(transactionUtils, 'handleLineaDelay')
.spyOn(transactionUtils, 'handleApprovalDelay')
.mockResolvedValueOnce();
const mockTraceFn = jest
.fn()
Expand Down Expand Up @@ -2500,6 +2500,44 @@ describe('BridgeStatusController', () => {
expect(mockTraceFn.mock.calls).toMatchSnapshot();
});

it('should delay after submitting base approval', async () => {
const handleBaseDelaySpy = jest
.spyOn(transactionUtils, 'handleApprovalDelay')
.mockResolvedValueOnce();
const mockTraceFn = jest
.fn()
.mockImplementation((_p, callback) => callback());

setupEventTrackingMocks(mockMessengerCall);
setupApprovalMocks(mockMessengerCall);
setupBridgeMocks(mockMessengerCall);

const { controller, startPollingForBridgeTxStatusSpy } = getController(
mockMessengerCall,
mockTraceFn,
);

const baseQuoteResponse = {
...mockEvmQuoteResponse,
quote: { ...mockEvmQuoteResponse.quote, srcChainId: 8453 },
trade: {
...(mockEvmQuoteResponse.trade as TxData),
gasLimit: undefined,
} as never,
};

const result = await controller.submitTx(baseQuoteResponse, false);
controller.stopAllPolling();

expect(mockTraceFn).toHaveBeenCalledTimes(2);
expect(handleBaseDelaySpy).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
expect(startPollingForBridgeTxStatusSpy).toHaveBeenCalledTimes(0);
expect(controller.state.txHistory[result.id]).toMatchSnapshot();
expect(mockMessengerCall.mock.calls).toMatchSnapshot();
expect(mockTraceFn.mock.calls).toMatchSnapshot();
});

it('should call handleMobileHardwareWalletDelay for hardware wallet on mobile', async () => {
const handleMobileHardwareWalletDelaySpy = jest
.spyOn(transactionUtils, 'handleMobileHardwareWalletDelay')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import {
getClientRequest,
getStatusRequestParams,
getUSDTAllowanceResetTx,
handleLineaDelay,
handleApprovalDelay,
handleMobileHardwareWalletDelay,
handleSolanaTxResponse,
} from './utils/transaction';
Expand Down Expand Up @@ -813,7 +813,7 @@ export class BridgeStatusController extends StaticIntervalPollingController<Brid
requireApproval,
});

await handleLineaDelay(quoteResponse);
await handleApprovalDelay(quoteResponse);
return approvalTxMeta;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-status-controller/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE: BridgeStatusControllerState

export const BRIDGE_PROD_API_BASE_URL = 'https://bridge.api.cx.metamask.io';

export const LINEA_DELAY_MS = 5000;
export const APPROVAL_DELAY_MS = 5000;

export enum TraceName {
BridgeTransactionApprovalCompleted = 'Bridge Transaction Approval Completed',
Expand Down
54 changes: 46 additions & 8 deletions packages/bridge-status-controller/src/utils/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import {
getStatusRequestParams,
getTxMetaFields,
handleSolanaTxResponse,
handleLineaDelay,
handleApprovalDelay,
handleMobileHardwareWalletDelay,
getClientRequest,
toBatchTxParams,
getAddTransactionBatchParams,
findAndUpdateTransactionsInBatch,
} from './transaction';
import { LINEA_DELAY_MS } from '../constants';
import { APPROVAL_DELAY_MS } from '../constants';
import type { BridgeStatusControllerMessenger } from '../types';

describe('Bridge Status Controller Transaction Utils', () => {
Expand Down Expand Up @@ -974,7 +974,7 @@ describe('Bridge Status Controller Transaction Utils', () => {
});
});

describe('handleLineaDelay', () => {
describe('handleApprovalDelay', () => {
beforeEach(() => {
jest.useFakeTimers();
jest.clearAllMocks();
Expand Down Expand Up @@ -1007,13 +1007,13 @@ describe('Bridge Status Controller Transaction Utils', () => {
} as unknown as QuoteResponse;

// Create a promise that will resolve after the delay
const delayPromise = handleLineaDelay(mockQuoteResponse);
const delayPromise = handleApprovalDelay(mockQuoteResponse);

// Verify that the timer was set with the correct delay
expect(jest.getTimerCount()).toBe(1);

// Fast-forward the timer
jest.advanceTimersByTime(LINEA_DELAY_MS);
jest.advanceTimersByTime(APPROVAL_DELAY_MS);

// Wait for the promise to resolve
await delayPromise;
Expand All @@ -1022,8 +1022,46 @@ describe('Bridge Status Controller Transaction Utils', () => {
expect(jest.getTimerCount()).toBe(0);
});

it('should not delay when source chain is not Linea', async () => {
// Create a minimal mock quote response with a non-Linea source chain
it('should delay when source chain is Base', async () => {
// Create a minimal mock quote response with Base as the source chain
const mockQuoteResponse = {
quote: {
srcChainId: ChainId.BASE,
// Other required properties with minimal values
requestId: 'test-request-id',
srcAsset: { address: '0x123', symbol: 'ETH', decimals: 18 },
srcTokenAmount: '1000000000000000000',
destChainId: ChainId.ETH,
destAsset: { address: '0x456', symbol: 'ETH', decimals: 18 },
destTokenAmount: '1000000000000000000',
bridgeId: 'test-bridge',
bridges: ['test-bridge'],
steps: [],
feeData: {},
},
// Required properties for QuoteResponse
trade: {} as TxData,
estimatedProcessingTimeInSeconds: 60,
} as unknown as QuoteResponse;

// Create a promise that will resolve after the delay
const delayPromise = handleApprovalDelay(mockQuoteResponse);

// Verify that the timer was set with the correct delay
expect(jest.getTimerCount()).toBe(1);

// Fast-forward the timer
jest.advanceTimersByTime(APPROVAL_DELAY_MS);

// Wait for the promise to resolve
await delayPromise;

// Verify that the timer was cleared
expect(jest.getTimerCount()).toBe(0);
});

it('should not delay when source chain is not Linea or Base', async () => {
// Create a minimal mock quote response with a non-Linea/Base source chain
const mockQuoteResponse = {
quote: {
srcChainId: ChainId.ETH,
Expand All @@ -1045,7 +1083,7 @@ describe('Bridge Status Controller Transaction Utils', () => {
} as unknown as QuoteResponse;

// Create a promise that will resolve after the delay
const delayPromise = handleLineaDelay(mockQuoteResponse);
const delayPromise = handleApprovalDelay(mockQuoteResponse);

// Verify that no timer was set
expect(jest.getTimerCount()).toBe(0);
Expand Down
10 changes: 5 additions & 5 deletions packages/bridge-status-controller/src/utils/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { v4 as uuid } from 'uuid';

import { calculateGasFees } from './gas';
import type { TransactionBatchSingleRequest } from '../../../transaction-controller/src/types';
import { LINEA_DELAY_MS } from '../constants';
import { APPROVAL_DELAY_MS } from '../constants';
import type {
BridgeStatusControllerMessenger,
SolanaTransactionMeta,
Expand Down Expand Up @@ -162,16 +162,16 @@ export const handleSolanaTxResponse = (
};
};

export const handleLineaDelay = async (
export const handleApprovalDelay = async (
quoteResponse: QuoteResponse<TxData | string>,
) => {
if (ChainId.LINEA === quoteResponse.quote.srcChainId) {
if ([ChainId.LINEA, ChainId.BASE].includes(quoteResponse.quote.srcChainId)) {
const debugLog = createProjectLogger('bridge');
debugLog(
'Delaying submitting bridge tx to make Linea confirmation more likely',
'Delaying submitting bridge tx to make Linea and Base confirmation more likely',
);
const waitPromise = new Promise((resolve) =>
setTimeout(resolve, LINEA_DELAY_MS),
setTimeout(resolve, APPROVAL_DELAY_MS),
);
await waitPromise;
}
Expand Down
Loading