Skip to content

fix: multichain sdk state + bug fixing#1358

Merged
elribonazo merged 5 commits into
mainfrom
fix/multichain-state-update
Oct 6, 2025
Merged

fix: multichain sdk state + bug fixing#1358
elribonazo merged 5 commits into
mainfrom
fix/multichain-state-update

Conversation

@elribonazo

@elribonazo elribonazo commented Oct 3, 2025

Copy link
Copy Markdown
Contributor

Explanation

Main objetive of this PR is to ensure we are setting the right state for the multichain SDK with all transports and flows, we should be able to capture the current state, "loaded", "pending", "connecting", "connected" and "disconnected"

Together with that simple fix, we are also including some secondary issues that have also been addressed as part of this PR:

  1. MWP transport not sending the right payload on RPC callbacks (Notifications) causing some glitches in the user interface (multichain react playground)

  2. Added better logging to both, default and mwp transport.

  3. Fix wrong state being reported, exclusively on Browser + Extension and default transport (show install modal flow)

  4. Prevent default transport from timing out requests, happens because we were not defining the default transport and it considers they are expired already if timeout is 0 or undefined

  5. If there's an error establishing connection with an existing stored transport, automatically remove it to allow the user to establish connection again on desired transport instead or leaving the Dapp in a connecting state permanently

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 highlighted breaking changes using the "BREAKING" category above as appropriate

Note

Unifies connection state management across flows, fixes MWP notifications and session handling, adds default transport timeouts, introduces a transport logger namespace, and updates tests accordingly.

  • State management:
    • Introduce handleConnection to set connecting/connected/disconnected consistently across connect, showInstallModal, and deeplink flows.
    • On init errors, remove stored transport and set state to pending; on stored transport setup, ensure final state is connected when applicable.
  • MWP transport (multichain/transports/mwp):
    • Send proper notifications (wallet_sessionChanged) for session-related RPCs and notify listeners before resolving.
    • Handle wallet_getSession/wallet_createSession errors; create session when missing; log active session with metamask-sdk:transport.
  • Default transport (multichain/transports/default):
    • Add default request timeout and use it by default; propagate RPC errors; create session when none exists.
  • Logging:
    • Add metamask-sdk:transport namespace to logger.
  • Tests:
    • Update expectations to assert disconnected after failed connections and refine state assertions across scenarios.

Written by Cursor Bugbot for commit 8a21cee. This will update automatically on new commits. Configure here.

@elribonazo
elribonazo marked this pull request as ready for review October 3, 2025 07:52
@elribonazo
elribonazo requested a review from a team as a code owner October 3, 2025 07:52
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@codecov

codecov Bot commented Oct 3, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.99%. Comparing base (b2e5984) to head (8a21cee).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1358   +/-   ##
=======================================
  Coverage   74.99%   74.99%           
=======================================
  Files         184      184           
  Lines        4519     4519           
  Branches     1108     1108           
=======================================
  Hits         3389     3389           
  Misses       1130     1130           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@elribonazo elribonazo self-assigned this Oct 3, 2025
.then(() => {
this.options.ui.factory.unload();
this.options.ui.factory.modal?.unmount();
this.state = 'connected';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see these referenced in a lot of places, would definitely make our lives easier if these 'connected', 'disconnected', etc.. states are an ENUM instead of hardcoded strings.

@elribonazo elribonazo Oct 3, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this.state is typed and has autocomplete, there's no need for an enum here, the problem that enums have in my opinion to enforce strings is that you are forced then to pass the enum value, while in reality its just a string.

those values are fully typed and when ur typing the value it shows u which values are available, easy auto-complete

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

my preference would be to use an enum. I mean the type right below SDKState is an enum too https://github.com/MetaMask/metamask-sdk/blob/main/packages/sdk-multichain/src/domain/multichain/index.ts#L10

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can we threat this as a separate issue? gonna add it in notion

const currentScopes = Object.keys(walletSession?.sessionScopes ?? {}) as Scope[];
const proposedScopes = options?.scopes ?? [];
const isSameScopes = currentScopes.every((scope) => proposedScopes.includes(scope)) && proposedScopes.every((scope) => currentScopes.includes(scope));
logger('isSameScopes', isSameScopes);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm imagining this log was for debugging purposes, do we want to keep it ?

Also, could we leave a // TODO comment here to come back to this implementation of isSameScopes, since we are only checking surface level scope for now, and not the actual accounts inside, as we previously discussed in one of our team syncs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

logger only enables if debug options are active. In order to show the debugging logs u need to add debug to your storage and also define the scope, multichain-sdk:* or multichain-sdk:core or multichain-sdk:transports but they are not activated by default and not visible by default

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Around checking the accounts also, instead of adding the comment i propose to create ticket in notion

}

private onWindowFocus(): void {
logger('onWindowFocus', this.isConnected());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

}

private notifyCallbacks(data: unknown): void {
logger('notifyCallbacks', data);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

}

private handleMessage(message: unknown): void {
logger('handleMessage', message);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

const currentScopes = Object.keys(walletSession?.sessionScopes ?? {}) as Scope[];
const proposedScopes = options?.scopes ?? [];
const isSameScopes = currentScopes.every((scope) => proposedScopes.includes(scope)) && proposedScopes.every((scope) => currentScopes.includes(scope));
logger('onResumeSuccess isSameScopes', isSameScopes, currentScopes, proposedScopes);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

params: walletSession,
});
resumeResolve();
logger('onResumeSuccess finished');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

logger('resuming session...');
connection = new Promise<void>((resumeResolve, resumeReject) => {
if (this.dappClient.state === 'CONNECTED') {
logger('already connected, onResumeSuccess');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

* Registers a callback for notifications from the wallet
*/
onNotification(callback: (data: unknown) => void): () => void {
logger('onNotification registered');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

logger('onNotification registered');
this.notificationCallbacks.add(callback);
return () => {
logger('onNotification unregistered');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here, is this just a debug log or do we want to keep it ?

@elribonazo
elribonazo enabled auto-merge (squash) October 3, 2025 09:22
@elribonazo
elribonazo force-pushed the fix/multichain-state-update branch from b961bb0 to fb2d0c4 Compare October 3, 2025 11:17
@elribonazo
elribonazo force-pushed the fix/multichain-state-update branch from fb2d0c4 to 8a21cee Compare October 3, 2025 12:27
Comment thread packages/sdk-multichain/src/multichain/transports/default/index.ts
@sonarqubecloud

sonarqubecloud Bot commented Oct 3, 2025

Copy link
Copy Markdown

@elribonazo
elribonazo requested a review from ffmcgee725 October 3, 2025 13:15
@chakra-guy
chakra-guy disabled auto-merge October 3, 2025 14:09
@elribonazo
elribonazo merged commit 7af4f97 into main Oct 6, 2025
44 checks passed
@elribonazo
elribonazo deleted the fix/multichain-state-update branch October 6, 2025 08:22
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.

4 participants