Skip to content

fix(transaction-pay-controller): stop adding subsidized fee to target amount#8488

Merged
matthewwalsh0 merged 3 commits intomainfrom
fix/remove-relay-subsidized-fee-double-count
Apr 17, 2026
Merged

fix(transaction-pay-controller): stop adding subsidized fee to target amount#8488
matthewwalsh0 merged 3 commits intomainfrom
fix/remove-relay-subsidized-fee-double-count

Conversation

@matthewwalsh0
Copy link
Copy Markdown
Member

@matthewwalsh0 matthewwalsh0 commented Apr 16, 2026

Explanation

Relay now returns currencyOut as the full amount the user receives — subsidized fees are no longer deducted from the output. This was verified across all trade types (EXACT_INPUT, EXPECTED_OUTPUT), protocol versions (v2 — v1 is effectively dead), and execute/non-execute modes.

Our normalizeQuote code was adding the subsidized fee USD amount back to the target amount for EXACT_INPUT trades, which now double-counts the fee. For example, on a 0.032 USDC → mUSD swap with ~$2.10 in subsidized fees, the target amount was being inflated to ~$2.13 instead of the correct ~$0.032.

Changes

  • Remove additionalTargetAmountUsd logic from normalizeQuote in relay-quotes.ts
  • Remove two tests that validated the old fee-addition behavior
  • Consolidate remaining subsidized fee test into a single "does not add subsidized fee to target amount" test
  • subsidizedFeeUsd is still used for zeroing the provider fee when fees are subsidized (unchanged)

References

Verified via live API calls to https://intents.dev-api.cx.metamask.io/relay/quote:

Config currencyOut.amount subsidized.amountUsd Protocol
EXACT_INPUT + overhead 32507 ~2.15 v2
EXACT_INPUT, no overhead 32507 ~0.035 v2
EXPECTED_OUTPUT + overhead 32507 ~2.17 v2
EXPECTED_OUTPUT, no overhead 32507 ~0.035 v2
protocolVersion=v1 (any) 32507 varies v2 (v1 ignored)

Note

Medium Risk
Changes how Relay targetAmount fiat/USD values are computed, which can affect user-visible quote/totals for max-amount flows. Risk is limited to quote presentation/math and is covered by updated unit tests.

Overview
Prevents Relay quote normalization (relay-quotes.ts) from adding subsidized fee USD back into the target amount, avoiding double-counting now that Relay returns currencyOut as the full user-received amount.

Updates Relay quote tests to assert subsidized fees do not change targetAmount (including stablecoin-fee cases) and records the fix in the CHANGELOG.

Reviewed by Cursor Bugbot for commit 7a08f64. Bugbot is set up for automated code reviews on this repo. Configure here.

@matthewwalsh0 matthewwalsh0 marked this pull request as ready for review April 16, 2026 15:55
@matthewwalsh0 matthewwalsh0 requested review from a team as code owners April 16, 2026 15:55
@matthewwalsh0
Copy link
Copy Markdown
Member Author

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.1.0-preview-b284401cb
@metamask-previews/accounts-controller@37.2.0-preview-b284401cb
@metamask-previews/address-book-controller@7.1.1-preview-b284401cb
@metamask-previews/ai-controllers@0.6.3-preview-b284401cb
@metamask-previews/analytics-controller@1.0.1-preview-b284401cb
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-b284401cb
@metamask-previews/announcement-controller@8.1.0-preview-b284401cb
@metamask-previews/app-metadata-controller@2.0.1-preview-b284401cb
@metamask-previews/approval-controller@9.0.1-preview-b284401cb
@metamask-previews/assets-controller@6.0.0-preview-b284401cb
@metamask-previews/assets-controllers@104.0.0-preview-b284401cb
@metamask-previews/base-controller@9.1.0-preview-b284401cb
@metamask-previews/base-data-service@0.1.1-preview-b284401cb
@metamask-previews/bridge-controller@70.1.1-preview-b284401cb
@metamask-previews/bridge-status-controller@70.0.5-preview-b284401cb
@metamask-previews/build-utils@3.0.4-preview-b284401cb
@metamask-previews/chain-agnostic-permission@1.5.0-preview-b284401cb
@metamask-previews/claims-controller@0.5.0-preview-b284401cb
@metamask-previews/client-controller@1.0.1-preview-b284401cb
@metamask-previews/compliance-controller@2.0.0-preview-b284401cb
@metamask-previews/composable-controller@12.0.1-preview-b284401cb
@metamask-previews/config-registry-controller@0.2.0-preview-b284401cb
@metamask-previews/connectivity-controller@0.2.0-preview-b284401cb
@metamask-previews/controller-utils@11.20.0-preview-b284401cb
@metamask-previews/core-backend@6.2.1-preview-b284401cb
@metamask-previews/delegation-controller@3.0.0-preview-b284401cb
@metamask-previews/earn-controller@12.0.0-preview-b284401cb
@metamask-previews/eip-5792-middleware@3.0.3-preview-b284401cb
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.0-preview-b284401cb
@metamask-previews/eip1193-permission-middleware@1.0.3-preview-b284401cb
@metamask-previews/ens-controller@19.1.1-preview-b284401cb
@metamask-previews/eth-block-tracker@15.0.1-preview-b284401cb
@metamask-previews/eth-json-rpc-middleware@23.1.1-preview-b284401cb
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-b284401cb
@metamask-previews/foundryup@1.0.1-preview-b284401cb
@metamask-previews/gas-fee-controller@26.1.1-preview-b284401cb
@metamask-previews/gator-permissions-controller@4.0.0-preview-b284401cb
@metamask-previews/geolocation-controller@0.1.2-preview-b284401cb
@metamask-previews/json-rpc-engine@10.2.4-preview-b284401cb
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-b284401cb
@metamask-previews/keyring-controller@25.2.0-preview-b284401cb
@metamask-previews/logging-controller@8.0.1-preview-b284401cb
@metamask-previews/message-manager@14.1.1-preview-b284401cb
@metamask-previews/messenger@1.1.1-preview-b284401cb
@metamask-previews/messenger-cli@0.1.0-preview-b284401cb
@metamask-previews/money-account-balance-service@0.1.0-preview-b284401cb
@metamask-previews/money-account-controller@0.1.0-preview-b284401cb
@metamask-previews/multichain-account-service@8.0.1-preview-b284401cb
@metamask-previews/multichain-api-middleware@2.0.0-preview-b284401cb
@metamask-previews/multichain-network-controller@3.0.6-preview-b284401cb
@metamask-previews/multichain-transactions-controller@7.0.4-preview-b284401cb
@metamask-previews/name-controller@9.1.1-preview-b284401cb
@metamask-previews/network-controller@30.0.1-preview-b284401cb
@metamask-previews/network-enablement-controller@5.0.2-preview-b284401cb
@metamask-previews/notification-services-controller@23.1.0-preview-b284401cb
@metamask-previews/permission-controller@12.3.0-preview-b284401cb
@metamask-previews/permission-log-controller@5.1.0-preview-b284401cb
@metamask-previews/perps-controller@3.1.1-preview-b284401cb
@metamask-previews/phishing-controller@17.1.1-preview-b284401cb
@metamask-previews/polling-controller@16.0.4-preview-b284401cb
@metamask-previews/preferences-controller@23.1.0-preview-b284401cb
@metamask-previews/profile-metrics-controller@3.1.3-preview-b284401cb
@metamask-previews/profile-sync-controller@28.0.2-preview-b284401cb
@metamask-previews/ramps-controller@13.2.0-preview-b284401cb
@metamask-previews/rate-limit-controller@7.0.1-preview-b284401cb
@metamask-previews/react-data-query@0.2.0-preview-b284401cb
@metamask-previews/remote-feature-flag-controller@4.2.0-preview-b284401cb
@metamask-previews/sample-controllers@4.0.4-preview-b284401cb
@metamask-previews/seedless-onboarding-controller@9.1.0-preview-b284401cb
@metamask-previews/selected-network-controller@26.1.0-preview-b284401cb
@metamask-previews/shield-controller@5.1.1-preview-b284401cb
@metamask-previews/signature-controller@39.2.0-preview-b284401cb
@metamask-previews/social-controllers@0.2.0-preview-b284401cb
@metamask-previews/storage-service@1.0.1-preview-b284401cb
@metamask-previews/subscription-controller@6.1.2-preview-b284401cb
@metamask-previews/transaction-controller@64.3.0-preview-b284401cb
@metamask-previews/transaction-pay-controller@19.2.0-preview-b284401cb
@metamask-previews/user-operation-controller@41.2.0-preview-b284401cb

… amount

