wagmi-extended 2.1.0 → 2.2.0

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.
@@ -0,0 +1,174 @@
1
+ import type { Address, Hex } from "viem";
2
+ import type { QueryClient } from "@tanstack/react-query";
3
+ import type { Config } from "wagmi";
4
+ import { getPublicClient } from "wagmi/actions";
5
+ import { queryConfig } from "../../query-config/index.js";
6
+ import { ensureClientAndConfig } from "../../utils/ensureClientAndConfig.js";
7
+
8
+ /** Reusable React Query key helper for deployment block lookups. */
9
+ export const deploymentBlockKey = (
10
+ chainId: number | undefined,
11
+ address: Address | undefined,
12
+ floor: bigint
13
+ ) => ["deploymentBlock", chainId, address?.toLowerCase(), floor] as const;
14
+
15
+ /**
16
+ * Internal helper: checks if there is bytecode at `address` on `blockNumber`.
17
+ */
18
+ async function hasCodeAtX(
19
+ address: Address,
20
+ blockNumber: bigint,
21
+ wagmiConfig: Config
22
+ ): Promise<boolean> {
23
+ const client = getPublicClient(wagmiConfig);
24
+ if (!client) throw new Error("Public client is missing");
25
+ const code: Hex | undefined = await client.getCode({ address, blockNumber });
26
+ return !!code && code !== "0x";
27
+ }
28
+
29
+ /**
30
+ * Internal helper: finds the earliest block where code exists at `address`,
31
+ * using exponential descent to find a lower bound and then binary search.
32
+ *
33
+ * @param address - Contract address to probe.
34
+ * @param floor - Optional lower bound (inclusive) to start from. If you know
35
+ * the contract cannot exist below this block, pass it to
36
+ * speed up the search. Defaults to `0n`.
37
+ * @returns The first block number (bigint) where code is present.
38
+ * @throws If no code exists at the latest block (i.e., contract not deployed).
39
+ */
40
+ async function findDeploymentBlockRpcX(
41
+ address: Address,
42
+ wagmiConfig: Config,
43
+ floor: bigint = 0n
44
+ ): Promise<bigint> {
45
+ const client = getPublicClient(wagmiConfig);
46
+ if (!client) throw new Error("Public client is missing");
47
+
48
+ const latest = await client.getBlockNumber();
49
+ if (!(await hasCodeAtX(address, latest, wagmiConfig))) {
50
+ const chainId = client.chain?.id ?? 0;
51
+ throw new Error(
52
+ `No code for ${address} at latest block ${latest} on chain ${chainId}.`
53
+ );
54
+ }
55
+
56
+ // If caller-supplied floor already has code, it *is* the first code block.
57
+ if (floor > 0n && (await hasCodeAtX(address, floor, wagmiConfig)))
58
+ return floor;
59
+
60
+ // Exponential descent to find a "no code" lower bound fast.
61
+ let lo = floor; // known (or assumed) no code
62
+ let hi = latest; // known has code
63
+ let step = 1n;
64
+
65
+ while (hi - step > lo) {
66
+ const probe = hi - step;
67
+ if (await hasCodeAtX(address, probe, wagmiConfig)) {
68
+ hi = probe; // still has code -> move upper bound down
69
+ step <<= 1n; // double the step
70
+ } else {
71
+ lo = probe; // found a no-code block
72
+ break;
73
+ }
74
+ }
75
+
76
+ // Binary search to the first block with code in (lo, hi]
77
+ while (lo + 1n < hi) {
78
+ const mid = lo + (hi - lo) / 2n;
79
+ if (await hasCodeAtX(address, mid, wagmiConfig)) hi = mid;
80
+ else lo = mid;
81
+ }
82
+ return hi;
83
+ }
84
+
85
+ /**
86
+ * Builds React Query options for caching the deployment block "forever".
87
+ *
88
+ * Use with `queryClient.fetchQuery(...)`.
89
+ *
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).
93
+ *
94
+ * @returns A fully-configured query options object (key + fn + metadata).
95
+ */
96
+ export function getDeploymentBlockQueryOptionsX(
97
+ address: Address,
98
+ floor: bigint = 0n,
99
+ wagmiConfig?: Config
100
+ ) {
101
+ if (!address) throw new Error("Address is required");
102
+
103
+ // Resolve config (caller may pass undefined; we'll normalize later in fetcher too)
104
+ // We only need chainId for the key; if wagmiConfig is missing here,
105
+ // we allow it since fetcher re-resolves. But key stability benefits from chainId.
106
+ const client = wagmiConfig ? getPublicClient(wagmiConfig) : undefined;
107
+ const chainId = client?.chain?.id;
108
+
109
+ return {
110
+ queryKey: deploymentBlockKey(chainId, address, floor),
111
+ queryFn: async () => {
112
+ if (!wagmiConfig)
113
+ throw new Error("wagmiConfig is required at execution time");
114
+ return findDeploymentBlockRpcX(address, wagmiConfig, floor);
115
+ },
116
+ ...queryConfig.metaDataQuery,
117
+ } as const;
118
+ }
119
+
120
+ /**
121
+ * Fetches (and caches) the first block where contract bytecode exists at `address`.
122
+ *
123
+ * Uses your shared `QueryClient` and Wagmi `Config` like other `fetch*X` helpers.
124
+ * Internally, this runs an **exponential descent** to find a safe lower bound,
125
+ * followed by an **optimal binary search** to pinpoint the exact deployment block.
126
+ *
127
+ * #### Caching
128
+ * - Query key: `["deploymentBlock", chainId, address.toLowerCase(), floor]`
129
+ * - For long-lived results, we apply `queryConfig.metaDataQuery` (tweak as needed).
130
+ *
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.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const block = await fetchDeploymentBlockX("0xContract...", 0n, queryClient, wagmiConfig);
138
+ * ```
139
+ *
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`.
144
+ *
145
+ * @returns The earliest block number (bigint) where bytecode exists.
146
+ *
147
+ * @throws If the public client is missing or if no code is present at the latest block.
148
+ */
149
+ export async function fetchDeploymentBlockX(
150
+ address: Address,
151
+ floor: bigint = 0n,
152
+ queryClient?: QueryClient,
153
+ wagmiConfig?: Config
154
+ ): Promise<bigint> {
155
+ if (!address) throw new Error("Address is required");
156
+
157
+ ({ queryClient, wagmiConfig } = ensureClientAndConfig(
158
+ queryClient,
159
+ wagmiConfig
160
+ ));
161
+
162
+ // Resolve chainId for a stable cache key
163
+ const client = getPublicClient(wagmiConfig);
164
+ const chainId = client?.chain?.id;
165
+ if (!chainId) throw new Error("Client chain ID is missing");
166
+
167
+ return queryClient.fetchQuery({
168
+ ...getDeploymentBlockQueryOptionsX(address, floor, wagmiConfig),
169
+ // Ensure the final key includes a concrete chainId
170
+ queryKey: deploymentBlockKey(chainId, address, floor),
171
+ // Reinstate metadata (in case your ensure/util merges)
172
+ ...queryConfig.metaDataQuery,
173
+ });
174
+ }
@@ -4,7 +4,7 @@ import { Address } from "viem";
4
4
  import { useAccount, useConfig } from "wagmi";
5
5
  import { fetchERC20DataX } from "../../fetch-functions/erc20/fetchERC20DataX.js";
6
6
 
7
- const HookFetchERC20DataQK = (
7
+ export const HookFetchERC20DataQK = (
8
8
  address?: Address,
9
9
  userAddress?: Address,
10
10
  spender?: Address
@@ -4,7 +4,7 @@ import { useAccount, useConfig } from "wagmi";
4
4
  import { fetchAllowanceX } from "../../fetch-functions/erc20/fetchAllowanceX.js";
5
5
  import { queryConfig } from "../../query-config/index.js";
6
6
 
7
- const HookFetchAssetAllowanceQK = (
7
+ export const HookFetchAssetAllowanceQK = (
8
8
  asset?: Address,
9
9
  spender?: Address,
10
10
  userAddress?: Address
@@ -4,7 +4,7 @@ import { Address } from "viem";
4
4
  import { useAccount, useConfig } from "wagmi";
5
5
  import { fetchERC4626DataX } from "../../fetch-functions/erc4626/fetchERC4626DataX.js";
6
6
 
7
- const HookFetchERC4626DataQK = (
7
+ export const HookFetchERC4626DataQK = (
8
8
  vault?: Address,
9
9
  userAddress?: Address,
10
10
  spender?: Address
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  export * from "./hooks/mutations/useContractWriteX.js";
5
5
  export * from "./hooks/mutations/useSendTransactionX.js";
6
6
  export * from "./hooks/mutations/useERC20ApproveX.js";
7
+ export * from "./hooks/mutations/useHandleTransactionMutationX.js";
7
8
 
8
9
  /* ------------- */
9
10
  /* Queries */
@@ -1,18 +1,30 @@
1
+ export enum QueryType {
2
+ MetaDataQuery = "metaDataQuery",
3
+ SemiSensitiveQuery = "semiSensitiveQuery",
4
+ LowSensitiveQuery = "lowSensitiveQuery",
5
+ ExpensiveQuery = "expensiveQuery",
6
+ PriceQuery = "priceQuery",
7
+ }
8
+
1
9
  export const queryConfig = {
2
10
  metaDataQuery: {
3
11
  staleTime: Number.POSITIVE_INFINITY,
4
- meta: { category: "metadata" } as const,
12
+ meta: { queryType: QueryType.MetaDataQuery } as const,
5
13
  },
6
14
  lowSensitiveQuery: {
7
15
  staleTime: 60_000,
8
- meta: { category: "lowSensitive" } as const,
16
+ meta: { queryType: QueryType.SemiSensitiveQuery } as const,
9
17
  },
10
18
  semiSensitiveQuery: {
11
19
  staleTime: 180_000,
12
- meta: { category: "semiSensitive" } as const,
20
+ meta: { queryType: QueryType.LowSensitiveQuery } as const,
21
+ },
22
+ priceQuery: {
23
+ staleTime: 30 * 60 * 1000,
24
+ meta: { queryType: QueryType.PriceQuery } as const,
13
25
  },
14
26
  expensiveQuery: {
15
27
  staleTime: 60 * 60 * 1000,
16
- meta: { category: "expensive" } as const,
28
+ meta: { queryType: QueryType.ExpensiveQuery } as const,
17
29
  },
18
30
  };
package/tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2019",
3
+ "target": "es2020",
4
4
  "module": "nodenext",
5
5
  "baseUrl": ".", // important when using paths
6
6
  "paths": {