@metamask-previews/bridge-status-controller 42.0.0-preview-48cbf01a → 42.0.0-preview-331bec5d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _BridgeStatusController_instances, _BridgeStatusController_pollingTokensByTxMetaId, _BridgeStatusController_clientId, _BridgeStatusController_fetchFn, _BridgeStatusController_config, _BridgeStatusController_addTransactionFn, _BridgeStatusController_addTransactionBatchFn, _BridgeStatusController_updateTransactionFn, _BridgeStatusController_estimateGasFeeFn, _BridgeStatusController_trace, _BridgeStatusController_markTxAsFailed, _BridgeStatusController_restartPollingForIncompleteHistoryItems, _BridgeStatusController_addTxToHistory, _BridgeStatusController_startPollingForTxId, _BridgeStatusController_getMultichainSelectedAccount, _BridgeStatusController_handleFetchFailure, _BridgeStatusController_fetchBridgeTxStatus, _BridgeStatusController_getSrcTxHash, _BridgeStatusController_updateSrcTxHash, _BridgeStatusController_wipeBridgeStatusByChainId, _BridgeStatusController_handleSolanaTx, _BridgeStatusController_waitForHashAndReturnFinalTxMeta, _BridgeStatusController_handleApprovalTx, _BridgeStatusController_handleEvmTransaction, _BridgeStatusController_handleUSDTAllowanceReset, _BridgeStatusController_calculateGasFees, _BridgeStatusController_handleEvmTransactionBatch, _BridgeStatusController_trackUnifiedSwapBridgeEvent;
12
+ var _BridgeStatusController_instances, _BridgeStatusController_pollingTokensByTxMetaId, _BridgeStatusController_clientId, _BridgeStatusController_fetchFn, _BridgeStatusController_config, _BridgeStatusController_addTransactionFn, _BridgeStatusController_addTransactionBatchFn, _BridgeStatusController_updateTransactionFn, _BridgeStatusController_estimateGasFeeFn, _BridgeStatusController_trace, _BridgeStatusController_markTxAsFailed, _BridgeStatusController_restartPollingForIncompleteHistoryItems, _BridgeStatusController_addTxToHistory, _BridgeStatusController_startPollingForTxId, _BridgeStatusController_getMultichainSelectedAccount, _BridgeStatusController_handleFetchFailure, _BridgeStatusController_fetchBridgeTxStatus, _BridgeStatusController_fetchCowOrderStatus, _BridgeStatusController_getSrcTxHash, _BridgeStatusController_updateSrcTxHash, _BridgeStatusController_wipeBridgeStatusByChainId, _BridgeStatusController_handleSolanaTx, _BridgeStatusController_waitForHashAndReturnFinalTxMeta, _BridgeStatusController_waitForTxConfirmation, _BridgeStatusController_handleApprovalTx, _BridgeStatusController_handleEvmTransaction, _BridgeStatusController_handleUSDTAllowanceReset, _BridgeStatusController_calculateGasFees, _BridgeStatusController_handleEvmTransactionBatch, _BridgeStatusController_trackUnifiedSwapBridgeEvent;
13
13
  import { formatChainIdToHex, isSolanaChainId, StatusTypes, UnifiedSwapBridgeEventName, formatChainIdToCaip, isCrossChain, isHardwareWallet, MetricsActionType } from "@metamask/bridge-controller";
14
14
  import { toHex } from "@metamask/controller-utils";
15
15
  import { StaticIntervalPollingController } from "@metamask/polling-controller";
@@ -22,6 +22,20 @@ import { getTxGasEstimates } from "./utils/gas.mjs";
22
22
  import { getFinalizedTxProperties, getPriceImpactFromQuote, getRequestMetadataFromHistory, getRequestParamFromHistory, getTradeDataFromHistory, getEVMTxPropertiesFromTransactionMeta, getTxStatusesFromHistory, getPreConfirmationPropertiesFromQuote } from "./utils/metrics.mjs";
23
23
  import { findAndUpdateTransactionsInBatch, getAddTransactionBatchParams, getClientRequest, getStatusRequestParams, getUSDTAllowanceResetTx, handleLineaDelay, handleMobileHardwareWalletDelay, handleSolanaTxResponse } from "./utils/transaction.mjs";
24
24
  import { generateActionId } from "./utils/transaction.mjs";
