Skip to content

Feature: sync with mobile v2#6673

Merged
estebanmino merged 4 commits intodevelopfrom
feature/sync-v2
Jun 12, 2019
Merged

Feature: sync with mobile v2#6673
estebanmino merged 4 commits intodevelopfrom
feature/sync-v2

Conversation

@estebanmino
Copy link
Copy Markdown
Contributor

@estebanmino estebanmino commented May 31, 2019

This PR updates how sync with mobile works.

A new code will be generated each 30 seconds of QR view opened, if there is no communication with mobile app

Communication goes as follows:

  • Extension defines cipherKey and channelName to first communication with mobile
  • Mobile app connects and gives the extension new cipherKey and channelName, for the extension to connect.
  • Extension connects and sync happens

@estebanmino estebanmino requested a review from brunobar79 May 31, 2019 22:16
Copy link
Copy Markdown
Contributor

@brunobar79 brunobar79 left a comment

Choose a reason for hiding this comment

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

Simple and elegant. 👏 👏 👏

@metamaskbot
Copy link
Copy Markdown
Collaborator

Builds ready [e207706]: chrome, firefox, edge, opera

@estebanmino estebanmino merged commit 71390db into develop Jun 12, 2019
@whymarrh whymarrh deleted the feature/sync-v2 branch June 12, 2019 15:12
github-merge-queue bot pushed a commit that referenced this pull request Jan 23, 2026
… to prevent global cascading re-renders (#39313)

## **Description**

Fixed `pendingConfirmationsSortedSelector` to use proper memoization
with `createSelector`. This selector was creating new arrays on every
call, breaking memoization and triggering cascade re-renders throughout
the confirmation flow and Routes components.

**What is the reason for the change?**
The selector was implemented as a plain function that returned new array
references on every invocation (via `.filter()` and `.sort()`), causing
unnecessary re-renders in all dependent components even when the
underlying state hadn't changed.

**What is the improvement/solution?**
Wrapped the selector with `createSelector` from reselect to memoize the
transformation. Now the selector only recalculates when
`getPendingApprovals` returns a different reference, preventing cascade
re-renders and improving performance across the confirmation flow.

## **Changelog**

CHANGELOG entry: Fixed critical performance issue slowing down all user
actions by fixing confirmations selector memoization

## **Related issues**

Fixes: Part of P0 effort to break global re-render cascade (#6669)

## **Manual testing steps**

1. This is a performance optimization with no visible UI changes
2. The memoization behavior is verified through unit tests
3. All existing confirmation flow functionality remains unchanged

## **Screenshots/Recordings**

N/A - This is a performance optimization with no visual changes. The
improvement is verified through unit tests that confirm the selector
returns the same reference when state is unchanged.

### **Before**

Selector created new arrays on every call, causing unnecessary
re-renders:
```typescript
export function pendingConfirmationsSortedSelector(state: ConfirmMetamaskState) {
  return getPendingApprovals(state)
    .filter(({ type }) => ConfirmationApprovalTypes.includes(type as ApprovalType))
    .sort((a1, a2) => a1.time - a2.time);
}
```

### **After**

Selector uses memoization to return stable references:
```typescript
export const pendingConfirmationsSortedSelector = createSelector(
  getPendingApprovals,
  (approvals) =>
    approvals
      .filter(({ type }) => ConfirmationApprovalTypes.includes(type as ApprovalType))
      .sort((a1, a2) => a1.time - a2.time),
);
```

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[P0] Fix `pendingConfirmationsSortedSelector`
(unmemoized)</issue_title>
> <issue_description>## Summary
> 
> Fix `pendingConfirmationsSortedSelector` to use proper memoization.
This is a **P0 critical fix** required to break the global re-render
cascade.
> 
> **Parent Epic:**
[#6669](https://github.com/MetaMask/MetaMask-planning/issues/6669) —
Break Global Re-render Cascade
> **Depends on:** Fix for `getPendingApprovals` (#6673)
> 
> ## Problem
> 
> `pendingConfirmationsSortedSelector` is a plain function that creates
2 new arrays on every call:
> 
> ```typescript
> // ui/pages/confirmations/selectors/confirm.ts
> export function pendingConfirmationsSortedSelector(state:
ConfirmMetamaskState) {
>   return getPendingApprovals(state)
> .filter(({ type }) => ConfirmationApprovalTypes.includes(type as
ApprovalType)) // ❌ NEW ARRAY
>     .sort((a1, a2) => a1.time - a2.time);  // ❌ NEW ARRAY
> }
> ```
> 
> **Used in Routes** to determine pending confirmation count and
navigation.
> 
> This breaks memoization and causes cascade re-renders across all
children on every state change.
> 
> ## Solution
> 
> Wrap in `createSelector` to memoize the transformation:
> 
> ```typescript
> export const pendingConfirmationsSortedSelector = createSelector(
>   getPendingApprovals,
>   (approvals) =>
>     approvals
> .filter(({ type }) => ConfirmationApprovalTypes.includes(type as
ApprovalType))
>       .sort((a1, a2) => a1.time - a2.time),
> );
> ```
> 
> ## Files
> 
> - `ui/pages/confirmations/selectors/confirm.ts`
> 
> ## Acceptance Criteria
> 
> - [ ] Selector returns same reference when underlying state unchanged
> - [ ] Unit test verifies memoization
> - [ ] No regression in existing tests
> 
> ## Estimated Effort
> 
> 0.5 hours
> 
> ## Labels
> 
> `perf`, `selector`, `P0`, `team-confirmations`</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes MetaMask/MetaMask-planning#6674

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Improves selector performance and adds tests around confirmation
sorting/filtering.
> 
> - **Memoizes** `pendingConfirmationsSortedSelector` with `reselect`
using `getPendingApprovals`, preserving sort-by-time and
confirmation-type filtering while returning stable references
> - **Tests**: verify sorted output, memoization (same reference when
unchanged, new reference on state change), and filtering of
non-confirmation approvals; `oldestPendingConfirmationSelector` behavior
validated
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
88f2be0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Jongsun Suh <jongsun.suh@icloud.com>
wantedsystem pushed a commit that referenced this pull request Jan 27, 2026
… to prevent global cascading re-renders (#39313)

## **Description**

Fixed `pendingConfirmationsSortedSelector` to use proper memoization
with `createSelector`. This selector was creating new arrays on every
call, breaking memoization and triggering cascade re-renders throughout
the confirmation flow and Routes components.

**What is the reason for the change?**
The selector was implemented as a plain function that returned new array
references on every invocation (via `.filter()` and `.sort()`), causing
unnecessary re-renders in all dependent components even when the
underlying state hadn't changed.

**What is the improvement/solution?**
Wrapped the selector with `createSelector` from reselect to memoize the
transformation. Now the selector only recalculates when
`getPendingApprovals` returns a different reference, preventing cascade
re-renders and improving performance across the confirmation flow.

## **Changelog**

CHANGELOG entry: Fixed critical performance issue slowing down all user
actions by fixing confirmations selector memoization

## **Related issues**

Fixes: Part of P0 effort to break global re-render cascade (#6669)

## **Manual testing steps**

1. This is a performance optimization with no visible UI changes
2. The memoization behavior is verified through unit tests
3. All existing confirmation flow functionality remains unchanged

## **Screenshots/Recordings**

N/A - This is a performance optimization with no visual changes. The
improvement is verified through unit tests that confirm the selector
returns the same reference when state is unchanged.

### **Before**

Selector created new arrays on every call, causing unnecessary
re-renders:
```typescript
export function pendingConfirmationsSortedSelector(state: ConfirmMetamaskState) {
  return getPendingApprovals(state)
    .filter(({ type }) => ConfirmationApprovalTypes.includes(type as ApprovalType))
    .sort((a1, a2) => a1.time - a2.time);
}
```

### **After**

Selector uses memoization to return stable references:
```typescript
export const pendingConfirmationsSortedSelector = createSelector(
  getPendingApprovals,
  (approvals) =>
    approvals
      .filter(({ type }) => ConfirmationApprovalTypes.includes(type as ApprovalType))
      .sort((a1, a2) => a1.time - a2.time),
);
```

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[P0] Fix `pendingConfirmationsSortedSelector`
(unmemoized)</issue_title>
> <issue_description>## Summary
> 
> Fix `pendingConfirmationsSortedSelector` to use proper memoization.
This is a **P0 critical fix** required to break the global re-render
cascade.
> 
> **Parent Epic:**
[#6669](MetaMask/MetaMask-planning#6669) —
Break Global Re-render Cascade
> **Depends on:** Fix for `getPendingApprovals` (#6673)
> 
> ## Problem
> 
> `pendingConfirmationsSortedSelector` is a plain function that creates
2 new arrays on every call:
> 
> ```typescript
> // ui/pages/confirmations/selectors/confirm.ts
> export function pendingConfirmationsSortedSelector(state:
ConfirmMetamaskState) {
>   return getPendingApprovals(state)
> .filter(({ type }) => ConfirmationApprovalTypes.includes(type as
ApprovalType)) // ❌ NEW ARRAY
>     .sort((a1, a2) => a1.time - a2.time);  // ❌ NEW ARRAY
> }
> ```
> 
> **Used in Routes** to determine pending confirmation count and
navigation.
> 
> This breaks memoization and causes cascade re-renders across all
children on every state change.
> 
> ## Solution
> 
> Wrap in `createSelector` to memoize the transformation:
> 
> ```typescript
> export const pendingConfirmationsSortedSelector = createSelector(
>   getPendingApprovals,
>   (approvals) =>
>     approvals
> .filter(({ type }) => ConfirmationApprovalTypes.includes(type as
ApprovalType))
>       .sort((a1, a2) => a1.time - a2.time),
> );
> ```
> 
> ## Files
> 
> - `ui/pages/confirmations/selectors/confirm.ts`
> 
> ## Acceptance Criteria
> 
> - [ ] Selector returns same reference when underlying state unchanged
> - [ ] Unit test verifies memoization
> - [ ] No regression in existing tests
> 
> ## Estimated Effort
> 
> 0.5 hours
> 
> ## Labels
> 
> `perf`, `selector`, `P0`, `team-confirmations`</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes MetaMask/MetaMask-planning#6674

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Improves selector performance and adds tests around confirmation
sorting/filtering.
> 
> - **Memoizes** `pendingConfirmationsSortedSelector` with `reselect`
using `getPendingApprovals`, preserving sort-by-time and
confirmation-type filtering while returning stable references
> - **Tests**: verify sorted output, memoization (same reference when
unchanged, new reference on state change), and filtering of
non-confirmation approvals; `oldestPendingConfirmationSelector` behavior
validated
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
88f2be0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Jongsun Suh <jongsun.suh@icloud.com>
github-merge-queue bot pushed a commit that referenced this pull request Mar 11, 2026
…#39309)

## **Description**

`ShieldSubscriptionProvider` was creating a new context value object on
every render and recreating `evaluateCohortEligibility` whenever any of
its 10 dependencies changed, causing unnecessary re-renders of the Home
component subtree.

Two fixes applied:

1. **Stabilized callback with ref pattern** -- Moved the
`evaluateCohortEligibility` implementation into a ref updated via
`useEffect`. The public callback delegates to the ref, keeping a stable
reference (`useCallback` with empty deps) while still accessing current
closure values.

2. **Memoized context value** -- Wrapped the provider value object in
`useMemo` keyed on the now-stable callback, preventing a new object
reference on every render.

This is fix 6 of 6 in the [P0 Global Cascade Re-render Fixes
epic](https://github.com/MetaMask/MetaMask-planning/issues/6669)
([RCA](https://docs.google.com/document/d/1KrzqL1IEQb-ctOKpOxhjBVr_-5jDXTgwC8a4r1ICBg0/edit?tab=t.5g1bh4tznel0),
[Profiling
Report](https://docs.google.com/document/d/1zlTg8rWYUFKZAsjQF3I9o7KaMA6uq7qcYjdfWKIZIJU/edit?tab=t.0#heading=h.dxmx8vunwu2s)).
Together, the 6 memoization fixes break the cascade chain originating in
Routes/Home that propagated through the entire component tree on every
navigation and state update.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/39309?quickstart=1)

## **Profiling Results** (all 6 fixes combined -- [Full
Report](https://docs.google.com/document/d/1zlTg8rWYUFKZAsjQF3I9o7KaMA6uq7qcYjdfWKIZIJU/edit?tab=t.0#heading=h.dxmx8vunwu2s))

We ran 7 rounds of controlled A/B benchmarks over 41 hours. Results
confirm the cascade was the dominant source of interactive UI latency.
WDYR audit: 0 cascade re-renders on treatment (RCA measured 125-200 per
action on baseline).

> Measured on test builds (`yarn build:test`) with React StrictMode,
Power User persona (30 accounts). All p-values from Welch's t-test,
confirmed by Mann-Whitney U. Effect sizes: Cohen's d = 0.93-5.11 (large
to very large). n=5-20 per metric.

### Per-interaction improvements

| Flow | Baseline | Treatment | Improvement | p |
|------|----------|-----------|-------------|---|
| Account switching (5 accts) | 3.1s | 1.7s | **-1.4s, 47% faster** |
0.044 |
| Asset detail nav (ETH) | 83ms | 37ms | **-46ms, 55% faster** | 0.044 |
| Asset detail nav (SOL) | 75ms | 31ms | **-44ms, 59% faster** | <0.001
|
| Send flow (e2e) | 928ms | 804ms | -124ms, 13% faster | 0.044 |
| Send: token select to form | 25ms | 13ms | **-12ms, 49% faster** |
0.040 |

### Cascade compounding under sustained interaction

Route cycling benchmark (3 round-trips: Send -> Assets -> Swap):

| Segment | Baseline | Treatment | Improvement | p |
|---------|----------|-----------|-------------|---|
| **Total (6 transitions)** | **19.9s** | **2.7s** | **-17.2s, 7x
faster** | 0.004 |
| sendToHome | 6.4s | 723ms | -5.7s, 89% faster | 0.018 |
| assetDetailsToHome | 6.3s | 659ms | -5.7s, 90% faster | 0.004 |
| swapToHome | 5.0s | 787ms | -4.2s, 84% faster | 0.001 |
| homeToSwap | 877ms | 227ms | -650ms, 74% faster | 0.002 |
| homeToAssetDetails | 628ms | 236ms | -392ms, 62% faster | 0.004 |
| homeToSend | 673ms | 82ms | -591ms, 88% faster | 0.046 |

The 7x is a **compounding effect** across successive transitions, not a
per-transition effect. Isolated transitions show only 10-15%
improvement. Each navigation accumulates subscription/listener state on
baseline, escalating re-render overhead -- consistent with the RCA's
cascade chain model (Routes -> Home -> all children).

This compounding behavior is itself a key finding: it means any
remaining or future cascade leak will degrade **non-linearly** with
session length, even if it looks trivial in a single benchmark.

**Production estimate:** StrictMode roughly doubles render work. The 7x
measured in dev builds is expected to land at ~2-7x in production
depending on wallet size, tree depth, and GC timing.

### Metrics to watch after merge

- **CI (`metamask-performance`):** `assetClickToPriceChart` should show
a step-change (83->37ms dev). `cascadeFixProfiling` preset can be added
for ongoing regression tracking.
- **Production Sentry (RUM):** Best signal at **p75/p95 navigation
latency** and **INP p95**, segmented by users with 10+ accounts. Expect
improvement on route transitions, account switching, token list
rendering. Signal will be diffuse due to network variance and device
heterogeneity -- don't expect a clean step-function.
- **TBT:** Cascade generates clusters of consecutive long tasks (>50ms)
on every state change. Fix eliminates these. Should show up in Sentry
performance dashboard once long-task tracking is enabled.

## **Changelog**

CHANGELOG entry: Fix memoization issue in top-level context provider
that was causing cascading re-renders.

## **Related issues**

Fixes: MetaMask/MetaMask-planning#6638

Part of epic: [MetaMask-planning#6669 -- P0 Global Cascade Re-render
Fixes](https://github.com/MetaMask/MetaMask-planning/issues/6669)

Sibling PRs:
- [#39310](#39310) --
Memoize MetaMetrics context
([#6640](https://github.com/MetaMask/MetaMask-planning/issues/6640))
- [#39311](#39311) --
Stabilize `withRouterHooks` params/location
([#6671](https://github.com/MetaMask/MetaMask-planning/issues/6671))
- [#39312](#39312) --
Fix `getPendingApprovals` selector
([#6673](https://github.com/MetaMask/MetaMask-planning/issues/6673))
- [#39313](#39313) --
Fix `pendingConfirmationsSortedSelector`
([#6674](https://github.com/MetaMask/MetaMask-planning/issues/6674))
- [#39314](#39314) --
Remove dead What's New modal code
([#6670](https://github.com/MetaMask/MetaMask-planning/issues/6670))

## **Manual testing steps**

## **Screenshots/Recordings**

## **Pre-merge author checklist**

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

## **Pre-merge reviewer checklist**

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


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes how `evaluateCohortEligibility` is wired (ref-backed stable
callback) and how the context value is memoized, which could alter
when/with-what state eligibility checks run if the ref is not kept in
sync. Added tests reduce risk but this touches subscription/cohort
gating that affects modal display behavior.
> 
> **Overview**
> Reduces cascading re-renders from `ShieldSubscriptionProvider` by
making the exported `evaluateCohortEligibility` function
reference-stable and memoizing the context `value` object.
> 
> Refactors `evaluateCohortEligibility` to delegate through a
`useRef`-stored implementation (updated during render so it’s available
in commit-phase callbacks) while keeping the public callback
`useCallback([])`. Adds a new Jest test suite validating child
rendering, stable callback/context identity across re-renders, and that
the stable callback still observes updated selector values.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
50a2fed. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Jongsun Suh <jongsun.suh@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants