Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion packages/sdk-multichain/src/multichain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class MultichainSDK extends MultichainCore {
private __provider: MultichainApiClient<RPCAPI> | undefined = undefined;
private __transport: ExtendedTransport | undefined = undefined;
private __dappClient: DappClient | undefined = undefined;

private __beforeUnloadListener: (() => void) | undefined;
public __state: SDKState = 'pending';
private listener: (() => void | Promise<void>) | undefined;

Expand Down Expand Up @@ -241,7 +241,26 @@ export class MultichainSDK extends MultichainCore {
}
}

private async onBeforeUnload() {
if (this.options.ui.factory.modal) {
//Modal is still visible, remove storage to prevent glitch with "connecting" state
await this.storage.removeTransport();
}
}

private createBeforeUnloadListener() {
if (typeof window !== 'undefined') {
window.addEventListener('beforeunload', this.onBeforeUnload.bind(this));
}
return () => {
if (typeof window !== 'undefined') {
window.removeEventListener('beforeunload', this.onBeforeUnload.bind(this));
}
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Memory Leak in Event Listener Binding

The createBeforeUnloadListener function has a memory leak. this.onBeforeUnload.bind(this) creates a new function reference each time it's called. This prevents the beforeunload event listener from being properly removed, as removeEventListener receives a different function reference than addEventListener, causing listeners to accumulate.

Fix in Cursor Fix in Web

private async showInstallModal(desktopPreferred: boolean, scopes: Scope[], caipAccountIds: CaipAccountId[]) {
// create the listener only once to avoid memory leaks
this.__beforeUnloadListener ??= this.createBeforeUnloadListener();
return new Promise<void>((resolve, reject) => {
// Use Connection Modal
this.options.ui.factory.renderInstallModal(
Expand Down Expand Up @@ -412,6 +431,7 @@ export class MultichainSDK extends MultichainCore {

async disconnect(): Promise<void> {
this.listener?.();
this.__beforeUnloadListener?.();

await this.__transport?.disconnect();
await this.storage.removeTransport();
Expand All @@ -420,6 +440,7 @@ export class MultichainSDK extends MultichainCore {
this.emit('stateChanged', 'disconnected');

this.listener = undefined;
this.__beforeUnloadListener = undefined;
this.__transport = undefined;
this.__provider = undefined;
this.__dappClient = undefined;
Expand Down
Loading