RPC Client Cache

closeAllRpcClients() — the only way to evict the process-global RPC client caches (which may hold API keys or basic-auth in their URLs) when the process is done making RPC calls, deliberately separate from Workspace.close().

Every outbound RPC call — reading a balance, simulating, broadcasting, polling a tracker — goes through a cached client. The library keeps one client per distinct endpoint URL, process-wide, and reuses it for the life of the process. closeAllRpcClients() is the one function that empties those caches.

shutdown.ts
import { closeAllRpcClients } from "wative-core";

// … your process makes RPC calls through the library …

await closeAllRpcClients(); // call once, when the process is done with RPC

Why the cache exists

Opening a fresh JSON-RPC client for every call would be wasteful, so the library memoizes. Two module-global maps hold the cached clients:

  • an EVM HTTP JSON-RPC client per endpoint, and
  • a Solana (@solana/web3.js) Connection per endpoint.

The maps are populated automatically the first time the library talks to a given network. You never create these clients yourself and there is no per-call setup cost after the first — that is the point of the cache.

What it keys on

The cache key is the canonical form of the network's rpcUrl, not the raw string. Canonicalization runs the URL through new URL(...).toString(), which:

  • lowercases the scheme and host — https://RPC.X.com/v1 and https://rpc.x.com/v1 share one client;
  • elides a default port — https://x.com:443/p collapses to https://x.com/p;
  • resolves .. segments and supplies a canonical trailing slash for an empty path;
  • strips the fragment — /v1#primary and /v1#backup are one endpoint.

It does not collapse a doubled slash inside the path, so https://x.com/a//b stays distinct from https://x.com/a/b. Two networks are considered "the same endpoint" exactly when they canonicalize to the same key and therefore share a cached client.

Errors and logs never echo the raw URL — the library redacts it to scheme://host before surfacing it — but the live cache entry is the unredacted key.

When to call

Call closeAllRpcClients() once, when the process is finished making RPC calls — typically as part of shutdown. It:

  • clears the EVM client map and the Solana Connection map, releasing the retained (possibly credential-bearing) endpoint URLs;
  • returns a Promise<void>await it so any release completes before exit;
  • releases every cached client of every kind, EVM and Solana alike.

It is safe to call when the caches are already empty (a fresh process that made no calls) — it simply resolves.

Why it is not wired into close()

The caches are process-global and shared by every workspace. closeAllRpcClients() is deliberately not called by Workspace.close() (nor by lock()), because a per-workspace teardown that emptied a shared cache would evict clients another workspace in the same process is still using.

So the two responsibilities are kept separate:

ScopeWhat tears it down
One workspace's in-memory state (keys, unlocked containers)Workspace.lock() / Workspace.close()
The process-global RPC client cachescloseAllRpcClients()

If your process opens and closes many workspaces over its lifetime, keep letting the RPC cache serve all of them, and call closeAllRpcClients() exactly once at the very end.

Last updated on