25
+ // CoW intent: API base and network path mapping (adjust as needed)
26
+ const COW_API_BASE = 'https://api.cow.fi';
27
+ const COW_NETWORK_PATHS = {
28
+ // Ethereum Mainnet
29
+ 1: 'mainnet',
30
+ // Arbitrum One
31
+ 42161: 'arbitrum_one',
32
+ // Base
33
+ 8453: 'base',
34
+ // Avalanche C-Chain
35
+ 43114: 'avalanche',
36
+ // Polygon PoS
37
+ 137: 'polygon',
38
+ };
25
39
  const metadata = {
26
40
  // We want to persist the bridge status state so that we can show the proper data for the Activity list
27
41
  // basically match the behavior of TransactionController
@@ -176,6 +190,8 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
176
190
  // We know it's in progress but not the exact status yet
177
191
  const txHistoryItem = {
178
192
  txMetaId: bridgeTxMeta.id,
193
+ originalTransactionId: bridgeTxMeta
194
+ .originalTransactionId || bridgeTxMeta.id,
179
195
  batchId: bridgeTxMeta.batchId,
180
196
  quote: quoteResponse.quote,
181
197
  startTime,
@@ -220,8 +236,9 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
220
236
  return;
221
237
  }
222
238
  const { quote } = txHistoryItem;
239
+ const isIntent = txId.startsWith('intent:');
223
240
  const isBridgeTx = isCrossChain(quote.srcChainId, quote.destChainId);
224
- if (isBridgeTx) {
241
+ if (isBridgeTx || isIntent) {
225
242
  __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[txId] = this.startPolling({
226
243
  bridgeTxMetaId: txId,
227
244
  });
@@ -280,6 +297,11 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
280
297
  });
281
298
  _BridgeStatusController_fetchBridgeTxStatus.set(this, async ({ bridgeTxMetaId, }) => {
282
299
  const { txHistory } = this.state;
300
+ // Intent-based items: poll CoW API instead of Bridge API
301
+ if (bridgeTxMetaId.startsWith('intent:')) {
302
+ await __classPrivateFieldGet(this, _BridgeStatusController_fetchCowOrderStatus, "f").call(this, { bridgeTxMetaId });
303
+ return;
304
+ }
283
305
  if (shouldSkipFetchDueToFetchFailures(txHistory[bridgeTxMetaId]?.attempts)) {
284
306
  return;
285
307
  }
@@ -331,8 +353,174 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
331
353
  }
332
354
  }
333
355
  }
334
- catch (e) {
335
- console.warn('Failed to fetch bridge tx status', e);
356
+ catch (error) {
357
+ console.warn('Failed to fetch bridge tx status', error);
358
+ __classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
359
+ }
360
+ });
361
+ _BridgeStatusController_fetchCowOrderStatus.set(this, async ({ bridgeTxMetaId, }) => {
362
+ const { txHistory } = this.state;
363
+ const historyItem = txHistory[bridgeTxMetaId];
364
+ if (!historyItem) {
365
+ return;
366
+ }
367
+ // Backoff handling
368
+ if (shouldSkipFetchDueToFetchFailures(historyItem.attempts)) {
369
+ return;
370
+ }
371
+ const orderUid = bridgeTxMetaId.replace(/^intent:/u, '');
372
+ const { srcChainId } = historyItem.quote;
373
+ const networkPath = COW_NETWORK_PATHS[srcChainId];
374
+ if (!networkPath) {
375
+ // Unsupported mapping: stop polling with failure
376
+ this.update((state) => {
377
+ state.txHistory[bridgeTxMetaId].status = {
378
+ status: StatusTypes.FAILED,
379
+ srcChain: {
380
+ chainId: srcChainId,
381
+ txHash: '',
382
+ },
383
+ };
384
+ });
385
+ return;
386
+ }
387
+ try {
388
+ const url = `${COW_API_BASE}/${networkPath}/api/v1/orders/${orderUid}`;
389
+ const res = await __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f").call(this, url, { method: 'GET' });
390
+ // CoW API status enum mapping
391
+ const rawStatus = typeof res === 'object' && res !== null && 'status' in res
392
+ ? (res.status ?? '')
393
+ : '';
394
+ const isComplete = rawStatus === 'fulfilled';
395
+ const isFailed = ['cancelled', 'expired'].includes(rawStatus);
396
+ const isPending = ['presignaturePending', 'open'].includes(rawStatus);
397
+ // Try to find a tx hash in common fields
398
+ let txHash = '';
399
+ let allHashes = [];
400
+ if (isComplete) {
401
+ // Prefer authoritative trades endpoint for one or multiple fills
402
+ const tradesUrl = `${COW_API_BASE}/${networkPath}/api/v1/trades?orderUid=${orderUid}`;
403
+ const trades = (await __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f").call(this, tradesUrl, { method: 'GET' })) ?? [];
404
+ allHashes = Array.isArray(trades)
405
+ ? trades
406
+ .map((t) => t?.txHash || t?.transactionHash)
407
+ .filter((h) => typeof h === 'string' && h.length > 0)
408
+ : [];
409
+ // Fallback to any hash on order if trades missing
410
+ if (allHashes.length === 0) {
411
+ const possible = [
412
+ res.txHash,
413
+ res.transactionHash,
414
+ res.executedTransaction,
415
+ res
416
+ .executedTransactionHash,
417
+ ].filter((h) => typeof h === 'string' && h.length > 0);
418
+ allHashes = possible;
419
+ }
420
+ txHash = allHashes[allHashes.length - 1] || '';
421
+ }
422
+ let statusType;
423
+ if (isComplete) {
424
+ statusType = StatusTypes.COMPLETE;
425
+ }
426
+ else if (isFailed) {
427
+ statusType = StatusTypes.FAILED;
428
+ }
429
+ else if (isPending) {
430
+ statusType = StatusTypes.PENDING;
431
+ }
432
+ else {
433
+ statusType = StatusTypes.PENDING; // Default to pending for unknown statuses
434
+ }
435
+ const newStatus = {
436
+ status: statusType,
437
+ srcChain: {
438
+ chainId: srcChainId,
439
+ txHash: txHash || historyItem.status.srcChain.txHash || '',
440
+ },
441
+ };
442
+ const newBridgeHistoryItem = {
443
+ ...historyItem,
444
+ status: newStatus,
445
+ completionTime: newStatus.status === StatusTypes.COMPLETE ||
446
+ newStatus.status === StatusTypes.FAILED
447
+ ? Date.now()
448
+ : undefined,
449
+ attempts: undefined,
450
+ srcTxHashes: allHashes.length > 0
451
+ ? Array.from(new Set([...(historyItem.srcTxHashes ?? []), ...allHashes]))
452
+ : historyItem.srcTxHashes,
453
+ };
454
+ this.update((state) => {
455
+ state.txHistory[bridgeTxMetaId] = newBridgeHistoryItem;
456
+ });
457
+ // Update the actual transaction in TransactionController to sync with CoW status
458
+ // Use the original transaction ID (not the intent: prefixed bridge history key)
459
+ const originalTxId = historyItem
460
+ .originalTransactionId || historyItem.txMetaId;
461
+ if (originalTxId && !originalTxId.startsWith('intent:')) {
462
+ try {
463
+ let transactionStatus;
464
+ if (isComplete) {
465
+ transactionStatus = TransactionStatus.confirmed;
466
+ }
467
+ else if (isFailed) {
468
+ transactionStatus = TransactionStatus.failed;
469
+ }
470
+ else if (isPending) {
471
+ transactionStatus = TransactionStatus.submitted;
472
+ }
473
+ else {
474
+ transactionStatus = TransactionStatus.submitted; // Default to submitted for unknown statuses
475
+ }
476
+ // Merge with existing TransactionMeta to avoid wiping required fields
477
+ const { transactions } = this.messagingSystem.call('TransactionController:getState');
478
+ const existingTxMeta = transactions.find((t) => t.id === originalTxId);
479
+ if (!existingTxMeta) {
480
+ console.warn('📝 [fetchCowOrderStatus] Skipping update; transaction not found', { originalTxId, bridgeHistoryKey: bridgeTxMetaId });
481
+ }
482
+ else {
483
+ const updatedTxMeta = {
484
+ ...existingTxMeta,
485
+ status: transactionStatus,
486
+ ...(txHash ? { hash: txHash } : {}),
487
+ ...(txHash
488
+ ? {
489
+ txReceipt: {
490
+ ...existingTxMeta.txReceipt,
491
+ transactionHash: txHash,
492
+ status: (isComplete ? '0x1' : '0x0'),
493
+ },
494
+ }
495
+ : {}),
496
+ };
497
+ __classPrivateFieldGet(this, _BridgeStatusController_updateTransactionFn, "f").call(this, updatedTxMeta, `BridgeStatusController - CoW order status updated: ${rawStatus}`);
498
+ }
499
+ }
500
+ catch (error) {
501
+ console.error('📝 [fetchCowOrderStatus] Failed to update transaction status', {
502
+ originalTxId,
503
+ bridgeHistoryKey: bridgeTxMetaId,
504
+ error,
505
+ });
506
+ }
507
+ }
508
+ const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
509
+ const isFinal = newStatus.status === StatusTypes.COMPLETE ||
510
+ newStatus.status === StatusTypes.FAILED;
511
+ if (isFinal && pollingToken) {
512
+ this.stopPollingByPollingToken(pollingToken);
513
+ delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
514
+ if (newStatus.status === StatusTypes.COMPLETE) {
515
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Completed, bridgeTxMetaId);
516
+ }
517
+ else if (newStatus.status === StatusTypes.FAILED) {
518
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Failed, bridgeTxMetaId);
519
+ }
520
+ }
521
+ }
522
+ catch {
523
+ // Network or API error: apply backoff
336
524
  __classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
337
525
  }
338
526
  });
@@ -417,6 +605,36 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
417
605
  }
418
606
  return finalTransactionMeta;
419
607
  });
608
+ // Waits until a given transaction (by id) reaches confirmed/finalized status or fails/times out.
609
+ _BridgeStatusController_waitForTxConfirmation.set(this, async (txId, { timeoutMs = 5 * 60000, // 5 minutes default
610
+ pollMs = 2000, } = {}) => {
611
+ const start = Date.now();
612
+ // Poll the TransactionController state for status changes
613
+ // We intentionally keep this simple to avoid extra wiring/subscriptions in this controller
614
+ // and because we only need it for the rare intent+approval path.
615
+ while (true) {
616
+ const { transactions } = this.messagingSystem.call('TransactionController:getState');
617
+ const meta = transactions.find((t) => t.id === txId);
618
+ if (meta) {
619
+ // Treat both 'confirmed' and 'finalized' as success to match TC lifecycle
620
+ if (meta.status === TransactionStatus.confirmed ||
621
+ // Some environments move directly to finalized
622
+ TransactionStatus.finalized ===
623
+ meta.status) {
624
+ return meta;
625
+ }
626
+ if (meta.status === TransactionStatus.failed ||
627
+ meta.status === TransactionStatus.dropped ||
628
+ meta.status === TransactionStatus.rejected) {
629
+ throw new Error('Approval transaction did not confirm');
630
+ }
631
+ }
632
+ if (Date.now() - start > timeoutMs) {
633
+ throw new Error('Timed out waiting for approval confirmation');
634
+ }
635
+ await new Promise((resolve) => setTimeout(resolve, pollMs));
636
+ }
637
+ });
420
638
  _BridgeStatusController_handleApprovalTx.set(this, async (isBridgeTx, quoteResponse, requireApproval) => {
421
639
  const { approval } = quoteResponse;
422
640
  if (approval) {
@@ -468,11 +686,17 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
468
686
  type: transactionType,
469
687
  origin: 'metamask',
470
688
  };
689
+ // Exclude gasLimit from trade to avoid type issues (it can be null)
690
+ const { gasLimit: tradeGasLimit, ...tradeWithoutGasLimit } = trade;
471
691
  const transactionParams = {
472
- ...trade,
692
+ ...tradeWithoutGasLimit,
473
693
  chainId: hexChainId,
474
- gasLimit: trade.gasLimit?.toString(),
475
- gas: trade.gasLimit?.toString(),
694
+ // Only add gasLimit and gas if they're valid (not undefined/null/zero)
695
+ ...(tradeGasLimit &&
696
+ tradeGasLimit !== 0 && {
697
+ gasLimit: tradeGasLimit.toString(),
698
+ gas: tradeGasLimit.toString(),
699
+ }),
476
700
  };
477
701
  const transactionParamsWithMaxGas = {
478
702
  ...transactionParams,
@@ -501,7 +725,11 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
501
725
  networkGasFeeEstimates: gasFeeEstimates,
502
726
  txGasFeeEstimates,
503
727
  });
504
- const maxGasLimit = toHex(transactionParams.gas ?? 0);
728
+ // Let the TransactionController handle gas limit estimation when no gas is provided
729
+ // This fixes the issue where approval transactions had zero gas
730
+ const maxGasLimit = transactionParams.gas
731
+ ? toHex(transactionParams.gas)
732
+ : undefined;
505
733
  return {
506
734
  maxFeePerGas,
507
735
  maxPriorityFeePerGas,
@@ -658,6 +886,211 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
658
886
  }
659
887
  return txMeta;
660
888
  };
889
+ /**
890
+ * UI-signed intent submission (fast path): the UI generates the EIP-712 signature and calls this with the raw signature.
891
+ * Here we POST the order to the intent backend (e.g., CoW) and create a synthetic history entry for UX.
892
+ *
893
+ * @param params - Object containing intent submission parameters
894
+ * @param params.quoteResponse - Quote carrying intent data
895
+ * @param params.signature - Hex signature produced by eth_signTypedData_v4
896
+ * @param params.accountAddress - The EOA submitting the order
897
+ * @returns A lightweight TransactionMeta-like object for history linking
898
+ */
899
+ this.submitIntent = async (params) => {
900
+ const { quoteResponse, signature, accountAddress } = params;
901
+ // Build pre-confirmation properties for error tracking parity with submitTx
902
+ const account = this.messagingSystem.call('AccountsController:getAccountByAddress', accountAddress);
903
+ const isHardwareAccount = Boolean(account) && isHardwareWallet(account);
904
+ const preConfirmationProperties = getPreConfirmationPropertiesFromQuote(quoteResponse, false, isHardwareAccount);
905
+ try {
906
+ const { intent } = quoteResponse
907
+ .quote;
908
+ if (!intent || intent.protocol !== 'cowswap') {
909
+ throw new Error('submitIntent: missing or unsupported intent');
910
+ }
911
+ // If backend provided an approval tx for this intent quote, submit it first (on-chain),
912
+ // then proceed with off-chain intent submission.
913
+ let approvalTxId;
914
+ if (quoteResponse.approval) {
915
+ const isBridgeTx = isCrossChain(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
916
+ // Handle approval silently for better UX in intent flows
917
+ const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleApprovalTx, "f").call(this, isBridgeTx, quoteResponse,
918
+ /* requireApproval */ false);
919
+ approvalTxId = approvalTxMeta?.id;
920
+ // Optionally wait for approval confirmation with timeout and graceful fallback
921
+ // CoW order can be created before allowance is mined, but waiting helps avoid MEV issues
922
+ if (approvalTxId) {
923
+ try {
924
+ // Wait with a shorter timeout and continue if it fails
925
+ await __classPrivateFieldGet(this, _BridgeStatusController_waitForTxConfirmation, "f").call(this, approvalTxId, {
926
+ timeoutMs: 30000,
927
+ pollMs: 3000, // Poll less frequently to avoid rate limits
928
+ });
929
+ }
930
+ catch (error) {
931
+ // Log but don't throw - continue with CoW order submission
932
+ console.warn('Approval confirmation failed, continuing with intent submission:', error);
933
+ }
934
+ }
935
+ }
936
+ // Map chainId to CoW API server path (prod)
937
+ const chainId = quoteResponse.quote.srcChainId;
938
+ const serverPath = COW_NETWORK_PATHS[chainId];
939
+ if (!serverPath) {
940
+ throw new Error(`submitIntent: unsupported chainId for CoW intents: ${chainId}`);
941
+ }
942
+ // Build OrderCreation payload (simplified)
943
+ const orderBody = {
944
+ ...intent.order,
945
+ feeAmount: '0',
946
+ from: accountAddress,
947
+ signature,
948
+ signingScheme: 'eip712',
949
+ };
950
+ // POST to CoW prod
951
+ const url = `https://api.cow.fi/${serverPath}/api/v1/orders`;
952
+ const res = await __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f").call(this, url, {
953
+ method: 'POST',
954
+ headers: { 'Content-Type': 'application/json' },
955
+ body: JSON.stringify(orderBody),
956
+ });
957
+ const orderUid = typeof res === 'string'
958
+ ? res
959
+ : (res?.uid ??
960
+ res
961
+ ?.orderUid ??
962
+ res?.id);
963
+ if (!orderUid) {
964
+ throw new Error('submitIntent: failed to submit order');
965
+ }
966
+ // Get initial order status from CoW API
967
+ let initialOrderStatus = 'open'; // Default status
968
+ try {
969
+ const orderStatusUrl = `${COW_API_BASE}/${serverPath}/api/v1/orders/${orderUid}`;
970
+ const orderStatusRes = await __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f").call(this, orderStatusUrl, {
971
+ method: 'GET',
972
+ });
973
+ initialOrderStatus =
974
+ orderStatusRes?.status ?? 'open';
975
+ }
976
+ catch (error) {
977
+ console.warn('📝 [submitIntent] Failed to get initial order status, using default:', error);
978
+ }
979
+ // Determine transaction type: swap for same-chain, bridge for cross-chain
980
+ const isCrossChainTx = isCrossChain(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
981
+ const transactionType = isCrossChainTx
982
+ ? TransactionType.bridge
983
+ : TransactionType.swap;
984
+ // Create actual transaction in Transaction Controller first
985
+ const networkClientId = this.messagingSystem.call('NetworkController:findNetworkClientIdByChainId', formatChainIdToHex(chainId));
986
+ const intentTransactionParams = {
987
+ chainId: formatChainIdToHex(chainId),
988
+ from: accountAddress,
989
+ to: intent.settlementContract ||
990
+ '0x9008D19f58AAbd9eD0D60971565AA8510560ab41',
991
+ data: `0x${orderUid.slice(-8)}`,
992
+ value: '0x0',
993
+ gas: '0x5208',
994
+ gasPrice: '0x3b9aca00', // 1 Gwei - will be converted to EIP-1559 fees if network supports it
995
+ };
996
+ const { transactionMeta: txMetaPromise } = await __classPrivateFieldGet(this, _BridgeStatusController_addTransactionFn, "f").call(this, intentTransactionParams, {
997
+ origin: 'metamask',
998
+ actionId: generateActionId(),
999
+ requireApproval: false,
1000
+ networkClientId,
1001
+ type: transactionType,
1002
+ swaps: {
1003
+ meta: {
1004
+ // Add token symbols from quoteResponse for proper display
1005
+ sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol,
1006
+ destinationTokenSymbol: quoteResponse.quote.destAsset.symbol,
1007
+ sourceTokenAmount: quoteResponse.quote.srcTokenAmount,
1008
+ destinationTokenAmount: quoteResponse.quote.destTokenAmount,
1009
+ sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals,
1010
+ destinationTokenDecimals: quoteResponse.quote.destAsset.decimals,
1011
+ sourceTokenAddress: quoteResponse.quote.srcAsset.address,
1012
+ destinationTokenAddress: quoteResponse.quote.destAsset.address,
1013
+ swapTokenValue: quoteResponse.sentAmount.amount,
1014
+ approvalTxId,
1015
+ swapMetaData: {
1016
+ isIntentTx: true,
1017
+ orderUid,
1018
+ intentType: isCrossChainTx ? 'bridge' : 'swap',
1019
+ },
1020
+ },
1021
+ },
1022
+ });
1023
+ const intentTxMeta = txMetaPromise;
1024
+ // Map initial CoW order status to TransactionController status
1025
+ const isComplete = initialOrderStatus === 'fulfilled';
1026
+ const isFailed = ['cancelled', 'expired'].includes(initialOrderStatus);
1027
+ const isPending = ['presignaturePending', 'open'].includes(initialOrderStatus);
1028
+ let initialTransactionStatus;
1029
+ if (isComplete) {
1030
+ initialTransactionStatus = TransactionStatus.confirmed;
1031
+ }
1032
+ else if (isFailed) {
1033
+ initialTransactionStatus = TransactionStatus.failed;
1034
+ }
1035
+ else if (isPending) {
1036
+ initialTransactionStatus = TransactionStatus.submitted;
1037
+ }
1038
+ else {
1039
+ initialTransactionStatus = TransactionStatus.submitted;
1040
+ }
1041
+ // Update transaction with proper initial status based on CoW order
1042
+ const statusUpdatedTxMeta = {
1043
+ ...intentTxMeta,
1044
+ status: initialTransactionStatus,
1045
+ };
1046
+ __classPrivateFieldGet(this, _BridgeStatusController_updateTransactionFn, "f").call(this, statusUpdatedTxMeta, `BridgeStatusController - Initial CoW order status: ${initialOrderStatus}`);
1047
+ // Update with actual transaction metadata
1048
+ const syntheticMeta = {
1049
+ ...statusUpdatedTxMeta,
1050
+ isIntentTx: true,
1051
+ orderUid,
1052
+ intentType: isCrossChainTx ? 'bridge' : 'swap',
1053
+ };
1054
+ // Record in bridge history with actual transaction metadata
1055
+ try {
1056
+ // Use intent: prefix for CoW transactions to route to CoW API
1057
+ const bridgeHistoryKey = `intent:${orderUid}`;
1058
+ // Create a bridge transaction metadata that includes the original txId
1059
+ const bridgeTxMetaForHistory = {
1060
+ ...syntheticMeta,
1061
+ id: bridgeHistoryKey,
1062
+ originalTransactionId: syntheticMeta.id, // Keep original txId for TransactionController updates
1063
+ };
1064
+ __classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
1065
+ accountAddress,
1066
+ bridgeTxMeta: bridgeTxMetaForHistory,
1067
+ statusRequest: {
1068
+ ...getStatusRequestParams(quoteResponse),
1069
+ srcTxHash: syntheticMeta.hash || '',
1070
+ },
1071
+ quoteResponse,
1072
+ slippagePercentage: 0,
1073
+ isStxEnabled: false,
1074
+ approvalTxId,
1075
+ });
1076
+ // Debug: Check if the bridge history was added
1077
+ // Start polling using the intent: prefixed key to route to CoW API
1078
+ __classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeHistoryKey);
1079
+ }
1080
+ catch (error) {
1081
+ console.error('📝 [submitIntent] Failed to add to bridge history', error);
1082
+ // non-fatal but log the error
1083
+ }
1084
+ return syntheticMeta;
1085
+ }
1086
+ catch (error) {
1087
+ __classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Failed, undefined, {
1088
+ error_message: error?.message,
1089
+ ...preConfirmationProperties,
1090
+ });
1091
+ throw error;
1092
+ }
1093
+ };
661
1094
  /**
662
1095
  * Tracks post-submission events for a cross-chain swap based on the history item
663
1096
  *
@@ -681,8 +1114,8 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
681
1114
  }
682
1115
  const selectedAccount = this.messagingSystem.call('AccountsController:getAccountByAddress', historyItem.account);
683
1116
  const { transactions } = this.messagingSystem.call('TransactionController:getState');
684
- const txMeta = transactions?.find(({ id }) => id === txMetaId);
685
- const approvalTxMeta = transactions?.find(({ id }) => id === historyItem.approvalTxId);
1117
+ const txMeta = transactions?.find((t) => t.id === txMetaId);
1118
+ const approvalTxMeta = transactions?.find((t) => t.id === historyItem.approvalTxId);
686
1119
  const requestParamProperties = getRequestParamFromHistory(historyItem);
687
1120
  if (eventName === UnifiedSwapBridgeEventName.StatusValidationFailed) {
688
1121
  const { chain_id_source, chain_id_destination, token_address_source, token_address_destination, } = requestParamProperties;
@@ -722,12 +1155,19 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
722
1155
  this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:wipeBridgeStatus`, this.wipeBridgeStatus.bind(this));
723
1156
  this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:resetState`, this.resetState.bind(this));
724
1157
  this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:submitTx`, this.submitTx.bind(this));
1158
+ this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:submitIntent`, this.submitIntent.bind(this));
725
1159
  this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:restartPollingForFailedAttempts`, this.restartPollingForFailedAttempts.bind(this));
726
1160
  this.messagingSystem.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:getBridgeHistoryItemByTxMetaId`, this.getBridgeHistoryItemByTxMetaId.bind(this));
727
1161
  // Set interval
728
1162
  this.setIntervalLength(REFRESH_INTERVAL_MS);
729
1163
  this.messagingSystem.subscribe('TransactionController:transactionFailed', ({ transactionMeta }) => {
730
1164
  const { type, status, id } = transactionMeta;
1165
+ // Skip intent transactions - they have their own tracking via CoW API
1166
+ // Skip intent transactions - they have their own tracking via CoW API
1167
+ if (transactionMeta
1168
+ .swapMetaData?.isIntentTx) {
1169
+ return;
1170
+ }
731
1171
  if (type &&
732
1172
  [
733
1173
  TransactionType.bridge,
@@ -763,7 +1203,7 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
763
1203
  __classPrivateFieldGet(this, _BridgeStatusController_restartPollingForIncompleteHistoryItems, "f").call(this);
764
1204
  }
765
1205
  }
766
- _BridgeStatusController_pollingTokensByTxMetaId = new WeakMap(), _BridgeStatusController_clientId = new WeakMap(), _BridgeStatusController_fetchFn = new WeakMap(), _BridgeStatusController_config = new WeakMap(), _BridgeStatusController_addTransactionFn = new WeakMap(), _BridgeStatusController_addTransactionBatchFn = new WeakMap(), _BridgeStatusController_updateTransactionFn = new WeakMap(), _BridgeStatusController_estimateGasFeeFn = new WeakMap(), _BridgeStatusController_trace = new WeakMap(), _BridgeStatusController_markTxAsFailed = new WeakMap(), _BridgeStatusController_restartPollingForIncompleteHistoryItems = new WeakMap(), _BridgeStatusController_addTxToHistory = new WeakMap(), _BridgeStatusController_startPollingForTxId = new WeakMap(), _BridgeStatusController_handleFetchFailure = new WeakMap(), _BridgeStatusController_fetchBridgeTxStatus = new WeakMap(), _BridgeStatusController_getSrcTxHash = new WeakMap(), _BridgeStatusController_updateSrcTxHash = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_handleSolanaTx = new WeakMap(), _BridgeStatusController_waitForHashAndReturnFinalTxMeta = new WeakMap(), _BridgeStatusController_handleApprovalTx = new WeakMap(), _BridgeStatusController_handleEvmTransaction = new WeakMap(), _BridgeStatusController_handleUSDTAllowanceReset = new WeakMap(), _BridgeStatusController_calculateGasFees = new WeakMap(), _BridgeStatusController_handleEvmTransactionBatch = new WeakMap(), _BridgeStatusController_trackUnifiedSwapBridgeEvent = new WeakMap(), _BridgeStatusController_instances = new WeakSet(), _BridgeStatusController_getMultichainSelectedAccount = function _BridgeStatusController_getMultichainSelectedAccount() {
1206
+ _BridgeStatusController_pollingTokensByTxMetaId = new WeakMap(), _BridgeStatusController_clientId = new WeakMap(), _BridgeStatusController_fetchFn = new WeakMap(), _BridgeStatusController_config = new WeakMap(), _BridgeStatusController_addTransactionFn = new WeakMap(), _BridgeStatusController_addTransactionBatchFn = new WeakMap(), _BridgeStatusController_updateTransactionFn = new WeakMap(), _BridgeStatusController_estimateGasFeeFn = new WeakMap(), _BridgeStatusController_trace = new WeakMap(), _BridgeStatusController_markTxAsFailed = new WeakMap(), _BridgeStatusController_restartPollingForIncompleteHistoryItems = new WeakMap(), _BridgeStatusController_addTxToHistory = new WeakMap(), _BridgeStatusController_startPollingForTxId = new WeakMap(), _BridgeStatusController_handleFetchFailure = new WeakMap(), _BridgeStatusController_fetchBridgeTxStatus = new WeakMap(), _BridgeStatusController_fetchCowOrderStatus = new WeakMap(), _BridgeStatusController_getSrcTxHash = new WeakMap(), _BridgeStatusController_updateSrcTxHash = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_handleSolanaTx = new WeakMap(), _BridgeStatusController_waitForHashAndReturnFinalTxMeta = new WeakMap(), _BridgeStatusController_waitForTxConfirmation = new WeakMap(), _BridgeStatusController_handleApprovalTx = new WeakMap(), _BridgeStatusController_handleEvmTransaction = new WeakMap(), _BridgeStatusController_handleUSDTAllowanceReset = new WeakMap(), _BridgeStatusController_calculateGasFees = new WeakMap(), _BridgeStatusController_handleEvmTransactionBatch = new WeakMap(), _BridgeStatusController_trackUnifiedSwapBridgeEvent = new WeakMap(), _BridgeStatusController_instances = new WeakSet(), _BridgeStatusController_getMultichainSelectedAccount = function _BridgeStatusController_getMultichainSelectedAccount() {
767
1207
  return this.messagingSystem.call('AccountsController:getSelectedMultichainAccount');
768
1208
  };
769
1209
  //# sourceMappingURL=bridge-status-controller.mjs.map