You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enabling the documented cache configuration without an explicit enabledMethods set makes both CoordinatorEdgeRpcProvider and RealmEdgeRpcProvider throw on their first read:
TypeError: Cannot read properties of undefined (reading 'has')
Basic reads without cache work. Explicitly supplying enabledMethods also works and caches the second identical read. This report covers only cache initialization, not other provider behavior.
Affected revision and environment
Default branch: mainnet-beta, rechecked before filing.
Commit: a34d5fc84580b8ef88d77c7d72fdcc515c467a3a.
Source package: @psy-protocol/psy-sdk, manifest version 2.0.4.
Linux x86_64 / WSL2, unchanged workspace lockfile.
Tests execute the exported provider classes from this checkout, not an independently downloaded registry build. Rust/WASM is not invoked by this reproducer.
Supported workflow and expected behavior
A developer enables caching using { cache: { ttl: 60000, maxSize: 1000 } }, then performs a normal read.
The cache example omits enabledMethods and says that the first read reaches the server and the second uses cache. The CacheConfig declaration explicitly makes the property optional, defaulting to all read methods.
Expected: both reads resolve successfully, with one transport request for two identical cached reads.
Those fields are initialized after super() returns. The base constructor therefore captures undefined as the default cacheConfig.enabledMethods. The later request calls the getter successfully, but still uses the earlier captured value for .has().
Minimal reproduction
In a disposable checkout of the pinned commit, use the project's locked dependencies:
cd psy-ts-sdk
pnpm install --frozen-lockfile
# Save the test below as packages/psy-sdk/src/provider/cache.issue.test.ts
unshare -Urn -- pnpm --filter @psy-protocol/psy-sdk exec jest --runInBand --no-cache --runTestsByPath src/provider/cache.issue.test.ts
unshare -Urn is the Linux network-isolation step used in verification. Packages must be installed before entering the isolated namespace. No compiler checkout, WASM module, live RPC, wallet, proving service or transaction is needed for this test.
The fixture implements the SDK's supported IHTTPClient injection interface and checks the real JSON-RPC request method/params. Responses are synthetic JSON-RPC 2.0 text envelopes. Neither the provider classes nor their cache logic are mocked.
Complete executed cache-only regression test
import{describe,expect,it}from"@jest/globals";import{CoordinatorEdgeRpcProvider}from"../coord-edge-rpc/client";import{CoordinatorEdgeRPCCommand}from"../coord-edge-rpc/types";import{RealmEdgeRpcProvider}from"../realm-edge-rpc/client";import{RealmEdgeRPCCommand}from"../realm-edge-rpc/types";importtype{IHTTPClient,ISimpleHTTPRequest,ISimpleHTTPResponse}from"../http/types";importtype{ClientConfig}from"./provider";// Synthetic JSON-RPC transport implementing the SDK's public injectable interface.// No external server, signing operation, proving operation, or WASM mock is involved.classReadTransportimplementsIHTTPClient{readonlyrequests: ISimpleHTTPRequest[]=[];constructor(privatereadonlymethod: string,privatereadonlyresult: number|string,privatereadonlyfailingUrl?: string){}asyncsendRequest(request: ISimpleHTTPRequest): Promise<ISimpleHTTPResponse>{this.requests.push(request);expect(request.method).toBe("POST");expect(request.responseType).toBe("text");expect(typeofrequest.body).toBe("string");constwire: {jsonrpc: string;id: string;method: string;params: unknown}=JSON.parse(String(request.body));expect(wire.jsonrpc).toBe("2.0");expect(wire.method).toBe(this.method);expect(wire.params).toEqual([]);if(request.url===this.failingUrl)thrownewError("ECONNREFUSED");return{statusCode: 200,body: JSON.stringify({jsonrpc: "2.0",id: wire.id,result: this.result})};}}typeReadClient={read(): Promise<unknown>;destroy(): void};typeScenario={name: string;method: string;value: number|string;create(config: ClientConfig,transport: IHTTPClient): ReadClient;};constscenarios: Scenario[]=[{name: "coordinator",method: CoordinatorEdgeRPCCommand.GetLatestCheckpointId,value: 42,create(config,transport){constclient=newCoordinatorEdgeRpcProvider("http://127.0.0.1:18001",config,transport);return{read: ()=>client.getLatestCheckpointId(),destroy: ()=>client.destroy()};},},{name: "realm",method: RealmEdgeRPCCommand.GetLatestCheckpointTreeRoot,value: "11".repeat(32),create(config,transport){constclient=newRealmEdgeRpcProvider("http://127.0.0.1:18002",config,transport);return{read: ()=>client.getLatestCheckpointTreeRoot(),destroy: ()=>client.destroy()};},},];for(constscenarioofscenarios){describe(`quality cache initialization: ${scenario.name}`,()=>{it("control: basic reads work without cache configuration",async()=>{consttransport=newReadTransport(scenario.method,scenario.value);constclient=scenario.create({},transport);try{expect(awaitclient.read()).toEqual(scenario.value);expect(awaitclient.read()).toEqual(scenario.value);expect(transport.requests).toHaveLength(2);}finally{client.destroy();}});it("control: explicitly selected read methods cache successfully",async()=>{consttransport=newReadTransport(scenario.method,scenario.value);constclient=scenario.create({cache: {enabledMethods: newSet([scenario.method])}},transport);try{expect(awaitclient.read()).toEqual(scenario.value);expect(awaitclient.read()).toEqual(scenario.value);expect(transport.requests).toHaveLength(1);}finally{client.destroy();}});it("omitted enabledMethods must default to the documented read methods",async()=>{consttransport=newReadTransport(scenario.method,scenario.value);constclient=scenario.create({cache: {ttl: 60000,maxSize: 1000}},transport);try{expect(awaitclient.read()).toEqual(scenario.value);expect(awaitclient.read()).toEqual(scenario.value);expect(transport.requests).toHaveLength(1);}finally{client.destroy();}});});}
Exact observed output
The cache-only test was rerun immediately before filing, from psy-ts-sdk/:
quality cache initialization: coordinator
✓ control: basic reads work without cache configuration
✓ control: explicitly selected read methods cache successfully
✕ omitted enabledMethods must default to the documented read methods
quality cache initialization: realm
✓ control: basic reads work without cache configuration
✓ control: explicitly selected read methods cache successfully
✕ omitted enabledMethods must default to the documented read methods
Tests: 2 failed, 4 passed, 6 total
Process exit: 1
TypeError: Cannot read properties of undefined (reading 'has')
at CoordinatorEdgeRpcProvider.rpc_with_url (src/provider/provider.ts:414:85)
at CoordinatorEdgeRpcProvider.getLatestCheckpointId (src/coord-edge-rpc/client.ts:146:21)
The Realm case fails at the same shared cache line through getLatestCheckpointTreeRoot. The same cache cases also reproduced in two earlier clean runs. Controls demonstrate that the transport responses and explicit cache path are functional, so this is not an unavailable RPC service or malformed fixture.
Impact and priority
Suggested priority: Medium / P2. A normal documented configuration prevents read calls from completing in both provider classes.
Demonstrated workarounds: disable caching or explicitly provide the enabled read-method set. No network-wide outage, transaction/fund impact, WASM failure or full release-package import claim is made. The introduction version was not established.
Duplicate research
The all-state tracker, accessible discussions, PR file lists and relevant history were reviewed. Immediately before filing there were no ordinary issues and five PRs: #1 open, #2–#5 merged. That empty issue count was not treated as novelty proof by itself.
feat: support contract ABI deployment flows #1 targets feat/shield-poseidon-bridge, not the tested default branch. Its ABI deployment/WASM work does not change this base Provider cache initialization.
The other PR patches did not identify or fix this default-cache mechanism.
Fresh all-state searches for enabledMethods, cache and getReadOnlyMethods returned no matching issue/PR.
Relevant Provider path history was inspected. The latest changes add cancellation propagation and health-check error handling, not a correction to constructor initialization order.
No matching report or fix was found in accessible history. Local history is shallow, so path history was also read through the commit API; private/external histories and unavailable oversized lockfile patches were not exhaustively reviewed.
Suggested fix direction
Resolve default read methods after derived initialization, or avoid reading derived instance fields through an overridden method in the base constructor. Preserve explicit enabledMethods overrides. Keep regression coverage for omitted/explicit configuration on both subclasses.
Summary
Enabling the documented cache configuration without an explicit
enabledMethodsset makes bothCoordinatorEdgeRpcProviderandRealmEdgeRpcProviderthrow on their first read:Basic reads without cache work. Explicitly supplying
enabledMethodsalso works and caches the second identical read. This report covers only cache initialization, not other provider behavior.Affected revision and environment
mainnet-beta, rechecked before filing.a34d5fc84580b8ef88d77c7d72fdcc515c467a3a.@psy-protocol/psy-sdk, manifest version2.0.4.Supported workflow and expected behavior
A developer enables caching using
{ cache: { ttl: 60000, maxSize: 1000 } }, then performs a normal read.The cache example omits
enabledMethodsand says that the first read reaches the server and the second uses cache. The CacheConfig declaration explicitly makes the property optional, defaulting to all read methods.Expected: both reads resolve successfully, with one transport request for two identical cached reads.
Actual behavior
The first read throws at provider.ts:414.
Root cause
The base constructor calls the overridden
getReadOnlyMethods()while constructingcacheConfig.The subclasses keep the read-only method sets in instance field initializers:
Those fields are initialized after
super()returns. The base constructor therefore capturesundefinedas the defaultcacheConfig.enabledMethods. The later request calls the getter successfully, but still uses the earlier captured value for.has().Minimal reproduction
In a disposable checkout of the pinned commit, use the project's locked dependencies:
unshare -Urnis the Linux network-isolation step used in verification. Packages must be installed before entering the isolated namespace. No compiler checkout, WASM module, live RPC, wallet, proving service or transaction is needed for this test.The fixture implements the SDK's supported
IHTTPClientinjection interface and checks the real JSON-RPC request method/params. Responses are synthetic JSON-RPC 2.0 text envelopes. Neither the provider classes nor their cache logic are mocked.Complete executed cache-only regression test
Exact observed output
The cache-only test was rerun immediately before filing, from
psy-ts-sdk/:The Realm case fails at the same shared cache line through
getLatestCheckpointTreeRoot. The same cache cases also reproduced in two earlier clean runs. Controls demonstrate that the transport responses and explicit cache path are functional, so this is not an unavailable RPC service or malformed fixture.Impact and priority
Suggested priority: Medium / P2. A normal documented configuration prevents read calls from completing in both provider classes.
Demonstrated workarounds: disable caching or explicitly provide the enabled read-method set. No network-wide outage, transaction/fund impact, WASM failure or full release-package import claim is made. The introduction version was not established.
Duplicate research
The all-state tracker, accessible discussions, PR file lists and relevant history were reviewed. Immediately before filing there were no ordinary issues and five PRs: #1 open, #2–#5 merged. That empty issue count was not treated as novelty proof by itself.
feat/shield-poseidon-bridge, not the tested default branch. Its ABI deployment/WASM work does not change this base Provider cache initialization.enabledMethods,cacheandgetReadOnlyMethodsreturned no matching issue/PR.No matching report or fix was found in accessible history. Local history is shallow, so path history was also read through the commit API; private/external histories and unavailable oversized lockfile patches were not exhaustively reviewed.
Suggested fix direction
Resolve default read methods after derived initialization, or avoid reading derived instance fields through an overridden method in the base constructor. Preserve explicit enabledMethods overrides. Keep regression coverage for omitted/explicit configuration on both subclasses.