fix: Better handle expired session and auth data for walletconnect (#3228)

* fix: Better handle expired session and auth data for walletconnect * fix: handle dApp disconnected sessions, reflecting the state locally * Update lib/src/screens/wallet_connect/services/walletkit_service.dart [skip ci --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

David Adegoke committed May 20, 2026 at 17:22 UTC d325ece6ea51cfe12c2260a55ab24761d16e67cb
1 file changed +72 -15
lib/src/screens/wallet_connect/services/walletkit_service.dart
+72 -15
@@ -2,6 +2,7 @@ import 'dart:async';
2 import 'dart:convert';
3 import 'dart:typed_data';
4
5 +import 'package:cw_core/utils/print_verbose.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:eth_sig_util/util/utils.dart';
8 import 'package:flutter/material.dart';
@@ -149,6 +150,9 @@ abstract class WalletKitServiceBase with Store {
150
151 _refreshPairings();
152
153 + sessions.clear();
154 + auth.clear();
155 +
156 final newSessions = _walletKit.sessions.getAll();
157 sessions.addAll(newSessions);
158
@@ -185,12 +189,16 @@ abstract class WalletKitServiceBase with Store {
189 final isOnline = _walletKit.core.connectivity.isOnline.value;
190 if (!isOnline) {
191 await Future.delayed(const Duration(milliseconds: 500));
188 - _emitEvent();
192 +Future<void> _emitEvent({int retries = 0}) async {
193 +final isOnline = _walletKit.core.connectivity.isOnline.value;
194 +if (!isOnline && retries < 3) {
195 +await Future.delayed(const Duration(milliseconds: 500));
196 + await _emitEvent(retries: ++retries);
197 return;
198 }
199
192 - final sessions = _walletKit.sessions.getAll();
193 - for (var session in sessions) {
200 + final engineSessions = _walletKit.sessions.getAll();
201 + for (var session in engineSessions) {
202 final chainKeys = walletKeyService.getKeysForChain(appStore.wallet!);
203 for (var chain in chainKeys) {
204 for (var chainID in chain.chains) {
@@ -200,13 +208,12 @@ abstract class WalletKitServiceBase with Store {
208 namespaces: session.namespaces,
209 );
210 if (events.contains('accountsChanged')) {
203 - final chainKeys = walletKeyService.getKeysForChain(appStore.wallet!);
204 - _walletKit.emitSessionEvent(
211 + await _walletKit.emitSessionEvent(
212 topic: session.topic,
213 chainId: chainID,
214 event: SessionEventParams(
215 name: 'accountsChanged',
209 - data: [chainKeys.first.publicKey],
216 + data: [chain.publicKey],
217 ),
218 );
219 }
@@ -215,6 +222,7 @@ abstract class WalletKitServiceBase with Store {
222 try {
223 await deletePairing(topic: session.pairingTopic);
224 } catch (_) {}
225 + sessions.removeWhere((s) => s.topic == session.topic);
226 _refreshPairings();
227 }
228 } catch (_) {}
@@ -242,6 +250,10 @@ abstract class WalletKitServiceBase with Store {
250 _walletKit.core.pairing.onPairingDelete.unsubscribe(_onPairingDelete);
251 _walletKit.core.pairing.onPairingExpire.unsubscribe(_onPairingDelete);
252
253 + sessions.clear();
254 + auth.clear();
255 + pairings.clear();
256 +
257 isInitialized = false;
258 }
259
@@ -511,23 +523,57 @@ abstract class WalletKitServiceBase with Store {
523
524 @action
525 Future<void> deletePairing({required String topic}) async {
514 - final topicSessions = sessions.where((element) => element.pairingTopic == topic);
526 + final topicSessions =
527 + sessions.where((element) => element.pairingTopic == topic).toList();
528
529 await _walletKit.core.pairing.disconnect(topic: topic);
530 for (var session in topicSessions) {
518 - await _walletKit.disconnectSession(
519 - topic: session.topic,
520 - reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(),
521 - );
531 + try {
532 + await _walletKit.disconnectSession(
533 + topic: session.topic,
534 + reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(),
535 + );
536 + } catch (_) {}
537 }
538 +
539 + await _removePairingTopicFromLocalStorage(topic);
540 + sessions.clear();
541 + sessions.addAll(_walletKit.sessions.getAll());
542 + _refreshPairings();
543 }
544
545 @action
546 Future<void> disconnectSession({required String topic}) async {
527 - await walletKit.disconnectSession(
528 - topic: topic,
529 - reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(),
530 - );
547 + String? pairingTopic;
548 + for (final s in sessions) {
549 + if (s.topic == topic) {
550 + pairingTopic = s.pairingTopic;
551 + break;
552 + }
553 + }
554 + pairingTopic ??= _walletKit.sessions.get(topic)?.pairingTopic;
555 +
556 + try {
557 + await walletKit.disconnectSession(
558 + topic: topic,
559 + reason: Errors.getSdkError(Errors.USER_DISCONNECTED).toSignError(),
560 + );
561 + } catch (e) {
562 + printV('disconnectSession: $e');
563 + }
564 +
565 + sessions.clear();
566 + sessions.addAll(_walletKit.sessions.getAll());
567 +
568 + if (pairingTopic != null &&
569 + !_walletKit.sessions.getAll().any((s) => s.pairingTopic == pairingTopic)) {
570 + await _removePairingTopicFromLocalStorage(pairingTopic);
571 + try {
572 + await _walletKit.core.pairing.disconnect(topic: pairingTopic);
573 + } catch (_) {}
574 + }
575 +
576 + _refreshPairings();
577 }
578
579 @action
@@ -637,6 +683,17 @@ abstract class WalletKitServiceBase with Store {
683 return jsonList.map((item) => item as String).toList();
684 }
685
686 + Future<void> _removePairingTopicFromLocalStorage(String pairingTopic) async {
687 + final key = getKeyForStoringTopicsForWallet();
688 + if (key.isEmpty) return;
689 +
690 + final topics = getPairingTopicsForWallet(key);
691 + if (!topics.contains(pairingTopic)) return;
692 +
693 + topics.remove(pairingTopic);
694 + await sharedPreferences.setString(key, jsonEncode(topics));
695 + }
696 +
697 Future<void> savePairingTopicToLocalStorage(String pairingTopic) async {
698 // Get key specific to the current wallet
699 final key = getKeyForStoringTopicsForWallet();