Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ describe('TransactionDetailsSummary', () => {
destAsset: {
symbol: SYMBOL_2_MOCK,
},
destChainId: Number(TARGET_CHAIN_ID_MOCK),
},
status: {
status: StatusTypes.COMPLETE,
destChain: {
chainId: Number(TARGET_CHAIN_ID_MOCK),
txHash: RECEIVE_HASH_MOCK,
},
},
Expand Down Expand Up @@ -217,7 +217,9 @@ describe('TransactionDetailsSummary', () => {
it('renders bridge line alternate title if symbols loading', () => {
useBridgeTxHistoryDataMock.mockReturnValue({
bridgeTxHistoryItem: {
quote: {},
quote: {
destChainId: Number(TARGET_CHAIN_ID_MOCK),
},
status: {
status: StatusTypes.COMPLETE,
},
Expand All @@ -231,7 +233,15 @@ describe('TransactionDetailsSummary', () => {
});

expect(
getByText(strings('transaction_details.summary_title.bridge_loading')),
getByText(
strings('transaction_details.summary_title.bridge_send_loading'),
),
).toBeDefined();

expect(
getByText(
strings('transaction_details.summary_title.bridge_receive_loading'),
),
).toBeDefined();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function TransactionSummary({
bridgeHistory?.bridgeTxHistoryItem?.completionTime ?? time;

const receiveChainIdNumber =
bridgeHistory?.bridgeTxHistoryItem?.status?.destChain?.chainId;
bridgeHistory?.bridgeTxHistoryItem?.quote?.destChainId;

const receiveChainId = receiveChainIdNumber
? toHex(receiveChainIdNumber)
Expand Down Expand Up @@ -282,7 +282,7 @@ function getLineTitle(
targetSymbol,
targetChain: networkName,
})
: strings('transaction_details.summary_title.bridge_loading');
: strings('transaction_details.summary_title.bridge_receive_loading');
}

switch (type) {
Expand All @@ -292,7 +292,7 @@ function getLineTitle(
sourceSymbol,
sourceChain: networkName,
})
: strings('transaction_details.summary_title.bridge_loading');
: strings('transaction_details.summary_title.bridge_send_loading');
case TransactionType.bridgeApproval:
return approveSymbol
? strings('transaction_details.summary_title.bridge_approval', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {
NetworkControllerStateChangeEvent,
} from '@metamask/network-controller';
import {
TransactionControllerGetStateAction,
TransactionControllerMessenger,
TransactionControllerStateChangeEvent,
TransactionControllerTransactionApprovedEvent,
TransactionControllerTransactionConfirmedEvent,
TransactionControllerTransactionDroppedEvent,
Expand Down Expand Up @@ -46,10 +48,12 @@ type MessengerActions =
| KeyringControllerSignTypedMessageAction
| NetworkControllerGetEIP1559CompatibilityAction
| NetworkControllerGetNetworkClientByIdAction
| RemoteFeatureFlagControllerGetStateAction;
| RemoteFeatureFlagControllerGetStateAction
| TransactionControllerGetStateAction;

type MessengerEvents =
| BridgeStatusControllerEvents
| TransactionControllerStateChangeEvent
| TransactionControllerTransactionApprovedEvent
| TransactionControllerTransactionConfirmedEvent
| TransactionControllerTransactionDroppedEvent
Expand Down Expand Up @@ -90,6 +94,7 @@ export function getTransactionControllerInitMessenger(
name: 'TransactionControllerInit',
allowedEvents: [
'BridgeStatusController:stateChange',
'TransactionController:stateChange',
'TransactionController:transactionApproved',
'TransactionController:transactionConfirmed',
'TransactionController:transactionDropped',
Expand All @@ -105,11 +110,13 @@ export function getTransactionControllerInitMessenger(
'ApprovalController:endFlow',
'ApprovalController:startFlow',
'ApprovalController:updateRequestState',
'BridgeStatusController:getState',
'BridgeStatusController:submitTx',
'DelegationController:signDelegation',
'NetworkController:getEIP1559Compatibility',
'KeyringController:signEip7702Authorization',
'KeyringController:signTypedMessage',
'TransactionController:getState',
],
});
}
134 changes: 125 additions & 9 deletions app/util/transactions/hooks/pay-hook.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {
TransactionControllerGetStateAction,
TransactionControllerState,
TransactionControllerStateChangeEvent,
TransactionControllerUnapprovedTransactionAddedEvent,
TransactionMeta,
TransactionStatus,
TransactionType,
} from '@metamask/transaction-controller';
import {
TransactionBridgeQuote,
Expand All @@ -14,20 +19,22 @@ import {
BridgeStatusControllerActions,
BridgeStatusControllerEvents,
BridgeStatusControllerState,
BridgeStatusControllerStateChangeEvent,
} from '@metamask/bridge-status-controller';
import { store } from '../../../store';
import { RootState } from '../../../reducers';
import { StatusTypes } from '@metamask/bridge-controller';
import { selectShouldUseSmartTransaction } from '../../../selectors/smartTransactionsController';
import { toHex } from '@metamask/controller-utils';

jest.mock('../../../selectors/smartTransactionsController');
jest.mock('../../transaction-controller');
jest.mock('../../../components/Views/confirmations/utils/bridge');

const TRANSACTION_ID_MOCK = '123-456';
const REQUIRED_TRANSACTION_ID_MOCK = '234-567';
const BRIDGE_TRANSACTION_ID_MOCK = '456-789';
const BRIDGE_TRANSACTION_ID_2_MOCK = '789-012';
const ERROR_MESSAGE_MOCK = 'Test error';

const QUOTE_MOCK = {
quote: {
Expand Down Expand Up @@ -59,8 +66,9 @@ const BRIDGE_TRANSACTION_META_2_MOCK = {
describe('Pay Publish Hook', () => {
let hook: PayHook;
let baseMessenger: Messenger<
BridgeStatusControllerActions,
BridgeStatusControllerActions | TransactionControllerGetStateAction,
| BridgeStatusControllerEvents
| TransactionControllerStateChangeEvent
| TransactionControllerUnapprovedTransactionAddedEvent
>;
let messengerMock: jest.Mocked<TransactionControllerInitMessenger>;
Expand All @@ -73,12 +81,45 @@ describe('Pay Publish Hook', () => {
BridgeStatusController['submitTx']
> = jest.fn();

const getTransactionStateMock = jest.fn();
const getBridgeStatusStateMock = jest.fn();
const refreshQuoteMock = jest.mocked(refreshQuote);

function runHook() {
return hook.getHook()(TRANSACTION_META_MOCK, '0x1234');
}

function unapprovedTransactionEvent(transactionId: string) {
baseMessenger.publish('TransactionController:unapprovedTransactionAdded', {
...TRANSACTION_META_MOCK,
id: transactionId,
chainId: toHex(123),
});
}

function updateTransactionStatus(
transactionId: string,
status: TransactionStatus,
) {
baseMessenger.publish(
'TransactionController:stateChange',
{
transactions: [
{
...TRANSACTION_META_MOCK,
id: transactionId,
status,
type: TransactionType.bridge,
error: {
message: ERROR_MESSAGE_MOCK,
},
},
],
} as TransactionControllerState,
[],
);
}

function updateBridgeStatus(
transactionId: string,
status: StatusTypes,
Expand All @@ -101,22 +142,33 @@ describe('Pay Publish Hook', () => {
beforeEach(() => {
jest.resetAllMocks();

baseMessenger = new Messenger<
BridgeStatusControllerActions,
| BridgeStatusControllerStateChangeEvent
| TransactionControllerUnapprovedTransactionAddedEvent
>();
baseMessenger = new Messenger();

baseMessenger.registerActionHandler(
'BridgeStatusController:getState',
getBridgeStatusStateMock,
);

baseMessenger.registerActionHandler(
'BridgeStatusController:submitTx',
submitTransactionMock,
);

baseMessenger.registerActionHandler(
'TransactionController:getState',
getTransactionStateMock,
);

messengerMock = baseMessenger.getRestricted({
name: 'TransactionControllerInitMessenger',
allowedActions: ['BridgeStatusController:submitTx'],
allowedActions: [
'BridgeStatusController:getState',
'BridgeStatusController:submitTx',
'TransactionController:getState',
],
allowedEvents: [
'BridgeStatusController:stateChange',
'TransactionController:stateChange',
'TransactionController:unapprovedTransactionAdded',
],
}) as jest.Mocked<TransactionControllerInitMessenger>;
Expand Down Expand Up @@ -152,6 +204,14 @@ describe('Pay Publish Hook', () => {
selectShouldUseSmartTransactionMock.mockReturnValue(false);

refreshQuoteMock.mockImplementation(async (_quote) => _quote);

getTransactionStateMock.mockReturnValue({
transactions: [],
} as unknown as TransactionControllerState);

getBridgeStatusStateMock.mockReturnValue({
txHistory: {},
} as BridgeStatusControllerState);
});

it('submits matching quotes to bridge status controller', async () => {
Expand Down Expand Up @@ -228,7 +288,7 @@ describe('Pay Publish Hook', () => {
return BRIDGE_TRANSACTION_META_MOCK;
});

await expect(runHook).rejects.toThrow('Bridge transaction failed');
await expect(runHook).rejects.toThrow('Bridge failed');
});

it('returns empty result once all bridges are complete', async () => {
Expand Down Expand Up @@ -256,4 +316,60 @@ describe('Pay Publish Hook', () => {

expect(refreshQuoteMock).toHaveBeenCalledWith(QUOTE_2_MOCK);
});

it.each([TransactionStatus.dropped, TransactionStatus.failed])(
'throws if required transaction status is %s',
async (status) => {
submitTransactionMock.mockReset();
submitTransactionMock.mockImplementationOnce(async () => {
unapprovedTransactionEvent(REQUIRED_TRANSACTION_ID_MOCK);

setTimeout(() => {
updateTransactionStatus(REQUIRED_TRANSACTION_ID_MOCK, status);
}, 0);

return BRIDGE_TRANSACTION_META_MOCK;
});

await expect(runHook()).rejects.toThrow(
`Required transaction failed - ${TransactionType.bridge} - ${ERROR_MESSAGE_MOCK}`,
);
},
);

it('waits for required transactions to confirm', async () => {
jest.spyOn(store, 'getState').mockReturnValue({
confirmationMetrics: {
transactionBridgeQuotesById: {
[TRANSACTION_ID_MOCK]: [QUOTE_MOCK],
},
},
} as unknown as RootState);

getBridgeStatusStateMock.mockReturnValue({
txHistory: {
[BRIDGE_TRANSACTION_ID_MOCK]: {
status: {
status: StatusTypes.COMPLETE,
},
},
},
});

submitTransactionMock.mockReset();
submitTransactionMock.mockImplementationOnce(async () => {
unapprovedTransactionEvent(REQUIRED_TRANSACTION_ID_MOCK);

setTimeout(() => {
updateTransactionStatus(
REQUIRED_TRANSACTION_ID_MOCK,
TransactionStatus.confirmed,
);
}, 0);

return BRIDGE_TRANSACTION_META_MOCK;
});

await runHook();
});
});
Loading
Loading