Skip to content

[Bug] Individual price/TVL calls still bypass RPC fallback — race condition + wrong client #393

@realproject7

Description

@realproject7

Summary

Home page still shows 429 errors after multicall PR #392. Two issues:

Issue 1: Race condition in StoryCardTVL

`BatchTokenDataProvider` uses React Query (async). On initial render, batch data isn't available yet, so `StoryCardTVL` sees `!batchEntry` = true and immediately fires individual `getTokenTVL()` calls. The batch arrives moments later but the individual calls are already in flight.

Fix: Don't enable the individual fallback query until the batch query has settled (loaded or errored). Add a `batchReady` flag to the context:

```typescript
// BatchTokenDataProvider.tsx
const BatchTokenDataContext = createContext<{
data: BatchTokenDataMap;
isReady: boolean;
}>({ data: new Map(), isReady: false });

const { data, isLoading } = useQuery({...});

<BatchTokenDataContext.Provider value={{ data: data ?? new Map(), isReady: !isLoading }}>
```

```typescript
// StoryCardTVL
const { data: batchMap, isReady } = useBatchTokenContext();
const batchEntry = batchMap.get(tokenAddress.toLowerCase());

const { data: individualTvl } = useQuery({
...
enabled: isReady && !batchEntry, // only fallback AFTER batch has loaded
});
```

Issue 2: Individual calls use server-side publicClient

`getTokenPrice()` and `getTokenTVL()` in `lib/price.ts` use the server-side `publicClient` which has no CORS headers. When called from browser components, the first endpoint (`mainnet.base.org`) gets hit and 429s show in console.

Fix: Make these functions accept an optional client parameter (like `getBatchTokenData` already does):

```typescript
export async function getTokenTVL(
tokenAddress: Address,
client: PublicClient = publicClient, // default to server, override for browser
) {
const result = await client.readContract({...});
}
```

Then in browser components, pass `browserClient`:
```typescript
queryFn: () => getTokenTVL(addr, browserClient),
```

Files to update

File Change
`src/components/BatchTokenDataProvider.tsx` Add `isReady` flag to context
`src/components/StoryCardStats.tsx` Wait for batch before individual fallback
`lib/price.ts` Add optional `client` param to `getTokenPrice()`, `getTokenTVL()`, `get24hPriceChange()`
`src/components/WriterTradingStats.tsx` Pass `browserClient` to `getTokenTVL()`
`src/components/ClaimRoyalties.tsx` Pass `browserClient` to `getTokenTVL()`
`src/components/ReaderPortfolio.tsx` Pass `browserClient` to `get24hPriceChange()`, `getTokenTVL()`

Server-side callers (API routes, story page SSR) keep using the default `publicClient` — no change needed.

Acceptance Criteria

  • No 429 errors in browser console on home page load
  • Individual price/TVL calls only fire if batch fails or is unavailable
  • Browser components pass `browserClient` for individual calls
  • Server-side routes unchanged
  • `npm run typecheck` passes

Branch

`task/{issue-number}-fix-rpc-race`

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/T3Assigned to T3 builder agentbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions