diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index d599896821b..79e32955fa2 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- API call for quotes should not be done unless user has requested non-zero token amount. ([#8554](https://github.com/MetaMask/core/pull/8554)) + ## [19.3.0] ### Added diff --git a/packages/transaction-pay-controller/src/utils/quotes.test.ts b/packages/transaction-pay-controller/src/utils/quotes.test.ts index def00a8b1a5..07229ff7f63 100644 --- a/packages/transaction-pay-controller/src/utils/quotes.test.ts +++ b/packages/transaction-pay-controller/src/utils/quotes.test.ts @@ -1,6 +1,8 @@ import { TransactionStatus } from '@metamask/transaction-controller'; -import type { TransactionMeta } from '@metamask/transaction-controller'; -import type { BatchTransaction } from '@metamask/transaction-controller'; +import type { + TransactionMeta, + BatchTransaction, +} from '@metamask/transaction-controller'; import type { Hex, Json } from '@metamask/utils'; import { cloneDeep } from 'lodash'; @@ -51,7 +53,12 @@ const TRANSACTION_DATA_MOCK: TransactionData = { sourceAmountRaw: '1000000000000000000', } as TransactionPaySourceAmount, ], - tokens: [{} as TransactionPayRequiredToken], + tokens: [ + { + amountRaw: '1000000', + skipIfBalance: false, + } as TransactionPayRequiredToken, + ], }; const TRANSACTION_META_MOCK = { @@ -619,6 +626,66 @@ describe('Quotes Utils', () => { expect(result).toBe(false); }); + it('does not request quotes if all required token amounts are zero', async () => { + const result = await run({ + transactionData: { + ...TRANSACTION_DATA_MOCK, + tokens: [ + { + ...TRANSACTION_DATA_MOCK.tokens[0], + amountRaw: '0', + skipIfBalance: false, + } as TransactionPayRequiredToken, + ], + }, + }); + + expect(updateTransactionDataMock).not.toHaveBeenCalled(); + expect(getQuotesMock).not.toHaveBeenCalled(); + expect(result).toBe(false); + }); + + it('requests quotes if a required token has a non-zero amount', async () => { + await run({ + transactionData: { + ...TRANSACTION_DATA_MOCK, + tokens: [ + { + ...TRANSACTION_DATA_MOCK.tokens[0], + amountRaw: '0', + skipIfBalance: false, + } as TransactionPayRequiredToken, + { + ...TRANSACTION_DATA_MOCK.tokens[0], + amountRaw: '1000', + skipIfBalance: false, + } as TransactionPayRequiredToken, + ], + }, + }); + + expect(updateTransactionDataMock).toHaveBeenCalled(); + }); + + it('requests quotes if all required token amounts are zero but isMaxAmount is true', async () => { + await run({ + transactionData: { + ...TRANSACTION_DATA_MOCK, + isMaxAmount: true, + tokens: [ + { + ...TRANSACTION_DATA_MOCK.tokens[0], + amountRaw: '0', + skipIfBalance: false, + } as TransactionPayRequiredToken, + ], + }, + }); + + expect(updateTransactionDataMock).toHaveBeenCalled(); + expect(getQuotesMock).toHaveBeenCalled(); + }); + it('refreshes payment token balance with live on-chain balance after quotes update', async () => { getLiveTokenBalanceMock.mockResolvedValue('9000000'); diff --git a/packages/transaction-pay-controller/src/utils/quotes.ts b/packages/transaction-pay-controller/src/utils/quotes.ts index a5448ba0558..269b5ff7e0f 100644 --- a/packages/transaction-pay-controller/src/utils/quotes.ts +++ b/packages/transaction-pay-controller/src/utils/quotes.ts @@ -65,8 +65,6 @@ export async function updateQuotes( return false; } - log('Updating quotes', { transactionId }); - const { accountOverride, isMaxAmount, @@ -78,6 +76,18 @@ export async function updateQuotes( tokens, } = transactionData; + const hasNonZeroTokenAmount = + !tokens?.length || + isMaxAmount === true || + tokens.some((token) => !token.skipIfBalance && token.amountRaw !== '0'); + + if (!hasNonZeroTokenAmount) { + log('Skipping quotes, all token amounts are zero', { transactionId }); + return false; + } + + log('Updating quotes', { transactionId }); + const from = accountOverride ?? (transaction.txParams.from as Hex); updateTransactionData(transactionId, (data) => {