diff --git a/packages/bridge-controller/src/bridge-controller.ts b/packages/bridge-controller/src/bridge-controller.ts index 0d0ce7787be..8244e2de0b9 100644 --- a/packages/bridge-controller/src/bridge-controller.ts +++ b/packages/bridge-controller/src/bridge-controller.ts @@ -337,14 +337,17 @@ export class BridgeController extends StaticIntervalPollingController => { + // Use override if provided (ex: perps case), otherwise use from request (ex: card case) + const featureId = featureIdOverride ?? quoteRequest.featureId ?? null; + const bridgeFeatureFlags = getBridgeFeatureFlags(this.messenger); // If featureId is specified, retrieve the quoteRequestOverrides for that featureId const quoteRequestOverrides = featureId @@ -640,6 +643,7 @@ export class BridgeController extends StaticIntervalPollingController { // Set the initial load time if this is the first fetch diff --git a/packages/bridge-controller/src/types.ts b/packages/bridge-controller/src/types.ts index 49e2d03d93b..b8855fdf5ab 100644 --- a/packages/bridge-controller/src/types.ts +++ b/packages/bridge-controller/src/types.ts @@ -224,6 +224,10 @@ export type QuoteRequest< * The fee that will be charged by MetaMask */ fee?: number; + /** + * Optional feature ID for feature-specific quote overrides and transaction types + */ + featureId?: FeatureId; }; export enum StatusTypes { diff --git a/packages/bridge-controller/src/utils/validators.ts b/packages/bridge-controller/src/utils/validators.ts index ac6577aa45e..5cf7b18d327 100644 --- a/packages/bridge-controller/src/utils/validators.ts +++ b/packages/bridge-controller/src/utils/validators.ts @@ -31,6 +31,8 @@ export enum FeeType { export enum FeatureId { PERPS = 'perps', + PREDICT = 'predict', + CARD = 'card', } export enum ActionTypes { diff --git a/packages/bridge-status-controller/src/bridge-status-controller.ts b/packages/bridge-status-controller/src/bridge-status-controller.ts index 7383d6d963e..aaf9727076e 100644 --- a/packages/bridge-status-controller/src/bridge-status-controller.ts +++ b/packages/bridge-status-controller/src/bridge-status-controller.ts @@ -21,6 +21,7 @@ import { isBitcoinTrade, isTronTrade, AbortReason, + FeatureId, } from '@metamask/bridge-controller'; import type { TraceCallback } from '@metamask/controller-utils'; import { toHex } from '@metamask/controller-utils'; @@ -77,10 +78,12 @@ import { import { findAndUpdateTransactionsInBatch, getAddTransactionBatchParams, + getApprovalTransactionType, getClientRequest, getHistoryKey, getIntentFromQuote, getStatusRequestParams, + getTransactionType, handleApprovalDelay, handleMobileHardwareWalletDelay, handleNonEvmTxResponse, @@ -220,6 +223,7 @@ export class BridgeStatusController extends StaticIntervalPollingController => { if (approval) { const approveTx = async (): Promise => { - await this.#handleUSDTAllowanceReset(resetApproval); + await this.#handleUSDTAllowanceReset( + resetApproval, + isBridgeTx, + featureId, + ); const approvalTxMeta = await this.#handleEvmTransaction({ - transactionType: isBridgeTx - ? TransactionType.bridgeApproval - : TransactionType.swapApproval, + transactionType: getApprovalTransactionType(isBridgeTx, featureId), trade: approval, requireApproval, }); @@ -1316,10 +1323,15 @@ export class BridgeStatusController extends StaticIntervalPollingController => { if (resetApproval) { await this.#handleEvmTransaction({ - transactionType: TransactionType.bridgeApproval, + transactionType: getApprovalTransactionType( + isBridgeTx ?? true, + featureId, + ), trade: resetApproval, }); } @@ -1615,6 +1627,7 @@ export class BridgeStatusController extends StaticIntervalPollingController (Date.now() + Math.random()).toString(); +/** + * Gets the appropriate transaction type based on whether it's a bridge transaction + * and the optional feature ID. + * + * @param isBridgeTx - Whether the transaction is a cross-chain bridge transaction + * @param featureId - Optional feature ID for feature-specific transaction types + * @returns The transaction type + */ +export const getBridgeTransactionType = ( + isBridgeTx: boolean, + featureId?: FeatureId, +): TransactionType => { + switch (featureId) { + case FeatureId.CARD: + return isBridgeTx + ? TransactionType.cardBridgeDeposit + : TransactionType.cardSwapDeposit; + // For now this handles the perps case for example but we could have more cases for it as well. + default: + return isBridgeTx ? TransactionType.bridge : TransactionType.swap; + } +}; + +/** + * Gets the appropriate approval transaction type based on whether it's a bridge transaction + * and the optional feature ID. + * + * @param isBridgeTx - Whether the transaction is a cross-chain bridge transaction + * @param featureId - Optional feature ID for feature-specific transaction types + * @returns The approval transaction type + */ +export const getBridgeApprovalTransactionType = ( + isBridgeTx: boolean, + featureId?: FeatureId, +): TransactionType => { + switch (featureId) { + case FeatureId.CARD: + return isBridgeTx + ? TransactionType.cardBridgeApproval + : TransactionType.cardSwapApproval; + default: + return isBridgeTx + ? TransactionType.bridgeApproval + : TransactionType.swapApproval; + } +}; + export const getStatusRequestParams = (quoteResponse: QuoteResponse) => { return { bridgeId: quoteResponse.quote.bridgeId, @@ -159,7 +207,7 @@ export const handleNonEvmTxResponse = ( chainId: hexChainId, networkClientId: snapId ?? hexChainId, txParams: { from: selectedAccountAddress, data: tradeData }, - type: isBridgeTx ? TransactionType.bridge : TransactionType.swap, + type: getBridgeTransactionType(isBridgeTx, quoteResponse.featureId), status: TransactionStatus.submitted, hash, // Add the transaction signature as hash origin: snapId, @@ -280,6 +328,7 @@ export const getAddTransactionBatchParams = async ({ }, sentAmount, toTokenAmount, + featureId, }, requireApproval = false, estimateGasFeeFn, @@ -325,9 +374,7 @@ export const getAddTransactionBatchParams = async ({ isGasless ? txFee : undefined, ); transactions.push({ - type: isBridgeTx - ? TransactionType.bridgeApproval - : TransactionType.swapApproval, + type: getBridgeApprovalTransactionType(isBridgeTx, featureId), params: toBatchTxParams(disable7702, resetApproval, gasFees), }); } @@ -342,9 +389,7 @@ export const getAddTransactionBatchParams = async ({ isGasless ? txFee : undefined, ); transactions.push({ - type: isBridgeTx - ? TransactionType.bridgeApproval - : TransactionType.swapApproval, + type: getBridgeApprovalTransactionType(isBridgeTx, featureId), params: toBatchTxParams(disable7702, approval, gasFees), }); } @@ -358,7 +403,7 @@ export const getAddTransactionBatchParams = async ({ isGasless ? txFee : undefined, ); transactions.push({ - type: isBridgeTx ? TransactionType.bridge : TransactionType.swap, + type: getBridgeTransactionType(isBridgeTx, featureId), params: toBatchTxParams(disable7702, trade, gasFees), assetsFiatValues: { sending: sentAmount?.valueInCurrency?.toString(), diff --git a/packages/transaction-controller/src/types.ts b/packages/transaction-controller/src/types.ts index 87e3dceef95..457dc9a912e 100644 --- a/packages/transaction-controller/src/types.ts +++ b/packages/transaction-controller/src/types.ts @@ -728,6 +728,26 @@ export enum TransactionType { */ cancel = 'cancel', + /** + * A bridge deposit transaction initiated via Card. + */ + cardBridgeDeposit = 'cardBridgeDeposit', + + /** + * An approval transaction for a bridge initiated via Card. + */ + cardBridgeApproval = 'cardBridgeApproval', + + /** + * A swap deposit transaction initiated via Card. + */ + cardSwapDeposit = 'cardSwapDeposit', + + /** + * An approval transaction for a swap initiated via Card. + */ + cardSwapApproval = 'cardSwapApproval', + /** * A transaction that is interacting with a smart contract's methods that we * have not treated as a special case, such as approve, transfer, and