Skip to content

rpc - #10014

Open
FuzzysTodd wants to merge 5885 commits into
MetaMask:migrate-json-rpc-engine-repofrom
FuzzysTodd:main
Open

rpc#10014
FuzzysTodd wants to merge 5885 commits into
MetaMask:migrate-json-rpc-engine-repofrom
FuzzysTodd:main

Conversation

@FuzzysTodd

@FuzzysTodd FuzzysTodd commented Aug 30, 2026

Copy link
Copy Markdown

Explanation

References

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 affect merge gates, release/npm publish, and merge-queue behavior across the monorepo; the removed rpc-methods patch may surface type or dependency issues if the upstream package is not already aligned.

Overview
This PR overhauls repo plumbing rather than controller/runtime RPC behavior. The headline RPC tie-in is dropping the Yarn patch for @metamask/rpc-methods (the old getLocale typing workaround); everything else is CI, ownership, and developer tooling.

CODEOWNERS moves from a single @MetaMask/devs rule to a generated, team-scoped file (with release-file rules), driven by codeowners.ts / yarn codeowners:generate. GitHub Actions gain merge-queue support, concurrency, security scanning, incremental lint/build/test (changed workspaces only), split Node matrices (18–24), Platform API docs build/deploy, wallet-cli e2e, and stricter “clean working tree” checks. New automation covers release validation (root version bump, release conflicts, PR comments), changelog checks (shared action + merge-queue [Unreleased] guard + @metamaskbot update-changelogs), stale release PR cleanup, extension/mobile update issues after publish, and delegated preview publish. Publish release is restructured (artifact upload, queued concurrency, npm publish v6, separate GitHub release job).

