wagmi-extended 2.1.1 → 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.
- package/dist/fetch-functions/common/fetchDeploymentBlockX.d.ts +54 -0
- package/dist/hooks/queries/useERC20Data.d.ts +1 -0
- package/dist/hooks/queries/useFetchAssetAllowanceX.d.ts +1 -0
- package/dist/hooks/queries/useFetchERC4626DataX.d.ts +1 -0
- package/dist/index.cjs.js +39 -23
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +39 -24
- package/dist/index.esm.js.map +1 -1
- package/dist/query-config/index.d.ts +17 -4
- package/package.json +1 -1
- package/src/fetch-functions/common/fetchDeploymentBlockX.ts +174 -0
- package/src/hooks/queries/useERC20Data.ts +1 -1
- package/src/hooks/queries/useFetchAssetAllowanceX.ts +1 -1
- package/src/hooks/queries/useFetchERC4626DataX.ts +1 -1
- package/src/query-config/index.ts +16 -4
- package/tsconfig.json +1 -1
|
@@ -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
|
|
@@ -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: {
|
|
12
|
+
meta: { queryType: QueryType.MetaDataQuery } as const,
|
|
5
13
|
},
|
|
6
14
|
lowSensitiveQuery: {
|
|
7
15
|
staleTime: 60_000,
|
|
8
|
-
meta: {
|
|
16
|
+
meta: { queryType: QueryType.SemiSensitiveQuery } as const,
|
|
9
17
|
},
|
|
10
18
|
semiSensitiveQuery: {
|
|
11
19
|
staleTime: 180_000,
|
|
12
|
-
meta: {
|
|
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: {
|
|
28
|
+
meta: { queryType: QueryType.ExpensiveQuery } as const,
|
|
17
29
|
},
|
|
18
30
|
};
|