diff --git a/packages/bridge-status-controller/src/strategy/index.ts b/packages/bridge-status-controller/src/strategy/index.ts index e029905587..53500b9aba 100644 --- a/packages/bridge-status-controller/src/strategy/index.ts +++ b/packages/bridge-status-controller/src/strategy/index.ts @@ -6,7 +6,9 @@ import { isBitcoinTrade, isEvmTxData, isNonEvmChainId, + isStellarTrade, isTronTrade, + StellarTradeData, Trade, TronTradeData, TxData, @@ -20,7 +22,12 @@ import { submitNonEvmHandler } from './non-evm-strategy'; import type { SubmitStrategyParams, SubmitStepResult } from './types'; const validateParams = < - TxDataType extends BitcoinTradeData | TronTradeData | string | TxData, + TxDataType extends + | BitcoinTradeData + | StellarTradeData + | TronTradeData + | string + | TxData, >( params: SubmitStrategyParams, ): params is SubmitStrategyParams => { @@ -38,6 +45,8 @@ const validateParams = < return txs.every((tx) => typeof tx === 'string'); case ChainId.BTC: return txs.every(isBitcoinTrade); + case ChainId.STELLAR: + return txs.every((tx) => typeof tx === 'string' || isStellarTrade(tx)); case ChainId.TRON: return txs.every(isTronTrade); default: diff --git a/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts b/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts index 1c57c21bf6..29a22dcfd6 100644 --- a/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts +++ b/packages/bridge-status-controller/src/strategy/non-evm-strategy.ts @@ -2,6 +2,7 @@ import { isTronChainId } from '@metamask/bridge-controller'; import type { BitcoinTradeData, + StellarTradeData, TronTradeData, TxData, } from '@metamask/bridge-controller'; @@ -20,7 +21,7 @@ import type { SubmitStrategyParams, SubmitStepResult } from './types'; */ const handleTronApproval = async ( args: SubmitStrategyParams< - TronTradeData | BitcoinTradeData | string | TxData + TronTradeData | BitcoinTradeData | StellarTradeData | string | TxData >, ) => { const { @@ -65,7 +66,7 @@ const handleTronApproval = async ( */ export async function* submitNonEvmHandler( args: SubmitStrategyParams< - BitcoinTradeData | TronTradeData | string | TxData + BitcoinTradeData | StellarTradeData | TronTradeData | string | TxData >, ): AsyncGenerator { const { diff --git a/packages/bridge-status-controller/src/utils/snaps.ts b/packages/bridge-status-controller/src/utils/snaps.ts index 45fd83cd2a..58f0e2cb2d 100644 --- a/packages/bridge-status-controller/src/utils/snaps.ts +++ b/packages/bridge-status-controller/src/utils/snaps.ts @@ -19,7 +19,7 @@ import { TransactionType, } from '@metamask/transaction-controller'; import type { TransactionMeta } from '@metamask/transaction-controller'; -import type { CaipChainId, Hex } from '@metamask/utils'; +import type { CaipAssetType, CaipChainId, Hex } from '@metamask/utils'; import { v4 as uuid } from 'uuid'; import type { @@ -72,6 +72,8 @@ export const createClientTransactionRequest = ( * @param srcChainId - The source chain ID * @param accountId - The account ID * @param snapId - The snap ID + * @param sourceAssetId - The source asset ID + * @param destAssetId - The destination asset ID * @returns The snap request object for signing and sending transaction */ export const getClientRequest = ( @@ -79,18 +81,33 @@ export const getClientRequest = ( srcChainId: number, accountId: AccountsControllerState['internalAccounts']['accounts'][string]['id'], snapId: string, + sourceAssetId?: CaipAssetType, + destAssetId?: CaipAssetType, ): Parameters[0] => { const scope = formatChainIdToCaip(srcChainId); const transaction = extractTradeData(trade); - // Tron trades need the visible flag and contract type to be included in the request options - const options = isTronTrade(trade) - ? { - visible: trade.visible, - type: trade.raw_data?.contract?.[0]?.type, - } - : undefined; + let options: Record | undefined; + + if (sourceAssetId !== undefined || destAssetId !== undefined) { + options = { + ...(sourceAssetId !== undefined && { + sourceAssetId, + }), + ...(destAssetId !== undefined && { + destAssetId, + }), + }; + } + + if (isTronTrade(trade)) { + // Tron trades need the visible flag and contract type to be included in the request options + options = { + visible: trade.visible, + type: trade.raw_data?.contract?.[0]?.type, + }; + } return createClientTransactionRequest( snapId, @@ -250,6 +267,8 @@ export const handleNonEvmTx = async ( quoteResponse.quote.srcChainId, selectedAccount.id, selectedAccount.metadata?.snap?.id, + quoteResponse.quote.srcAsset.assetId, + quoteResponse.quote.destAsset.assetId, ); const requestResponse = (await messenger.call( 'SnapController:handleRequest', diff --git a/packages/bridge-status-controller/src/utils/transaction.test.ts b/packages/bridge-status-controller/src/utils/transaction.test.ts index 1f8688d614..446d78ccce 100644 --- a/packages/bridge-status-controller/src/utils/transaction.test.ts +++ b/packages/bridge-status-controller/src/utils/transaction.test.ts @@ -4,6 +4,7 @@ import { FeeType, formatChainIdToCaip, formatChainIdToHex, + getNativeAssetForChainId, } from '@metamask/bridge-controller'; import type { QuoteMetadata, @@ -1669,6 +1670,87 @@ describe('Bridge Status Controller Transaction Utils', () => { createClientRequestSpy.mockRestore(); }); + + it('should include Stellar source and destination asset IDs as options when trade is not Tron', () => { + const stellarTrade = { + xdrBase64: 'AAAABg==', + } as never; + + const mockAccount = { + id: 'test-account-id', + metadata: { + snap: { id: 'test-snap-id' }, + }, + }; + + const sourceAssetId = getNativeAssetForChainId(ChainId.STELLAR).assetId; + const destAssetId = getNativeAssetForChainId(ChainId.ETH).assetId; + + const result = snaps.getClientRequest( + stellarTrade, + ChainId.STELLAR, + mockAccount.id, + mockAccount.metadata.snap.id, + sourceAssetId, + destAssetId, + ); + + expect(result).toMatchObject({ + origin: 'metamask', + snapId: 'test-snap-id', + handler: 'onClientRequest', + request: { + id: expect.any(String), + jsonrpc: '2.0', + method: 'signAndSendTransaction', + params: { + transaction: 'AAAABg==', + scope: formatChainIdToCaip(ChainId.STELLAR), + accountId: 'test-account-id', + options: { + sourceAssetId, + destAssetId, + }, + }, + }, + }); + }); + + it('should omit destAssetId option for Stellar trades when destination asset ID is not provided', () => { + const stellarTrade = { + xdr: 'AAAABg==', + } as never; + + const mockAccount = { + id: 'test-account-id', + metadata: { + snap: { id: 'test-snap-id' }, + }, + }; + + const sourceAssetId = getNativeAssetForChainId(ChainId.STELLAR).assetId; + + const result = snaps.getClientRequest( + stellarTrade, + ChainId.STELLAR, + mockAccount.id, + mockAccount.metadata.snap.id, + sourceAssetId, + ); + + expect(result).toMatchObject({ + request: { + params: { + options: { + sourceAssetId, + }, + }, + }, + }); + expect( + (result.request.params as { options: Record }).options, + ).not.toHaveProperty('destAssetId'); + }); }); describe('getAddTransactionBatchParams', () => {