From f534343b2b44cf54cec648380d7f9615feb5967e Mon Sep 17 00:00:00 2001 From: Goktug Poyraz Date: Fri, 31 Jul 2026 13:14:10 +0300 Subject: [PATCH 1/2] feat(transaction-controller): add strategy field to MetamaskPayMetadata Co-authored-by: Sisyphus --- packages/transaction-controller/CHANGELOG.md | 1 + packages/transaction-controller/src/types.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/packages/transaction-controller/CHANGELOG.md b/packages/transaction-controller/CHANGELOG.md index 40c49b64977..16cc879204c 100644 --- a/packages/transaction-controller/CHANGELOG.md +++ b/packages/transaction-controller/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Export `getEffectiveRecipient` utility that returns the actual recipient of a transaction, decoding it from calldata for ERC-20/ERC-721/ERC-1155 token transfer methods where `txParams.to` is the token contract ([#9699](https://github.com/MetaMask/core/pull/9699)) +- Add optional `strategy` field to `MetamaskPayMetadata` to persist the MetaMask Pay strategy used to fund the transaction ([#9733](https://github.com/MetaMask/core/pull/9733)) ## [69.3.0] diff --git a/packages/transaction-controller/src/types.ts b/packages/transaction-controller/src/types.ts index 5b5315297da..96cd15dd08c 100644 --- a/packages/transaction-controller/src/types.ts +++ b/packages/transaction-controller/src/types.ts @@ -2176,6 +2176,9 @@ export type MetamaskPayMetadata = { /** Source chain transaction hash if no local transaction. */ sourceHash?: Hex; + /** Pay strategy used to fund the transaction (e.g. "relay", "fiat"). */ + strategy?: string; + /** Total amount of target token provided in fiat currency. */ targetFiat?: string; From 71afa7ea50e3681c8830a4ea75b8344fe28f6610 Mon Sep 17 00:00:00 2001 From: Goktug Poyraz Date: Fri, 31 Jul 2026 13:14:10 +0300 Subject: [PATCH 2/2] feat(transaction-pay-controller): persist quote strategy in metamaskPay metadata Co-authored-by: Sisyphus --- packages/transaction-pay-controller/CHANGELOG.md | 1 + .../src/utils/quotes.test.ts | 15 +++++++++++++++ .../src/utils/quotes.ts | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/packages/transaction-pay-controller/CHANGELOG.md b/packages/transaction-pay-controller/CHANGELOG.md index 9d9fa75da16..551d29e8f7f 100644 --- a/packages/transaction-pay-controller/CHANGELOG.md +++ b/packages/transaction-pay-controller/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add `atomic` field on `TransactionConfig` / `TransactionData` for a generic non-atomic post-Relay flow: when `atomic` is `false`, Relay bridges to an internally derived recipient (`getPaymentOverrideData` recipient for post-quote flows, otherwise the transaction's own `from`) and the second leg is submitted separately after completion, replacing the removed `relay-post-ma-vault` module ([#9497](https://github.com/MetaMask/core/pull/9497)) +- Persist the quote strategy in the transaction's `metamaskPay.strategy` metadata so clients can derive pay metrics after `transactionData` is gone (e.g. after a restart) ([#9733](https://github.com/MetaMask/core/pull/9733)) ### Changed diff --git a/packages/transaction-pay-controller/src/utils/quotes.test.ts b/packages/transaction-pay-controller/src/utils/quotes.test.ts index da84b82a549..cc2a96f5f4e 100644 --- a/packages/transaction-pay-controller/src/utils/quotes.test.ts +++ b/packages/transaction-pay-controller/src/utils/quotes.test.ts @@ -908,6 +908,7 @@ describe('Quotes Utils', () => { bridgeFeeFiat: TOTALS_MOCK.fees.provider.usd, chainId: TRANSACTION_DATA_MOCK.paymentToken?.chainId, networkFeeFiat: TOTALS_MOCK.fees.sourceNetwork.estimate.usd, + strategy: TransactionPayStrategy.Across, targetFiat: TOTALS_MOCK.targetAmount.usd, tokenAddress: TRANSACTION_DATA_MOCK.paymentToken?.address, totalFiat: TOTALS_MOCK.total.usd, @@ -915,6 +916,19 @@ describe('Quotes Utils', () => { }); }); + it('does not persist strategy in metadata when there are no executable quotes', async () => { + getQuotesMock.mockResolvedValue([ + { ...QUOTE_MOCK, strategy: TransactionPayStrategy.None }, + ]); + + await run(); + + const transactionMetaMock = {} as TransactionMeta; + updateTransactionMock.mock.calls[0][1](transactionMetaMock); + + expect(transactionMetaMock.metamaskPay?.strategy).toBeUndefined(); + }); + it('updates metrics in metadata for fiat payment with no payment token', async () => { await run({ transactionData: { @@ -932,6 +946,7 @@ describe('Quotes Utils', () => { bridgeFeeFiat: TOTALS_MOCK.fees.provider.usd, chainId: undefined, networkFeeFiat: TOTALS_MOCK.fees.sourceNetwork.estimate.usd, + strategy: TransactionPayStrategy.Across, targetFiat: TOTALS_MOCK.targetAmount.usd, tokenAddress: undefined, totalFiat: TOTALS_MOCK.total.usd, diff --git a/packages/transaction-pay-controller/src/utils/quotes.ts b/packages/transaction-pay-controller/src/utils/quotes.ts index c6dcd8488e9..b953bd9139e 100644 --- a/packages/transaction-pay-controller/src/utils/quotes.ts +++ b/packages/transaction-pay-controller/src/utils/quotes.ts @@ -184,6 +184,7 @@ export async function updateQuotes( isPostQuote, messenger: messenger as never, paymentToken, + strategy: executableQuotes[0]?.strategy, totals, transactionId, }); @@ -222,6 +223,7 @@ export async function updateQuotes( * @param request.messenger - Messenger instance. * @param request.paymentToken - Payment token (source for standard flows, destination for post-quote). * @param request.selectedFiatPayment - Selected fiat payment method ID. + * @param request.strategy - Strategy of the executable quotes. * @param request.totals - Calculated totals. * @param request.transactionId - ID of the transaction to sync. */ @@ -232,6 +234,7 @@ function syncTransaction({ messenger, paymentToken, selectedFiatPayment, + strategy, totals, transactionId, }: { @@ -241,6 +244,7 @@ function syncTransaction({ isPostQuote?: boolean; messenger: TransactionPayControllerMessenger; paymentToken: TransactionPaymentToken | undefined; + strategy?: TransactionPayStrategy; totals: TransactionPayTotals; transactionId: string; }): void { @@ -278,6 +282,7 @@ function syncTransaction({ chainId: paymentToken?.chainId, isPostQuote, networkFeeFiat: totals.fees.sourceNetwork.estimate.usd, + strategy, targetFiat: totals.targetAmount.usd, tokenAddress: paymentToken?.address, totalFiat: totals.total.usd,