Relay now returns currencyOut as the full amount the user receives,
with subsidized fees no longer deducted from the output. Our code was
adding the subsidized fee back to the target amount for EXACT_INPUT
trade types, which now double-counts the fee.
@matthewwalsh0 matthewwalsh0 force-pushed the fix/remove-relay-subsidized-fee-double-count branch from b284401 to 7a08f64 Compare April 17, 2026 08:19
@matthewwalsh0 matthewwalsh0 enabled auto-merge April 17, 2026 09:02
@matthewwalsh0 matthewwalsh0 added this pull request to the merge queue Apr 17, 2026
Merged via the queue into main with commit 5b9400b Apr 17, 2026
346 checks passed
@matthewwalsh0 matthewwalsh0 deleted the fix/remove-relay-subsidized-fee-double-count branch April 17, 2026 09:07
github-merge-queue bot pushed a commit to MetaMask/metamask-mobile that referenced this pull request Apr 17, 2026
…2.1 (#28978)

## **Description**

Bumps `@metamask/transaction-pay-controller` from `^19.1.1` to
`^19.2.1`.

### Changes included (since 19.1.1)

**19.2.1**
- Fix: Resolve correct `networkClientId` for source chain in Relay
execute flow ([#8492](MetaMask/core#8492))
- Fix: Stop double-counting subsidized fees in Relay quote target
amounts ([#8488](MetaMask/core#8488))

**19.2.0**
- Bump `@metamask/ramps-controller` from `^13.1.0` to `^13.2.0`
- Bump `@metamask/transaction-controller` from `^64.2.0` to `^64.3.0`

**19.1.3**
- Bump `@metamask/assets-controller` from `^5.0.1` to `^6.0.0`
- Bump `@metamask/bridge-controller` from `^70.1.0` to `^70.1.1`
- Fix: Resolve the effective transaction type from `nestedTransactions`
when the parent is a batch transaction

**19.1.2**
- Bump `@metamask/base-controller` from `^9.0.1` to `^9.1.0`
- Bump `@metamask/assets-controller` from `^5.0.0` to `^5.0.1`
- Bump `@metamask/assets-controllers` from `^103.1.1` to `^104.0.0`
- Bump `@metamask/bridge-controller` from `^70.0.1` to `^70.1.0`

## **Changelog**

CHANGELOG entry: null

## **Related issues**

## **Manual testing steps**

## **Screenshots/Recordings**

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Dependency bumps touch transaction/bridge/payment controller logic,
which can affect critical send/relay flows. There is also a likely
compile/runtime issue from removing `NETWORK_CHAIN_ID.MANTLE` while it’s
still referenced in the image mapping.
> 
> **Overview**
> Updates MetaMask core transaction dependencies, bumping
`@metamask/transaction-pay-controller` to `^19.2.1` and aligning related
controllers in `yarn.lock` (notably `@metamask/transaction-controller`
to `64.3.0`, plus newer `assets`/`bridge`/`ramps` controller versions).
> 
> Removes the `MANTLE` entry from `NETWORK_CHAIN_ID` in
`customNetworks.tsx` while leaving `CustomNetworkImgMapping` referencing
`NETWORK_CHAIN_ID.MANTLE`, which may break builds/type-checking or
runtime mapping for Mantle.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
78d9b57. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
vinnyhoward pushed a commit to MetaMask/metamask-mobile that referenced this pull request Apr 17, 2026
…2.1 (#28978)

## **Description**

Bumps `@metamask/transaction-pay-controller` from `^19.1.1` to
`^19.2.1`.

### Changes included (since 19.1.1)

**19.2.1**
- Fix: Resolve correct `networkClientId` for source chain in Relay
execute flow ([#8492](MetaMask/core#8492))
- Fix: Stop double-counting subsidized fees in Relay quote target
amounts ([#8488](MetaMask/core#8488))

**19.2.0**
- Bump `@metamask/ramps-controller` from `^13.1.0` to `^13.2.0`
- Bump `@metamask/transaction-controller` from `^64.2.0` to `^64.3.0`

**19.1.3**
- Bump `@metamask/assets-controller` from `^5.0.1` to `^6.0.0`
- Bump `@metamask/bridge-controller` from `^70.1.0` to `^70.1.1`
- Fix: Resolve the effective transaction type from `nestedTransactions`
when the parent is a batch transaction

**19.1.2**
- Bump `@metamask/base-controller` from `^9.0.1` to `^9.1.0`
- Bump `@metamask/assets-controller` from `^5.0.0` to `^5.0.1`
- Bump `@metamask/assets-controllers` from `^103.1.1` to `^104.0.0`
- Bump `@metamask/bridge-controller` from `^70.0.1` to `^70.1.0`

## **Changelog**

CHANGELOG entry: null

## **Related issues**

## **Manual testing steps**

## **Screenshots/Recordings**

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Dependency bumps touch transaction/bridge/payment controller logic,
which can affect critical send/relay flows. There is also a likely
compile/runtime issue from removing `NETWORK_CHAIN_ID.MANTLE` while it’s
still referenced in the image mapping.
> 
> **Overview**
> Updates MetaMask core transaction dependencies, bumping
`@metamask/transaction-pay-controller` to `^19.2.1` and aligning related
controllers in `yarn.lock` (notably `@metamask/transaction-controller`
to `64.3.0`, plus newer `assets`/`bridge`/`ramps` controller versions).
> 
> Removes the `MANTLE` entry from `NETWORK_CHAIN_ID` in
`customNetworks.tsx` while leaving `CustomNetworkImgMapping` referencing
`NETWORK_CHAIN_ID.MANTLE`, which may break builds/type-checking or
runtime mapping for Mantle.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
78d9b57. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants