diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index 9d5911e9fb1..23d8e687e4a 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Ignore synthetic gas legs when determining Across support for perps direct deposits ([#8527](https://github.com/MetaMask/core/pull/8527)) - Route Across status polling through the configured Across API base and support `depositTxnRef`/`fillTxnRef` for Across status responses ([#8512](https://github.com/MetaMask/core/pull/8512)) ## [19.2.1] diff --git a/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.test.ts b/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.test.ts index e3620d83ad3..8d60ba13d94 100644 --- a/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.test.ts +++ b/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.test.ts @@ -117,6 +117,86 @@ describe('AcrossStrategy', () => { ).toBe(true); }); + it('ignores synthetic gas legs for supported perps direct deposits', () => { + const strategy = new AcrossStrategy(); + expect( + strategy.supports({ + ...baseRequest, + transaction: { + ...TRANSACTION_META_MOCK, + type: TransactionType.perpsDeposit, + } as TransactionMeta, + requests: [ + { + from: '0xabc' as Hex, + sourceBalanceRaw: '100', + sourceChainId: CHAIN_ID_ARBITRUM, + sourceTokenAddress: ARBITRUM_USDC_ADDRESS, + sourceTokenAmount: '100', + targetAmountMinimum: '100', + targetChainId: CHAIN_ID_ARBITRUM, + targetTokenAddress: ARBITRUM_USDC_ADDRESS, + }, + { + from: '0xabc' as Hex, + sourceBalanceRaw: '100', + sourceChainId: CHAIN_ID_ARBITRUM, + sourceTokenAddress: ARBITRUM_USDC_ADDRESS, + sourceTokenAmount: '100', + targetAmountMinimum: '0', + targetChainId: CHAIN_ID_ARBITRUM, + targetTokenAddress: + '0x0000000000000000000000000000000000000000' as Hex, + }, + ], + }), + ).toBe(true); + }); + + it('returns false when all requests are synthetic zero-minimum legs', () => { + const strategy = new AcrossStrategy(); + expect( + strategy.supports({ + ...baseRequest, + requests: [ + { + from: '0xabc' as Hex, + sourceBalanceRaw: '100', + sourceChainId: CHAIN_ID_ARBITRUM, + sourceTokenAddress: ARBITRUM_USDC_ADDRESS, + sourceTokenAmount: '100', + targetAmountMinimum: '0', + targetChainId: CHAIN_ID_ARBITRUM, + targetTokenAddress: + '0x0000000000000000000000000000000000000000' as Hex, + }, + ], + }), + ).toBe(false); + }); + + it('treats max-amount requests as actionable even with zero minimums', () => { + const strategy = new AcrossStrategy(); + expect( + strategy.supports({ + ...baseRequest, + requests: [ + { + from: '0xabc' as Hex, + isMaxAmount: true, + sourceBalanceRaw: '100', + sourceChainId: '0x1' as Hex, + sourceTokenAddress: '0xabc' as Hex, + sourceTokenAmount: '100', + targetAmountMinimum: '0', + targetChainId: '0x2' as Hex, + targetTokenAddress: '0xdef' as Hex, + }, + ], + }), + ).toBe(true); + }); + it('returns false for unsupported perps deposits', () => { const strategy = new AcrossStrategy(); expect( diff --git a/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.ts b/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.ts index 5643e58aff1..09d9269b9d5 100644 --- a/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.ts +++ b/packages/transaction-pay-controller/src/strategy/across/AcrossStrategy.ts @@ -10,6 +10,7 @@ import { getPayStrategiesConfig } from '../../utils/feature-flags'; import { getAcrossQuotes } from './across-quotes'; import { submitAcrossQuotes } from './across-submit'; import { isSupportedAcrossPerpsDepositRequest } from './perps'; +import { isAcrossQuoteRequest } from './requests'; import type { AcrossQuote } from './types'; export class AcrossStrategy implements PayStrategy { @@ -20,8 +21,14 @@ export class AcrossStrategy implements PayStrategy { return false; } + const actionableRequests = request.requests.filter(isAcrossQuoteRequest); + + if (actionableRequests.length === 0) { + return false; + } + if (request.transaction?.type === TransactionType.perpsDeposit) { - return request.requests.every((singleRequest) => + return actionableRequests.every((singleRequest) => isSupportedAcrossPerpsDepositRequest( singleRequest, request.transaction?.type, @@ -30,7 +37,7 @@ export class AcrossStrategy implements PayStrategy { } // Across doesn't support same-chain swaps (e.g. mUSD conversions). - return request.requests.every( + return actionableRequests.every( (singleRequest) => singleRequest.sourceChainId !== singleRequest.targetChainId, ); diff --git a/packages/transaction-pay-controller/src/strategy/across/across-quotes.ts b/packages/transaction-pay-controller/src/strategy/across/across-quotes.ts index ee84439f0f2..b612c92aa0c 100644 --- a/packages/transaction-pay-controller/src/strategy/across/across-quotes.ts +++ b/packages/transaction-pay-controller/src/strategy/across/across-quotes.ts @@ -20,6 +20,7 @@ import { estimateQuoteGasLimits } from '../../utils/quote-gas'; import { getTokenFiatRate } from '../../utils/token'; import { getAcrossDestination } from './across-actions'; import { normalizeAcrossRequest } from './perps'; +import { isAcrossQuoteRequest } from './requests'; import { getAcrossOrderedTransactions } from './transactions'; import type { AcrossAction, @@ -50,12 +51,7 @@ export async function getAcrossQuotes( log('Fetching quotes', requests); try { - const normalizedRequests = requests.filter( - (singleRequest) => - singleRequest.isMaxAmount === true || - (singleRequest.targetAmountMinimum !== undefined && - singleRequest.targetAmountMinimum !== '0'), - ); + const normalizedRequests = requests.filter(isAcrossQuoteRequest); if (normalizedRequests.length === 0) { return []; diff --git a/packages/transaction-pay-controller/src/strategy/across/requests.ts b/packages/transaction-pay-controller/src/strategy/across/requests.ts new file mode 100644 index 00000000000..77b967af562 --- /dev/null +++ b/packages/transaction-pay-controller/src/strategy/across/requests.ts @@ -0,0 +1,9 @@ +import type { QuoteRequest } from '../../types'; + +export function isAcrossQuoteRequest(request: QuoteRequest): boolean { + return ( + request.isMaxAmount === true || + (request.targetAmountMinimum !== undefined && + request.targetAmountMinimum !== '0') + ); +}