-
Notifications
You must be signed in to change notification settings - Fork 802
chore: resubscribe when no result is received #7022
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v2.0
Are you sure you want to change the base?
Conversation
…resubscribes and waits again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements a resubscription mechanism to handle pending requests that haven't received responses from peer clients. The feature uses a heartbeat to periodically check for requests waiting too long and automatically resubscribes to topics to ensure message delivery.
Key changes:
- Added
requestWaitingForResultmap to track pending requests with timestamps - Implemented heartbeat-based monitoring that resubscribes to topics for stale requests
- Added diagnostic logging in canary tests to identify requests stuck without responses
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| packages/sign-client/src/controllers/engine.ts | Implements core resubscription logic with request tracking, heartbeat monitoring, and cleanup on response receipt |
| packages/sign-client/src/constants/engine.ts | Defines configuration constants for resubscribe interval (30s) and max attempts (3) |
| packages/sign-client/test/canary/canary.spec.ts | Adds diagnostic logging to track pending requests in canary tests and enhances log function with level parameter |
Comments suppressed due to low confidence (2)
packages/sign-client/src/controllers/engine.ts:1573
- The
deleteProposalmethod doesn't clean up entries fromrequestWaitingForResultmap when proposals are deleted (except for the error case on line 397). When proposals expire or are deleted through other paths (lines 569, 606, 1098, 1468, 2235, 2307, 2770, 2908), the corresponding entries remain in the map, leading to unnecessary resubscription attempts. Consider addingthis.requestWaitingForResult.delete(id)in thedeleteProposalmethod.
private deleteProposal: EnginePrivate["deleteProposal"] = async (id, expirerHasDeleted) => {
if (expirerHasDeleted) {
try {
const proposal = this.client.proposal.get(id);
const event = this.client.core.eventClient.getEvent({ topic: proposal.pairingTopic });
event?.setError(EVENT_CLIENT_SESSION_ERRORS.proposal_expired);
} catch (error) {}
}
await Promise.all([
this.client.proposal.delete(id, getSdkError("USER_DISCONNECTED")),
expirerHasDeleted ? Promise.resolve() : this.client.core.expirer.del(id),
]);
this.addToRecentlyDeleted(id, "proposal");
};
packages/sign-client/src/controllers/engine.ts:1558
- The
deleteSessionmethod doesn't clean up entries from therequestWaitingForResultmap when sessions are deleted. Requests associated with the deleted topic will remain in the map and continue to trigger resubscription attempts. Consider iterating through the map and removing entries whererequest.topic === topic.
private deleteSession: EnginePrivate["deleteSession"] = async (params) => {
const { topic, expirerHasDeleted = false, emitEvent = true, id = 0 } = params;
const { self } = this.client.session.get(topic);
// Await the unsubscribe first to avoid deleting the symKey too early below.
await this.client.core.relayer.unsubscribe(topic);
await this.client.session.delete(topic, getSdkError("USER_DISCONNECTED"));
this.addToRecentlyDeleted(topic, "session");
if (this.client.core.crypto.keychain.has(self.publicKey)) {
await this.client.core.crypto.deleteKeyPair(self.publicKey);
}
if (this.client.core.crypto.keychain.has(topic)) {
await this.client.core.crypto.deleteSymKey(topic);
}
if (!expirerHasDeleted) this.client.core.expirer.del(topic);
// remove any deeplinks from storage after the session is deleted
// to avoid navigating to incorrect deeplink later on
this.client.core.storage
.removeItem(WALLETCONNECT_DEEPLINK_CHOICE)
.catch((e) => this.client.logger.warn(e));
// reset the queue state back to idle if a request for the deleted session is still in the queue
if (topic === this.sessionRequestQueue.queue[0]?.topic) {
this.sessionRequestQueue.state = ENGINE_QUEUE_STATES.idle;
}
await Promise.all(
this.getPendingSessionRequests()
.filter((r) => r.topic === topic)
.map((r) => this.deletePendingSessionRequest(r.id, getSdkError("USER_DISCONNECTED"))),
);
if (emitEvent) this.client.events.emit("session_delete", { id, topic });
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| export const MAX_RESUBSCRIBE_ATTEMPTS_ON_PENDING_RESULTS = 3; | ||
|
|
||
| export const RESUBSCRIBE_INTERVAL = 30; |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RESUBSCRIBE_INTERVAL constant lacks a unit suffix or documentation. Consider renaming it to RESUBSCRIBE_INTERVAL_SECONDS or adding a JSDoc comment to clarify it represents seconds, especially since other constants in the file use named constants from @walletconnect/time (e.g., FIVE_MINUTES, ONE_DAY).
| export const RESUBSCRIBE_INTERVAL = 30; | |
| export const RESUBSCRIBE_INTERVAL_SECONDS = 30; |
| import { ISignClient } from "@walletconnect/types"; | ||
|
|
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import ISignClient.
| import { ISignClient } from "@walletconnect/types"; |
Description
This PR implements a resubscription mechanism to handle pending requests that haven't received responses from peer clients. The feature uses a heartbeat to periodically check for requests waiting too long and automatically resubscribes to topics to ensure message delivery.
Key changes:
Type of change
How has this been tested?
Checklist
Additional Information (Optional)
Please include any additional information that may be useful for the reviewer.
Note
Adds heartbeat-driven resubscription for requests awaiting results and tracks them across propose/request/ping flows; tests now log any pending requests.
requestWaitingForResultmap to track RPCs awaiting responses; entries added forwc_sessionPropose,wc_sessionRequest, andwc_sessionPing, and cleared on response/error.RESUBSCRIBE_INTERVALandMAX_RESUBSCRIBE_ATTEMPTS_ON_PENDING_RESULTSwith debug/warn logging.fromMilisecondsandHEARTBEAT_EVENTS; wires heartbeat duringinit().RESUBSCRIBE_INTERVALandMAX_RESUBSCRIBE_ATTEMPTS_ON_PENDING_RESULTSinconstants/engine.ts.engine.requestWaitingForResultitems for both clients if any.Written by Cursor Bugbot for commit d8ac736. This will update automatically on new commits. Configure here.