Developer experience: root .eslintrc.js is removed (lint still wired via lint:eslint elsewhere), oxfmt is added alongside Prettier, .nvmrc switches to lts/*, Dependabot gets cooldowns and GitHub Actions updates, PR template drops inline changelog blocks in favor of process links, and agent skills / Bugbot pointers are added. Misc: .gitignore expansions, actionlint/zizmor config, and linguist rules for tsconfig.** files.

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

stanleyyconsensys and others added 30 commits July 28, 2026 13:36
…taMask#9564)

## Explanation

This PR add support for non-evm snap to ship `balance metadata` via
- keyringApi.getAccountBalances
- keyringEvent.AccountBalancesUpdated
 

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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**
> Additive, optional field on snap balance mapping with tests; no auth
or breaking API changes.
> 
> **Overview**
> **Snap-backed balances can now carry chain-specific metadata** (e.g.
Stellar spendable balance or trustline flags) through `AssetsController`
state instead of dropping it after keyring responses.
> 
> `SnapDataSource` copies optional `metadata` from snap keyring
`Balance` objects into `assetsBalance` on both
**`keyring_getAccountBalances` fetch** and
**`AccountsController:accountBalancesUpdated`** handling. Entries
without metadata stay `{ amount }` only. The balance-updated event
payload type documents the optional `metadata` field.
> 
> Tests cover fetch and event paths with Stellar-style metadata; the
changelog records the behavior under Unreleased.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f9a02c2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…aMask#8338)

## Explanation

`Messenger.delegate()` accepts a partial list of actions/events without
compile-time enforcement that all required items are present. This means
missing a delegation (e.g., a newly added `AllowedAction`) silently
compiles and only fails at runtime.

`delegateAll()` wraps `delegate()` with a `RequireExhaustive` branded
intersection type that produces a clear TypeScript error showing exactly
which actions or events are missing from the list. Own-namespace actions
of the delegatee are automatically excluded from the check.

Example error when an action is missing:
```
Property '__missing' is missing in type '[...]'
  but required in type '{ __missing: "KeyringController:signTypedMessage" }'.
```

## References

## Changelog

### `@metamask/messenger`

- **Added**: `delegateAll()` method — strict alternative to `delegate()`
that requires all external actions and events to be listed
- **Added**: `MessengerNamespace<M>` utility type — extracts the
namespace string literal from a Messenger type
- **Added**: `RequireExhaustive<Required, Provided>` utility type —
validates tuple exhaustiveness at compile time

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> API addition only (`delegateAll` delegates to existing `delegate`);
main risk is consumers adopting stricter typing, not runtime behavior
changes.
> 
> **Overview**
> Adds **`Messenger.delegateAll`**, a typed wrapper around
**`delegate`** that requires every **external** action and event on the
delegatee messenger to be listed (own-namespace items are excluded).
Missing entries fail at compile time via a **`RequireExhaustive`**
branded type that surfaces **`__MISSING_DELEGATIONS__`**, and the source
messenger must also type-check as able to provide those items.
> 
> Also exports **`MessengerNamespace`** for extracting a messenger’s
namespace literal from its type.
> 
> **Testing/tooling:** Jest unit tests for runtime behavior, new
**TSTyche** (`*.tst.ts`) compile-time tests, **`test`** split into
**`test:unit`** + **`test:types`**, coverage/build/lint configs updated
to exclude type-test files, and **yarn.config** allows the messenger
package’s custom test script.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
22ed38b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…aMask#9673)

## Explanation

`QuoteStatusManager.reportFinalised` surfaces a `QuoteStatusUpdateError`
through the
`onError` callback when it is asked to finalize a `txMetaId` that has no
tracked
quote-status entry. Those reports carried only an empty `quoteId`, the
`txMetaId`, and
`srcChainId`, so when one lands in Sentry there is nothing tying it to
the on-chain
source transaction — which is exactly what you need to tell whether the
entry was never
created, was created under a different source hash, or had already been
evicted by TTL.
`QuoteStatusUpdateErrorDetails` has carried an optional `srcTxHash`
field since MetaMask#9596,
but nothing on the finalization path populated it.

This adds an optional `srcTxHash` parameter to `reportFinalised`,
forwards it into the
error details, and supplies it from every caller:

| Caller | Source of the hash |
| --- | --- |
| `#onTransactionFailed` | `txMeta.hash` |
| `#onTransactionConfirmed` (swap branch) | `txMeta.hash` |
| `#handleOldHistoryItem` (polling permanently ends) | history item's
`status.srcChain.txHash` |
| `#fetchBridgeTxStatus` (final status) | the `settlementTxHash` already
computed for `#reportSubmittedOnce` |
| `SubmitStep.PublishCompletedEvent` | history item's
`status.srcChain.txHash` |
| `QuoteStatusManager.init()` reconciliation (both calls) |
`entry.srcTxHash` |

Things that may not be obvious:

- The two `init()` call sites cannot actually reach the missing-entry
branch, since that
loop iterates entries that are by definition in the store. They pass
`srcTxHash` purely
  so every call site is consistent.
- In `#handleOldHistoryItem` and `SubmitStep.PublishCompletedEvent` the
`txHistory` lookup
is hoisted into a local rather than indexing state twice; the latter
case is now wrapped
  in braces because it declares a `const`.
- This is not a breaking change: `QuoteStatusManager` is not exported
from
  `src/index.ts`, and the new parameter is optional.
- One unrelated re-indent of an existing boolean expression inside
`#processEntry`'s
  `retry()` closure, so the file passes the Prettier check.

## References

https://consensyssoftware.atlassian.net/browse/SWAPS-4841

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Optional parameter and richer error metadata only; no change to
successful finalization or backend sync behavior.
> 
> **Overview**
> **Quote status finalization errors** now include the source
transaction hash when `reportFinalised` cannot find a tracked entry, so
Sentry reports tie failures to the on-chain source tx (not just
`txMetaId` and `srcChainId`).
> 
> `QuoteStatusManager.reportFinalised` gains an optional `srcTxHash`
argument that is forwarded into `QuoteStatusUpdateError` details.
**Bridge status controller** call sites pass the hash from the relevant
context (`txMeta.hash`, bridge history `status.srcChain.txHash`, or
`settlementTxHash`); startup reconciliation passes `entry.srcTxHash`.
Tests and the package changelog are updated accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4439794. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
)

## Explanation

`SnapDataSource` previously delivered snap-sourced balance updates (from
`AccountsController:accountBalancesUpdated`) by iterating
`activeSubscriptions` and calling each subscription's own
`onAssetsUpdate`. This meant updates were silently dropped whenever no
active subscription was tracked for the chain at the moment the event
arrived — which happens for networks like Tron, where a snap can report
asset types (e.g. energy/bandwidth) that another data source (e.g. the
WebSocket data source) owns the base chain for, but doesn't itself
support. Since that other data source, not `SnapDataSource`, held the
"active" subscription, the snap's updates never reached
`AssetsController`.

The fix has `SnapDataSource` accept a single `onAssetsUpdate` callback
in its constructor and report all snap balance updates directly to
`AssetsController` through that callback, instead of fanning them out
through per-subscription state. This decouples delivery from
subscription bookkeeping: since snaps push real-time updates and don't
need polling, `subscribe()`/`activeSubscriptions` no longer need to
store or invoke their own `onAssetsUpdate`, which also let the
associated subscribe/unsubscribe/destroy logic be simplified (no more
`isUpdate` branching or per-subscription cleanup for snap chains).

`AssetsController` now passes `onAssetsUpdate: (response) =>
this.handleAssetsUpdate(response, 'SnapDataSource')` when constructing
`SnapDataSource`, mirroring how the other data sources are wired up.

## References

* Fixes an issue where Tron (and similar snap-sourced) asset updates
could be silently dropped when no active `SnapDataSource` subscription
existed for the chain.

## Checklist

- [x] 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
EOF

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how snap balance updates reach unified `assetsBalance` state;
behavior is narrower but touches the multichain assets pipeline where
incorrect delivery affects displayed balances.
> 
> **Overview**
> Fixes snap-sourced balances (e.g. Tron energy/bandwidth) being
**silently dropped** when `SnapDataSource` had no matching entry in
`activeSubscriptions`—common when another data source owns the chain
subscription.
> 
> **`SnapDataSource`** now takes a required **`onAssetsUpdate`** at
construction. `AccountsController:accountBalancesUpdated` and
subscribe-time initial fetches call that callback directly instead of
iterating `activeSubscriptions` or invoking per-subscription
`onAssetsUpdate`. Subscribe logic is slimmed down (no `isUpdate` /
subscription bookkeeping for delivery; less destroy cleanup).
> 
> **`AssetsController`** passes `onAssetsUpdate: (response) =>
this.handleAssetsUpdate(response, 'SnapDataSource')` when creating
`SnapDataSource`. Tests and changelog updated; Jest coverage thresholds
adjusted slightly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0745a3b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#9526)

## Explanation

The generic parameter has been removed from the `invalidateQueries`
method, simplifying the type signature. In practice this was never used.
It also created an odd inconsistency between the method type and the
action type, which would be nice to avoid.

## References

N/A

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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
- I didn't test with a preview build, but I did search the entire
organization for any references where the generic type param was used,
and found none

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Narrow API/type-signature change with identical runtime behavior;
breaking only for external callers that explicitly parameterized
`invalidateQueries`, which the team reports were none.
> 
> **Overview**
> **Breaking (documented):** `BaseDataService.invalidateQueries` no
longer accepts a `TPageData` type parameter; optional filters are now
typed as `InvalidateQueryFilters<Json>` and still delegate to the query
client unchanged.
> 
> This aligns the public method signature with
`DataServiceInvalidateQueriesAction`, which already used `Json` for
filters. The unreleased changelog notes the break even though no in-org
callers used the generic.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2faae35. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

Activity mappers in were using a hardcoded `nativeAssetsByCaipChainId`
table. This doesn't scale and breaks on new chains for native metadata.

This PR removes that table and replaces with an `assetType` field,
setting its value to `native` when applicable so clients can resolve the
correct native asset.

## References

N/A

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

Removes condition that prevents fetching token prices when cached values
exist in the asset controller states. Token prices will now be fetched
on each quote fetch to prevent displaying stale fiat amounts

Testing
- Get Swap quote with native dest asset, note fiat values
- Switch the assets
- The src fiat amount should roughly equal the previous dest fiat amount
- Keep repeating and try other asset combinations

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

Fixes https://consensyssoftware.atlassian.net/browse/SWAPS-4851

## 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](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]
> **Medium Risk**
> Changes quote-time pricing behavior and trims bridge-controller
messenger dependencies, which can affect fiat display accuracy and
client wiring but does not alter transaction or auth flows.
> 
> **Overview**
> Fixes stale fiat amounts in swap/bridge UI by **always** refreshing
token prices when quotes are fetched, instead of skipping the price API
when rates already exist in controller or assets state.
> 
> `#fetchAssetExchangeRates` no longer filters asset IDs through
`selectIsAssetExchangeRateInState` or builds exchange-rate sources from
`MultichainAssetsRatesController`, `TokenRatesController`, and related
messenger calls. Every quote fetch requests spot prices for the involved
tokens (including native assets) and merges results into
`assetExchangeRates`. Price requests now ask for both the user currency
and **`usd`**.
> 
> Messenger **`AllowedActions`** drops `TokenRatesController:getState`
and `MultichainAssetsRatesController:getState`; currency for the fetch
still comes from `CurrencyRateController` or
`AssetsController:getExchangeRatesForBridge` when the assets-controller
rates flag is on. Tests that asserted the old skip-fetch behavior are
removed; selector tests add edge cases for missing rates and batch-sell
`quoteRequestIndex` defaults.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bb983c4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

Releases bridge-controller and bridge-status-controller
<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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**
> Release-only version and changelog updates; consumer-facing risk is
limited to the documented patch/minor bridge fixes, not large refactors
in this diff.
> 
> **Overview**
> **Monorepo release 1153.0.0** that cuts and wires new versions of
`@metamask/bridge-controller` and `@metamask/bridge-status-controller`
(version fields, changelogs, `yarn.lock`).
> 
> **`@metamask/bridge-controller@78.0.1`** (patch): documents dependency
bumps and a fix to **always fetch and persist token prices** into
`assetExchangeRates` ([MetaMask#9687]).
> 
> **`@metamask/bridge-status-controller@74.6.0`**: when a finalization
is reported for a transaction with **no tracked quote-status entry**,
**`QuoteStatusUpdateError` now includes `srcTxHash`** in the error
details ([MetaMask#9673]); depends on **`@metamask/bridge-controller@^78.0.1`**.
> 
> No application source changes appear in this diff—only
release/versioning artifacts.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7680037. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

Adds the user-facing `mm wallet send` command that sends a transaction
end-to-end through the daemon-hosted `TransactionController`, closing
MetaMask#9513. This is the CLI surface on top of the daemon's transaction
capability (MetaMask#9512 / MetaMask#9612): it collects transaction parameters,
dispatches them to the daemon, waits for the broadcast, and reports the
resulting transaction hash.

Send a transaction. `--value` is in ether; select the network with
`--network-client-id` or `--chain-id`; the sender defaults to the
selected account. The command previews the resolved plan and asks for
confirmation before broadcasting, then prints the transaction hash:

Because the daemon auto-approves the confirmation prompt — or your
explicit `--yes` — is the only boundary before funds move; use
`--dry-run` first if unsure. Gas is estimated automatically unless
overridden with `--gas` / `--max-fee-per-gas` /
`--max-priority-fee-per-gas` / `--gas-price` (each a `0x`-prefixed hex
quantity).

### Dedicated `sendTransaction` RPC handler

`TransactionController:addTransaction` returns a `Result` shaped like `{
transactionMeta, result }`, where `result` is a `Promise<hash>` that
resolves once the transaction is signed and broadcast. That promise is
**not JSON-serializable**, so it cannot travel back over the daemon's
generic `call` dispatch. The daemon therefore exposes a dedicated
`sendTransaction` handler (`src/daemon/send-transaction.ts`) that,
server-side:

1. resolves the network client — from `networkClientId`, or from
`chainId` via `NetworkController:findNetworkClientIdByChainId`;
2. resolves the sender — the provided `--from`, or the selected account;
3. calls `addTransaction(txParams, { networkClientId, origin:
'metamask', isInternal: true })` (internal, so it skips
origin/permitted-account validation and is auto-approved by the headless
daemon);
4. **awaits the broadcast** and re-reads the live record so the returned
status reflects the post-broadcast state (`submitted`), not the
`unapproved` creation snapshot;
5. returns a serializable `{ transactionHash, transactionId, status }`.

Params are validated with superstruct at the daemon boundary (exactly
one of `networkClientId` / `chainId`; `0x` address and hex quantities).

### The `mm wallet send` command

A thin client over that handler (`src/commands/wallet/send.ts`):


```sh
➜  wallet-cli: yarn mm wallet send --help
  Send a transaction through the daemon-hosted TransactionController. Estimates gas automatically
  unless overridden, signs, broadcasts, and prints the resulting transaction hash. The daemon
  auto-approves, so the confirmation boundary is this command.

USAGE
  $ mm wallet send --to <value> [--value <value>] [--from <value>] [--data <value>]
    [--network-client-id <value>] [--chain-id <value>] [--gas <value>] [--max-fee-per-gas <value>]
    [--max-priority-fee-per-gas <value>] [--gas-price <value>] [--dry-run] [-y] [-t <value>]

FLAGS
  -t, --timeout=<value>                   Response timeout in milliseconds
  -y, --yes                               Skip the confirmation prompt and broadcast immediately.
      --chain-id=<value>                  Chain ID (0x-prefixed hex) to resolve to a network client.
                                          Provide this or --network-client-id, not both.
      --data=<value>                      Calldata as a 0x-prefixed hex string (for contract calls)
      --dry-run                           Resolve the network client and sender and validate params,
                                          but do not broadcast.
      --from=<value>                      Sender address (0x-prefixed). Defaults to the selected
                                          account.
      --gas=<value>                       Gas limit override, as a 0x-prefixed hex quantity
      --gas-price=<value>                 Legacy gasPrice override, as a 0x-prefixed hex wei quantity
      --max-fee-per-gas=<value>           maxFeePerGas override, as a 0x-prefixed hex wei quantity
      --max-priority-fee-per-gas=<value>  maxPriorityFeePerGas override, as a 0x-prefixed hex wei
                                          quantity
      --network-client-id=<value>         Network client to send on. Provide this or --chain-id, not
                                          both.
      --to=<value>                        (required) Recipient address (0x-prefixed)
      --value=<value>                     [default: 0] Amount to send, in ether (e.g. 0.01). Defaults
                                          to 0.

EXAMPLES
  $ mm wallet send --to 0xRecipient --value 0.01 --chain-id 0x1

  $ mm wallet send --to 0xRecipient --value 0.01 --network-client-id mainnet --yes

  $ mm wallet send --to 0xContract --data 0xabcdef --value 0 --chain-id 0x1 --dry-run
```

## Testing

- Unit tests for the handler (network/sender resolution, internal
submit, dry-run, broadcast + live status, param validation) and the
command (arg parsing, preview/confirm/abort, `--yes`, `--dry-run`, error
surfaces); **100% coverage maintained**.
- **Real-chain e2e** (`tests/wallet-send.e2e.test.ts`): boots a local
`anvil` node, adds it as a custom network, and drives the built `mm` CLI
to sign, broadcast, and mine a real transaction (asserts receipt
`status: 0x1` and a recipient balance increase). It is
**skip-if-absent**; CI installs `anvil` for it only when
`packages/wallet-cli/` changed. See
`packages/wallet-cli/tests/README.md`.
- `build`, package `test`, `test:e2e`, `yarn lint`, `changelog:validate`
pass.

## References

- Closes MetaMask#9513
- Builds on MetaMask#9512 / MetaMask#9611 (daemon transaction-capable) and MetaMask#9509 (the
`TransactionController` slot); the mutating-RPC safety prerequisite
MetaMask#9511 landed in MetaMask#9608.
- `packages/wallet-cli/src/daemon/send-transaction.ts`,
`src/commands/wallet/send.ts`, `tests/wallet-send.e2e.test.ts`.

> [!NOTE]
> Stacked on `sirtimid/wallet-cli-daemon-transaction-capable` (MetaMask#9612);
this PR is based on that branch so the diff is scoped. Once MetaMask#9612
merges, this will be rebased onto `main` and retargeted.

## Checklist

- [x] Tests cover the handler and command (unit) plus a real-chain
broadcast (e2e); 100% coverage maintained.
- [x] `build`, `test`, `test:e2e`, `yarn lint:fix`, `yarn lint`,
`changelog:validate` pass.
- [x] Confirmation prompt (`--yes` to skip) and `--dry-run` gate a real,
irreversible send.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> Adds an irreversible on-chain send path through a headless daemon that
auto-approves transactions; mistakes in confirmation, `--yes`, or
timeout/retry behavior could move real funds despite CLI safeguards.
> 
> **Overview**
> Introduces **`mm wallet send`** so users can broadcast through the
daemon-hosted `TransactionController`, with ether `--value`, network
selection via `--network-client-id` or `--chain-id`, optional gas
overrides, **`--dry-run`**, and an interactive preview (or **`--yes`**
to skip).
> 
> Because `addTransaction`'s broadcast promise cannot cross JSON-RPC,
the daemon gains a dedicated **`sendTransaction`** handler that resolves
network/sender, submits as internal/auto-approved, awaits the hash, and
returns `{ transactionHash, transactionId, status }` (with **`dryRun`**
for preview-only).
> 
> The CLI dry-runs before confirm, then pins the resolved **`from`** /
**`networkClientId`** on broadcast; it validates RPC results, uses a
longer default broadcast timeout with duplicate-send warnings on
timeout, and documents that the daemon still auto-approves—so the
command prompt is the main fund-moving gate.
> 
> **CI/testing:** wallet-cli e2e installs **anvil** via
`@metamask/foundryup`, sets **`MM_E2E_REQUIRE_ANVIL`**, adds a
real-chain send e2e (anvil + custom network), shares daemon cleanup
helpers, and updates knip/README/changelog accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0f0e34f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Erik Marks <25517051+rekmarks@users.noreply.github.com>
## Explanation

Releases `@metamask/client-utils` **1.2.1 → 1.3.0**:
- Adds optional `assetType` on TokenAmount and Fee so clients can
resolve icons when `assetId` is absent
- Removes hardcoded `nativeAssetsByCaipChainId` lookup

Related to MetaMask#9671.

## References

* Related to MetaMask#9671

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

`V6_DEFI_POSITION_TYPES` in `@metamask/core-backend` did not match the
Accounts API / Zerion wallet fungible position `position_types` filter
values, so typed DeFi rows and downstream grouping could disagree with
real API data.

This PR updates the allowed position types to: `deposit`, `loan`,
`locked`, `staked`, `reward`, `wallet`, `investment`. In
`@metamask/assets-controllers`, `DEFI_POSITION_LIABILITY_TYPES` is
updated from `lending` to `loan` so liability market-value handling
stays aligned with the corrected type.

Tested locally in both extension and mobile. 

<img width="828" height="556" alt="image"
src="https://github.com/user-attachments/assets/4a4bde02-47a0-432c-9acb-88d65f0c9e56"
/>


## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

Related to https://consensyssoftware.atlassian.net/browse/ASSETS-3800

## Checklist

- [X] I've updated the test suite for new or updated code as appropriate
- [X] 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)
- [x] 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]
> **Medium Risk**
> Breaking type changes in `@metamask/core-backend` can fail
compile-time checks for consumers using removed position types;
incorrect liability handling before this fix could have mis-stated DeFi
totals.
> 
> **Overview**
> **Aligns v6 DeFi `positionType` values with the Accounts API / Zerion
fungible position types** so typed API rows and client grouping match
live responses.
> 
> In `@metamask/core-backend`, `V6_DEFI_POSITION_TYPES` (and
`V6DeFiPositionType`) is replaced with `deposit`, `loan`, `locked`,
`staked`, `reward`, `wallet`, and `investment`, dropping the previous
set (e.g. `lending`, `yield`, `liquidity_pool`, `rewards`). A Zerion API
doc link is added on the constant.
> 
> In `@metamask/assets-controllers`, `DEFI_POSITION_LIABILITY_TYPES` now
treats **`loan`** (not `lending`) as the liability type when subtracting
borrow exposure from protocol `marketValue`. Tests for v6 grouping are
updated for `loan` and `reward` position types.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f3eee31. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…etaMask#9672)

Avoid Sentry rate limits by nesting pipeline timings as subspans and
emitting them only on the first unlock-session fetch, plus a single
AggregatedBalanceSelector span for all wallets.

## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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**
> Changes are limited to observability wiring and defensive error
handling; asset fetch and state-update behavior is unchanged aside from
when spans are emitted.
> 
> **Overview**
> Reduces Sentry span volume and improves Assets Health dashboard
fidelity by restructuring how `AssetsController` emits traces.
> 
> **Tracing helpers** — Inline `#emitTrace` is replaced by
`utils/trace.ts` (`emitTrace` / `withTrace`). Omitting `trace` no-ops at
call sites; numeric span data (especially `duration_ms`) is mirrored
into tags and used to backdate `startTime` for p95 charts. `withTrace`
treats Sentry/adapter rejections as best-effort so `getAssets` force
updates and `handleAssetsUpdate` still complete.
> 
> **Nested spans & session gating** — Fetch and update pipelines wrap
work in parent spans (`AssetsFetchPipeline`, `AssetsBackgroundFetch`,
`AssetsUpdateEnrichment`); per-source timings and summary spans
(`AssetsFullFetch`, `AssetsControllerFirstInitFetch`,
`AssetsUpdatePipeline`, etc.) nest via `parentContext`. Fast and
background fetch lanes are sibling `withTrace` calls, not nested.
Pipeline, data-source, and enrichment spans emit only until the first
`forceUpdate` in an unlock session (`#firstInitFetchReported`); later
polls, force refreshes, and subscription enrichment skip those spans
(reset on lock/`#stop`).
> 
> **Balance selector** — `calculateBalanceForAllWallets` emits one
`AggregatedBalance` / `AggregatedBalanceSelector` pair for the full
wallet pass instead of a root span per account group.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7001768. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary
- Expands the incremental `lint:tsc` rollout from
[MetaMask#9652](MetaMask#9652) by enabling
typechecking for 11 more packages that already pass with no TypeScript
errors.
- Adds `tsconfig.lint.json` for: `analytics-controller`,
`announcement-controller`, `app-metadata-controller`, `build-utils`,
`client-controller`, `foundryup`, `local-node-utils`,
`preferences-controller`, `rate-limit-controller`,
`stellar-quickstart-up`, `storage-service`.
- Updates root `tsconfig.lint.json` references accordingly (closed
dependency set on packages already enabled in the parent PR).

## Test plan
- [x] `yarn lint:tsc` passes with the expanded package set
- [ ] CI lint job passes on this branch


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Config-only expansion of incremental typecheck coverage; no
application or runtime code changes.
> 
> **Overview**
> Continues the incremental **`lint:tsc`** rollout by wiring **11 more
packages** into the root TypeScript project build so `tsc --build
tsconfig.lint.json` typechecks them in CI.
> 
> Each package gets a new **`tsconfig.lint.json`** that extends its
normal `tsconfig.json` plus **`tsconfig.packages.lint.json`**, writes
cache under **`.tsc-lint-cache`**, and declares **`references`** only to
dependencies already in the lint graph (controllers → `base-controller`
/ `messenger`; **`storage-service`** → `messenger`; standalone tools use
empty references). Root **`tsconfig.lint.json`** adds matching
**`references`** for: `analytics-controller`, `announcement-controller`,
`app-metadata-controller`, `build-utils`, `client-controller`,
`foundryup`, `local-node-utils`, `preferences-controller`,
`rate-limit-controller`, `stellar-quickstart-up`, and `storage-service`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f55e4f4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
MetaMask#9681)

…P-44 entry

Arc (eip155:5042) was enabled before its native asset (USDC) had a
SLIP-44 registry entry, so it used the erc20:0x0 zero-address
placeholder in SPOT_PRICES_SUPPORT_INFO. The Arc team has since
registered slip44:5042 for it.

## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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**
> Single mapping correction for Arc in the CodeFi v2 spot-prices table;
no auth, data migration, or broad logic changes.
> 
> **Overview**
> Updates Arc’s **native asset identifier** in
`SPOT_PRICES_SUPPORT_INFO` (`codefi-v2.ts`) from the temporary
`erc20:0x0` placeholder to **`slip44:5042`**, matching the registered
SLIP-44 entry for Arc’s native USDC on chain `5042` / `0x13b2`.
> 
> The **changelog** records this under Fixed for
`@metamask/assets-controllers`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2e85266. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…Mask#9653)

## Explanation

Removes old behaviour that was kept prior from WS issues around data in
incorrect format, but now has been fixed.
Also did some test spec cleaning...

## References

MetaMask/metamask-extension#44786

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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)
- [x] 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


### Example Extension Test:
<img width="534" height="658" alt="Screenshot 2026-07-29 at 12 37 58"
src="https://github.com/user-attachments/assets/4b0e4417-bbe6-44ce-bcf7-433ac1305575"
/>

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes core balance aggregation math used for wallet/group fiat
totals; the fix aligns with the invariant that state amounts are
human-readable, but any edge case that still stored raw base units would
now be mispriced.
> 
> **Overview**
> Fixes **incorrect portfolio totals** when token balances are large
human-readable amounts (e.g. billions of tokens with 9 decimals) by
removing the legacy `scaleToHumanIfRaw` path in `balance.ts`
aggregation.
> 
> **`assetsBalance` amounts are always human-readable**, but the old
heuristic treated values ≥ `10^decimals` as raw base units and divided
by `10^decimals`, which **under-counted** those holdings in
`getAggregatedBalanceForAccount`, `getAggregatedBalanceForAccountIds`,
`calculateBalanceForAllWallets`, and
`calculateBalanceChangeForAccountGroup` (metamask-extension#44786).
> 
> Tests add a TangYuan regression case, shared
`arrangeAssetsControllerState` helpers, and coverage for group
aggregation when only `accountTreeState` is passed (no `accountsById`).
Changelog documents the fix.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7056a0b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…bridges (MetaMask#9682)

## Summary

Saved gas preferences were excluded from every transaction with
`isInternal: true`. This also excluded wallet-initiated transfers, which
are marked internal by the extension API even though they should use
saved gas settings.

This changes the exclusion to a dedicated transaction-type list
containing swaps and bridge transactions. Wallet transfers can now apply
saved gas preferences while swaps and bridges remain protected from
underpriced saved fees.

## Related PRs

- MetaMask#9401
- MetaMask/metamask-extension#43317

## Testing

- `yarn jest --config packages/transaction-controller/jest.config.js
--runInBand packages/transaction-controller/src/utils/gas-fees.test.ts
--coverage=false`
- Added coverage for internal wallet transactions and swap/bridge
exclusions.

## Changelog

Added an Unreleased changelog entry for
`@metamask/transaction-controller`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes which transactions receive saved gas fees at submit time;
wallet sends gain user prefs while swap/bridge behavior stays guarded,
but any misclassified `type` could get the wrong fee path.
> 
> **Overview**
> **Saved gas preferences** were skipped for every transaction with
`isInternal: true`, which incorrectly blocked wallet-initiated transfers
(often marked internal) from using the user’s advanced gas settings.
> 
> `updateGasFees` now ignores saved gas only for **swap and bridge**
transaction types (`SWAP_TRANSACTION_TYPES`, `bridge`, `bridgeApproval`)
instead of all internal transactions. Internal `simpleSend` transfers
can apply saved fees again; aggregator/relay-driven swaps and bridges
still avoid user-saved fees that could underprice them.
> 
> Tests cover internal wallet sends applying saved gas and parameterized
cases for swap/bridge types still ignoring them.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
61d4074. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

### Context

Updating the amount of a Money Account deposit currently fans one
logical user action out through multiple nested transaction updates,
parent calldata rebuilds, controller publications, gas estimations, and
quote generations. In the reference iOS simulator trace, Update/Done to
an executable quote took **8.503 seconds**:

| Segment | Baseline |
| --- | ---: |
| Update/Done → Relay request | 3,393.546 ms |
| Relay request | 4,107.940 ms |
| Relay response → executable quote | 1,001.045 ms |
| **Total** | **8,502.531 ms** |

Relay is a third-party request whose latency varies independently. This
proof of concept targets client-controlled work before and after that
request.

### What this PR changes

This PR adds two generic Transaction Controller primitives while
preserving Transaction Pay's existing reactive architecture. The final
Core diff is limited to `packages/transaction-controller`; Money
Account-specific orchestration lives in the companion Mobile PR.

#### TransactionController

- Adds `updateTransactionMetadata`, a synchronous messenger action that
atomically mutates the latest transaction metadata, with an optional
`skipResimulate` flag that defaults to normal automatic re-simulation.
- Callers opt out explicitly with `skipResimulate: true`; normal callers
retain automatic re-simulation by default.
- Normal transaction update APIs retain their existing re-simulation
behavior.
- Exports `updateEIP7702BatchData`, a pure synchronous utility that
validates indexed nested-transaction calldata updates, returns a new
nested-transaction list, and rebuilds parent EIP-7702 batch calldata
once.
- Keeps `updateAtomicBatchData` as the existing asynchronous API for
consumers that require gas estimation, while synchronously clearing
stale gas, gas-limit, gas-used, simulation, security-alert, and
gas-revert metadata when new batch calldata is published.

#### Transaction Pay architecture

Transaction Pay continues to use its existing state-driven quote
lifecycle:

1. The client asynchronously prepares transaction-specific calldata.
2. The client commits required assets, all nested calldata, and parent
EIP-7702 calldata in one `updateTransactionMetadata` call.
3. `TransactionPayController` observes the Transaction Controller state
change.
4. It derives required payment tokens from the updated transaction.
5. The existing latest-wins quote path starts one quote request and
aborts any older quote request for the same transaction.

The earlier proof-of-concept-specific
`TransactionPayController.updateAmount` action, constructor preparation
callback, explicit quote invocation, quote suppression set, and
duplicate quote-cancellation API have been removed. This leaves
transaction metadata as the integration boundary and the existing
listener as the single quote trigger.

### Flow

```text
Before
======
Update/Done
    |
    +--> update approval call --> rebuild parent --> publish
    |                                      `--> quote generation A (superseded)
    |
    `--> update deposit call  --> rebuild parent --> publish
                                           `--> quote generation B --> Relay

After
=====
Update/Done
    |
    `--> client prepares complete approval + deposit calldata
             |
             `--> one synchronous metadata update
                     - required assets
                     - all nested calldata
                     - parent EIP-7702 calldata
                     - clear stale derived metadata
                     - skip automatic re-simulation
                          |
                          `--> existing state listener
                                   |
                                   `--> one latest-wins quote --> Relay
```

### Mobile first adopter

The companion Mobile PR owns the Money Account-specific asynchronous
preparation because it has the required vault configuration, provider
access, and transaction builders.

Its coordinator:

- shares identical in-flight amount intents;
- prevents superseded asynchronous preparations from committing stale
results;
- validates the approval/deposit template again against current metadata
at commit time;
- derives `requiredAssets` from current metadata rather than replacing
it from a stale pre-request snapshot;
- applies both indexed calldata updates in one `updateEIP7702BatchData`
call;
- clears stale gas, simulation, security-alert, and simulation-revert
metadata;
- relies on the existing Transaction Pay listener to launch the quote.

The optimized path remains feature-gated, and unsupported Money Account
intents retain the legacy path.

### Configurable automatic re-simulation

Changing `txParams.data` through the normal transaction update path
triggers an asynchronous re-simulation check. For this MetaMask Pay
flow, calldata is produced by controlled builders using known providers,
and re-simulation is not required to construct the vendor quote.
`updateTransactionMetadata` lets controlled callers make this
performance and security policy explicit while leaving all other update
paths unchanged.

### Benchmark result

A follow-up React Native DevTools trace used the same inferred
Update/Done and executable-commit anchors:

| Segment | Baseline | PoC | Change |
| --- | ---: | ---: | ---: |
| Update/Done → Relay request | 3,393.546 ms | **1,512.891 ms** |
**-55.4%** |
| Relay request | 4,107.940 ms | 5,118.617 ms | +24.6% |
| Relay response → executable quote | 1,001.045 ms | **805.860 ms** |
**-19.5%** |
| **Observed total** | **8,502.531 ms** | **7,437.368 ms** | **-12.5%**
|

The Relay request regressed by 1.011 seconds in the second sample even
though this PR does not optimize Relay. Treating Relay as the same
4.108-second contribution in both traces gives a normalized PoC total of
**6,426.691 ms**, a **2,075.840 ms / 24.4% end-to-end improvement**.
Client-controlled time fell from **4,394.591 ms to 2,318.751 ms**, a
**47.2% reduction**.

Structural trace evidence:

- 29 → 19 requests in the measured window
- 18 → 11 requests before Relay
- two → one `eth_estimateGas` calls
- 1,640 ms → 483 ms of pre-request React scheduler time
- 1,091 ms → 313 ms of pre-request render time

These are two simulator traces, not production p50/p95 measurements.
Relay latency should continue to be reported separately, and old/new
runs should be alternated over multiple samples. The trace predates the
final review-driven architecture, so it is directional evidence rather
than a benchmark of the exact final code.

### Correctness and rollout constraints

- Indexed nested updates are validated before transaction state is
published.
- Required assets, nested calldata, and parent calldata are committed
together or not at all.
- The EIP-7702 utility is applied against current transaction metadata.
- Superseded Mobile preparations cannot commit stale transaction data.
- Transaction Pay's existing state listener remains the only quote
trigger.
- Existing quote generation remains latest-wins per transaction.
- Normal transaction update flows continue to use automatic
re-simulation.
- The Mobile integration is feature-gated and retains the legacy path.
- Security/privacy review is still required before rollout to confirm
whether Sentinel gates execution only or must also gate vendor
disclosure.

### Validation

Core:

- Focused Transaction Controller tests pass with coverage disabled;
Transaction Pay listener and quote integration tests also pass.
- `yarn build` passes for the complete monorepo.
- Transaction Controller messenger action-type checks pass.
- Transaction Controller changelog validation passes.
- Targeted ESLint and `git diff --check` pass.
- `yarn lint:misc:check` passes, including the final EIP-7702 utility
formatting fix.
- `yarn workspace @metamask/transaction-controller run test
--runTestsByPath src/TransactionController.test.ts --coverage=false`
passes.

Mobile companion:

- 33 focused tests pass for amount coordination, controller
initialization, and the confirmation hook.
- Targeted ESLint, Prettier, messenger action-type checks, and `git diff
--check` pass.
- TypeScript validation passes when checked against the locally built
Transaction Controller from this PR; the Mobile PR must consume the
released package version before merge.

## References

- Mobile first-adopter PR:
[MetaMask/metamask-mobile#33433](MetaMask/metamask-mobile#33433)
- Baseline trace SHA-256:
`28e517a73d33db3aa63470f9bc55bdbb47fc61060b09227dce3c5df2d4ae13a9`
- PoC trace SHA-256:
`e80c86da8913924a228627d851ab75965174a30c7607cee1416917aa2c34f`

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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
- [ ] I've introduced breaking changes in this PR and have prepared
draft pull requests for clients and consumer packages to resolve them

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core transaction state, gas estimation, and re-simulation
policy; incorrect metadata clearing or `skipResimulate` misuse could
affect quotes and safety checks for EIP-7702 batch flows.
> 
> **Overview**
> Adds **`updateTransactionMetadata`** and the messenger action
**`TransactionController:updateTransactionMetadata`**, so callers can
mutate the latest transaction metadata in one synchronous update via a
callback, with optional **`skipResimulate`** to bypass automatic
re-simulation when calldata changes.
> 
> Exports pure **`updateEIP7702BatchData`** (and
**`NestedTransactionUpdate`**) to apply indexed nested calldata updates,
rebuild parent EIP-7702 batch data without mutating the input, and
validate duplicate or missing indices.
> 
> **`updateAtomicBatchData`** now uses that helper, validates the
transaction exists up front, clears stale gas, simulation,
security-alert, and gas-revert metadata when batch calldata changes
(while preserving receipt/simulation reverts where applicable), and
returns the regenerated batch calldata.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1344bed. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
)

## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

Veda labels its linear annualized yield as APY, which underreports the
compounded yield shown to Money account users. Convert all Veda vault
APR fields to daily compounded APY.

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->
Fixes: [MUSD-1207: [Feature] APY display – Convert APR to compounded APY
in MetaMask
UI](https://consensyssoftware.atlassian.net/browse/MUSD-1207)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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)
- [x] 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]
> **Medium Risk**
> Changes numeric yield values exposed to UI and downstream consumers of
`getVaultApy`, which can affect displayed rates and any logic keyed on
prior APR-like values.
> 
> **Overview**
> **Vault APY from `getVaultApy` is now daily-compounded APY**, not the
linear APR values Veda returns under “APY” field names.
> 
> A new `convertAprToApy` helper applies `(1 + apr/365)^365 - 1` during
`normalizeVaultApyResponse`, covering top-level `apy`,
`globalApyBreakdown` (`maturityApy`, `realApy`), and each
`realApyBreakdown` entry (`apy`, `apyNet`). **Fees are unchanged.** Docs
and types note that raw Veda fields are APR and that consumers receive
compounded decimals.
> 
> Tests and changelog are updated to match the new numbers (e.g. 5% APR
→ ~5.13% APY).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a13c518. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

Major
- core-backend

Minor
- assets-controller
- transaction-controller

Patch
- assets-controllers
- client-utils

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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]
> **Medium Risk**
> The major `core-backend` DeFi position-type change can break consumers
that relied on the old `V6_DEFI_POSITION_TYPES` union; widespread
`transaction-controller` bumps also affect tx/gas behavior from 69.3.0.
> 
> **Overview**
> This is a **release train** PR: it bumps the root monorepo to
**1155.0.0** and publishes aligned versions for the packages called out
in the release notes, with **changelog sections** moved from
`[Unreleased]` into the new tags and **`package.json` / `yarn.lock`**
updated so dependents pick up the new ranges.
> 
> **`@metamask/core-backend` 8.0.0** (major) carries a **breaking**
alignment of `V6_DEFI_POSITION_TYPES` / `V6DeFiPositionType` with
Accounts API / Zerion fungible position types (`deposit`, `loan`,
`locked`, `staked`, `reward`, `wallet`, `investment`). Anything on
**`^7.0.0`** is bumped to **`^8.0.0`** in this diff (e.g.
`assets-controller`, `assets-controllers`, `client-utils`,
`transaction-controller`).
> 
> **Minor** releases: **`@metamask/assets-controller` 11.3.0**,
**`@metamask/assets-controllers` 110.0.1**,
**`@metamask/transaction-controller` 69.3.0**. **Patch**:
**`@metamask/client-utils` 1.3.1**. Downstream controllers (bridge,
bridge-status, earn, EIP-5792, wallet, etc.) only change their
**`transaction-controller`** (and where applicable **assets** /
**core-backend**) dependency pins and changelog notes for **MetaMask#9693**.
> 
> There is **no application source** in this diff beyond version
metadata and lockfile resolution.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
45eb927. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ape (MetaMask#9650)

## Explanation

Ramps (buy/sell) orders currently have no representation in the shared,
cross-client `ActivityItem` model that Send/Swap/Bridge/etc. already
use, so consumers (extension, mobile) have had to maintain bespoke,
order-specific UI to show them instead of the generic activity list and
details pipeline.

This PR:

- Adds `'rampBuy' | 'rampSell'` to `ActivityKind` and a matching arm to
the `ActivityItem` union, covering the order's fiat/token amounts, fee,
provider info (id/name/link), status description, and manual-payment
details.
- Adds an optional `id` field to the shared `ActivityData` base type. A
pending ramp order has an empty `txHash` (no on-chain tx exists yet
while awaiting fiat settlement), so consumers need a stable identifier
to key list rows / open details before settlement. `id` is populated
only for `rampBuy`/`rampSell` (from the provider's order id) and left
`undefined` for every other kind — no behavior change for existing
consumers.
- Adds `mapRampsOrder`, a pure mapper (following the existing
`mapLocalTransaction`/`mapApiTransaction`/`mapKeyringTransaction`
pattern) that normalizes a ramps order into an `ActivityItem`, mapping
the 8-value `RampsOrderStatus` down to the existing 4-value `Status`.
- The mapper's input type (`RampsOrderLike`) redeclares the narrow
subset of `RampsOrder` (from `@metamask/ramps-controller`) it needs,
rather than importing it — `client-utils` and `ramps-controller` don't
depend on each other today, and this keeps it that way.
- Chain id normalization is chain-agnostic: it reuses the existing
`formatChainIdToCaip` helper (already used elsewhere in this package for
Solana/Bitcoin/Stellar-aware mappers) rather than hardcoding an EVM
namespace, so it passes through an already-CAIP-formatted chain id
unchanged and only assumes `eip155` for a bare numeric/hex reference
(today's only observed format from the ramps API).

## References

Extension Client PR:
MetaMask/metamask-extension#44689

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Additive types and a pure mapper in client-utils; no auth or
transaction submission changes, though consumers must handle new
`ActivityKind` variants and optional ramp `chainId`.
> 
> **Overview**
> Adds **`rampBuy` / `rampSell`** to the shared activity model so ramps
orders can use the same list/details pipeline as other activity kinds.
Ramp items carry fiat/token amounts, fees, provider metadata, payment
instructions, and an optional **`data.id`** when there is no real
**`hash`** yet (pending fiat settlement).
> 
> Introduces **`mapRampsOrder`** (plus exported **`RampsOrderLike`**) to
normalize ramps controller–shaped orders: status → shared **`Status`**,
buy/sell/deposit typing, chain id resolution (network → crypto
chain/asset fallbacks), and filtering that returns **`null`** for hidden
statuses or **`excludeFromPurchases`**. Placeholder **`txHash`** values
are dropped so list keys do not collide.
> 
> **`formatChainIdToCaip`** now treats an empty chain id as
**`undefined`** (avoids **`eip155:0`** on precreated stub orders).
Changelog updated; tests cover the mapper and CAIP edge case.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
68ca1fb. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: George Weiler <george.weiler@consensys.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

analytics-controller is enrolled in `lint:tsc` (it has a
`tsconfig.lint.json` and is listed in the root `tsconfig.lint.json`).
Once it starts importing geolocation-controller and controller-utils
(the geo enrichment work in MetaMask#9691), and neither of those is a `lint:tsc`
project yet, tsc pulls their source straight into the
analytics-controller project and fails with rootDir errors:

```
error TS6059: File '.../controller-utils/src/types.ts' is not under 'rootDir' '.../analytics-controller'
error TS6307: File '.../controller-utils/src/types.ts' is not listed within the file list of project '.../analytics-controller/tsconfig.lint.json'
```

This turns the `lint:tsc` job red on MetaMask#9691 and blocks the work.

This PR removes `packages/analytics-controller/tsconfig.lint.json` and
its entry in the root `tsconfig.lint.json`, so analytics-controller is
no longer type checked by `lint:tsc`. `yarn lint:tsc` is green with this
change.

This is a stopgap to unblock the current work, not the end state. The
proper fix is to make controller-utils and geolocation-controller
`lint:tsc` projects (give them their own `tsconfig.lint.json`) and
enroll analytics-controller again. That is a bigger change because it
also turns on type checking for those packages' test files, which
surfaces separate pre existing type errors that need their own cleanup.

## References

- Unblocks MetaMask#9691

## 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](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
## Explanation

Minor
- client-utils (`1.3.1` → `1.4.0`)
- Add `mapRampsOrder` for mapping ramps buy/sell orders into the shared
activity item shape, and add `rampBuy`/`rampSell` to `ActivityKind` and
`ActivityItem` ([MetaMask#9650](MetaMask#9650))

## References

* Related to MetaMask#9650

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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
…troller (MetaMask#9270)

## Explanation

Today `NetworkController` emits no analytics. It publishes
`rpcEndpointUnavailable` and `rpcEndpointDegraded` events, and **each
client app independently** subscribes to them and turns them into the
`RPC Service Unavailable` / `RPC Service Degraded` Segment events. The
extension still routes those through `MetaMetricsController`; mobile
already routes them through `AnalyticsController`. The handlers are near
identical duplication across both repos.

This moves that translation into `NetworkController` so it is written
once and delivered through the `AnalyticsController:trackEvent` action
over the messenger (which is the whole reason `AnalyticsController`
exists). Clients can then delete their duplicated handlers and just wire
the new option.

The feature is fully opt in via a new optional `analytics` constructor
option. When omitted, no analytics are emitted and `AnalyticsController`
is never called, so existing consumers are unaffected. The two genuinely
client specific pieces are injected because core cannot know them:

- `isRpcEndpointUrlPublic(url)`: whether an endpoint URL is safe to
report verbatim (depends on the client network lists, Quicknode URLs,
Infura key)
- `rpcServiceEventsSampleRate`: the proportion of events to emit
(depends on the build environment, typically 1% in production and 100%
in dev)

Core reads `analyticsId` via `AnalyticsController:getState`, applies the
sample rate with `generateDeterministicRandomNumber`, skips local
connection errors, builds the properties, and delivers the event.
`AnalyticsController` keeps owning consent gating.

## What changed

- New `src/rpc-service-events.ts`: pure helpers
(`sanitizeRpcEndpointUrl`, `buildRpcServiceEventProperties`,
`toAnalyticsTrackingEvent`) plus the `NetworkControllerAnalyticsOptions`
and `RpcServiceEventName` types
- `src/NetworkController.ts`: new optional `analytics` option, widened
`AllowedActions` with `AnalyticsController:getState` +
`AnalyticsController:trackEvent`, two guarded self subscriptions, and a
private `#trackRpcServiceEvent`
- Added `@metamask/analytics-controller` dependency and tsconfig
references (no dependency cycle)
- `index.ts`, `tests/helpers.ts`, `CHANGELOG.md`, and a new
`tests/NetworkController.analytics.test.ts`

## Open question

`AnalyticsTrackingEvent` has no `category` field, so the old `category:
'Network'` is not carried for now. Confirming with the analytics owners
how they want category mapped before relying on it downstream.

## Follow ups (separate repos)

- metamask-extension: delete `messenger-action-handlers.ts` +
`utils.ts`, pass the `analytics` option, delegate the two
AnalyticsController actions. This is where the extension stops using
`MetaMetricsController` for these events.
- metamask-mobile: same deletion and wiring (it already delegates
AnalyticsController).

## References

N/A

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
- [x] I've manually tested code paths covered by the new tests

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Breaking messenger requirements affect all NetworkController
consumers; analytics touches RPC failure paths but defaults to off and
failures are swallowed via captureException.
> 
> **Overview**
> **Centralizes RPC health analytics in `NetworkController`** so clients
no longer need duplicate subscribers on `rpcEndpointUnavailable` /
`rpcEndpointDegraded`. The controller subscribes to those events and,
when configured, emits **RPC Service Unavailable** and **RPC Service
Degraded** via `AnalyticsController:trackEvent`.
> 
> New optional constructor/instance option **`analyticsOptions`**
(`isRpcEndpointUrlPublic`, `rpcServiceEventsSampleRate`) defaults to no
tracking (`sample rate 0`, URLs reported as `'custom'`). Tracking skips
local connection errors, requires a non-empty `analyticsId`, applies
deterministic sampling, and sanitizes endpoint URLs in new
`rpc-service-analytics` helpers.
> 
> **Breaking messenger wiring:** `NetworkControllerMessenger` must
delegate **`AnalyticsController:getState`** and
**`AnalyticsController:trackEvent`**. `@metamask/wallet` passes
`analyticsOptions` through and delegates those actions; **`wallet-cli`**
registers no-op Analytics handlers (empty `analyticsId`) so daemon
startup does not throw. Adds `@metamask/analytics-controller` dependency
and tests.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
10a3f4c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…Mask#9655)

## Explanation

Abandoned `release/*` PRs can sit open and block other engineers from
starting a new release. There is no automated cleanup today, so stale
release candidates have to be closed by hand.

This PR adds a scheduled GitHub Actions workflow (every 30 minutes, plus
`workflow_dispatch`) that:

- Finds open same-repo PRs whose head branch starts with `release/`
- Closes them after **3 hours of inactivity** (`updated_at`)
- Leaves a comment explaining why
- Deletes the release branch **only when its tip SHA is unchanged**

Fork heads, PRs in the merge queue, PRs with auto-merge enabled, and PRs
labeled `release:keep-open` are skipped. The releasing docs are updated
to describe this policy.

Before destructive actions, the workflow:

- Fetches a fresh GraphQL snapshot and skips if the PR is no longer
eligible (open / skip label / fork / 3h / merge-queue or auto-merge)
- Closes before commenting so a failed close does not reset the stale
timer
- Re-fetches the branch tip immediately before `deleteRef` and skips
deletion unless the SHA still matches
- Comments only after a successful close and the delete attempt;
tip-move skips and real delete failures get distinct note wording
(successful deletes rely on GitHub’s normal closed-PR UI)
- Continues past per-PR API failures so one error does not abort the
rest of the run

## Flow

```mermaid
flowchart TD
  trigger(["cron every 30m / workflow_dispatch"]) --> checkout[Checkout and setup]
  checkout --> listOpen[List open PRs via REST paginate]
  listOpen --> candidates[Filter candidates:<br/>same-repo release/*,<br/>no release:keep-open]

  candidates --> more{"More candidates?"}
  more -->|no| done([Done])
  more -->|yes| snap[GraphQL snapshot]

  snap --> elig{"Eligible?<br/>OPEN, not fork, not skip label,<br/>updatedAt older than 3h,<br/>not merge-queue / auto-merge"}
  elig -->|no| more
  elig -->|yes| closePR[Close PR]

  closePR --> closeOk{Close succeeded?}
  closeOk -->|no| more
  closeOk -->|yes| getRef[git.getRef for head branch]

  getRef --> tipOk{"Tip SHA still matches<br/>snapshot headRefOid?"}
  tipOk -->|refresh failed| keepRefresh[Keep branch:<br/>kept-refresh-failed]
  tipOk -->|moved| keepMoved[Keep branch:<br/>kept-head-moved]
  tipOk -->|unchanged| deleteRef[git.deleteRef]

  deleteRef --> delOk{Delete succeeded?}
  delOk -->|yes| deleted[Branch deleted]
  delOk -->|no| keepFail[Keep branch:<br/>kept-delete-failed]

  keepRefresh --> comment[Comment with outcome]
  keepMoved --> comment
  deleted --> comment
  keepFail --> comment
  comment --> more
```

## References

N/A

## Checklist

- [ ] I've updated the test suite for new or updated code as appropriate
- [x] 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](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

## Test plan

- [ ] Confirm workflow checks / actionlint pass on this PR
- [ ] Optionally run via `workflow_dispatch` against a throwaway stale
`release/*` PR and verify comment, close, and branch delete
- [ ] Confirm a recently updated release PR is left alone
- [ ] Confirm a PR with `release:keep-open` is left alone
- [ ] Confirm a late push (head SHA change) causes branch deletion to be
skipped and the comment says the branch was kept

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> The workflow can close PRs and delete `release/*` branches using
`GITHUB_TOKEN`; safeguards (fresh snapshot, SHA check, skip label) limit
mistakes but mistaken closure would disrupt in-flight releases.
> 
> **Overview**
> Adds automated cleanup for abandoned **release** PRs so they no longer
block new releases.
> 
> A new scheduled GitHub Actions workflow runs every 30 minutes (and on
`workflow_dispatch`) and executes `scripts/close-stale-release-prs.mts`.
That script finds open same-repo PRs on `release/*` heads, closes them
after **3 hours** without activity (`updatedAt`), posts an explanatory
comment, and deletes the branch only if the tip SHA is unchanged. It
skips fork heads, merge-queue/auto-merge PRs, and anything labeled
`release:keep-open`. Per-PR failures are logged and do not stop the rest
of the run.
> 
> **Releasing** docs now describe the 3-hour policy and the opt-out
label. Root `package.json` gains `@actions/core` and `@actions/github`
for the script.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
528c27c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

After MetaMask#9671 removed the hardcoded nativeAssetsByCaipChainId list,
clients are left to resolve native tokens for certain activity types
where the API has not provided metadata.

This PR restores native `assetId`s using the `@metamask/slip44` package.

## References

- Follow-up to MetaMask#9671
- Extension consumer will bump `@metamask/client-utils` after publish

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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

Made with [Cursor](https://cursor.com)
## Explanation

This is a release for `network-enablement-controller` and all the
dependent packages.

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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]
> **Medium Risk**
> No new code in the diff, but shipping network-enablement 6.0.0 and
config-registry 1.0.0 propagates documented breaking messenger and
state-shape changes to all dependents that adopt this release train.
> 
> **Overview**
> Monorepo release **1157.0.0** with no runtime code edits in this
PR—only version bumps, changelogs, `package.json` dependency ranges, and
`yarn.lock`.
> 
> **`@metamask/network-enablement-controller` 6.0.0** is the anchor
release; dependents are republished to pull **`^6.0.0`** and aligned
asset packages: **`assets-controller` 11.3.1**, **`assets-controllers`
110.0.2**, **`network-connection-banner-controller` 0.1.1**, plus
**`bridge-controller`** and **`transaction-pay-controller`** dependency
updates. **`@metamask/config-registry-controller`** is released as
**1.0.0** and wired into network-enablement at **`^1.0.0`**.
> 
> Consumers upgrading through this release inherit **breaking changes
already documented** in those packages’ changelogs (e.g.
network-enablement’s **`ConfigRegistryController:getState`** messenger
requirement and config-registry **`networks`** keyed by
**`Caip2ChainId`**), not new logic introduced here.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
06b5ed2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Explanation

Releases `@metamask/client-utils@1.5.0`.

Restores native `assetId` on activity tokens and network fees when
available.

## References

- Follow-up release for
[MetaMask#9701](MetaMask#9701)
- Replaces closed [MetaMask#9703](MetaMask#9703)
(auto-closed after 3hr inactivity window)

## Checklist

- [ ] I've updated the test suite for new or updated code as appropriate
- [x] 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/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

Made with [Cursor](https://cursor.com)
…#9691)

## Explanation

Metric events need to carry the user's geolocation (country, region,
timezone) before reaching downstream destinations, as decided in
[ADR-0008](MetaMask/decisions#217). Today
Extension events reach Segment unenriched, and the underlying
geolocation API only returned a country-level location code. This PR
implements the MetaMask-native, client-side enrichment path the ADR
selected.

**`geolocation-controller`**

- `GeolocationApiService` now targets the `v2` geolocation endpoint and
exposes `fetchGeolocationData()`, returning a `GeolocationData` object
(`{ country, region, timezone }`) where each field is independently
validated and `null` when missing/invalid. The existing
`fetchGeolocation()` location-code API is preserved, now derived from
the data via a shared `toLocationCode()` helper. TTL caching and
in-flight request deduplication are retained.
- `GeolocationController` gains `getGeolocationData()` and three new
(non-persisted) state fields: `country`, `region`, `timezone`.
- New messenger actions: `GeolocationController:getGeolocationData` and
`GeolocationApiService:fetchGeolocationData`.

**`analytics-controller`**

- Optionally attaches `country_code`, `region`, and `timezone` to
`context.location` on non-anonymous track, identify, and view payloads.
Only fields the API could determine are included, and caller-provided
`location` fields are preserved (resolved fields take precedence).
- Enrichment is gated behind a new `isGeolocationEnabled` constructor
option (default `false`), matching the existing
`isAnonymousEventsFeatureEnabled` / `isEventQueuePersistenceEnabled` /
`isPreConsentQueueEnabled` pattern. When disabled, the controller never
calls the geolocation action, so compositions that don't opt in don't
need to register it.
- When enabled, geolocation is resolved once during `init` — now
asynchronous (`Promise<void>`) — before any queued/pre-consent events
are replayed, so replayed events carry the same location as new ones.
Resolution is best-effort: failures are logged and events still deliver.
- Per the ADR, **anonymous payloads carry no location**.

Both packages' changelogs are updated, and the monorepo dependency graph
in the README reflects the new `analytics-controller →
geolocation-controller` edge.

## References

- Implements [ADR-0008: Geo-Enrichment of Extension and Mobile Metric
Events](MetaMask/decisions#217)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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)
- [x] 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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Breaking async `init()` and new geolocation API semantics affect all
analytics consumers; opt-in enrichment touches PII-adjacent context on
identified events, with documented wiring order when enabled.
> 
> **Overview**
> **Geolocation** moves to the **`v2`** JSON API: new
**`fetchGeolocationData`** / **`getGeolocationData`** return validated
**`country`**, **`region`**, and **`timezone`** (each nullable). Legacy
**`fetchGeolocation`** / **`getGeolocation`** stay best-effort and
derive ISO location codes via **`toLocationCode`** (US/CA still get
region suffix). **`getGeolocationData`** **rejects** on failure so
callers avoid stale enrichment. Controller state gains **`country`**,
**`region`**, **`timezone`**.
> 
> **Analytics** optionally adds **`country_code`**, **`region`**, and
**`timezone`** under **`context.location`** on **non-anonymous** track,
identify, and view payloads when **`isGeolocationEnabled`** is true
(default **false**). Location is resolved once in async **`init()`**
(before queue replay) via
**`GeolocationController:getGeolocationData`**; failures are logged and
events still send without location. Anonymous split payloads still omit
location; **`init()`** is now **`Promise<void>`** and deduplicates
concurrent calls.
> 
> Adds **`@metamask/geolocation-controller`** to analytics-controller
and documents the new dependency edge in the README.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
06cd92e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Explanation
Create a new package to extract various pieces of code related to the
money account.

The intention of this is to capture as much of non-platform specific
code as possible, such that we can more easily share code between mobile
and extension.

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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**
> New additive package with no in-repo consumers yet; behavior is
covered by tests and only affects clients once they adopt the
dependency.
> 
> **Overview**
> Introduces **`@metamask/money-account-utils`** as a new Earn-owned
workspace package so money-account logic can be shared across Extension
and Mobile instead of living only in clients.
> 
> The initial export is **mUSD support ported from MetaMask Mobile**:
token metadata and per-chain address/CAIP asset maps,
**`getTokenDisplaySymbol`** (registry `MUSD` → branded `mUSD`), and
guards **`isMusdToken`**, **`isMusdTokenOnChain`**, and
**`isMusdOnMoneyAccountChain`** (Money Account activity limited to Monad
while mUSD is also on Mainnet/Linea/BSC). Unit tests target full
coverage.
> 
> Monorepo wiring adds the package to **CODEOWNERS**, **`teams.json`**,
root **`tsconfig`**, README package list/dependency graph, and
**`yarn.lock`**. Root **`lint:eslint`** Node heap is raised from 8192MB
to 10240MB.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1c82229. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…ts (MetaMask#9699)

## Explanation

The `PhishingController` address poisoning check
([MetaMask#8171](MetaMask#8171)) builds its
known-recipients set from confirmed transactions in
`TransactionController` state, using `txParams.to` as the recipient. For
ERC-20/ERC-721/ERC-1155 token transfer methods (`transfer`,
`transferFrom`, `safeTransferFrom`), `txParams.to` is the **token
contract**, not the address receiving the tokens — the actual recipient
is encoded in the calldata. As a result:

* A lookalike of the actual token recipient did **not** trigger a
poisoning warning (the address the user actually pays is missing from
the set).
* A lookalike of the token contract **did** trigger a warning (the
contract is not a recipient the user pays).

This was validated live against a dev build of the extension: after a
confirmed ERC-20 `transfer`, the token contract address appeared in the
known-recipients set while the decoded calldata recipient did not.

### Solution

`TransactionController` already solves this exact problem for
first-time-interaction checks with a private `getEffectiveRecipient`
helper that decodes the recipient from calldata for token transfer types
(falling back to `txParams.to` otherwise). This PR:

* Extracts `getEffectiveRecipient` from
`utils/first-time-interaction.ts` into a new shared `utils/recipient.ts`
and exports it from `@metamask/transaction-controller` (behavior
unchanged for the first-time-interaction flow).
* Uses it in `PhishingController.#getRecipientAddressesFromTransaction`
so confirmed token transfers contribute the **decoded recipient** to the
known-recipients set, replacing the token contract address.

Generic contract interactions are intentionally unchanged: their
`txParams.to` is still added, since there is no decodable recipient to
substitute.


## Manual testing

1. In `metamask-extension/package.json`, add both Core preview packages
to `resolutions`:

   ```json
"@metamask/phishing-controller":
"npm:@metamask-previews/phishing-controller@17.3.0-preview-5da60b6",
"@metamask/transaction-controller":
"npm:@metamask-previews/transaction-controller@69.3.0-preview-5da60b6"
   ```

2. Run `yarn install` and start a development build of MetaMask
Extension.
3. Submit and confirm a standard ERC-20 `transfer` to a test recipient
address.
4. Inspect the address-poisoning known recipients after the transaction
is confirmed, or check lookalike candidates through
`PhishingController.checkAddressPoisoning`.
5. Confirm that a lookalike of the recipient encoded in the transfer
calldata matches the actual recipient and triggers address-poisoning
detection.
6. Confirm that a lookalike of the ERC-20 contract address does not
match the contract as a known recipient from this transfer.
7. Submit and confirm a generic contract interaction, then confirm its
`txParams.to` contract address is still added as a known recipient.

## References

* Related to [MetaMask#8171](MetaMask#8171)
(original address poisoning detection)
* Related to PSAFE-555

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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]
> **Medium Risk**
> Changes security-sensitive address-poisoning recipient hydration;
logic is covered by tests and reuses existing decoding with fallbacks to
`txParams.to`.
> 
> **Overview**
> **Address poisoning** now builds known recipients from the real payee
on confirmed ERC-20/ERC-721/ERC-1155 transfers, not the token contract
in `txParams.to`.
> 
> `getEffectiveRecipient` is moved out of first-time-interaction into
shared `utils/recipient.ts`, exported from
`@metamask/transaction-controller`, and wired into
`PhishingController.#getRecipientAddressesFromTransaction`.
First-time-interaction behavior is unchanged; generic contract
interactions still use `txParams.to`. Tests cover decoding and the
phishing integration case.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5da60b6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
jiexi and others added 27 commits August 26, 2026 21:22
## Explanation

* Replace KycService.submitConsents (POST /consents) with session-scoped
GET/POST /sessions/{id}/disclaimers for idOS + KYC-provider consents ({
key, version } + credentialReusabilityConsentGiven).
* Record Iron T&Cs separately via POST /vendors/{vendor}/disclaimers
(submitVendorDisclaimers); those content ids are no longer sent on the
session disclaimer POST.
*Consents path order: vendor T&Cs → create UKYC session → session
catalog/consents → SumSub. A 409 is re-checked with a GET and only
treated as success when every accepted document is consented.

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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]
> **High Risk**
> Breaking API and consent orchestration for identity/KYC flows;
incorrect 409 or catalog handling could block verification or skip
required legal consents.
> 
> **Overview**
> **Breaking:** Removes `KycService.submitConsents` (`POST /consents`)
and replaces it with three endpoints: **`submitVendorDisclaimers`** for
Iron/vendor T&C ids, **`fetchSessionDisclaimers`** /
**`submitSessionDisclaimers`** for the idOS + SumSub catalog using `{
key, version }` and **`credentialReusabilityConsentGiven`**.
> 
> On the non-MoonPay consents path, **`KycController`** now runs
**vendor T&Cs → UKYC session create → session disclaimers → SumSub**
(vendor content ids are no longer bundled into session consent POSTs).
**`acceptTermsAndStartSession`** accepts optional
**`credentialReusabilityConsentGiven`**; T&C2 booleans map onto catalog
documents. **409** on session disclaimer POST triggers a re-GET and only
continues when every accepted item is actually consented—otherwise the
flow fails closed and rewinds to **`terms`**.
> 
> UKYC session creation is extracted into **`#createUkycSession`** so
the consents path can create the session before recording disclaimers;
**`startSumSub`** skips wrapping-key/session steps when a session id
already exists. Adds in-memory **`sessionDisclaimers`** /
**`credentialReusabilityConsentGiven`** state and related types; docs
and tests follow the new contract.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
06dc3da. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…9955)

## Explanation

`RampsService.getProviders` and `RampsController.getProviders`
previously discarded the API `sorted` ranking metadata and returned only
the provider list. Clients that need the backend-ranked default (for
example, de-ranking Transak Native in EU/US) had to reconstruct order
locally or hardcode a preferred provider.

This change keeps `sorted` on `ProvidersResponse`, returns the full
response from both methods, and exports `ProvidersResponse` /
`ProviderSortOrder` from the package. Controller state still stores
`providers` only. Existing callers that read `.providers` are unchanged.

This is not a breaking change. After this package is published, clients
can consume `sorted` instead of maintaining a local response type.

## Verification

`RampsService.getProviders` and `RampsController.getProviders` now
return backend ranking metadata (`sorted`) and keep controller state as
the provider list only.

![getProviders ranking
tests](https://vhs.charm.sh/vhs-455vmvhqRJ38a6Ak5OKyhl.gif)

Live ranking this package now preserves (from [API
MetaMask#1135](consensys-vertical-apps/va-mmcx-onramp-api#1135)):

### Before

![BEFORE](https://vhs.charm.sh/vhs-2Vm48LnDQtVOzEMBE8jSWY.gif)

### After

![AFTER](https://vhs.charm.sh/vhs-4bfkwclfppzMVhiGz9DC9K.gif)

User-visible default-provider selection is covered on [Mobile
#35200](MetaMask/metamask-mobile#35200).

## References

* Related to
[TRAM-3837](https://consensyssoftware.atlassian.net/browse/TRAM-3837)
* API:
consensys-vertical-apps/va-mmcx-onramp-api#1135
* Mobile: MetaMask/metamask-mobile#35200
* Workload:
consensys-vertical-apps/va-mmcx-onramp-workload#564

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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


[TRAM-3837]:
https://consensyssoftware.atlassian.net/browse/TRAM-3837?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive API surface on an existing fetch path; controller state and
`.providers`-only callers are unchanged.
> 
> **Overview**
> **`getProviders`** on **`RampsService`** and **`RampsController`** now
forwards the API’s **`sorted`** ranking metadata instead of returning
only **`providers`**. New exported types **`ProvidersResponse`** and
**`ProviderSortOrder`** describe the shape; **`sorted`** is normalized
to an empty array when missing or not an array.
> 
> The controller still writes only the provider list into
**`state.providers.data`** and does not persist **`sorted`**, so callers
that only use **`.providers`** behave as before. Consumers can read
**`sorted`** from the method return value to apply backend default
ordering (e.g. de-ranking certain providers by region) without
duplicating types or hardcoding order locally.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
aa9cf92. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…k#9944)

## Explanation

- Aligns the KYC client with the new capability-auth API: POST /sessions
returns encryption schemas; wrapped secrets go to POST
/sessions/:id/authorizations.
- Drops POST /wrapping-key
- Wraps both the `data_encryption_key` and the `ukyc_capability_token`
with the same NaCl box, packing clientPublicKey || ciphertext into {
data, nonce }.
- Verify both schemas’ `jwtChains` before wrapping. Stop the SumSub
sub-flow if reset() lands mid-flight so a superseded run does not create
a journey or launch the SDK.

## References

See: consensys-vertical-apps/va-mmcx-kyc-api#38

## 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](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]
> **High Risk**
> Breaking API and crypto flow changes for UKYC session authorization;
mistakes could block SumSub or mishandle encryption keys and capability
tokens.
> 
> **Overview**
> **Breaking change:** UKYC session setup no longer uses `POST
/wrapping-key` or `KycService.getWrappingKey`. **`createUkycSession`**
only creates the session and returns per-secret **encryption schemas**;
wrapped **`data_encryption_key`** and **`ukyc_capability_token`** are
sent in a follow-up **`setAuthorizations`** call (`POST
/sessions/{id}/authorizations`).
> 
> **`KycController`** verifies both schemas’ **`jwtChain`** values,
wraps each secret with **`wrapEncryptionKey`**, and posts authorizations
before **`createJourney`**. The **`vendorProcessing`** short-circuit
(relay approved, vendor still pending) is driven by the
**authorizations** response, not session creation.
> 
> **Wire format:** **`wrapEncryptionKey`** now returns **`{ data, nonce
}`** where **`data`** embeds the client X25519 public key ahead of the
ciphertext so the server can open the box without a prior
key-registration step; the capability token is wrapped as UTF-8 bytes of
the encoded header string, not a plaintext API field.
> 
> Tests and docs are updated; exports drop wrapping-key types and add
**`EncryptionSchema`**, **`CapabilityAuthorization`**, and
**`SetAuthorizationsParams`**. Additional **`reset()`** guards prevent
stale **`setAuthorizations`**, journey creation, or SDK launch after the
flow is superseded.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
56ae4c8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

When the Terminal v2 snapshot fails, the controller falls back to
Hyperliquid market data and its local category map. That map still
labels `xyz:CBRS` and `xyz:SPCX` as pre-IPO, although both are public
stocks.

This changes only those two fallback entries to `stock`. It does not
call the deprecated Terminal v1 endpoint or change the v2 request path.

## References

No issue.

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Static category map correction for two symbols on the Hyperliquid
fallback path only; no order, auth, or API behavior changes.
> 
> **Overview**
> When Terminal v2 market metadata is unavailable, perps still
classifies HIP-3 markets via **`HIP3_ASSET_MARKET_TYPES`**.
**`xyz:CBRS`** and **`xyz:SPCX`** were still mapped to **pre-IPO** even
though they trade as public stocks.
> 
> This PR moves both symbols into the **stock** entries (and drops them
from the pre-IPO block) so fallback-driven badges, filters, and category
pills match their listing status. **`xyz:IPOP`** stays pre-IPO. Tests
and the **Unreleased** changelog entry are updated accordingly; Terminal
v2 routing is unchanged.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
252b563. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…#9991)

## Description

`yarn validate:changelog` does not exist. The script is
`changelog:validate`, both at the root and per package. AGENTS.md had
the two halves the wrong way round in both places it mentions it.

Running the documented command just errors with `Couldn't find a script
named "validate:changelog"`, which is easy to misread as "nothing to
validate" rather than "you ran the wrong thing".

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Documentation-only correction with no runtime or API impact.
> 
> **Overview**
> **AGENTS.md** now documents the real Yarn script for changelog checks:
both mentions of the linting/formatting and post-update changelog steps
use **`yarn changelog:validate`** instead of the non-existent **`yarn
validate:changelog`**.
> 
> That aligns agent and contributor docs with the root
**`package.json`** script and workspace usage elsewhere in the repo, so
following the guide no longer fails with a missing-script error.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
fc5f8bb. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
…taMask#9992)

## Explanation

Pending trade drafts currently restore amount, leverage, TP/SL, limit
price, order type, and reduce-only, but not long/short. Clients that
restore a sized draft therefore reopen a short as a long (the form
default). That is an accidental-trade risk once Pro starts restoring
drafts
([MetaMask/metamask-mobile#35367](MetaMask/metamask-mobile#35367)).

This adds an optional `direction` (`'long' | 'short'`) to
`savePendingTradeConfiguration` / `getPendingTradeConfiguration` and to
the pending-config selector. Older drafts without the field still
restore as today. Direction is not a global preference; it lives only on
the 30-second per-market draft and is not copied onto
`selectedOrderType`.

## References

* Related to
[MetaMask/metamask-mobile#35367](MetaMask/metamask-mobile#35367)
* Related to [MetaMask#9922](MetaMask#9922)
* Related to
[TAT-3706](https://consensyssoftware.atlassian.net/browse/TAT-3706)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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

Made with [Cursor](https://cursor.com)

[TAT-3706]:
https://consensyssoftware.atlassian.net/browse/TAT-3706?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

Minor release for `@metamask/remote-feature-flag-controller` -> `6.0.0`
-> `6.1.0`
Minor release for `@metamask/perps-controller` -> `13.0.0` -> `13.1.0`
Also bumps dependencies

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Dependency and changelog-only release with a backward-compatible
optional API on feature-flag refresh; no breaking changes in this PR.
> 
> **Overview**
> **Release 1213.0.0** cuts `@metamask/remote-feature-flag-controller`
at **6.1.0** (from 6.0.0) and rolls that dependency through the
monorepo.
> 
> The **6.1.0** release documents an optional **`force`** parameter on
**`updateRemoteFeatureFlags`**, so callers can refresh remote flags even
when the cache has not expired (default `false`; still no fetch when the
controller is disabled).
> 
> This PR updates **`package.json`** / **`yarn.lock`** and **Unreleased
changelogs** for every direct consumer—e.g. **`assets-controller`**,
**`assets-controllers`**, **`bridge-controller`**,
**`config-registry-controller`**, **`core-backend`**,
**`network-controller`**, **`transaction-controller`**, **`wallet`**,
**`wallet-cli`**, and others—to depend on **`^6.1.0`**. A few packages
(e.g. **`ramps-controller`**, **`wallet-cli`**) consolidate changelog
lines to reflect the full bump path to 6.1.0.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3b2fc15. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Explanation

`@metamask/platform-api-docs` documents the platform API — every
messenger action and event a project exposes. Until now it had one way
of finding them: parse every TypeScript source and declaration file it
can reach (the scan directories, `packages/*/src`, and
`node_modules/@metamask/*/dist/**/*.d.cts`) and walk every type alias
named `*Messenger`.

That is the right approach for this monorepo, which has no single
messenger aggregating every capability. It is a poor fit for a client,
which already declares the complete set on its root messenger.
Re-deriving that from the whole dependency tree means parsing ~11,600
files in `metamask-mobile` and ~4,500 in `metamask-extension`, to
rediscover something the client has written down in one place.

This PR adds a second strategy that reads what the client already
declares.

### `--strategy`

- **`scan`** (default) — unchanged behaviour, and the only option for a
project with no single aggregating messenger.
- **`root-messenger`** — resolves the two types named by
`--root-actions` and `--root-events` (each written `<file>#<TypeName>`)
and lets the TypeScript type checker enumerate them. Only the named
files are opened; the checker pulls in the rest.

Flags belonging to the strategy that wasn't selected are rejected rather
than ignored, via a yargs `.check`, so a mistaken invocation fails
loudly instead of quietly producing docs built the wrong way. The
`<file>#<TypeName>` references are parsed in a yargs `.coerce`, so a
malformed one is reported like any other
bad argument before work begins.

### Why the type checker rather than the AST

This is the non-obvious part. The two clients declare their root unions
differently:

- `metamask-mobile` writes `GlobalActions` by hand as a union of type
references. A syntactic walk would work.
- `metamask-extension` derives `RootMessengerActions` from a registry of
messenger factories via
`MessengerActions<ReturnType<(typeof
MESSENGER_FACTORIES)[…]['getMessenger']>>`.
**There is no syntactic union to walk** — only the type checker can say
what it contains.

Going through the checker handles both shapes with one code path. Once
it reports *which* capability types are in the union, each declaration
is handed to the **existing** extractor in `extraction.ts`, so JSDoc,
handler/payload signatures, source links, and deprecation flags come out
identical to `scan`. The new module is a discovery front-end, not a
second extractor.

Two details worth knowing:

- A capability declared as a **type alias** carries its name and JSDoc
on the *alias* symbol; the plain symbol points at the anonymous object
type. An **interface** has no alias symbol, being its own declaration,
so both are
consulted. Missing the interface case silently dropped 61 actions and 8
events on mobile before it was fixed.
- For a lone generic instantiation (`type Actions = Foo<Bar>`) the
checker attributes the alias to the *root union itself*, which would
hand the extractor the wrong declaration. That case is guarded.

### Failure behaviour

Generation now fails loudly instead of producing an empty site.
`writeOutput` deletes `docs/` before writing, so a root union that
resolves to nothing — a renamed type, or imports that don't resolve —
would previously have replaced a
published docs directory with an empty one and exited `0`. It now
throws, naming both references.

Capability types that can't be documented are reported with their names
rather than counted, in three buckets: declared inline (no name or
JSDoc), unresolved (`any`/`unknown`, usually a failed import), and
unextractable (a shape the extractor rejects). A count alone isn't
actionable at this scale.

### Also fixed: MDX escaping

`escapeJsDocTextForMdx` escaped `{` and `}` but not `<`, which MDX reads
as the start of a JSX tag. A `@returns` comment such as
`Promise<PointsBoostDto[]>` therefore **failed the site build** rather
than rendering:

```
Unexpected character `[` (U+005B) in name, expected a name character…
```

This is pre-existing and independent of the new strategy — `scan`
produces the byte-identical line — but it blocked `--build` and
`--serve` for both clients, so it is fixed here. Affects description,
`@param`, and `@returns` text; handler and payload signatures were
already safe inside fenced code blocks.

---

## Benchmarks

Measured on a warm checkout, doc generation only.

| Project | `scan` | `root-messenger` | Speedup |
| --- | --- | --- | --- |
| `metamask-extension` | 47.9s | **4.7s** | ~10× |
| `metamask-mobile` | 96.9s | **5.7s** | ~17× |

`scan` parses ~5,600 `app/**/*.ts` plus ~6,026 `.d.cts` in mobile, and
~4,472 `.d.cts` in the extension. `root-messenger` opens the entry file
and lets the checker pull in only what the union references.

## Strategy comparison

### `metamask-mobile`

| | `scan` | `root-messenger` |
| --- | --- | --- |
| Namespaces | 112 | 103 |
| Actions | 1184 | 1157 |
| Events | 181 | 171 |
| Unique capabilities | 1365 | 1328 (97.3%) |

The 37 not documented are mostly controllers genuinely **not on the root
messenger** — `PasskeyController` alone accounts for 17, plus
`RatesController` (4), the sample controllers, and the decrypt/encrypt
message managers. Nothing is found by `root-messenger` that `scan`
misses.

### `metamask-extension`

| | `scan` | `root-messenger` |
| --- | --- | --- |
| Namespaces | 119 | 115 |
| Actions | 1209 | 1085 |
| Events | 183 | 176 |
| Unique capabilities | 1392 | 1261 (90.6%) |

99 of the 131-capability gap is `PerpsController`, and the cause is
worth flagging to the extension team rather than treating as a tool
limitation:

```ts
export type PerpsControllerMessenger = Messenger<
  'PerpsController',
  AllowedActions,   // actions Perps may CALL
  AllowedEvents
>;
```

`RootMessengerActions` is `MessengerActions<ChildMessengers>` — the
union of what each child messenger is **allowed to call**, not what each
controller **provides**. A controller whose actions are only invoked
from the UI, never from another controller's messenger, never appears.
`PerpsController` is registered in
`MESSENGER_FACTORIES` yet contributes **zero** constituents.

`root-messenger` documents exactly what the named types contain. Full
coverage in. the extension needs an aggregate of *provided* actions,
which is an extension-side change.

Conversely, `root-messenger` finds 6 capabilities `scan`
misses(`MultichainRoutingService` ×4, `PPOMController` ×2).

Reported-but-skipped, current run: mobile 39 unextractable; extension 2
inline + 33 unextractable.

---

## Usage in clients

Once published, add the dependency and two scripts. For
`metamask-mobile`:

```json
{
  "scripts": {
    "docs:platform-api:build": "platform-api-docs --build --project-label Mobile --strategy root-messenger --root-actions 'app/core/Engine/types.ts#GlobalActions' --root-events 'app/core/Engine/types.ts#GlobalEvents'",
    "docs:platform-api:serve": "platform-api-docs --serve --project-label Mobile --site-base-url / --strategy root-messenger --root-actions 'app/core/Engine/types.ts#GlobalActions' --root-events 'app/core/Engine/types.ts#GlobalEvents'"
  }
}
```

For `metamask-extension`, the label and references change:

```
--project-label Extension
--root-actions 'app/scripts/lib/messenger.ts#RootMessengerActions'
--root-events  'app/scripts/lib/messenger.ts#RootMessengerEvents'
```

Notes for consumers:

- Keep the `#` **inside quotes** — unquoted, most shells treat it as a
comment and silently truncate the argument.
- `--root-actions` / `--root-events` are relative to the project path,
not the shell's working directory.
- Output defaults to `<project-path>/.platform-api-docs`; gitignore it.
- `metamask-extension` additionally needs its `postcss-loader/jiti`
resolution narrowed to `postcss-loader@^8.2.1/jiti`. The unversioned
form also stubs the `postcss-loader@^7.3.4` that `@docusaurus/bundler`
depends on, replacing `jiti` with an empty package and breaking the site
build. Narrowing preserves the stub's original intent for the
extension's own `postcss-loader@8.2.1`.

## References

Fixes: https://consensyssoftware.atlassian.net/browse/WPC-1202
* Consumer PR (mobile):
MetaMask/metamask-mobile#26526
* Consumer PR (extension, includes the `jiti` resolution fix):
MetaMask/metamask-extension#40352

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Changes are confined to the docs CLI and generation pipeline; default
`scan` behavior is preserved with an explicit `strategy` field in tests.
> 
> **Overview**
> Adds a **`root-messenger`** discovery path alongside the existing
**`scan`** default: the CLI accepts `--strategy`, `--root-actions`, and
`--root-events` (`<file>#<TypeName>`), validates that strategy-specific
flags are not mixed, and routes generation through type-checker
resolution of the project’s root action/event unions instead of scanning
the whole tree.
> 
> New **`root-messenger-discovery`** walks those unions (including
checker-derived unions like `MessengerActions<…>`), reuses the shared
extractor in **`extraction.ts`** (with
**`classifyMessengerCapabilityTypeDeclaration`** exported for reuse),
warns on skipped inline or unextractable capabilities, and **throws**
when unions resolve to `any`/`unknown` or would produce zero docs so an
empty site cannot overwrite published output.
> 
> **`escapeJsDocTextForMdx`** now escapes `<` as well as braces so
generic types in JSDoc do not break MDX builds. README, changelog, and
broad CLI/generate/discovery tests cover the new behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1da2bbd. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
## Explanation

`isMaliciousC2Domain` previously checked each request against the C2
blocklist using `Array.includes`, which performs a linear O(n) scan of
hashed domain strings on every call. For a list in the
thousands-of-entries range, each call scans the full list up to 6 times
— once for the exact hostname hash, up to 5 times for parent domain
hashes.

This PR switches the internal representation to `Set<string>`, reducing
each of those scans to an O(1) hash lookup.

**Benchmark results (internal profiling):**
- Per-request time: 2–8ms → 0.05–0.5ms (~10–100x improvement depending
on page complexity; ~50x averaged across pages)
- Potential CPU savings: up to ~300ms per page load
- Background tabs also benefit — confirmed they generate significant
background network requests that all route through this check

**Implementation notes:**
- The conversion from `string[]` → `Set<string>` happens once at
construction time via a new unexported
`InternalPhishingDetectorConfiguration` type. The public
`PhishingDetectorConfiguration` type keeps `c2DomainBlocklist?:
string[]` unchanged — no API break for downstream consumers.
- `isMaliciousC2Domain` now uses `.size` / `.has` in place of `.length`
/ `.includes`
- `getDefaultPhishingDetectorConfig` accepts and threads
`c2DomainBlocklist` through as `string[]`; `processConfigs` no longer
performs a redundant intermediate `Set` construction

Fixes: MetaMask/MetaMask-planning#5611

## References

- Fixes MetaMask/MetaMask-planning#5611

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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/contributing.md#updating-changelogs),
highlighting breaking changes as necessary
- [x] I've prepared draft pull requests for clients and consumer
packages to resolve any breaking changes


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Behavior-preserving internal data-structure change on a hot path;
public types and matching logic are unchanged.
> 
> **Overview**
> **C2 domain blocklist checks** in
`PhishingDetector.isMaliciousC2Domain` now use a `Set` internally
instead of scanning a `string[]` with `includes`, so each hostname and
parent-domain hash lookup is O(1) rather than O(n) (up to several
lookups per request).
> 
> Arrays from config are converted to `Set<string>` once in the
constructor via an internal `InternalPhishingDetectorConfiguration`
type; the exported `PhishingDetectorConfiguration` still exposes
`c2DomainBlocklist?: string[]`. `getDefaultPhishingDetectorConfig` now
accepts and forwards an optional `c2DomainBlocklist` override. The
changelog records the performance change.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0efe622. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

Hyperliquid historical orders were mapped through a separate inline path
that dropped `triggerPx` and the normalized trigger order type.
Consumers therefore received trigger orders without
`Order.triggerPrice`, preventing clients such as MetaMask Mobile from
displaying the trigger price in order history.

This change reuses the existing `adaptOrderFromSDK` mapping used for
open orders, then overlays historical lifecycle fields such as status,
timestamps, and filled/remaining size. It also preserves ordinary
historical market-order classification when Hyperliquid supplies a
non-empty `limitPx` as a slippage cap.

Provider tests now cover distinct trigger and limit prices for Take
Profit Limit and Stop Market orders, plus a regular market order
carrying a slippage-cap price. There are no public type changes or
breaking changes.

Validation:
- `yarn workspace @metamask/perps-controller run test`
- `yarn eslint
packages/perps-controller/src/providers/HyperLiquidProvider.ts
packages/perps-controller/tests/src/providers/HyperLiquidProvider.history.test.ts`
- `yarn build`

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

- Related to
[MetaMask/metamask-mobile#35118](MetaMask/metamask-mobile#35118)
- Addresses [the production history boundary review
finding](MetaMask/metamask-mobile#35118 (comment))

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Localized to HyperLiquid historical order mapping and adapter edge
cases; no public API or breaking type changes, with expanded unit tests.
> 
> **Overview**
> **HyperLiquid order history** now goes through the same
`adaptOrderFromSDK` path as open orders, instead of a separate inline
mapper that dropped trigger metadata.
> 
> Historical rows still get lifecycle overlays (`status`,
`statusTimestamp` / `lastUpdated`, and `remainingSize` from the
exchange’s current `sz`). **`orderType`** for history is derived from
HyperLiquid’s detailed type via
`HISTORICAL_ORDER_TYPE_BY_DETAILED_TYPE`, so trigger and market orders
are not mislabeled as limits when `limitPx` is only a slippage cap.
> 
> **`adaptOrderFromSDK`** tolerates missing `oid`, `orderType`, and
empty `limitPx` so partial history payloads still map instead of failing
silently.
> 
> Tests cover trigger/limit price preservation for TP/SL history, market
orders with slippage-cap prices, and the detailed-type → limit/market
mapping table.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cdd11cd. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

`@metamask/perps-controller` v13 treated HyperLiquid's valid
`waitingForTrigger` TP/SL acknowledgement as a failure because it has no
order ID. The trigger was created, but Mobile displayed
`TPSL_UPDATE_FAILED`.

This change accepts `waitingForTrigger` and reconciles its order ID only
when needed to clean up a mixed-result batch. It reuses existing
REST/cache order IDs to avoid an extra happy-path request, while
preserving rollback for rejected, unknown, and incomplete responses. No
dependencies or other packages changed, and there are no breaking
changes.

## References

- Related to TAT-3846

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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]
> **Medium Risk**
> Changes failure/rollback paths for `updatePositionTPSL` and open-order
reconciliation; incorrect matching could mis-cancel triggers or
mis-report protection loss, though matching requires a single
unambiguous candidate.
> 
> **Overview**
> **Fixes false `TPSL_UPDATE_FAILED` when HyperLiquid accepts position
TP/SL with a bare `waitingForTrigger` acknowledgement (no order ID in
the response).**
> 
> `updatePositionTPSL` now classifies TP/SL placement via
`#readTpslOrderPlacementOutcome`, treating `waitingForTrigger` as
success alongside resting and filled. On the happy path it returns
success without requiring an order ID for every leg. When a mixed batch
fails, `#reconcileTpslOrderPlacementOutcomes` can match new trigger
orders from `frontendOpenOrders` (size, trigger price, side, TP vs SL)
using a pre-placement order ID snapshot so cleanup can cancel reconciled
legs; unreconciled `waitingForTrigger` legs surface
`TPSL_PROTECTION_LOST` instead of a generic update failure. Restoration
after partial failure uses the same TP/SL outcome logic. Tests cover
combined/mixed statuses, replacement with cancel, reconciliation before
mixed-failure cleanup, and restore-on-unknown/incomplete responses.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d3cffda. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

Currently, the lint, build, and test matrix jobs in
`lint-build-test.yml` cancel all other in-progress matrix combinations
as soon as one fails, because `fail-fast` defaults to `true`. This means
a single failing package can hide the results of other packages in the
same run, making it harder to see the full picture of what's broken.

This PR sets `fail-fast: false` on all matrix strategies in the
workflow, so every matrix combination runs to completion regardless of
whether another one fails.

## References

N/A

## 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](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**
> Workflow-only CI behavior change; no application code, security, or
release paths affected.
> 
> **Overview**
> Sets **`fail-fast: false`** on matrix strategies in
`lint-build-test.yml` so one failing combination no longer cancels the
rest of the matrix.
> 
> This applies to **`lint`** (per lint script), **`validate-changelog`**
(per package), **`test-18`**, **`test-20`**, and **`test-22`** (per
changed workspace package), and **`test-wallet-cli-e2e`** (per Node
version). CI runs may take slightly longer when something fails, but PR
checks should surface **all** failing lint scripts, changelog
validations, and package tests in a single run instead of stopping after
the first failure.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
175ba85. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

The first TP/SL edit after Mobile reload could fail before cancellation
or placement because the builder-fee approval lookup sent the one-off
`maxBuilderFee` request over a reconnecting WebSocket. HyperLiquid
rejected it with `WebSocketRequestError`, which `updatePositionTPSL`
surfaced as `TPSL_UPDATE_FAILED`; a later retry succeeded after
reconnection.

This routes builder-fee approval reads through the existing HTTP
`InfoClient`, which is designed for request/response operations and
remains available during WebSocket churn. `maxBuilderFee` was not a
subscription, so this does not remove live updates or change existing
approval-cache semantics. Genuine approval failures still block
replacement before existing protection is cancelled.

Adds regression coverage with distinct WebSocket and HTTP clients and
updates the perps-controller changelog. No dependencies or other
packages are changed.

Validation:
- Full `@metamask/perps-controller` test suite
- Builder-fee and strategy-order suites
- Monorepo build
- Changelog validation and scoped lint/format checks
- Mobile simulator: first `+50/-50` TP/SL edit succeeded after reload

## References

- Discovered while validating
[MetaMask#9995](MetaMask#9995)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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**
> Narrow transport change for a read-only approval lookup on an existing
HTTP code path; regression test covers the TP/SL cold-start scenario.
> 
> **Overview**
> Fixes a case where the **first position TP/SL update after app
reload** could fail with `TPSL_UPDATE_FAILED` because the builder-fee
approval check (`maxBuilderFee`) went over a reconnecting HyperLiquid
WebSocket and threw `WebSocketRequestError`.
> 
> `#checkBuilderFeeApproval` in `HyperLiquidProvider` now requests the
info client with **`useHttp: true`**, so that read uses the HTTP
`InfoClient` path that stays usable during socket churn. Approval
caching and real approval/signing behavior are unchanged; only the
lookup transport changes.
> 
> Adds a regression test that simulates a failing WebSocket
`maxBuilderFee` and asserts cold-start `updatePositionTPSL` succeeds via
HTTP, plus an **Unreleased** changelog entry.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3e2cffb. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Arthur Breton <arthur.breton@consensys.net>
## Summary
- Add `has_sufficient_funds` to Unified SwapBridge Quotes Received
metrics for
[SWAPS-4977](https://consensyssoftware.atlassian.net/browse/SWAPS-4977).
- Preserve explicit slippage intent and normalized slippage limits
across quote, submission, and history metrics for
[SWAPS-4979](https://consensyssoftware.atlassian.net/browse/SWAPS-4979).
- Support client fallbacks for incomplete quote USD and token-symbol
data, while preserving Quick Buy and Batch Sell legacy behavior.

## Test plan
- [x] `yarn workspace @metamask/bridge-controller test --coverage=false
--runInBand --testPathPatterns=src/utils/metrics/properties.test.ts`
- [x] `yarn workspace @metamask/bridge-status-controller test
--coverage=false --runInBand
--testPathPatterns=src/utils/metrics.test.ts`
- [x] Bridge status-controller tests and Batch Sell tests
- [x] Core typecheck and targeted ESLint
- [x] Prettier and `git diff --check`


[SWAPS-4977]:
https://consensyssoftware.atlassian.net/browse/SWAPS-4977?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[SWAPS-4979]:
https://consensyssoftware.atlassian.net/browse/SWAPS-4979?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Analytics and metrics payload changes only; no transaction or auth
logic. Snapshot/test updates reflect corrected slippage and
custom_slippage semantics.
> 
> **Overview**
> Fixes **Unified SwapBridge** analytics so quote-through-completion
events carry consistent balance and slippage fields.
> 
> **Quotes Received** now includes **`has_sufficient_funds`** (from the
quote request’s insufficient-balance flag). **`slippage_limit`** is
always a number (**`0`** means auto when no explicit limit), including
on request metadata and in **`getQuotesReceivedProperties`**, which also
accepts optional client fallbacks for slippage, **`custom_slippage`**,
USD amount, and token symbols when quote data is incomplete.
> 
> **Bridge status** threads **`quotesReceivedContext`** into
submit/pre-confirmation metrics, persists **`customSlippage`** on
history, and prefers that (with Batch Sell / Quick Buy legacy inference)
when rebuilding **`custom_slippage`** and **`slippage_limit`** after
submission instead of inferring only from quote slippage.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b915d67. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

`ExampleDataService` is not type checked, so we had missed some type
inference issues. This PR fixes them.

## References

N/A

## 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](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**
> Test-only example service typing and pagination guards; no production
runtime behavior change.
> 
> **Overview**
> Updates **`ExampleDataService.getActivity`** so infinite-query types
infer correctly once the example is type-checked.
> 
> **`fetchInfiniteQuery`** no longer passes an explicit
**`GetActivityResponse`** generic; **`initialPageParam`** is **`null as
PageParam`** so **`TPageParam`** lines up with
**`getPreviousPageParam`** / **`getNextPageParam`**. Pagination branches
use **`'after' in pageParam`** and **`'before' in pageParam`** instead
of optional chaining on **`pageParam`**, with eslint disables where the
project restricts that syntax.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f9f85dc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Explanation

Release bridge controllers

<!--
Thanks for your contribution! Take a moment to answer these questions so
that reviewers have the information they need to properly understand
your changes:

* What is the current state of things and why does it need to change?
* What is the solution your changes offer and how does it work?
* Are there any changes whose purpose might not obvious to those
unfamiliar with the domain?
* If your primary goal was to update one package but you found you had
to update another one along the way, why did you do so?
* If you had to upgrade a dependency, why did you do so?
-->

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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 with dependency alignment; no new
runtime logic in this diff.
> 
> **Overview**
> This PR **cuts release 1214.0.0** by bumping versions and finalizing
changelogs—there is no application source change in the diff.
> 
> **`@metamask/bridge-controller` 80.1.0** documents shipped work from
earlier PRs: Unified SwapBridge **quote** metrics now include
sufficient-funds and normalized slippage fields ([MetaMask#9986]), and
`@metamask/remote-feature-flag-controller` is bumped to `^6.1.0`
([MetaMask#9980).
> 
> **`@metamask/bridge-status-controller` 75.4.0** documents
post-submission Unified SwapBridge metrics that preserve **slippage
intent** and **normalized slippage limits** ([MetaMask#9986]), and raises its
dependency on `@metamask/bridge-controller` to `^80.1.0`. Root
`package.json` and `yarn.lock` are updated to match.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
811ae77. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Explanation

Bumps `@metamask/eth-sig-util` from `^8.2.0` to `^9.0.0` across the four
packages that depend on it (`eth-json-rpc-middleware`,
`keyring-controller`, `message-manager`, `signature-controller`).

`v9.0.0` contains two breaking changes:

- Drops Node.js 18 and 20 support (minimum is now Node.js 22) — this
repo already runs on Node.js 22+, so no impact.
- `signTypedData` now rejects ambiguous `bool` values (e.g. `0`, `1`,
`'0'`, `''`) instead of coercing them via `Boolean()`. None of the
affected packages pass non-boolean values to `bool` fields, so no code
changes were required.

**Dependency update cascade:**
- `eth-sig-util@9.0.0` update: [MetaMask/eth-sig-util
CHANGELOG](https://github.com/MetaMask/eth-sig-util/blob/main/CHANGELOG.md#900)
- Core controllers: this PR
- Keyring updates:
[MetaMask/accounts#626](MetaMask/accounts#626)
- Mobile resolution:
[MetaMask/metamask-mobile#35410](MetaMask/metamask-mobile#35410)
- Extension resolution:
[MetaMask/metamask-extension#45854](MetaMask/metamask-extension#45854)

## References

- [`@metamask/eth-sig-util` v9.0.0
changelog](https://github.com/MetaMask/eth-sig-util/blob/main/CHANGELOG.md#900)

## 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](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]
> **Medium Risk**
> Touches signing and typed-data validation paths without local code or
test updates; v9’s stricter bool handling could affect edge-case dapp
payloads even if core packages don’t rely on coercion.
> 
> **Overview**
> Bumps **`@metamask/eth-sig-util`** from **`^8.2.0`** to **`^9.0.0`**
in **`eth-json-rpc-middleware`**, **`keyring-controller`**,
**`message-manager`**, and **`signature-controller`**, with matching
**Unreleased** changelog entries and **`yarn.lock`** resolution to
**9.0.0**. There are **no application source changes** in this PR.
> 
> Consumers inherit **v9** behavior indirectly: **`signTypedData`** no
longer coerces ambiguous values into EIP-712 **`bool`** fields, and the
library’s minimum Node version is **22** (this monorepo is already on
Node 22+). Downstream clients may need their own lockfile/resolution
updates to stay aligned.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1565522. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
)

## Explanation

Money account "Pay with" previously read Buy-scoped payment methods even
though Money quotes use a deposit asset and a different
eligible-provider set. That mismatch could offer methods such as Revolut
Pay that cannot produce a Money deposit quote (TRAM-3838 /
metamask-mobile#34109).

### Shared context API and provenance

`getPaymentMethodsForContext` is a new shared Core API. It combines
payment-method fetch and automatic-selection behavior that already
existed in Mobile UB2 with Core's existing provider-resolution helpers,
while allowing callers to choose whether the result updates Buy state.

`#resolveProviderIdsForPaymentMethods` is a new adapter over existing
Core behavior. It reuses `#resolveAllProvidersFlag`,
`#filterProviderIdsBySupport`, `#getSupportingProvidersForRegion`, and
`#resolveProviderIdsForQuote` instead of creating a separate provider
policy.

`mergePaymentMethodsById` was newly designed for multi-provider fan-out.
It was not copied from Mobile.

`normalizeRampsAssetId` is not new behavior. Mobile has carried the same
rule since April 2026 as `normalizeAssetIdForApi`
(metamask-mobile#29037). This PR promotes it into Core's public API so
the rule lives in one place and Mobile can drop its copy.
`providerServesAsset` now uses it too, which is a behavior change:
non-EVM CAIP-19 references are no longer lowercased, since Solana base58
and bitcoin bech32 references are case-sensitive and lowercasing both
sides could report a provider as serving an asset it does not serve.

### Merge scope

`mergePaymentMethodsById` returns a single provider list unchanged by
identity. It preserves the provider's array, row references, ordering,
length, and duplicate IDs, so single-provider consumers such as
standalone Buy keep verbatim provider output just as legacy
`getPaymentMethods` did.

Deduping runs only when two or more provider lists are combined, and it
keeps the first-seen entry per canonical ID. There is no field-level
collision policy, because payment-method metadata is provider-invariant:
the API serves it from a per-region catalog (`PaymentMethodsList.ts`)
and the `provider` query narrows that catalog by ID-set membership
(`RegionsV2Service.ts`, `providerPaymentIds.has(payment.id)`) without
rewriting fields. Two providers returning the same payment ID therefore
return identical `score`, `delay`, and `name`. The same data is already
deduped first-seen-wins by the API itself for its multi-region path.

Provider fan-out does vary array order, because the API sorts by
reliability per request. That affects ordering, not field values, and
`mergePaymentMethodsById` preserves first-seen encounter order.

### Stateful write guards

`updateState: true` reproduces legacy Buy catalog and selection writes,
but it is guarded because the controller mutates shared state from
inside the query function. A consumer's query cache cannot undo that
side effect after a stale request commits.

The write requires the stored region, selected asset, and selected
provider to match the request context. Stored region is trimmed and
lowercased before comparison, and a missing region remains fail-closed.
EVM asset IDs are compared case-insensitively while non-EVM asset case
is preserved.

A region-only stale write is reachable when an older region request
remains in flight, the user changes region, and the new region
repopulates the same asset and provider before the older response
returns. Legacy behavior checked asset and provider but not region.
These guards close that latent gap rather than adding a cache-only
safeguard.

Two same-context stateful requests cannot be ordered by those guards,
because the context key falls back to the globally selected provider
when more than one provider is passed. Rather than order them with a
request sequence, `updateState: true` now throws when provider
resolution yields more than one provider, before any fetch is issued.
The shared Buy catalog has one slot per region, token, and selected
provider, so a fan-out write has nowhere unambiguous to land. Callers
wanting a multi-provider catalog pass `updateState: false` and read the
returned `methods`. UB2 already passes exactly one explicit selected
provider, and MMPay deposit contexts are read-only, so no current caller
is affected.

An earlier revision of this PR ordered these writes with a per-request
sequence counter instead. It was removed: the race it guarded is
unreachable for a single-provider write (the state context key and the
fetch cache key move together, so concurrent duplicates collapse in
`executeRequest`'s pending dedupe), and the counter was the source of
both Bugbot findings on this PR, the second introduced by the fix for
the first.

### Catalog writers

`paymentMethods.data` and `paymentMethods.selected` have two writers in
Core: the pre-existing `getPaymentMethods` and the new
`getPaymentMethodsForContext`. Neither one's write guard is aware of the
other, so a client calling both for the same context has always been
able to interleave them.

The consumer PR removes Mobile's last call to `getPaymentMethods` (it
was in `app/components/UI/Ramp/queries/paymentMethods.ts` on `main`), so
once both land, `getPaymentMethodsForContext` is Mobile's only writer of
that state. `getPaymentMethods` stays exported and unchanged for other
consumers.

### Scope boundary

This PR does not include `getSmartSelectedQuote` or `applyQuoteContext`.
Payment-method context is the minimum shared boundary required by the
implemented Money and UB2 consumers. Quote choice and quote-state
application still have consumer-specific orchestration and remain
deferred to the tracked UB2 dumbification follow-up (TRAM-3856).

## References

* Fixes: TRAM-3838
* Related: MetaMask/metamask-mobile#34109
* Implemented and tested consumer:
MetaMask/metamask-mobile#35346 (supersedes
#34469)
* Final consumer preview:
`@metamask-previews/ramps-controller@20.0.0-preview-a2a428472`
* Final Core commit: `a2a428472d39421ffb5aaa04789bfdc45f573bd1`
* Preview publication:
MetaMask#9801 (comment)
* Related tech debt: TRAM-3856 (UB2 dumbification follow-up under
TRAM-3460)

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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]
> **Medium Risk**
> Touches payment-method catalog state, provider eligibility matching,
and new concurrent-write guards; behavior change for non-EVM asset
matching could affect which providers are considered to serve an asset.
> 
> **Overview**
> Adds **`getPaymentMethodsForContext`** (and the messenger action) so
Money/headless flows can load payment methods for a **deposit asset and
provider set** using the same resolution rules as **`getQuotes`**,
without defaulting to the Buy catalog’s selected token/provider. By
default the call is **request-only**; optional **`updateState: true`**
writes `paymentMethods` only when region, selected token, and selected
provider still match, and **throws** if resolution yields more than one
provider.
> 
> Multi-provider fetches **fan out**, **merge** via new
**`mergePaymentMethodsById`** / **`pickPaymentMethod`**, and tolerate
partial failures. **`normalizeRampsAssetId`** is exported and used for
comparisons; **`providerServesAsset`** / **`getProvidersServingAsset`**
now **stop lowercasing non-EVM** CAIP-19 ids so Solana/bitcoin
references stay case-sensitive.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
aefeed7. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

Releases `@metamask/ramps-controller` 20.0.0 to 20.1.0. No other package
is being released, so the monorepo version goes 1214.0.0 to 1215.0.0.

The whole branch is `yarn create-release-branch` output plus one commit
from `github-actions[bot]`. Nothing was edited by hand.

The bump is minor. Everything in the new section is additive except one
`Fixed` entry, and nothing in it is marked `**BREAKING:**`:

- **Added** `getPaymentMethodsForContext()` and the
`RampsController:getPaymentMethodsForContext` messenger action, plus the
`normalizeRampsAssetId()` export
([MetaMask#9801](MetaMask#9801)).
- **Added** the `TERMINAL_ORDER_STATUSES` / `isTerminalOrderStatus()`
exports ([MetaMask#9679](MetaMask#9679)).
- **Changed** `getProviders` to preserve backend ranking metadata
([MetaMask#9955](MetaMask#9955)), and bumped
`@metamask/remote-feature-flag-controller` to `^6.1.0`
([MetaMask#9945](MetaMask#9945),
[MetaMask#9980](MetaMask#9980)).
- **Fixed** `providerServesAsset()` / `getProvidersServingAsset()` so
non-EVM CAIP-19 references are no longer lowercased when matched against
a provider's `supportedCryptoCurrencies`
([MetaMask#9801](MetaMask#9801)). Solana base58 and
bitcoin bech32 references are case-sensitive, so lowercasing both sides
could report a provider as serving an asset it does not serve. EVM ids
still match regardless of checksum casing.

### Packages deliberately left out of this release

`create-release-branch` flagged four dependencies of `ramps-controller`
that have unreleased changes: `@metamask/base-controller`,
`@metamask/controller-utils`, `@metamask/messenger`, and
`@metamask/profile-sync-controller`. All four are listed as
`intentionally-skip` in the release spec. None of the PRs in this
release changed `packages/ramps-controller/package.json`, so the
declared ranges are untouched and the currently published versions of
those four still satisfy them.

### Dependent range bump

`@metamask/transaction-pay-controller` is the only in-repo consumer of
`ramps-controller`. Its range moves from `^20.0.0` to `^20.1.0`, which
is the tool's usual dependent-range update, and `github-actions[bot]`
added the matching changelog entry. It does not need a release of its
own, since `^20.0.0` already accepted 20.1.0.

`yarn changelog:validate`, `yarn constraints`, and `yarn install
--immutable` all pass on this branch.

## References

* Releases: MetaMask#9801 (TRAM-3838),
MetaMask#9679,
MetaMask#9955,
MetaMask#9945,
MetaMask#9980
* Consumer waiting on this release:
MetaMask/metamask-mobile#35346
* Related: MetaMask/metamask-mobile#34109

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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, changelog, and lockfile updates only; ramps 20.1.0 is a minor
release with additive API and a targeted provider-matching fix, with no
breaking changes in this cut.
> 
> **Overview**
> **Release-only PR** — no runtime code in the diff. It cuts
**`@metamask/ramps-controller` `20.0.0` → `20.1.0`**, bumps the root
monorepo version **`1214.0.0` → `1215.0.0`**, moves the existing
`[Unreleased]` ramps changelog into **`[20.1.0]`**, and refreshes
**`yarn.lock`**.
> 
> The in-repo consumer **`@metamask/transaction-pay-controller`** gets a
dependent range update **`@metamask/ramps-controller` `^20.0.0` →
`^20.1.0`** plus an `[Unreleased]` changelog line; its own package
version is **not** released here.
> 
> What ships in **ramps-controller 20.1.0** (documented in the promoted
changelog): **`getPaymentMethodsForContext`** / messenger action,
**`normalizeRampsAssetId`**, **`TERMINAL_ORDER_STATUSES` /
`isTerminalOrderStatus`**, **`getProviders` ranking metadata
preservation**, dependency bump for
**`remote-feature-flag-controller`**, and a **fix** so non-EVM CAIP-19
asset matching is no longer incorrectly lowercased in
**`providerServesAsset` / `getProvidersServingAsset`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ec32856. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…etaMask#9973)

## Explanation

Adds spam cleanup for users. This is additional filtering to ensure we
are protected if any external system adds in spam (WS, API, Migration).

This is because our external sources can fail and add in spam:
- API filters can loosen, and add in spam tokens.
- WS has no filtering right now, so airdrops are added to the wallet.

This safeguards ourselves by adding additional cleanup on
startup/unlock. We need a longer term discussion & decision with Assets
team to highlight this cleanup issue and building a trustless system.

Builds:
- MetaMask/metamask-extension#45795
- MetaMask/metamask-mobile#35328

Code Walkthrough:
https://www.loom.com/share/7c60194c8be44a6c8d1e7ee8dc559ae7

## References

https://consensyssoftware.atlassian.net/browse/ASSETS-3905

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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)
- [x] 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]
> **Medium Risk**
> Unlock can mutate persisted `assetsInfo`/`assetsBalance` based on
Token API occurrence heuristics; misclassification or API errors could
hide legitimate tokens, though the feature flag defaults off and
failures are designed to no-op.
> 
> **Overview**
> **Adds unlock-time spam token cleanup** so wallets can drop
airdrop/scam ERC-20s that slipped in via loose API filters or unfiltered
websocket balances, without waiting on upstream fixes.
> 
> On `KeyringController:unlock`, `AssetsController` now runs
`#runSpamCleanup`, which calls `cleanSpamAssets` from the heal/migration
module. That path hits the Token API (`suggestedOccurrenceFloors` +
batched `/v3/assets`), classifies sweepable EVM ERC-20s by occurrence vs
per-chain floors, and applies a single state patch removing spam from
`assetsInfo` and `assetsBalance` while leaving natives, non-EVM assets,
default-tracked tokens (e.g. mUSD), and `customAssets` alone. It only
runs when the keyring is unlocked, basic functionality is on, and remote
flags set `assetsUnifyState.useUnlockCleanup` to **true**; API failures
leave state unchanged and can report via `captureException`.
> 
> The PR is mostly **test and fixture work**: dedicated spam-cleanup
unit and integration suites, `nock` HTTP stubs, production-derived
scam-wallet state, captured API responses, and expanded
`MockAssetControllerMessenger` helpers. Changelog and `nock`
devDependency updates accompany the wiring change.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d00abac. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…mp API requests (MetaMask#9983)

## Explanation

The on-ramp API is adding server-side feature flags (LaunchDarkly) that
are gated per client product and client version — e.g. "enable feature X
for MetaMask Mobile >= 8.10.0". To evaluate those gates, the API needs
to know which client is calling.

This PR adds optional `clientProduct` and `clientVersion` constructor
options to `RampsService` and `TransakService`. When the host supplies
them, every on-ramp API fetch carries two query params:

- `clientProduct` (e.g. `metamask-mobile`)
- `clientVersion` (the app SemVer, not the ramps-controller package
version)

Identity travels **in the URL, not in headers**, because the on-ramp CDN
caches responses keyed by URL. If gating identity lived in headers, two
clients in different flag cohorts would share the same cache entry and
one cohort's response could be served to the other. Query params make
the cache key and the flag decision agree by construction.

Both fields are optional. Hosts that don't pass them send no identity
params, and the API fails closed (feature off) — so this is fully
backwards compatible.

Also exported for hosts and tests: the `RampsClientIdentity` type, the
`addRampsClientIdentityParams` helper, and the
`RAMPS_CLIENT_PRODUCT_PARAM` / `RAMPS_CLIENT_VERSION_PARAM` constants.

The `controller` query param is intentionally left as the
ramps-controller package version; the new params carry the app identity
instead of overloading it.

## References

Related to the on-ramp API version-gated feature flag rollout (Unified
Buy 2.0 catalog gating / fiat soft-default) —
[TRAM-3828](https://consensyssoftware.atlassian.net/browse/TRAM-3828).

- API: consensys-vertical-apps/va-mmcx-onramp-api#1141
- Mobile: MetaMask/metamask-mobile#35359
- Extension: MetaMask/metamask-extension#45823

## Changelog

Updated `packages/ramps-controller/CHANGELOG.md` (Unreleased → Added).

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc) for new or updated code as
appropriate
- [x] I've communicated my changes to consumers by updating changelogs
for packages I've changed
- [x] No breaking changes introduced

Made with [Cursor](https://cursor.com)

## Demo

<div>
<a href="https://www.loom.com/share/9f913895e9bb4ccd95a3a79138566f4c">
<p>Introducing Launchdarkly flags for Ramps Backend - Watch Video</p>
    </a>
<a href="https://www.loom.com/share/9f913895e9bb4ccd95a3a79138566f4c">
<img style="max-width:300px;"
src="https://cdn.loom.com/sessions/thumbnails/9f913895e9bb4ccd95a3a79138566f4c-36a975aecd13f632-full-play.gif#t=0.1">
    </a>
  </div>

[TRAM-3828]:
https://consensyssoftware.atlassian.net/browse/TRAM-3828?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…k#9968)

## Explanation

Pro mode's increase/decrease UX (TAT-3705) needs a current → projected
margin and liquidation. That projection is protocol math, not UI
formatting: HyperLiquid applies selected leverage to the whole isolated
position before the fill, and maintenance margin depends on the tier at
the *liquidation* notional. Clients that recompute this locally get both
cases wrong, and they can also show a false projection for cross-margin
positions or drop a valid margin preview when `liquidationPrice` is
null.

This adds a read-only `previewPositionModify` API on `PerpsController` /
`PerpsProvider`. The client supplies the live position and proposed
order; the HyperLiquid provider loads the asset's margin table from
cached `meta` and returns a discriminated result:

- `open` — increase, decrease, or flip with remaining size > 0
- `full_close` — no remaining size (invalid leftover states are
unrepresentable)
- `unsupported` — `cross_margin` or a provider that cannot project (MYX)
- `none` — no meaningful modify

Margin and liquidation availability are independent, so a missing live
liquidation or missing multi-tier table withholds only liquidation.
Isolated liquidation uses the maintenance tier at `size * liqPrice`,
including that tier's maintenance deduction.

Clients should use `resulting.direction` (not the order direction) when
validating TP/SL against the projected liquidation.

This is a **breaking** `PerpsProvider` interface addition.
Mobile/Extension should consume it after the next
`@metamask/perps-controller` release; they do not implement
`PerpsProvider` themselves. Client wiring is intentionally not in this
PR.

## References

* TAT-3705 — UX for increasing / decreasing position in Pro mode (before
→ after). This PR supplies the calculation source of truth the ticket
does not specify.
* Related Mobile work: keep the before → after presentation, wrapping,
and localization; replace the local `positionModifyPreview` arithmetic
with this API after the controller is released.

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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)
- [x] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR. Client consumption follows the `@metamask/perps-controller`
release; no client source changes are included here.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> New read-only trading projection logic (isolated margin, leverage
reallocation, tiered liquidation) could mislead TP/SL UX if wrong, and
the breaking `PerpsProvider` addition requires all implementers to ship
the new method.
> 
> **Overview**
> Adds a **breaking** read-only **`previewPositionModify`** API on
`PerpsController` and `PerpsProvider` so clients can show before/after
margin and liquidation when modifying an isolated position, without
placing an order.
> 
> Callers pass the live position plus a proposed order (size, direction,
expected price, selected leverage, optional fees). Results are a
discriminated union: **`open`** (increase / decrease / flip with
projected size, entry, mark-based leverage, margin, and optional
liquidation), **`full_close`**, **`unsupported`** (cross-margin or MYX),
or **`none`** when the order would not meaningfully change the position.
Margin and liquidation are reported independently so a missing margin
table or live liq price does not hide a valid margin projection.
> 
> **HyperLiquid** loads margin tiers from cached `meta` and implements
the math in new `hyperLiquidPositionPreview` utilities (tiered
maintenance, mark-based isolated liquidation). **Aggregated** routing
follows `providerId` / `position.providerId`. Types, messenger actions,
package exports, changelog, mocks, and broad unit tests cover delegation
and edge cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4a2b2be. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
… on kyc session creation (MetaMask#9993)

## Explanation

- remove unused wrapUserKey helper
- pass sessionClientPublicKey into ukyc session creation
- add residenceCountry to create ukyc session flow


## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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]
> **Medium Risk**
> Changes the UKYC session contract and crypto wiring for document
verification; consumers calling `createUkycSession` directly must adopt
the new required fields.
> 
> **Overview**
> **BREAKING:** `KycService.createUkycSession` (`POST /sessions`) now
requires `sessionClientPublicKey` (unpadded base64url X25519) and
`residenceCountry` (ISO 3166-1 alpha-3). Direct callers must supply
both; the controller generates the per-session keypair, registers the
public key at session creation, and uses the private key with the
existing `wrapEncryptionKey` path for `setAuthorizations`.
> 
> `KycController` resolves `residenceCountry` from `geoCountry` or
`KycService:getGeoCountry`, persists it on state when fetched, and
aborts UKYC session creation if `reset()` bumps the flow generation
while geo is in flight.
> 
> Removes the unused `wrapUserKey` helper and its tests; docs and
`wrapEncryptionKey` comments now describe explicit session client
public-key registration instead of implying registration was absent.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
578d94e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

Releases `@metamask/ramps-controller` 20.1.0 to 20.2.0 so Mobile and
Extension can consume the client-identity query params from
[MetaMask#9983](MetaMask#9983). The monorepo
version goes 1215.0.0 to 1216.0.0.

No other package is being published.
`@metamask/transaction-pay-controller` only tightens its workspace range
to `^20.2.0` (same pattern as
[Release/1215.0.0](MetaMask#10004)).
Unreleased deps of ramps-controller (`base-controller`,
`controller-utils`, `messenger`, `profile-sync-controller`) are
intentionally skipped — this release does not rely on those unreleased
changes.

The bump is **minor**. The new section is additive and nothing in it is
marked `**BREAKING:**`:

- **Added** optional `clientProduct` and `clientVersion` constructor
options on `RampsService` and `TransakService`, sent as `clientProduct`
/ `clientVersion` query params on every on-ramp API fetch so the API can
evaluate version-gated feature flags per client. Identity travels in the
URL (not headers) because the on-ramp CDN cache key is the URL. Also
exports `RampsClientIdentity`, `addRampsClientIdentityParams`, and the
param name constants
([MetaMask#9983](MetaMask#9983)).

Jira:
[TRAM-3828](https://consensyssoftware.atlassian.net/browse/TRAM-3828)

## Changelog

Updated `packages/ramps-controller/CHANGELOG.md` (Unreleased → 20.2.0)
and recorded the workspace bump in
`packages/transaction-pay-controller/CHANGELOG.md`.

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc) for new or updated code as
appropriate
- [x] I've communicated my changes to consumers by updating changelogs
for packages I've changed
- [x] No breaking changes introduced


Made with [Cursor](https://cursor.com)

[TRAM-3828]:
https://consensyssoftware.atlassian.net/browse/TRAM-3828?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…k#10008)

## Explanation

- Adds `idosRelayBaseUrl` constructor param and uses it fetch well
knowns to validate the ukycCapabilityToken public key
- Renames `fractalEncryptionBaseUrl` to `idosEnclaveBaseUrl`. Continues
using it to validate dataEncryptionKey public key

## References

<!--
Are there any issues that this pull request is tied to?
Are there other links that reviewers should consult to understand these
changes better?
Are there client or consumer pull requests to adopt any breaking
changes?

For example:

* Fixes #12345
* Related to #67890
-->

## 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](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]
> **High Risk**
> Changes JWT attestation for UKYC encryption keys and capability tokens
with breaking constructor and messenger renames; incorrect relay/enclave
URLs or missing config could block SumSub or weaken trust assumptions.
> 
> **Overview**
> **Breaking:** UKYC authorization wrapping no longer validates both
encryption schemas against a single Fractal JWKS.
`fractalEncryptionBaseUrl` / `fetchJwks` are renamed to
**`idosEnclaveBaseUrl`** / **`fetchIdosEnclaveJwks`**, and hosts must
also pass **`idosRelayBaseUrl`** and wire
**`KycService:fetchIdosRelayJwks`**.
> 
> Before wrapping `data_encryption_key` and `ukyc_capability_token`,
**`KycController`** fetches both well-known JWKS in parallel and
verifies **`encryptionDataKey`** against the enclave keys and
**`ukycCapabilityToken`** against the relay keys, then checks each
attested session server public key as before. **`KycService`**
implements the two fetches via a shared **`#fetchWellKnownJwks`** helper
(cached, unauthenticated).
> 
> Docs, changelog, messenger action types, and tests (including
per-schema JWKS verification and reset-during-fetch cases) are updated
accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
630ba21. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
## Explanation

Releases `@metamask/perps-controller` **13.1.0 → 14.0.0**. The monorepo
version goes **1216.0.0 → 1217.0.0**.

No other package is being published. Perps-controller has no in-monorepo
dependents that need a workspace range bump.

The bump is **major**. Adding a required method on the public
`PerpsProvider` type is a breaking type change:

- **Added (breaking):** `PerpsController.previewPositionModify` and
required `PerpsProvider.previewPositionModify` so clients can read an
isolated-margin post-trade projection without placing an order
([MetaMask#9968](MetaMask#9968)). Consumers that
**implement** `PerpsProvider` must add the method. In-repo providers
(`HyperLiquidProvider`, `MYXProvider`, `AggregatedPerpsProvider`)
already implement it. Mobile and Extension consume the controller; they
do not implement `PerpsProvider`, so they can upgrade without a source
change. Client wiring to *call* the API is follow-up (pro-mode
increase/decrease UX).
- **Fixed:** Transient HyperLiquid WebSocket disconnects no longer fail
the first TP/SL update; builder-fee approval is checked over HTTP
([MetaMask#9997](MetaMask#9997)).
- **Fixed:** `updatePositionTPSL` no longer reports `TPSL_UPDATE_FAILED`
when HyperLiquid accepts a trigger with `waitingForTrigger`; accepted
triggers without response order IDs are reconciled before mixed-failure
cleanup ([MetaMask#9995](MetaMask#9995)).

## Changelog

Moved Unreleased entries in `packages/perps-controller/CHANGELOG.md`
under `[14.0.0]`. Breaking entry is prefixed with `**BREAKING:**` and
includes adaptation notes.

## References

- Source PRs: [MetaMask#9968](MetaMask#9968),
[MetaMask#9997](MetaMask#9997),
[MetaMask#9995](MetaMask#9995)

- No draft Mobile/Extension PRs: neither client implements
`PerpsProvider`. Preview-build upgrade PRs are not required to keep
those clients compiling.

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] 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 — **N/A for clients:** they do not
implement `PerpsProvider`. In-repo implementors were updated in
[MetaMask#9968](MetaMask#9968).

## Release review

- [x] No packages with version bumps that should not be published
(`@metamask/perps-controller` only)
- [x] New version string matches impact (major 14.0.0 for required
`PerpsProvider` method)
- [x] Changelog entries are consumer-facing, categorized, and link to
the introducing PRs
- [x] Breaking change is prefixed with `**BREAKING:**` and describes how
to adapt
- [x] No Unreleased leftover entries for this package
- [ ] Recheck `main` for commits merged while this PR is open and update
changelogs if they touch perps-controller

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> No runtime or type changes in the diff; risk is limited to consumers
upgrading to 14.0.0 and implementing the new required `PerpsProvider`
method if they are custom providers.
> 
> **Overview**
> **Release 1217.0.0** cuts a new publish of
**`@metamask/perps-controller` only** (13.1.0 → **14.0.0**); the root
monorepo version moves 1216.0.0 → 1217.0.0.
> 
> The diff is versioning and changelog housekeeping: Unreleased notes
are filed under **`[14.0.0]`**, compare links are updated, and a minor
punctuation fix is applied on a 13.1.0 entry. The **14.0.0** notes
describe what ships with this tag—chiefly the **breaking** required
`PerpsProvider.previewPositionModify` /
`PerpsController.previewPositionModify` API
([MetaMask#9968](MetaMask#9968)), plus HyperLiquid
TP/SL fixes ([MetaMask#9997](MetaMask#9997),
[MetaMask#9995](https://github.com/MetaMask/core/pull/9995))—already merged on
`main`, not introduced in this PR’s file changes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
95c2e07. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​lavamoat/​preinstall-always-fail@​1.0.0 ⏵ 2.1.08110048 +583100
Updated@​metamask/​eth-snap-keyring@​2.0.0 ⏵ 24.0.0991009199 +450 -50
Updated@​metamask/​keyring-api@​1.0.0 ⏵ 24.0.0100 +110010099 +650 -50
Updated@​metamask/​snaps-controllers@​3.0.0 ⏵ 19.0.179 -1310076 +39950
Updated@​types/​deep-freeze-strict@​1.1.0 ⏵ 1.1.210010054 -1179100
Updated@​types/​punycode@​2.1.0 ⏵ 2.1.410010060 -180100
Updatedjest-environment-jsdom@​27.5.1 ⏵ 30.4.1100 +110062 -291 -1100
Added@​types/​json-rpc-random-id@​1.0.3641006479100
Added@​yarnpkg/​types@​4.0.01001006682100
Added@​yarnpkg/​cli@​4.17.1971006795100
Updatedjest-environment-node@​27.5.1 ⏵ 30.4.110010067 +388 +1100
Updatedsimple-git-hooks@​2.8.1 ⏵ 2.11.19110069 -3181100
Updatedjest@​27.5.1 ⏵ 30.4.210010069 +289 -4100
Added@​docusaurus/​preset-classic@​3.10.1991007093100
Updated@​spruceid/​siwe-parser@​1.1.3 ⏵ 2.1.010010070 +184100
Added@​types/​yargs-parser@​21.0.31001007080100
Added@​types/​unzipper@​0.10.111001007080100
Added@​ethereumjs/​tx@​5.4.01001001009070
Added@​ethereumjs/​util@​9.1.01001001009070
Updatedwebextension-polyfill@​0.7.0 ⏵ 0.12.0100 +1100100 +18070
Updated@​ethersproject/​keccak256@​5.7.0 ⏵ 5.8.01001007180100
Added@​types/​better-sqlite3@​7.6.131001007185100
Updated@​typescript-eslint/​parser@​5.62.0 ⏵ 8.54.010010071 +198100
Added@​docusaurus/​types@​3.10.1991007293100
Added@​types/​npm-which@​3.0.4791007280100
Updatedeslint-config-prettier@​8.10.0 ⏵ 9.1.0100 +110072 +183100
Addedethers@​6.16.07210010084100
Added@​types/​node-fetch@​2.6.121001007280100
Updated@​metamask/​eth-query@​3.0.1 ⏵ 4.0.010010072 +185100
Updatedextension-port-stream@​2.0.1 ⏵ 3.0.010010072 -188 +2100
Added@​metamask/​keyring-internal-api@​12.0.01001007396100
Added@​yarnpkg/​parsers@​3.0.31001007382100
Added@​easyops-cn/​docusaurus-search-local@​0.55.1981007391100
See 153 more rows in the dashboard

View full report

@socket-security

Copy link
Copy Markdown

Caution

MetaMask internal reviewing guidelines:

  • Do not ignore-all
  • Each alert has instructions on how to review if you don't know what it means. If lost, ask your Security Liaison or the supply-chain group
  • Copy-paste ignore lines for specific packages or a group of one kind with a note on what research you did to deem it safe.
    @SocketSecurity ignore npm/PACKAGE@VERSION
Action Severity Alert  (click "▶" to expand/collapse)
Block Critical
Critical CVE: Arbitrary code execution in npm protobufjs

CVE: GHSA-xq3m-2v4x-88gg Arbitrary code execution in protobufjs (CRITICAL)

Affected versions: >= 8.0.0 < 8.0.1; < 7.5.5

Patched version: 7.5.5

From: packages/notification-services-controller/package.jsonnpm/firebase@11.2.0npm/protobufjs@7.4.0

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/protobufjs@7.4.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block Critical
Critical CVE: npm websocket-driver: Message corruption via abuse of protocol length headers

CVE: GHSA-xv26-6w52-cph6 websocket-driver: Message corruption via abuse of protocol length headers (CRITICAL)

Affected versions: < 0.7.5

Patched version: 0.7.5

From: packages/notification-services-controller/package.jsonnpm/@docusaurus/core@3.10.1npm/@docusaurus/preset-classic@3.10.1npm/firebase@11.2.0npm/websocket-driver@0.7.4

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/websocket-driver@0.7.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @emnapi/runtime is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package.jsonnpm/@easyops-cn/docusaurus-search-local@0.55.1npm/jest@30.4.2npm/knip@6.23.0npm/@emnapi/runtime@1.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@emnapi/runtime@1.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: npm @grpc/grpc-js: An incoming malformed compressed message can cause a client or server crash

CVE: GHSA-99f4-grh7-6pcq @grpc/grpc-js: An incoming malformed compressed message can cause a client or server crash (HIGH)

Affected versions: < 1.9.16; >= 1.10.0 < 1.10.12; >= 1.11.0 < 1.11.4; >= 1.12.0 < 1.12.7; >= 1.13.0 < 1.13.5; >= 1.14.0 < 1.14.4

Patched version: 1.9.16

From: packages/notification-services-controller/package.jsonnpm/firebase@11.2.0npm/@grpc/grpc-js@1.9.15

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@grpc/grpc-js@1.9.15. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: npm @grpc/grpc-js: A malformed request can cause a server crash

CVE: GHSA-5375-pq7m-f5r2 @grpc/grpc-js: A malformed request can cause a server crash (HIGH)

Affected versions: < 1.9.16; >= 1.10.0 < 1.10.12; >= 1.11.0 < 1.11.4; >= 1.12.0 < 1.12.7; >= 1.13.0 < 1.13.5; >= 1.14.0 < 1.14.4

Patched version: 1.9.16

From: packages/notification-services-controller/package.jsonnpm/firebase@11.2.0npm/@grpc/grpc-js@1.9.15

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@grpc/grpc-js@1.9.15. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @metamask/snaps-controllers is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/account-tree-controller/package.jsonnpm/@metamask/snaps-controllers@19.0.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@metamask/snaps-controllers@19.0.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @noble/hashes is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/kyc-controller/package.jsonnpm/@noble/hashes@1.8.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@noble/hashes@1.8.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @noble/hashes is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/kyc-controller/package.jsonnpm/@noble/hashes@1.8.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@noble/hashes@1.8.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @pnpm/network.ca-file is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/platform-api-docs/package.jsonnpm/@docusaurus/core@3.10.1npm/@docusaurus/preset-classic@3.10.1npm/@pnpm/network.ca-file@1.0.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@pnpm/network.ca-file@1.0.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm @protobufjs/float is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/notification-services-controller/package.jsonnpm/firebase@11.2.0npm/@protobufjs/float@1.0.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@protobufjs/float@1.0.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm apg-js is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/controller-utils/package.jsonnpm/@spruceid/siwe-parser@2.1.0npm/siwe@2.3.2npm/apg-js@4.4.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/apg-js@4.4.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: npm axios Requests Vulnerable To Possible SSRF and Credential Leakage via Absolute URL

CVE: GHSA-jr5f-v2jv-69x6 axios Requests Vulnerable To Possible SSRF and Credential Leakage via Absolute URL (HIGH)

Affected versions: >= 1.0.0 < 1.8.2; < 0.30.0

Patched version: 1.8.2

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter

CVE: GHSA-p92q-9vqr-4j8v Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter (HIGH)

Affected versions: >= 1.0.0 < 1.16.0; < 0.32.0

Patched version: 1.16.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Allocation of Resources Without Limits or Throttling in Axios

CVE: GHSA-777c-7fjr-54vf Allocation of Resources Without Limits or Throttling in Axios (HIGH)

Affected versions: >= 1.7.0 < 1.16.0

Patched version: 1.16.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection

CVE: GHSA-hfxv-24rg-xrqf Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection (HIGH)

Affected versions: >= 1.0.0 < 1.16.0; < 0.32.0

Patched version: 1.16.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection

CVE: GHSA-j5f8-grm9-p9fc Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection (HIGH)

Affected versions: >= 1.0.0 < 1.16.0; < 0.32.0

Patched version: 1.16.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: npm axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config.proxy`

CVE: GHSA-35jp-ww65-95wh axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in config.proxy (HIGH)

Affected versions: >= 1.0.0 < 1.16.0

Patched version: 1.16.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: npm axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gadget in Config Merge

CVE: GHSA-3g43-6gmg-66jw axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollution Gadget in Config Merge (HIGH)

Affected versions: >= 1.0.0 < 1.15.2; >= 0.19.0 < 0.31.1

Patched version: 1.15.2

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios is Vulnerable to Denial of Service via __proto__ Key in mergeConfig

CVE: GHSA-43fc-jf86-j433 Axios is Vulnerable to Denial of Service via proto Key in mergeConfig (HIGH)

Affected versions: >= 1.0.0 < 1.13.5; < 0.30.3

Patched version: 1.13.5

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Header Injection via Prototype Pollution

CVE: GHSA-6chq-wfr3-2hj9 Axios: Header Injection via Prototype Pollution (HIGH)

Affected versions: >= 1.0.0 < 1.15.1; < 0.31.1

Patched version: 1.15.1

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0

CVE: GHSA-pmwg-cvhr-8vh7 Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0 (HIGH)

Affected versions: >= 1.0.0 < 1.15.1; < 0.31.1

Patched version: 1.15.1

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking

CVE: GHSA-pf86-5x62-jrwf Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking (HIGH)

Affected versions: >= 1.0.0 < 1.15.1; < 0.31.1

Patched version: 1.15.1

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking

CVE: GHSA-q8qp-cvcw-x6jj Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking (HIGH)

Affected versions: >= 1.0.0 < 1.15.2

Patched version: 1.15.2

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
High CVE: Axios is vulnerable to DoS attack through lack of data size check

CVE: GHSA-4hjh-wcwx-xvwj Axios is vulnerable to DoS attack through lack of data size check (HIGH)

Affected versions: >= 1.0.0 < 1.12.0; >= 0.28.0 < 0.30.2

Patched version: 1.12.0

From: packages/notification-services-controller/package.jsonnpm/contentful@10.15.0npm/axios@1.7.5

ℹ Read more on: This package | This alert | What is a CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known high severity CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/axios@1.7.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Block High
Obfuscated code: npm better-sqlite3 is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/wallet-cli/package.jsonnpm/better-sqlite3@12.10.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/better-sqlite3@12.10.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

See 912 more rows in the dashboard

View full report

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 584bcba. Configure here.

child-workspace-package-names: ${{ steps.workspace-package-names.outputs.child-workspace-package-names }}
merge-base: ${{ steps.fetch-merge-base.outputs.merge-base }}
package-names: ${{ steps.packages.outputs.package-names }}
changed-paths: ${{ steps.packages.outputs.changed-paths }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Matrix outputs can drop package data

High Severity

The prepare job fans out across four Node versions, but only the 24.x shard sets package-names, changed-paths, and merge-base. GitHub keeps outputs from the last matrix shard to finish, so a later 18.x, 20.x, or 22.x finish can replace those values with empty strings. Downstream jobs then hit fromJson on empty output and fail, or skip the intended package set.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 584bcba. Configure here.

TARGET_REF=$(git merge-base "origin/$BASE_REF" "origin/$PR_BRANCH")
git fetch origin "$TARGET_REF"

UPDATED_CHANGELOGS=$(git diff --name-only "$TARGET_REF" "origin/$PR_BRANCH" | grep -E 'CHANGELOG\.md$' || true)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Changelog check fails for fork PRs

Medium Severity

The changelog diff step runs git merge-base and git show against origin/$PR_BRANCH. That remote ref exists only for same-repo branches. Fork pull requests keep the head branch on the fork, so both commands fail under set -e and validate-changelog-diffs goes red for community PRs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 584bcba. Configure here.

MERGE_BASE=$(git merge-base HEAD refs/remotes/origin/main)
for package in "${PACKAGES[@]}"; do
MAIN_VERSION=$(git show "$MERGE_BASE:$package" | jq -r .version)
HEAD_VERSION=$(jq -r .version "$package")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Release check crashes on new packages

Medium Severity

The released-package scan runs git show on every current package.json at the merge base and treats failure as fatal. A package added in the PR does not exist on that base, so the command fails under set -euo pipefail and the release conflict check never finishes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 584bcba. Configure here.

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.