@metamask-previews/bridge-status-controller 64.2.0-preview-d24ded2 → 64.2.0-preview-749d0638
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.
- package/CHANGELOG.md +7 -0
- package/dist/bridge-status-controller.cjs +403 -15
- package/dist/bridge-status-controller.cjs.map +1 -1
- package/dist/bridge-status-controller.d.cts +16 -1
- package/dist/bridge-status-controller.d.cts.map +1 -1
- package/dist/bridge-status-controller.d.mts +16 -1
- package/dist/bridge-status-controller.d.mts.map +1 -1
- package/dist/bridge-status-controller.mjs +403 -15
- package/dist/bridge-status-controller.mjs.map +1 -1
- package/dist/types.cjs +9 -7
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +26 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +26 -16
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +9 -7
- package/dist/types.mjs.map +1 -1
- package/dist/utils/intent-api.cjs +73 -0
- package/dist/utils/intent-api.cjs.map +1 -0
- package/dist/utils/intent-api.d.cts +24 -0
- package/dist/utils/intent-api.d.cts.map +1 -0
- package/dist/utils/intent-api.d.mts +24 -0
- package/dist/utils/intent-api.d.mts.map +1 -0
- package/dist/utils/intent-api.mjs +68 -0
- package/dist/utils/intent-api.mjs.map +1 -0
- package/dist/utils/transaction.d.cts +21 -2
- package/dist/utils/transaction.d.cts.map +1 -1
- package/dist/utils/transaction.d.mts +21 -2
- package/dist/utils/transaction.d.mts.map +1 -1
- package/dist/utils/validators.cjs +23 -1
- package/dist/utils/validators.cjs.map +1 -1
- package/dist/utils/validators.d.cts +51 -0
- package/dist/utils/validators.d.cts.map +1 -1
- package/dist/utils/validators.d.mts +51 -0
- package/dist/utils/validators.d.mts.map +1 -1
- package/dist/utils/validators.mjs +22 -1
- package/dist/utils/validators.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -9,18 +9,20 @@ 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_handleNonEvmTx, _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_fetchIntentOrderStatus, _BridgeStatusController_updateBridgeHistoryFromIntentOrder, _BridgeStatusController_getSrcTxHash, _BridgeStatusController_updateSrcTxHash, _BridgeStatusController_wipeBridgeStatusByChainId, _BridgeStatusController_handleNonEvmTx, _BridgeStatusController_waitForHashAndReturnFinalTxMeta, _BridgeStatusController_waitForTxConfirmation, _BridgeStatusController_handleApprovalTx, _BridgeStatusController_handleEvmTransaction, _BridgeStatusController_handleUSDTAllowanceReset, _BridgeStatusController_calculateGasFees, _BridgeStatusController_handleEvmTransactionBatch, _BridgeStatusController_convertBridgeQuoteToIntentQuote, _BridgeStatusController_mapIntentOrderStatusToTransactionStatus, _BridgeStatusController_trackUnifiedSwapBridgeEvent;
|
|
13
13
|
import { formatChainIdToHex, isNonEvmChainId, StatusTypes, UnifiedSwapBridgeEventName, formatChainIdToCaip, isCrossChain, isEvmTxData, isHardwareWallet, MetricsActionType, isBitcoinTrade, isTronTrade, AbortReason } from "@metamask/bridge-controller";
|
|
14
14
|
import { toHex } from "@metamask/controller-utils";
|
|
15
15
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
16
16
|
import { TransactionStatus, TransactionType } from "@metamask/transaction-controller";
|
|
17
17
|
import { numberToHex } from "@metamask/utils";
|
|
18
18
|
import { BRIDGE_PROD_API_BASE_URL, BRIDGE_STATUS_CONTROLLER_NAME, DEFAULT_BRIDGE_STATUS_CONTROLLER_STATE, MAX_ATTEMPTS, REFRESH_INTERVAL_MS, TraceName } from "./constants.mjs";
|
|
19
|
+
import { IntentApiImpl } from "./utils/intent-api.mjs";
|
|
19
20
|
import { BridgeClientId } from "./types.mjs";
|
|
20
21
|
import { fetchBridgeTxStatus, getStatusRequestWithSrcTxHash, shouldSkipFetchDueToFetchFailures } from "./utils/bridge-status.mjs";
|
|
21
22
|
import { getTxGasEstimates } from "./utils/gas.mjs";
|
|
22
23
|
import { getFinalizedTxProperties, getPriceImpactFromQuote, getRequestMetadataFromHistory, getRequestParamFromHistory, getTradeDataFromHistory, getEVMTxPropertiesFromTransactionMeta, getTxStatusesFromHistory, getPreConfirmationPropertiesFromQuote } from "./utils/metrics.mjs";
|
|
23
24
|
import { findAndUpdateTransactionsInBatch, getAddTransactionBatchParams, getClientRequest, getStatusRequestParams, handleApprovalDelay, handleMobileHardwareWalletDelay, handleNonEvmTxResponse, generateActionId } from "./utils/transaction.mjs";
|
|
25
|
+
import { IntentOrderStatus } from "./utils/validators.mjs";
|
|
24
26
|
const metadata = {
|
|
25
27
|
// We want to persist the bridge status state so that we can show the proper data for the Activity list
|
|
26
28
|
// basically match the behavior of TransactionController
|
|
@@ -177,6 +179,8 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
177
179
|
// We know it's in progress but not the exact status yet
|
|
178
180
|
const txHistoryItem = {
|
|
179
181
|
txMetaId: bridgeTxMeta.id,
|
|
182
|
+
originalTransactionId: bridgeTxMeta
|
|
183
|
+
.originalTransactionId || bridgeTxMeta.id, // Keep original for intent transactions
|
|
180
184
|
batchId: bridgeTxMeta.batchId,
|
|
181
185
|
quote: quoteResponse.quote,
|
|
182
186
|
startTime,
|
|
@@ -222,8 +226,9 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
222
226
|
return;
|
|
223
227
|
}
|
|
224
228
|
const { quote } = txHistoryItem;
|
|
229
|
+
const isIntent = txId.startsWith('intent:');
|
|
225
230
|
const isBridgeTx = isCrossChain(quote.srcChainId, quote.destChainId);
|
|
226
|
-
if (isBridgeTx) {
|
|
231
|
+
if (isBridgeTx || isIntent) {
|
|
227
232
|
__classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[txId] = this.startPolling({
|
|
228
233
|
bridgeTxMetaId: txId,
|
|
229
234
|
});
|
|
@@ -282,6 +287,11 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
282
287
|
});
|
|
283
288
|
_BridgeStatusController_fetchBridgeTxStatus.set(this, async ({ bridgeTxMetaId, }) => {
|
|
284
289
|
const { txHistory } = this.state;
|
|
290
|
+
// Intent-based items: poll intent provider instead of Bridge API
|
|
291
|
+
if (bridgeTxMetaId.startsWith('intent:')) {
|
|
292
|
+
await __classPrivateFieldGet(this, _BridgeStatusController_fetchIntentOrderStatus, "f").call(this, { bridgeTxMetaId });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
285
295
|
if (shouldSkipFetchDueToFetchFailures(txHistory[bridgeTxMetaId]?.attempts)) {
|
|
286
296
|
return;
|
|
287
297
|
}
|
|
@@ -338,8 +348,33 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
338
348
|
}
|
|
339
349
|
}
|
|
340
350
|
}
|
|
341
|
-
catch (
|
|
342
|
-
console.warn('Failed to fetch bridge tx status',
|
|
351
|
+
catch (error) {
|
|
352
|
+
console.warn('Failed to fetch bridge tx status', error);
|
|
353
|
+
__classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
_BridgeStatusController_fetchIntentOrderStatus.set(this, async ({ bridgeTxMetaId, }) => {
|
|
357
|
+
const { txHistory } = this.state;
|
|
358
|
+
const historyItem = txHistory[bridgeTxMetaId];
|
|
359
|
+
if (!historyItem) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
// Backoff handling
|
|
363
|
+
if (shouldSkipFetchDueToFetchFailures(historyItem.attempts)) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const orderId = bridgeTxMetaId.replace(/^intent:/u, '');
|
|
368
|
+
const { srcChainId } = historyItem.quote;
|
|
369
|
+
// Extract provider name from order metadata or default to empty
|
|
370
|
+
const providerName = historyItem.quote.intent?.protocol ?? '';
|
|
371
|
+
const intentApi = new IntentApiImpl(__classPrivateFieldGet(this, _BridgeStatusController_config, "f").customBridgeApiBaseUrl, __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f"));
|
|
372
|
+
const intentOrder = await intentApi.getOrderStatus(orderId, providerName, srcChainId.toString(), __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f"));
|
|
373
|
+
// Update bridge history with intent order status
|
|
374
|
+
__classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_updateBridgeHistoryFromIntentOrder).call(this, bridgeTxMetaId, intentOrder, historyItem);
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
console.error('Failed to fetch intent order status:', error);
|
|
343
378
|
__classPrivateFieldGet(this, _BridgeStatusController_handleFetchFailure, "f").call(this, bridgeTxMetaId);
|
|
344
379
|
}
|
|
345
380
|
});
|
|
@@ -430,6 +465,36 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
430
465
|
}
|
|
431
466
|
return finalTransactionMeta;
|
|
432
467
|
});
|
|
468
|
+
// Waits until a given transaction (by id) reaches confirmed/finalized status or fails/times out.
|
|
469
|
+
_BridgeStatusController_waitForTxConfirmation.set(this, async (txId, { timeoutMs = 5 * 60000, // 5 minutes default
|
|
470
|
+
pollMs = 2000, } = {}) => {
|
|
471
|
+
const start = Date.now();
|
|
472
|
+
// Poll the TransactionController state for status changes
|
|
473
|
+
// We intentionally keep this simple to avoid extra wiring/subscriptions in this controller
|
|
474
|
+
// and because we only need it for the rare intent+approval path.
|
|
475
|
+
while (true) {
|
|
476
|
+
const { transactions } = this.messenger.call('TransactionController:getState');
|
|
477
|
+
const meta = transactions.find((tx) => tx.id === txId);
|
|
478
|
+
if (meta) {
|
|
479
|
+
// Treat both 'confirmed' and 'finalized' as success to match TC lifecycle
|
|
480
|
+
if (meta.status === TransactionStatus.confirmed ||
|
|
481
|
+
// Some environments move directly to finalized
|
|
482
|
+
TransactionStatus.finalized ===
|
|
483
|
+
meta.status) {
|
|
484
|
+
return meta;
|
|
485
|
+
}
|
|
486
|
+
if (meta.status === TransactionStatus.failed ||
|
|
487
|
+
meta.status === TransactionStatus.dropped ||
|
|
488
|
+
meta.status === TransactionStatus.rejected) {
|
|
489
|
+
throw new Error('Approval transaction did not confirm');
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (Date.now() - start > timeoutMs) {
|
|
493
|
+
throw new Error('Timed out waiting for approval confirmation');
|
|
494
|
+
}
|
|
495
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
496
|
+
}
|
|
497
|
+
});
|
|
433
498
|
_BridgeStatusController_handleApprovalTx.set(this, async (isBridgeTx, srcChainId, approval, resetApproval, requireApproval) => {
|
|
434
499
|
if (approval) {
|
|
435
500
|
const approveTx = async () => {
|
|
@@ -483,11 +548,17 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
483
548
|
type: transactionType,
|
|
484
549
|
origin: 'metamask',
|
|
485
550
|
};
|
|
551
|
+
// Exclude gasLimit from trade to avoid type issues (it can be null)
|
|
552
|
+
const { gasLimit: tradeGasLimit, ...tradeWithoutGasLimit } = trade;
|
|
486
553
|
const transactionParams = {
|
|
487
|
-
...
|
|
554
|
+
...tradeWithoutGasLimit,
|
|
488
555
|
chainId: hexChainId,
|
|
489
|
-
gasLimit
|
|
490
|
-
|
|
556
|
+
// Only add gasLimit and gas if they're valid (not undefined/null/zero)
|
|
557
|
+
...(tradeGasLimit &&
|
|
558
|
+
tradeGasLimit !== 0 && {
|
|
559
|
+
gasLimit: tradeGasLimit.toString(),
|
|
560
|
+
gas: tradeGasLimit.toString(),
|
|
561
|
+
}),
|
|
491
562
|
};
|
|
492
563
|
const transactionParamsWithMaxGas = {
|
|
493
564
|
...transactionParams,
|
|
@@ -734,6 +805,168 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
734
805
|
}
|
|
735
806
|
return txMeta;
|
|
736
807
|
};
|
|
808
|
+
/**
|
|
809
|
+
* UI-signed intent submission (fast path): the UI generates the EIP-712 signature and calls this with the raw signature.
|
|
810
|
+
* Here we submit the order to the intent provider and create a synthetic history entry for UX.
|
|
811
|
+
*
|
|
812
|
+
* @param params - Object containing intent submission parameters
|
|
813
|
+
* @param params.quoteResponse - Quote carrying intent data
|
|
814
|
+
* @param params.signature - Hex signature produced by eth_signTypedData_v4
|
|
815
|
+
* @param params.accountAddress - The EOA submitting the order
|
|
816
|
+
* @returns A lightweight TransactionMeta-like object for history linking
|
|
817
|
+
*/
|
|
818
|
+
this.submitIntent = async (params) => {
|
|
819
|
+
const { quoteResponse, signature, accountAddress } = params;
|
|
820
|
+
// Build pre-confirmation properties for error tracking parity with submitTx
|
|
821
|
+
const account = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_getMultichainSelectedAccount).call(this, accountAddress);
|
|
822
|
+
const isHardwareAccount = Boolean(account) && isHardwareWallet(account);
|
|
823
|
+
const preConfirmationProperties = getPreConfirmationPropertiesFromQuote(quoteResponse, false, isHardwareAccount);
|
|
824
|
+
try {
|
|
825
|
+
const { intent } = quoteResponse
|
|
826
|
+
.quote;
|
|
827
|
+
if (!intent) {
|
|
828
|
+
throw new Error('submitIntent: missing intent data');
|
|
829
|
+
}
|
|
830
|
+
// If backend provided an approval tx for this intent quote, submit it first (on-chain),
|
|
831
|
+
// then proceed with off-chain intent submission.
|
|
832
|
+
let approvalTxId;
|
|
833
|
+
if (quoteResponse.approval) {
|
|
834
|
+
const isBridgeTx = isCrossChain(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
|
|
835
|
+
// Handle approval silently for better UX in intent flows
|
|
836
|
+
const approvalTxMeta = await __classPrivateFieldGet(this, _BridgeStatusController_handleApprovalTx, "f").call(this, isBridgeTx, quoteResponse.quote.srcChainId, quoteResponse.approval && isEvmTxData(quoteResponse.approval)
|
|
837
|
+
? quoteResponse.approval
|
|
838
|
+
: undefined, quoteResponse.resetApproval,
|
|
839
|
+
/* requireApproval */ false);
|
|
840
|
+
approvalTxId = approvalTxMeta?.id;
|
|
841
|
+
// Optionally wait for approval confirmation with timeout and graceful fallback
|
|
842
|
+
// Intent order can be created before allowance is mined, but waiting helps avoid MEV issues
|
|
843
|
+
if (approvalTxId) {
|
|
844
|
+
try {
|
|
845
|
+
// Wait with a shorter timeout and continue if it fails
|
|
846
|
+
await __classPrivateFieldGet(this, _BridgeStatusController_waitForTxConfirmation, "f").call(this, approvalTxId, {
|
|
847
|
+
timeoutMs: 30000, // 30 seconds instead of 5 minutes
|
|
848
|
+
pollMs: 3000, // Poll less frequently to avoid rate limits
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
catch (error) {
|
|
852
|
+
// Log but don't throw - continue with intent order submission
|
|
853
|
+
console.warn('Approval confirmation failed, continuing with intent submission:', error);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
// Create intent quote from bridge quote response
|
|
858
|
+
const intentQuote = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_convertBridgeQuoteToIntentQuote).call(this, quoteResponse, intent);
|
|
859
|
+
const chainId = quoteResponse.quote.srcChainId;
|
|
860
|
+
const submissionParams = {
|
|
861
|
+
srcChainId: chainId.toString(),
|
|
862
|
+
quoteId: intentQuote.id,
|
|
863
|
+
signature,
|
|
864
|
+
order: intentQuote.metadata.order,
|
|
865
|
+
userAddress: accountAddress,
|
|
866
|
+
aggregatorId: 'cowswap',
|
|
867
|
+
};
|
|
868
|
+
const intentApi = new IntentApiImpl(__classPrivateFieldGet(this, _BridgeStatusController_config, "f").customBridgeApiBaseUrl, __classPrivateFieldGet(this, _BridgeStatusController_fetchFn, "f"));
|
|
869
|
+
const intentOrder = (await intentApi.submitIntent(submissionParams, __classPrivateFieldGet(this, _BridgeStatusController_clientId, "f")));
|
|
870
|
+
const orderUid = intentOrder.id;
|
|
871
|
+
// Determine transaction type: swap for same-chain, bridge for cross-chain
|
|
872
|
+
const isCrossChainTx = isCrossChain(quoteResponse.quote.srcChainId, quoteResponse.quote.destChainId);
|
|
873
|
+
const transactionType = isCrossChainTx
|
|
874
|
+
? TransactionType.bridge
|
|
875
|
+
: TransactionType.swap;
|
|
876
|
+
// Create actual transaction in Transaction Controller first
|
|
877
|
+
const networkClientId = this.messenger.call('NetworkController:findNetworkClientIdByChainId', formatChainIdToHex(chainId));
|
|
878
|
+
const intentTransactionParams = {
|
|
879
|
+
chainId: formatChainIdToHex(chainId),
|
|
880
|
+
from: accountAddress,
|
|
881
|
+
to: intent.settlementContract ??
|
|
882
|
+
'0x9008D19f58AAbd9eD0D60971565AA8510560ab41', // Default settlement contract
|
|
883
|
+
data: `0x${orderUid.slice(-8)}`, // Use last 8 chars of orderUid to make each transaction unique
|
|
884
|
+
value: '0x0',
|
|
885
|
+
gas: '0x5208', // Minimal gas for display purposes
|
|
886
|
+
gasPrice: '0x3b9aca00', // 1 Gwei - will be converted to EIP-1559 fees if network supports it
|
|
887
|
+
};
|
|
888
|
+
const { transactionMeta: txMetaPromise } = await __classPrivateFieldGet(this, _BridgeStatusController_addTransactionFn, "f").call(this, intentTransactionParams, {
|
|
889
|
+
origin: 'metamask',
|
|
890
|
+
actionId: generateActionId(),
|
|
891
|
+
requireApproval: false,
|
|
892
|
+
networkClientId,
|
|
893
|
+
type: transactionType,
|
|
894
|
+
skipInitialGasEstimate: true,
|
|
895
|
+
swaps: {
|
|
896
|
+
meta: {
|
|
897
|
+
// Add token symbols from quoteResponse for proper display
|
|
898
|
+
sourceTokenSymbol: quoteResponse.quote.srcAsset.symbol,
|
|
899
|
+
destinationTokenSymbol: quoteResponse.quote.destAsset.symbol,
|
|
900
|
+
sourceTokenAmount: quoteResponse.quote.srcTokenAmount,
|
|
901
|
+
destinationTokenAmount: quoteResponse.quote.destTokenAmount,
|
|
902
|
+
sourceTokenDecimals: quoteResponse.quote.srcAsset.decimals,
|
|
903
|
+
destinationTokenDecimals: quoteResponse.quote.destAsset.decimals,
|
|
904
|
+
sourceTokenAddress: quoteResponse.quote.srcAsset.address,
|
|
905
|
+
destinationTokenAddress: quoteResponse.quote.destAsset.address,
|
|
906
|
+
swapTokenValue: quoteResponse.sentAmount.amount,
|
|
907
|
+
approvalTxId,
|
|
908
|
+
swapMetaData: {
|
|
909
|
+
isIntentTx: true,
|
|
910
|
+
orderUid,
|
|
911
|
+
intentType: isCrossChainTx ? 'bridge' : 'swap',
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
const intentTxMeta = txMetaPromise;
|
|
917
|
+
// Map intent order status to TransactionController status
|
|
918
|
+
const initialTransactionStatus = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_mapIntentOrderStatusToTransactionStatus).call(this, intentOrder.status);
|
|
919
|
+
// Update transaction with proper initial status based on intent order
|
|
920
|
+
const statusUpdatedTxMeta = {
|
|
921
|
+
...intentTxMeta,
|
|
922
|
+
status: initialTransactionStatus,
|
|
923
|
+
};
|
|
924
|
+
// Update with actual transaction metadata
|
|
925
|
+
const syntheticMeta = {
|
|
926
|
+
...statusUpdatedTxMeta,
|
|
927
|
+
isIntentTx: true,
|
|
928
|
+
orderUid,
|
|
929
|
+
intentType: isCrossChainTx ? 'bridge' : 'swap',
|
|
930
|
+
};
|
|
931
|
+
// Record in bridge history with actual transaction metadata
|
|
932
|
+
try {
|
|
933
|
+
// Use 'intent:' prefix for intent transactions
|
|
934
|
+
const bridgeHistoryKey = `intent:${orderUid}`;
|
|
935
|
+
// Create a bridge transaction metadata that includes the original txId
|
|
936
|
+
const bridgeTxMetaForHistory = {
|
|
937
|
+
...syntheticMeta,
|
|
938
|
+
id: bridgeHistoryKey, // Use intent: prefix for bridge history key
|
|
939
|
+
originalTransactionId: syntheticMeta.id, // Keep original txId for TransactionController updates
|
|
940
|
+
};
|
|
941
|
+
__classPrivateFieldGet(this, _BridgeStatusController_addTxToHistory, "f").call(this, {
|
|
942
|
+
accountAddress,
|
|
943
|
+
bridgeTxMeta: bridgeTxMetaForHistory,
|
|
944
|
+
statusRequest: {
|
|
945
|
+
...getStatusRequestParams(quoteResponse),
|
|
946
|
+
srcTxHash: syntheticMeta.hash ?? '',
|
|
947
|
+
},
|
|
948
|
+
quoteResponse,
|
|
949
|
+
slippagePercentage: 0,
|
|
950
|
+
isStxEnabled: false,
|
|
951
|
+
approvalTxId,
|
|
952
|
+
});
|
|
953
|
+
// Start polling using the intent: prefixed key to route to intent manager
|
|
954
|
+
__classPrivateFieldGet(this, _BridgeStatusController_startPollingForTxId, "f").call(this, bridgeHistoryKey);
|
|
955
|
+
}
|
|
956
|
+
catch (error) {
|
|
957
|
+
console.error('📝 [submitIntent] Failed to add to bridge history', error);
|
|
958
|
+
// non-fatal but log the error
|
|
959
|
+
}
|
|
960
|
+
return syntheticMeta;
|
|
961
|
+
}
|
|
962
|
+
catch (error) {
|
|
963
|
+
__classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Failed, undefined, {
|
|
964
|
+
error_message: error?.message,
|
|
965
|
+
...preConfirmationProperties,
|
|
966
|
+
});
|
|
967
|
+
throw error;
|
|
968
|
+
}
|
|
969
|
+
};
|
|
737
970
|
/**
|
|
738
971
|
* Tracks post-submission events for a cross-chain swap based on the history item
|
|
739
972
|
*
|
|
@@ -759,13 +992,12 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
759
992
|
const requestParamProperties = getRequestParamFromHistory(historyItem);
|
|
760
993
|
// Always publish StatusValidationFailed event, regardless of featureId
|
|
761
994
|
if (eventName === UnifiedSwapBridgeEventName.StatusValidationFailed) {
|
|
762
|
-
const { chain_id_source, chain_id_destination, token_address_source, token_address_destination, } = requestParamProperties;
|
|
763
995
|
this.messenger.call('BridgeController:trackUnifiedSwapBridgeEvent', eventName, {
|
|
764
996
|
...baseProperties,
|
|
765
|
-
chain_id_source,
|
|
766
|
-
chain_id_destination,
|
|
767
|
-
token_address_source,
|
|
768
|
-
token_address_destination,
|
|
997
|
+
chain_id_source: requestParamProperties.chain_id_source,
|
|
998
|
+
chain_id_destination: requestParamProperties.chain_id_destination,
|
|
999
|
+
token_address_source: requestParamProperties.token_address_source,
|
|
1000
|
+
token_address_destination: requestParamProperties.token_address_destination,
|
|
769
1001
|
refresh_count: historyItem.attempts?.counter ?? 0,
|
|
770
1002
|
});
|
|
771
1003
|
return;
|
|
@@ -776,8 +1008,8 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
776
1008
|
}
|
|
777
1009
|
const selectedAccount = this.messenger.call('AccountsController:getAccountByAddress', historyItem.account);
|
|
778
1010
|
const { transactions } = this.messenger.call('TransactionController:getState');
|
|
779
|
-
const txMeta = transactions?.find((
|
|
780
|
-
const approvalTxMeta = transactions?.find((
|
|
1011
|
+
const txMeta = transactions?.find((tx) => tx.id === txMetaId);
|
|
1012
|
+
const approvalTxMeta = transactions?.find((tx) => tx.id === historyItem.approvalTxId);
|
|
781
1013
|
const requiredEventProperties = {
|
|
782
1014
|
...baseProperties,
|
|
783
1015
|
...requestParamProperties,
|
|
@@ -804,12 +1036,18 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
804
1036
|
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:wipeBridgeStatus`, this.wipeBridgeStatus.bind(this));
|
|
805
1037
|
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:resetState`, this.resetState.bind(this));
|
|
806
1038
|
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:submitTx`, this.submitTx.bind(this));
|
|
1039
|
+
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:submitIntent`, this.submitIntent.bind(this));
|
|
807
1040
|
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:restartPollingForFailedAttempts`, this.restartPollingForFailedAttempts.bind(this));
|
|
808
1041
|
this.messenger.registerActionHandler(`${BRIDGE_STATUS_CONTROLLER_NAME}:getBridgeHistoryItemByTxMetaId`, this.getBridgeHistoryItemByTxMetaId.bind(this));
|
|
809
1042
|
// Set interval
|
|
810
1043
|
this.setIntervalLength(REFRESH_INTERVAL_MS);
|
|
811
1044
|
this.messenger.subscribe('TransactionController:transactionFailed', ({ transactionMeta }) => {
|
|
812
1045
|
const { type, status, id } = transactionMeta;
|
|
1046
|
+
// Skip intent transactions - they have their own tracking via CoW API
|
|
1047
|
+
if (transactionMeta
|
|
1048
|
+
.swapMetaData?.isIntentTx) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
813
1051
|
if (type &&
|
|
814
1052
|
[
|
|
815
1053
|
TransactionType.bridge,
|
|
@@ -845,7 +1083,157 @@ export class BridgeStatusController extends StaticIntervalPollingController() {
|
|
|
845
1083
|
__classPrivateFieldGet(this, _BridgeStatusController_restartPollingForIncompleteHistoryItems, "f").call(this);
|
|
846
1084
|
}
|
|
847
1085
|
}
|
|
848
|
-
_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_handleNonEvmTx = 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(accountAddress) {
|
|
1086
|
+
_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_fetchIntentOrderStatus = new WeakMap(), _BridgeStatusController_getSrcTxHash = new WeakMap(), _BridgeStatusController_updateSrcTxHash = new WeakMap(), _BridgeStatusController_wipeBridgeStatusByChainId = new WeakMap(), _BridgeStatusController_handleNonEvmTx = 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(accountAddress) {
|
|
849
1087
|
return this.messenger.call('AccountsController:getAccountByAddress', accountAddress);
|
|
1088
|
+
}, _BridgeStatusController_updateBridgeHistoryFromIntentOrder = function _BridgeStatusController_updateBridgeHistoryFromIntentOrder(bridgeTxMetaId, intentOrder, historyItem) {
|
|
1089
|
+
const { srcChainId } = historyItem.quote;
|
|
1090
|
+
// Map intent order status to bridge status using enum values
|
|
1091
|
+
let statusType;
|
|
1092
|
+
const isComplete = [
|
|
1093
|
+
IntentOrderStatus.CONFIRMED,
|
|
1094
|
+
IntentOrderStatus.COMPLETED,
|
|
1095
|
+
].includes(intentOrder.status);
|
|
1096
|
+
const isFailed = [
|
|
1097
|
+
IntentOrderStatus.FAILED,
|
|
1098
|
+
IntentOrderStatus.EXPIRED,
|
|
1099
|
+
].includes(intentOrder.status);
|
|
1100
|
+
const isPending = [IntentOrderStatus.PENDING].includes(intentOrder.status);
|
|
1101
|
+
const isSubmitted = [IntentOrderStatus.SUBMITTED].includes(intentOrder.status);
|
|
1102
|
+
if (isComplete) {
|
|
1103
|
+
statusType = StatusTypes.COMPLETE;
|
|
1104
|
+
}
|
|
1105
|
+
else if (isFailed) {
|
|
1106
|
+
statusType = StatusTypes.FAILED;
|
|
1107
|
+
}
|
|
1108
|
+
else if (isPending) {
|
|
1109
|
+
statusType = StatusTypes.PENDING;
|
|
1110
|
+
}
|
|
1111
|
+
else if (isSubmitted) {
|
|
1112
|
+
statusType = StatusTypes.SUBMITTED;
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
statusType = StatusTypes.UNKNOWN;
|
|
1116
|
+
}
|
|
1117
|
+
// Extract transaction hashes from intent order
|
|
1118
|
+
const txHash = intentOrder.txHash ?? '';
|
|
1119
|
+
// Check metadata for additional transaction hashes
|
|
1120
|
+
const metadataTxHashes = Array.isArray(intentOrder.metadata.txHashes)
|
|
1121
|
+
? intentOrder.metadata.txHashes
|
|
1122
|
+
: [];
|
|
1123
|
+
let allHashes;
|
|
1124
|
+
if (metadataTxHashes.length > 0) {
|
|
1125
|
+
allHashes = metadataTxHashes;
|
|
1126
|
+
}
|
|
1127
|
+
else if (txHash) {
|
|
1128
|
+
allHashes = [txHash];
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
allHashes = [];
|
|
1132
|
+
}
|
|
1133
|
+
const newStatus = {
|
|
1134
|
+
status: statusType,
|
|
1135
|
+
srcChain: {
|
|
1136
|
+
chainId: srcChainId,
|
|
1137
|
+
txHash: txHash ?? historyItem.status.srcChain.txHash ?? '',
|
|
1138
|
+
},
|
|
1139
|
+
};
|
|
1140
|
+
const newBridgeHistoryItem = {
|
|
1141
|
+
...historyItem,
|
|
1142
|
+
status: newStatus,
|
|
1143
|
+
completionTime: newStatus.status === StatusTypes.COMPLETE ||
|
|
1144
|
+
newStatus.status === StatusTypes.FAILED
|
|
1145
|
+
? Date.now()
|
|
1146
|
+
: undefined,
|
|
1147
|
+
attempts: undefined,
|
|
1148
|
+
srcTxHashes: allHashes.length > 0
|
|
1149
|
+
? Array.from(new Set([...(historyItem.srcTxHashes ?? []), ...allHashes]))
|
|
1150
|
+
: historyItem.srcTxHashes,
|
|
1151
|
+
};
|
|
1152
|
+
this.update((state) => {
|
|
1153
|
+
state.txHistory[bridgeTxMetaId] = newBridgeHistoryItem;
|
|
1154
|
+
});
|
|
1155
|
+
// Update the actual transaction in TransactionController to sync with intent status
|
|
1156
|
+
// Use the original transaction ID (not the intent: prefixed bridge history key)
|
|
1157
|
+
const originalTxId = historyItem.originalTransactionId ?? historyItem.txMetaId;
|
|
1158
|
+
if (originalTxId && !originalTxId.startsWith('intent:')) {
|
|
1159
|
+
try {
|
|
1160
|
+
const transactionStatus = __classPrivateFieldGet(this, _BridgeStatusController_instances, "m", _BridgeStatusController_mapIntentOrderStatusToTransactionStatus).call(this, intentOrder.status);
|
|
1161
|
+
// Merge with existing TransactionMeta to avoid wiping required fields
|
|
1162
|
+
const { transactions } = this.messenger.call('TransactionController:getState');
|
|
1163
|
+
const existingTxMeta = transactions.find((tx) => tx.id === originalTxId);
|
|
1164
|
+
if (existingTxMeta) {
|
|
1165
|
+
const updatedTxMeta = {
|
|
1166
|
+
...existingTxMeta,
|
|
1167
|
+
status: transactionStatus,
|
|
1168
|
+
...(txHash ? { hash: txHash } : {}),
|
|
1169
|
+
...(txHash
|
|
1170
|
+
? {
|
|
1171
|
+
txReceipt: {
|
|
1172
|
+
...existingTxMeta.txReceipt,
|
|
1173
|
+
transactionHash: txHash,
|
|
1174
|
+
status: (isComplete ? '0x1' : '0x0'),
|
|
1175
|
+
},
|
|
1176
|
+
}
|
|
1177
|
+
: {}),
|
|
1178
|
+
};
|
|
1179
|
+
__classPrivateFieldGet(this, _BridgeStatusController_updateTransactionFn, "f").call(this, updatedTxMeta, `BridgeStatusController - Intent order status updated: ${intentOrder.status}`);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
console.warn('📝 [fetchIntentOrderStatus] Skipping update; transaction not found', { originalTxId, bridgeHistoryKey: bridgeTxMetaId });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
catch (error) {
|
|
1186
|
+
console.error('📝 [fetchIntentOrderStatus] Failed to update transaction status', {
|
|
1187
|
+
originalTxId,
|
|
1188
|
+
bridgeHistoryKey: bridgeTxMetaId,
|
|
1189
|
+
error,
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const pollingToken = __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
|
|
1194
|
+
const isFinal = newStatus.status === StatusTypes.COMPLETE ||
|
|
1195
|
+
newStatus.status === StatusTypes.FAILED;
|
|
1196
|
+
if (isFinal && pollingToken) {
|
|
1197
|
+
this.stopPollingByPollingToken(pollingToken);
|
|
1198
|
+
delete __classPrivateFieldGet(this, _BridgeStatusController_pollingTokensByTxMetaId, "f")[bridgeTxMetaId];
|
|
1199
|
+
if (newStatus.status === StatusTypes.COMPLETE) {
|
|
1200
|
+
__classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Completed, bridgeTxMetaId);
|
|
1201
|
+
}
|
|
1202
|
+
else if (newStatus.status === StatusTypes.FAILED) {
|
|
1203
|
+
__classPrivateFieldGet(this, _BridgeStatusController_trackUnifiedSwapBridgeEvent, "f").call(this, UnifiedSwapBridgeEventName.Failed, bridgeTxMetaId);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}, _BridgeStatusController_convertBridgeQuoteToIntentQuote = function _BridgeStatusController_convertBridgeQuoteToIntentQuote(quoteResponse, intent) {
|
|
1207
|
+
return {
|
|
1208
|
+
id: `bridge-${Date.now()}`,
|
|
1209
|
+
provider: intent.protocol,
|
|
1210
|
+
srcAmount: quoteResponse.quote.srcTokenAmount,
|
|
1211
|
+
destAmount: quoteResponse.quote.destTokenAmount,
|
|
1212
|
+
estimatedGas: '21000',
|
|
1213
|
+
estimatedTime: 300, // 5 minutes
|
|
1214
|
+
priceImpact: 0,
|
|
1215
|
+
fees: [],
|
|
1216
|
+
validUntil: Date.now() + 300000, // 5 minutes from now
|
|
1217
|
+
metadata: {
|
|
1218
|
+
order: intent.order,
|
|
1219
|
+
settlementContract: intent.settlementContract ?? '',
|
|
1220
|
+
chainId: quoteResponse.quote.srcChainId,
|
|
1221
|
+
bridgeQuote: quoteResponse,
|
|
1222
|
+
},
|
|
1223
|
+
};
|
|
1224
|
+
}, _BridgeStatusController_mapIntentOrderStatusToTransactionStatus = function _BridgeStatusController_mapIntentOrderStatusToTransactionStatus(intentStatus) {
|
|
1225
|
+
switch (intentStatus) {
|
|
1226
|
+
case IntentOrderStatus.PENDING:
|
|
1227
|
+
case IntentOrderStatus.SUBMITTED:
|
|
1228
|
+
return TransactionStatus.submitted;
|
|
1229
|
+
case IntentOrderStatus.CONFIRMED:
|
|
1230
|
+
case IntentOrderStatus.COMPLETED:
|
|
1231
|
+
return TransactionStatus.confirmed;
|
|
1232
|
+
case IntentOrderStatus.FAILED:
|
|
1233
|
+
case IntentOrderStatus.EXPIRED:
|
|
1234
|
+
return TransactionStatus.failed;
|
|
1235
|
+
default:
|
|
1236
|
+
return TransactionStatus.submitted;
|
|
1237
|
+
}
|
|
850
1238
|
};
|
|
851
1239
|
//# sourceMappingURL=bridge-status-controller.mjs.map
|