Skip to content

fix: Add resolveSourceAmount callback - #9817

Merged
OGPoyraz merged 4 commits into
mainfrom
ogp/CONF-1787
Aug 11, 2026
Merged

fix: Add resolveSourceAmount callback#9817
OGPoyraz merged 4 commits into
mainfrom
ogp/CONF-1787

Conversation

@OGPoyraz

@OGPoyraz OGPoyraz commented Aug 11, 2026

Copy link
Copy Markdown
Member

Explanation

For a Money Account Max deposit (e.g. "Send from Money Account → Perps"), calculateSourceAmount derives the Relay quote's source amount as token.amountUsd / usdRate * 10^decimals. Since mUSD's usdRate is just under $1, this inflates the mUSD source above the on‑chain redeemable, so the Relay simulation reverts (the error scales with amount, hence the intermittent failures). The exact redeemable (bare mUSD + vaulted vmUSD) is only known to the client.

This adds an optional, generic resolveSourceAmount constructor callback: (request) => { sourceAmountRaw } | undefined. It's consulted in calculateSourceAmount after the existing skip guards and before the fiat gross‑up; a returned raw value is used verbatim (human derived from it), and undefined falls through unchanged. Opt‑in per return value, so existing flows are unaffected and no new state fields are added. Also exports the related types.

References

  • Fixes CONF-1787 — [MA → Perps] max perps deposit with Money Account doesn't always work
  • Consumer: MetaMask/metamask-mobile implements the callback to return the on‑chain redeemable for Money Account Max

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes how quote source amounts are computed on an opt-in path in transaction-pay quoting; incorrect callback values could cause failed simulations or wrong pay amounts, though default flows are unchanged.

Overview
Adds an optional resolveSourceAmount constructor hook on TransactionPayController so clients can supply an exact atomic source amount for Relay quotes instead of the default fiat-derived calculation (amountUsd / usdRate).

The callback is synchronous, receives { isMaxAmount, paymentOverride }, and returns { sourceAmountRaw } or undefined to keep existing behavior. TransactionPayController forwards it into updateSourceAmounts / calculateSourceAmount, which consult it after skip guards (same-token, zero amount) and before fiat math and max-balance logic; a returned raw value is used verbatim with human amount derived from payment token decimals.

Related types are exported from the package public API. calculateSourceAmount also moves the zero-amount check earlier so resolveSourceAmount is not called for skipped tokens. Tests cover forwarding from the controller, verbatim use, fallback, and guard behavior; changelog documents the new option.

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

@OGPoyraz OGPoyraz changed the title Add resolveSourceAmount callback fix: Add resolveSourceAmount callback Aug 11, 2026
@OGPoyraz
OGPoyraz marked this pull request as ready for review August 11, 2026 08:03
@OGPoyraz
OGPoyraz requested review from a team as code owners August 11, 2026 08:03
@OGPoyraz
OGPoyraz deployed to default-branch August 11, 2026 08:04 — with GitHub Actions Active

/** Required (target) token to be covered. */
token: TransactionPayRequiredToken;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just pass may be transaction object and transactionMeta here ?

@OGPoyraz OGPoyraz Aug 11, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed it's relevant for callbacks like getDelegationTransaction but for specific to MA override, isMax and paymentOverride is in fact enough for now. (I will remove paymentToken and token thanks for the ping)

Shall we add that in future if we need transaction?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, lets keep it as future task

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthewwalsh0
matthewwalsh0 requested a review from jpuri August 11, 2026 09:50
jpuri
jpuri previously approved these changes Aug 11, 2026
@OGPoyraz
OGPoyraz added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit 03e90c4 Aug 11, 2026
53 of 59 checks passed
@OGPoyraz
OGPoyraz deleted the ogp/CONF-1787 branch August 11, 2026 10:06
@OGPoyraz OGPoyraz mentioned this pull request Aug 11, 2026
4 tasks
pull Bot pushed a commit to dmrazzy/core that referenced this pull request Aug 11, 2026
## Explanation

Release `1186.0.0` with a minor version bump for:

- **`@metamask/transaction-pay-controller`** `26.2.3` → `26.3.0`

### `@metamask/transaction-pay-controller@26.3.0`

#### Added
- Add optional `resolveSourceAmount` constructor option to
`TransactionPayController`, allowing clients to supply an exact atomic
source amount for a required token that bypasses the default
fiat-derived source calculation
([MetaMask#9817](MetaMask#9817))

#### Changed
- Bump `@metamask/transaction-controller` from `^69.5.0` to `^69.5.1`
([MetaMask#9798](MetaMask#9798))
- Bump `@metamask/keyring-controller` from `^27.1.0` to `^27.1.1`
([MetaMask#9791](MetaMask#9791))
- Bump `@metamask/assets-controllers` from `^111.0.0` to `^111.1.0`
([MetaMask#9793](MetaMask#9793))
- Bump `@metamask/assets-controller` from `^13.1.1` to `^13.1.2`
([MetaMask#9813](MetaMask#9813))

### Dependency updates

No downstream packages depend on `@metamask/transaction-pay-controller`,
so no additional changelog entries were needed.

## References

- [MetaMask#9817](MetaMask#9817) — Add
`resolveSourceAmount` constructor option
- [MetaMask#9798](MetaMask#9798) — Bump
transaction-controller
- [MetaMask#9791](MetaMask#9791) — Bump
keyring-controller
- [MetaMask#9793](MetaMask#9793) — Bump
assets-controllers
- [MetaMask#9813](MetaMask#9813) — Bump
assets-controller

## Checklist

- [ ] I've updated the test suite for new or updated code as appropriate
- [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Version and changelog-only release PR with no code changes in the
diff.
> 
> **Overview**
> **Release `1186.0.0`** updates the monorepo version and publishes
**`@metamask/transaction-pay-controller` `26.2.3` → `26.3.0`**.
> 
> The new **`26.3.0`** changelog section documents what ships in that
package: an optional **`resolveSourceAmount`** constructor on
`TransactionPayController` so clients can supply an exact atomic source
amount and skip the default fiat-derived calculation, plus patch bumps
to `transaction-controller`, `keyring-controller`, `assets-controllers`,
and `assets-controller`. The diff itself is version and changelog
bookkeeping only—no application source changes in this PR.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
633bddf. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
pull Bot pushed a commit to Reality2byte/metamask-mobile that referenced this pull request Aug 12, 2026
…#34590)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
runway-github Bot added a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user
SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and
[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance
Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
tommasini pushed a commit to MetaMask/metamask-mobile that referenced this pull request Aug 12, 2026
…to `TPC` (#34688)

- fix: cp-8.7.0 Add `resolveSourceAmount` callback into `TPC` (#34590)

<!--
mms-check directive vocabulary — read by
.github/scripts/shared/pr-template-checks.ts
at module load to build the validation plan. Directives are invisible in
rendered
markdown and must NOT be removed or edited without updating the
validator registry.

  type=text           Section must contain non-placeholder prose.
  type=changelog      Section must have a valid CHANGELOG entry: line.
type=issue-link Section must have a Fixes:/Closes:/Refs: line with a
value.
type=manual-testing Section must have real testing steps or an explicit
N/A.
type=screenshot Section must have evidence (image/URL) or an explicit
N/A.
type=checklist Section must have all checkboxes consciously checked.
required=true|false Whether a missing/invalid section runs the validator
at all.
blocking=true|false Whether a failure of this check fails the CI
workflow.
Default: false — failures are shown as warnings in the sticky
                      comment but do not block the PR.

Sections without a directive are checked for structural presence only.
-->

## **Description**

<!-- mms-check: type=text required=true -->

For a Money Account Max deposit (e.g. "Send from Money Account →
Perps"), the pay controller derives the Relay quote's source amount from
a fiat conversion (`amountUsd / usdRate`). Since mUSD's rate is just
under `$1`, this inflates the mUSD source above the on-chain redeemable,
so the Relay simulation reverts (the error scales with amount, hence the
intermittent failures).

This wires up the new generic `resolveSourceAmount` callback added in
`@metamask/transaction-pay-controller` (MetaMask/core#9817) so the
client supplies the exact atomic redeemable instead:

- Adds a `redeemableRaw` field to the `moneyBalance` slice (+
setter/selector).
- `useMoneyAccountBalance` stashes the exact atomic redeemable
(`vmusdValueInMusd`) so it can be read synchronously outside React.
- New `resolveSourceAmount` callback returns that cached atomic amount
for Money Account Max (and `undefined` otherwise), registered in the
controller init.

## **Changelog**

<!-- mms-check: type=changelog required=true blocking=true -->

CHANGELOG entry: Fixed max Perps deposits funded from the Money Account
sometimes failing at quote time.

## **Related issues**

<!-- mms-check: type=issue-link required=true -->

Fixes: CONF-1787
Refs: MetaMask/core#9817

## **Manual testing steps**

<!-- mms-check: type=manual-testing required=true -->

```gherkin
Feature: Max Perps deposit funded from the Money Account

  Scenario: user deposits their full Money Account balance into Perps
    Given a Money Account with a non-trivial balance (e.g. > $30)
    When the user sends from Money Account to Perps and selects Max
    Then the Relay quote simulation succeeds (no revert)
    And the source amount equals the on-chain redeemable, not the grossed-up fiat amount
```

## **Screenshots/Recordings**

<!-- mms-check: type=screenshot required=true -->

### **Before**

<!-- Relay simulation reverts on max Perps deposit from Money Account.
-->

N/A

### **After**

<!-- [screenshots/recordings] -->

N/A

## **Pre-merge author checklist**

<!-- mms-check: type=checklist required=true -->

- [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.

#### Performance checks (if applicable)

- [ ] I've tested on Android
  - Ideally on a mid-range device; emulator is acceptable
- [ ] I've tested with a power user scenario
- Use these [power-user

SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93)
to import wallets with many accounts and tokens
- [ ] I've instrumented key operations with Sentry traces for production
performance metrics
- See [`trace()`](/app/util/trace.ts) for usage and

[`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274)
for an example

For performance guidelines and tooling, see the [Performance

Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers).

## **Pre-merge reviewer checklist**

<!--
Reviewer checklist items follow the same semantics as the author
checklist: an
unchecked box is ambiguous, a checked box means the reviewer consciously
assessed that responsibility. See `docs/readme/ready-for-review.md`.
-->

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches transaction-pay source-amount resolution for Money Account
funding, so a bad cache or address mismatch could under/over-quote max
deposits. Scope is narrow and guarded by account matching plus unit
tests.
> 
> **Overview**
> Fixes intermittent Relay quote failures on **max Money Account →
Perps** deposits by supplying the exact on-chain redeemable instead of a
fiat-converted amount that could exceed balance.
> 
> Caches atomic redeemable (`vmusdValueInMusd`) in the `moneyBalance`
slice from `useMoneyAccountBalance`, then wires a new
`resolveSourceAmount` callback into `TransactionPayController` (via
`@metamask/transaction-pay-controller` `^26.3.0`) that returns that
cached raw amount only for Money Account max flows when the address
matches.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
845084f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
[6f6cb05](6f6cb05)

Co-authored-by: Ömer Göktuğ Poyraz <omergoktugpoyraz@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants