wagmi-extended 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,53 @@ export const deploymentBlockKey = (
12
12
  floor: bigint
13
13
  ) => ["deploymentBlock", chainId, address?.toLowerCase(), floor] as const;
14
14
 
15
+ /** Internal: SSR-safe localStorage guards */
16
+ const canUseBrowserStorage =
17
+ typeof window !== "undefined" && typeof window.localStorage !== "undefined";
18
+
19
+ /** Internal: build a stable localStorage key */
20
+ const lsKeyForDeploymentBlock = (
21
+ chainId: number,
22
+ address: Address,
23
+ floor: bigint
24
+ ) =>
25
+ `wagmi-extended:deploymentBlock:${chainId}:${address.toLowerCase()}:${floor.toString()}`;
26
+
27
+ /** Internal: read bigint from localStorage (SSR safe) */
28
+ function readDeploymentBlockFromLS(
29
+ chainId: number,
30
+ address: Address,
31
+ floor: bigint
32
+ ): bigint | undefined {
33
+ if (!canUseBrowserStorage) return undefined;
34
+ try {
35
+ const raw = window.localStorage.getItem(
36
+ lsKeyForDeploymentBlock(chainId, address, floor)
37
+ );
38
+ return raw ? BigInt(raw) : undefined;
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ }
43
+
44
+ /** Internal: write bigint to localStorage (SSR safe) */
45
+ function writeDeploymentBlockToLS(
46
+ chainId: number,
47
+ address: Address,
48
+ floor: bigint,
49
+ value: bigint
50
+ ) {
51
+ if (!canUseBrowserStorage) return;
52
+ try {
53
+ window.localStorage.setItem(
54
+ lsKeyForDeploymentBlock(chainId, address, floor),
55
+ value.toString()
56
+ );
57
+ } catch {
58
+ /* ignore quota/security errors */
59
+ }
60
+ }
61
+
15
62
  /**
16
63
  * Internal helper: checks if there is bytecode at `address` on `blockNumber`.
17
64
  */
@@ -87,18 +134,26 @@ async function findDeploymentBlockRpcX(
87
134
  *
88
135
  * Use with `queryClient.fetchQuery(...)`.
89
136
  *
90
- * @param address - Contract address to probe.
91
- * @param floor - Optional lower bound (inclusive) to speed up search. Defaults to `0n`.
92
- * @param wagmiConfig - Wagmi `Config` (optional; resolved via `ensureClientAndConfig` if omitted).
137
+ * @param address - Contract address to probe.
138
+ * @param floor - Optional lower bound (inclusive) to speed up search. Defaults to `0n`.
139
+ * @param wagmiConfig - Wagmi `Config` (optional; resolved via `ensureClientAndConfig` if omitted).
140
+ * @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
93
141
  *
94
- * @returns A fully-configured query options object (key + fn + metadata).
142
+ * Local Storage behavior (SSR-safe):
143
+ * - If not disabled and a value exists in `localStorage`, the `queryFn` will
144
+ * return it immediately without performing RPC calls.
145
+ * - After an on-chain discovery, the result is written to `localStorage`
146
+ * (unless disabled). This pairs nicely with `staleTime: Infinity` to
147
+ * avoid future refetches.
95
148
  */
96
149
  export function getDeploymentBlockQueryOptionsX(
97
150
  address: Address,
98
151
  floor: bigint = 0n,
99
- wagmiConfig?: Config
152
+ wagmiConfig?: Config,
153
+ options?: { disableLocalStorage?: boolean }
100
154
  ) {
101
155
  if (!address) throw new Error("Address is required");
156
+ const disableLocalStorage = options?.disableLocalStorage ?? false;
102
157
 
103
158
  // Resolve config (caller may pass undefined; we'll normalize later in fetcher too)
104
159
  // We only need chainId for the key; if wagmiConfig is missing here,
@@ -111,9 +166,31 @@ export function getDeploymentBlockQueryOptionsX(
111
166
  queryFn: async () => {
112
167
  if (!wagmiConfig)
113
168
  throw new Error("wagmiConfig is required at execution time");
114
- return findDeploymentBlockRpcX(address, wagmiConfig, floor);
169
+
170
+ const c = getPublicClient(wagmiConfig);
171
+ const cid = c?.chain?.id;
172
+ if (!cid) throw new Error("Client chain ID is missing");
173
+
174
+ // Try localStorage first (no refetches if we already know it)
175
+ if (!disableLocalStorage) {
176
+ const fromLS = readDeploymentBlockFromLS(cid, address, floor);
177
+ if (fromLS !== undefined) return fromLS;
178
+ }
179
+
180
+ // Otherwise do the discovery via RPC
181
+ const discovered = await findDeploymentBlockRpcX(
182
+ address,
183
+ wagmiConfig,
184
+ floor
185
+ );
186
+
187
+ // Persist to localStorage for subsequent sessions
188
+ if (!disableLocalStorage) {
189
+ writeDeploymentBlockToLS(cid, address, floor, discovered);
190
+ }
191
+ return discovered;
115
192
  },
116
- ...queryConfig.metaDataQuery,
193
+ ...queryConfig.metaDataQuery, // typically sets staleTime: Infinity, gcTime, etc.
117
194
  } as const;
118
195
  }
119
196
 
@@ -126,21 +203,31 @@ export function getDeploymentBlockQueryOptionsX(
126
203
  *
127
204
  * #### Caching
128
205
  * - Query key: `["deploymentBlock", chainId, address.toLowerCase(), floor]`
129
- * - For long-lived results, we apply `queryConfig.metaDataQuery` (tweak as needed).
206
+ * - Long-lived results: `queryConfig.metaDataQuery` (e.g., `staleTime: Infinity`)
130
207
  *
131
- * #### Performance
132
- * - **O(log N)** `eth_getCode` calls, where `N` is the gap between the latest
133
- * block and the deployment block—optimal among comparison-based strategies.
208
+ * #### Local Storage (SSR-safe)
209
+ * - Before calling `fetchQuery`, we seed the Query Cache from `localStorage`
210
+ * (unless `disableLocalStorage` is true). When a cached value is present,
211
+ * `fetchQuery` will *not* execute the `queryFn`, fully preventing RPC refetches.
212
+ * - After on-chain discovery, the result is written back to `localStorage`
213
+ * (unless disabled).
134
214
  *
135
215
  * @example
136
216
  * ```ts
137
- * const block = await fetchDeploymentBlockX("0xContract...", 0n, queryClient, wagmiConfig);
217
+ * const block = await fetchDeploymentBlockX(
218
+ * "0xContract...",
219
+ * 0n,
220
+ * queryClient,
221
+ * wagmiConfig,
222
+ * { disableLocalStorage: false }
223
+ * );
138
224
  * ```
139
225
  *
140
- * @param address - Contract address to probe.
141
- * @param floor - Optional lower bound (inclusive) to speed up search. Defaults to `0n`.
142
- * @param queryClient - Optional TanStack `QueryClient`. If omitted, resolved by `ensureClientAndConfig`.
143
- * @param wagmiConfig - Optional Wagmi `Config`. If omitted, resolved by `ensureClientAndConfig`.
226
+ * @param address - Contract address to probe.
227
+ * @param floor - Optional lower bound (inclusive). Defaults to `0n`.
228
+ * @param queryClient - Optional TanStack `QueryClient`. If omitted, resolved by `ensureClientAndConfig`.
229
+ * @param wagmiConfig - Optional Wagmi `Config`. If omitted, resolved by `ensureClientAndConfig`.
230
+ * @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
144
231
  *
145
232
  * @returns The earliest block number (bigint) where bytecode exists.
146
233
  *
@@ -150,7 +237,8 @@ export async function fetchDeploymentBlockX(
150
237
  address: Address,
151
238
  floor: bigint = 0n,
152
239
  queryClient?: QueryClient,
153
- wagmiConfig?: Config
240
+ wagmiConfig?: Config,
241
+ options?: { disableLocalStorage?: boolean }
154
242
  ): Promise<bigint> {
155
243
  if (!address) throw new Error("Address is required");
156
244
 
@@ -159,15 +247,27 @@ export async function fetchDeploymentBlockX(
159
247
  wagmiConfig
160
248
  ));
161
249
 
162
- // Resolve chainId for a stable cache key
163
250
  const client = getPublicClient(wagmiConfig);
164
251
  const chainId = client?.chain?.id;
165
252
  if (!chainId) throw new Error("Client chain ID is missing");
166
253
 
254
+ const key = deploymentBlockKey(chainId, address, floor);
255
+ const disableLocalStorage = options?.disableLocalStorage ?? false;
256
+
257
+ // Seed cache from localStorage so fetchQuery returns immediately w/o running queryFn
258
+ if (!disableLocalStorage) {
259
+ const fromLS = readDeploymentBlockFromLS(chainId, address, floor);
260
+ if (fromLS !== undefined) {
261
+ queryClient.setQueryData(key, fromLS);
262
+ }
263
+ }
264
+
167
265
  return queryClient.fetchQuery({
168
- ...getDeploymentBlockQueryOptionsX(address, floor, wagmiConfig),
266
+ ...getDeploymentBlockQueryOptionsX(address, floor, wagmiConfig, {
267
+ disableLocalStorage,
268
+ }),
169
269
  // Ensure the final key includes a concrete chainId
170
- queryKey: deploymentBlockKey(chainId, address, floor),
270
+ queryKey: key,
171
271
  // Reinstate metadata (in case your ensure/util merges)
172
272
  ...queryConfig.metaDataQuery,
173
273
  });