Skip to content

fix(shared): make ApiManager refresh paths actually replace the stale api#1259

Merged
ebma merged 2 commits into
stagingfrom
fix/api-manager-refresh-noop
Jul 8, 2026
Merged

fix(shared): make ApiManager refresh paths actually replace the stale api#1259
ebma merged 2 commits into
stagingfrom
fix/api-manager-refresh-noop

Conversation

@ebma

@ebma ebma commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

populateApi() in packages/shared/src/services/pendulum/apiManager.ts returns the cached instance when one exists, but it was also used as the "refresh" mechanism in two places:

  • getApi() when a spec version change is detected (logs "refreshing the api...")
  • executeApiCall() on the "Transaction has a bad signature" retry

In both cases the cached instance was returned unchanged, so the refresh was a no-op — the stale ApiPromise kept being used.

Changes

  • Add refreshApi(), which removes the cached instance from apiInstances, disconnects the old ApiPromise (best-effort with a warning log), and creates a fresh connection. The connect-and-cache logic is shared with populateApi via a private createAndCacheApi helper, so populateApi/populateAllApis remain idempotent for normal startup.
  • getApi() uses refreshApi() on the spec-version-change path, and also for forceRefresh: true, which had the same no-op bug (no current callers pass it).
  • The bad-signature retry in executeApiCall() uses refreshApi() and rebuilds the extrinsic via createCall(refreshedApi.api) — the original call object is bound to the stale api's registry, so re-signing it would defeat the refresh even with a new connection.

Tests

New ApiManager api refresh block in apiManager.test.ts stubs the private connectApi with fake instances whose reported spec version is controllable:

  • populateApi idempotency (cached instance reused, no reconnect, no disconnect)
  • spec-version-change path: a simulated runtime upgrade must yield a new instance, disconnect the old one, and update apiInstances
  • forceRefresh path: same replacement + disconnect semantics

Both refresh tests were verified to fail against the previous implementation (getApi returned the identical stale instance).

Verified with bun test in packages/shared (19 pass), bun typecheck, and bun lint.

… api

populateApi() returns the cached instance when one exists, but it was
also used as the refresh mechanism in getApi() (on spec version change)
and executeApiCall() (on bad-signature retry). In both cases the cached
ApiPromise was returned unchanged, so the refresh was a no-op and the
stale connection kept being used.

Add refreshApi(), which disconnects the cached instance and replaces it
with a fresh connection, and use it on the spec-version-change path,
the forceRefresh path, and the bad-signature retry. The retry also
rebuilds the extrinsic against the refreshed api, since the original
call is bound to the stale registry. populateApi/populateAllApis stay
idempotent for normal startup.

Add tests covering populateApi idempotency and both refresh paths; the
refresh tests fail against the previous implementation.
@ebma ebma requested a review from Copilot July 8, 2026 08:50
@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi ready!

Name Link
🔨 Latest commit b5db18e
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a4e118a883860000848d43f
😎 Deploy Preview https://deploy-preview-1259--vortexfi.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit b5db18e
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a4e118a3dc2df0008bf9b9f
😎 Deploy Preview https://deploy-preview-1259--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes a stale-cache bug in ApiManager where “refresh” code paths were reusing an already-cached ApiPromise, making refresh operations (spec-version change and bad-signature retry) effectively no-ops.

Changes:

  • Introduces refreshApi() to discard a cached API instance and create a new connection, and refactors connect/cache behavior into createAndCacheApi().
  • Updates getApi() to refresh on spec-version changes and when forceRefresh is requested.
  • Updates the bad-signature retry path in executeApiCall() to refresh and rebuild the extrinsic against the refreshed API registry; adds tests covering refresh semantics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/shared/src/services/pendulum/apiManager.ts Adds explicit refresh behavior and updates refresh call sites (spec-version change + bad-signature retry).
packages/shared/src/services/pendulum/apiManager.test.ts Adds unit tests asserting refresh replaces cached instances and disconnects stale ones.
Comments suppressed due to low confidence (1)

packages/shared/src/services/pendulum/apiManager.ts:141

  • createAndCacheApi stores the new API instance in apiInstances before ensuring it is connected and isReady. If another request calls populateApi/getApi concurrently, it can observe the cached instance and start using it while it is still connecting. Caching should happen only after the API is confirmed ready (or the method should otherwise guard concurrent readers).
    const instanceKey = this.generateInstanceKey(networkName, index);
    const newApi = await this.connectApi(networkName, index);
    this.apiInstances.set(instanceKey, newApi);

    if (!newApi.api.isConnected) await newApi.api.connect();
    await newApi.api.isReady;


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/shared/src/services/pendulum/apiManager.ts
Address review feedback on refreshApi: the stale instance was removed
and disconnected before the replacement connection existed, so a failed
reconnect left the network without any cached api. Create the new
instance first, then swap and best-effort disconnect the stale one.

Also cache new instances only after connect/isReady so concurrent
readers never observe a still-connecting api.

Add a test asserting a failed refresh keeps the previous instance
cached and undisconnected.
@ebma ebma merged commit 77610a9 into staging Jul 8, 2026
6 of 8 checks passed
@ebma ebma deleted the fix/api-manager-refresh-noop branch July 8, 2026 09:01
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.

2 participants