wagmi-extended 2.2.1 → 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.
- package/README.md +53 -4
- package/dist/fetch-functions/common/fetchDeploymentBlockX.d.ts +35 -15
- package/dist/index.cjs.js +90 -20
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +90 -20
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/fetch-functions/common/fetchDeploymentBlockX.ts +120 -20
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Whether you're building a DeFi platform, a governance system, or any blockchain
|
|
|
22
22
|
- [Non-hook functions setup](#non-hook-functions-setup)
|
|
23
23
|
- [Error handling](#error-handling)
|
|
24
24
|
- [fetchTokenX](#fetchTokenX)
|
|
25
|
+
- [fetchDeploymentBlockX](#fetchDeploymentBlockX)
|
|
25
26
|
- [Contributing](#contributing)
|
|
26
27
|
- [Donations](#support--donations)
|
|
27
28
|
- [License](#license)
|
|
@@ -365,14 +366,58 @@ const tokensData = await Promise.all(tokenAddresses.map((token) =>
|
|
|
365
366
|
|
|
366
367
|
> **Note:** : if you did not setup configs (queryClient and wagmi config) you can call by passing client and config as params: `fetchTokenX(token, queryClient, wagmiConfig)`.
|
|
367
368
|
|
|
369
|
+
### fetchDeploymentBlockX
|
|
370
|
+
|
|
371
|
+
The `fetchDeploymentBlockX` function finds and caches the earliest block where a contract was deployed
|
|
372
|
+
(i.e., the first block containing bytecode at a given address).
|
|
373
|
+
|
|
374
|
+
It uses an **exponential descent** followed by a **binary search**, making it optimal for locating deployment blocks with minimal RPC calls.
|
|
375
|
+
|
|
376
|
+
- **Exponential descent**: quickly finds a safe lower bound with no code.
|
|
377
|
+
- **Binary search**: efficiently pinpoints the exact deployment block in logarithmic steps.
|
|
378
|
+
|
|
379
|
+
#### Features
|
|
380
|
+
|
|
381
|
+
- **React Query caching**: results stored under the key
|
|
382
|
+
`["deploymentBlock", chainId, address.toLowerCase(), floor]`.
|
|
383
|
+
- **LocalStorage caching (browser only)**:
|
|
384
|
+
- On the **first run**, the block is computed and stored in both React Query and `localStorage`.
|
|
385
|
+
- On **subsequent runs**, the value is read from `localStorage` to fully prevent RPC refetches.
|
|
386
|
+
- **SSR-safe**: all localStorage access is gated by `typeof window !== 'undefined'`.
|
|
387
|
+
- **Opt-out**: pass `{ disableLocalStorage: true }` to bypass read/write of `localStorage`.
|
|
388
|
+
|
|
389
|
+
#### Example
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
import { fetchDeploymentBlockX } from "wagmi-extended";
|
|
393
|
+
|
|
394
|
+
async function main() {
|
|
395
|
+
const deploymentBlock = await fetchDeploymentBlockX(
|
|
396
|
+
"0xYourContractAddress",
|
|
397
|
+
0n
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
console.log("Contract was deployed at block:", deploymentBlock.toString());
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
main();
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Performance
|
|
407
|
+
|
|
408
|
+
Performs O(log N) RPC calls, where N is the distance between the latest block and the deployment block.
|
|
409
|
+
|
|
410
|
+
- Much more efficient than linear scanning.
|
|
411
|
+
- Automatically cached by React Query under the key:
|
|
412
|
+
|
|
368
413
|
## Contributing
|
|
369
414
|
|
|
370
415
|
This project is open source and we welcome contributions from the community! If you have ideas, improvements, or bug fixes, please feel free to open pull requests or file issues. Your help makes this project better for everyone.
|
|
371
416
|
|
|
372
|
-
- **Open Issues & PRs:**
|
|
417
|
+
- **Open Issues & PRs:**
|
|
373
418
|
You can report bugs or request features by opening an issue on [GitHub Issues](https://github.com/WingsDevelopment/wagmi-extended/issues). Similarly, feel free to open a pull request (PR) with your changes.
|
|
374
419
|
|
|
375
|
-
- **Star the Project:**
|
|
420
|
+
- **Star the Project:**
|
|
376
421
|
If you like `wagmi-extended` or find it useful, please consider starring the repository on [GitHub](https://github.com/WingsDevelopment/wagmi-extended). It helps the project gain visibility and motivates further development.
|
|
377
422
|
|
|
378
423
|
Thank you for your support and contributions!
|
|
@@ -381,10 +426,10 @@ Thank you for your support and contributions!
|
|
|
381
426
|
|
|
382
427
|
If you enjoy this project and would like to support its ongoing development, please consider donating!
|
|
383
428
|
|
|
384
|
-
- **Buy Me a Coffee:**
|
|
429
|
+
- **Buy Me a Coffee:**
|
|
385
430
|
[buymeacoffee.com/srdjanr160N](https://buymeacoffee.com/srdjanr160N)
|
|
386
431
|
|
|
387
|
-
- **Ethereum Donation Address:**
|
|
432
|
+
- **Ethereum Donation Address:**
|
|
388
433
|
`0x410A11ed53a9a59094F24D2ae4ACbeF7f84955a1`
|
|
389
434
|
|
|
390
435
|
Any donation, no matter how small, is greatly appreciated!
|
|
@@ -398,3 +443,7 @@ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
|
398
443
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
399
444
|
|
|
400
445
|
For more information, please refer to <http://unlicense.org/>
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
```
|
|
@@ -8,13 +8,21 @@ export declare const deploymentBlockKey: (chainId: number | undefined, address:
|
|
|
8
8
|
*
|
|
9
9
|
* Use with `queryClient.fetchQuery(...)`.
|
|
10
10
|
*
|
|
11
|
-
* @param address
|
|
12
|
-
* @param floor
|
|
13
|
-
* @param wagmiConfig
|
|
11
|
+
* @param address - Contract address to probe.
|
|
12
|
+
* @param floor - Optional lower bound (inclusive) to speed up search. Defaults to `0n`.
|
|
13
|
+
* @param wagmiConfig - Wagmi `Config` (optional; resolved via `ensureClientAndConfig` if omitted).
|
|
14
|
+
* @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* Local Storage behavior (SSR-safe):
|
|
17
|
+
* - If not disabled and a value exists in `localStorage`, the `queryFn` will
|
|
18
|
+
* return it immediately without performing RPC calls.
|
|
19
|
+
* - After an on-chain discovery, the result is written to `localStorage`
|
|
20
|
+
* (unless disabled). This pairs nicely with `staleTime: Infinity` to
|
|
21
|
+
* avoid future refetches.
|
|
16
22
|
*/
|
|
17
|
-
export declare function getDeploymentBlockQueryOptionsX(address: Address, floor?: bigint, wagmiConfig?: Config
|
|
23
|
+
export declare function getDeploymentBlockQueryOptionsX(address: Address, floor?: bigint, wagmiConfig?: Config, options?: {
|
|
24
|
+
disableLocalStorage?: boolean;
|
|
25
|
+
}): {
|
|
18
26
|
readonly staleTime: number;
|
|
19
27
|
readonly meta: {
|
|
20
28
|
readonly queryType: import("../../query-config/index.js").QueryType.MetaDataQuery;
|
|
@@ -31,24 +39,36 @@ export declare function getDeploymentBlockQueryOptionsX(address: Address, floor?
|
|
|
31
39
|
*
|
|
32
40
|
* #### Caching
|
|
33
41
|
* - Query key: `["deploymentBlock", chainId, address.toLowerCase(), floor]`
|
|
34
|
-
* -
|
|
42
|
+
* - Long-lived results: `queryConfig.metaDataQuery` (e.g., `staleTime: Infinity`)
|
|
35
43
|
*
|
|
36
|
-
* ####
|
|
37
|
-
* -
|
|
38
|
-
*
|
|
44
|
+
* #### Local Storage (SSR-safe)
|
|
45
|
+
* - Before calling `fetchQuery`, we seed the Query Cache from `localStorage`
|
|
46
|
+
* (unless `disableLocalStorage` is true). When a cached value is present,
|
|
47
|
+
* `fetchQuery` will *not* execute the `queryFn`, fully preventing RPC refetches.
|
|
48
|
+
* - After on-chain discovery, the result is written back to `localStorage`
|
|
49
|
+
* (unless disabled).
|
|
39
50
|
*
|
|
40
51
|
* @example
|
|
41
52
|
* ```ts
|
|
42
|
-
* const block = await fetchDeploymentBlockX(
|
|
53
|
+
* const block = await fetchDeploymentBlockX(
|
|
54
|
+
* "0xContract...",
|
|
55
|
+
* 0n,
|
|
56
|
+
* queryClient,
|
|
57
|
+
* wagmiConfig,
|
|
58
|
+
* { disableLocalStorage: false }
|
|
59
|
+
* );
|
|
43
60
|
* ```
|
|
44
61
|
*
|
|
45
|
-
* @param address
|
|
46
|
-
* @param floor
|
|
47
|
-
* @param queryClient
|
|
48
|
-
* @param wagmiConfig
|
|
62
|
+
* @param address - Contract address to probe.
|
|
63
|
+
* @param floor - Optional lower bound (inclusive). Defaults to `0n`.
|
|
64
|
+
* @param queryClient - Optional TanStack `QueryClient`. If omitted, resolved by `ensureClientAndConfig`.
|
|
65
|
+
* @param wagmiConfig - Optional Wagmi `Config`. If omitted, resolved by `ensureClientAndConfig`.
|
|
66
|
+
* @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
|
|
49
67
|
*
|
|
50
68
|
* @returns The earliest block number (bigint) where bytecode exists.
|
|
51
69
|
*
|
|
52
70
|
* @throws If the public client is missing or if no code is present at the latest block.
|
|
53
71
|
*/
|
|
54
|
-
export declare function fetchDeploymentBlockX(address: Address, floor?: bigint, queryClient?: QueryClient, wagmiConfig?: Config
|
|
72
|
+
export declare function fetchDeploymentBlockX(address: Address, floor?: bigint, queryClient?: QueryClient, wagmiConfig?: Config, options?: {
|
|
73
|
+
disableLocalStorage?: boolean;
|
|
74
|
+
}): Promise<bigint>;
|
package/dist/index.cjs.js
CHANGED
|
@@ -857,6 +857,33 @@ async function fetchERC4626DataX(vault, user, spender, queryClient, wagmiConfig)
|
|
|
857
857
|
|
|
858
858
|
/** Reusable React Query key helper for deployment block lookups. */
|
|
859
859
|
const deploymentBlockKey = (chainId, address, floor) => ["deploymentBlock", chainId, address?.toLowerCase(), floor];
|
|
860
|
+
/** Internal: SSR-safe localStorage guards */
|
|
861
|
+
const canUseBrowserStorage = typeof window !== "undefined" && typeof window.localStorage !== "undefined";
|
|
862
|
+
/** Internal: build a stable localStorage key */
|
|
863
|
+
const lsKeyForDeploymentBlock = (chainId, address, floor) => `wagmi-extended:deploymentBlock:${chainId}:${address.toLowerCase()}:${floor.toString()}`;
|
|
864
|
+
/** Internal: read bigint from localStorage (SSR safe) */
|
|
865
|
+
function readDeploymentBlockFromLS(chainId, address, floor) {
|
|
866
|
+
if (!canUseBrowserStorage)
|
|
867
|
+
return undefined;
|
|
868
|
+
try {
|
|
869
|
+
const raw = window.localStorage.getItem(lsKeyForDeploymentBlock(chainId, address, floor));
|
|
870
|
+
return raw ? BigInt(raw) : undefined;
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
return undefined;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/** Internal: write bigint to localStorage (SSR safe) */
|
|
877
|
+
function writeDeploymentBlockToLS(chainId, address, floor, value) {
|
|
878
|
+
if (!canUseBrowserStorage)
|
|
879
|
+
return;
|
|
880
|
+
try {
|
|
881
|
+
window.localStorage.setItem(lsKeyForDeploymentBlock(chainId, address, floor), value.toString());
|
|
882
|
+
}
|
|
883
|
+
catch {
|
|
884
|
+
/* ignore quota/security errors */
|
|
885
|
+
}
|
|
886
|
+
}
|
|
860
887
|
/**
|
|
861
888
|
* Internal helper: checks if there is bytecode at `address` on `blockNumber`.
|
|
862
889
|
*/
|
|
@@ -920,15 +947,22 @@ async function findDeploymentBlockRpcX(address, wagmiConfig, floor = 0n) {
|
|
|
920
947
|
*
|
|
921
948
|
* Use with `queryClient.fetchQuery(...)`.
|
|
922
949
|
*
|
|
923
|
-
* @param address
|
|
924
|
-
* @param floor
|
|
925
|
-
* @param wagmiConfig
|
|
950
|
+
* @param address - Contract address to probe.
|
|
951
|
+
* @param floor - Optional lower bound (inclusive) to speed up search. Defaults to `0n`.
|
|
952
|
+
* @param wagmiConfig - Wagmi `Config` (optional; resolved via `ensureClientAndConfig` if omitted).
|
|
953
|
+
* @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
|
|
926
954
|
*
|
|
927
|
-
*
|
|
955
|
+
* Local Storage behavior (SSR-safe):
|
|
956
|
+
* - If not disabled and a value exists in `localStorage`, the `queryFn` will
|
|
957
|
+
* return it immediately without performing RPC calls.
|
|
958
|
+
* - After an on-chain discovery, the result is written to `localStorage`
|
|
959
|
+
* (unless disabled). This pairs nicely with `staleTime: Infinity` to
|
|
960
|
+
* avoid future refetches.
|
|
928
961
|
*/
|
|
929
|
-
function getDeploymentBlockQueryOptionsX(address, floor = 0n, wagmiConfig) {
|
|
962
|
+
function getDeploymentBlockQueryOptionsX(address, floor = 0n, wagmiConfig, options) {
|
|
930
963
|
if (!address)
|
|
931
964
|
throw new Error("Address is required");
|
|
965
|
+
const disableLocalStorage = options?.disableLocalStorage ?? false;
|
|
932
966
|
// Resolve config (caller may pass undefined; we'll normalize later in fetcher too)
|
|
933
967
|
// We only need chainId for the key; if wagmiConfig is missing here,
|
|
934
968
|
// we allow it since fetcher re-resolves. But key stability benefits from chainId.
|
|
@@ -939,9 +973,25 @@ function getDeploymentBlockQueryOptionsX(address, floor = 0n, wagmiConfig) {
|
|
|
939
973
|
queryFn: async () => {
|
|
940
974
|
if (!wagmiConfig)
|
|
941
975
|
throw new Error("wagmiConfig is required at execution time");
|
|
942
|
-
|
|
976
|
+
const c = actions.getPublicClient(wagmiConfig);
|
|
977
|
+
const cid = c?.chain?.id;
|
|
978
|
+
if (!cid)
|
|
979
|
+
throw new Error("Client chain ID is missing");
|
|
980
|
+
// Try localStorage first (no refetches if we already know it)
|
|
981
|
+
if (!disableLocalStorage) {
|
|
982
|
+
const fromLS = readDeploymentBlockFromLS(cid, address, floor);
|
|
983
|
+
if (fromLS !== undefined)
|
|
984
|
+
return fromLS;
|
|
985
|
+
}
|
|
986
|
+
// Otherwise do the discovery via RPC
|
|
987
|
+
const discovered = await findDeploymentBlockRpcX(address, wagmiConfig, floor);
|
|
988
|
+
// Persist to localStorage for subsequent sessions
|
|
989
|
+
if (!disableLocalStorage) {
|
|
990
|
+
writeDeploymentBlockToLS(cid, address, floor, discovered);
|
|
991
|
+
}
|
|
992
|
+
return discovered;
|
|
943
993
|
},
|
|
944
|
-
...queryConfig.metaDataQuery,
|
|
994
|
+
...queryConfig.metaDataQuery, // typically sets staleTime: Infinity, gcTime, etc.
|
|
945
995
|
};
|
|
946
996
|
}
|
|
947
997
|
/**
|
|
@@ -953,39 +1003,59 @@ function getDeploymentBlockQueryOptionsX(address, floor = 0n, wagmiConfig) {
|
|
|
953
1003
|
*
|
|
954
1004
|
* #### Caching
|
|
955
1005
|
* - Query key: `["deploymentBlock", chainId, address.toLowerCase(), floor]`
|
|
956
|
-
* -
|
|
1006
|
+
* - Long-lived results: `queryConfig.metaDataQuery` (e.g., `staleTime: Infinity`)
|
|
957
1007
|
*
|
|
958
|
-
* ####
|
|
959
|
-
* -
|
|
960
|
-
*
|
|
1008
|
+
* #### Local Storage (SSR-safe)
|
|
1009
|
+
* - Before calling `fetchQuery`, we seed the Query Cache from `localStorage`
|
|
1010
|
+
* (unless `disableLocalStorage` is true). When a cached value is present,
|
|
1011
|
+
* `fetchQuery` will *not* execute the `queryFn`, fully preventing RPC refetches.
|
|
1012
|
+
* - After on-chain discovery, the result is written back to `localStorage`
|
|
1013
|
+
* (unless disabled).
|
|
961
1014
|
*
|
|
962
1015
|
* @example
|
|
963
1016
|
* ```ts
|
|
964
|
-
* const block = await fetchDeploymentBlockX(
|
|
1017
|
+
* const block = await fetchDeploymentBlockX(
|
|
1018
|
+
* "0xContract...",
|
|
1019
|
+
* 0n,
|
|
1020
|
+
* queryClient,
|
|
1021
|
+
* wagmiConfig,
|
|
1022
|
+
* { disableLocalStorage: false }
|
|
1023
|
+
* );
|
|
965
1024
|
* ```
|
|
966
1025
|
*
|
|
967
|
-
* @param address
|
|
968
|
-
* @param floor
|
|
969
|
-
* @param queryClient
|
|
970
|
-
* @param wagmiConfig
|
|
1026
|
+
* @param address - Contract address to probe.
|
|
1027
|
+
* @param floor - Optional lower bound (inclusive). Defaults to `0n`.
|
|
1028
|
+
* @param queryClient - Optional TanStack `QueryClient`. If omitted, resolved by `ensureClientAndConfig`.
|
|
1029
|
+
* @param wagmiConfig - Optional Wagmi `Config`. If omitted, resolved by `ensureClientAndConfig`.
|
|
1030
|
+
* @param options.disableLocalStorage - If `true`, skip reading/writing localStorage (default `false`).
|
|
971
1031
|
*
|
|
972
1032
|
* @returns The earliest block number (bigint) where bytecode exists.
|
|
973
1033
|
*
|
|
974
1034
|
* @throws If the public client is missing or if no code is present at the latest block.
|
|
975
1035
|
*/
|
|
976
|
-
async function fetchDeploymentBlockX(address, floor = 0n, queryClient, wagmiConfig) {
|
|
1036
|
+
async function fetchDeploymentBlockX(address, floor = 0n, queryClient, wagmiConfig, options) {
|
|
977
1037
|
if (!address)
|
|
978
1038
|
throw new Error("Address is required");
|
|
979
1039
|
({ queryClient, wagmiConfig } = ensureClientAndConfig(queryClient, wagmiConfig));
|
|
980
|
-
// Resolve chainId for a stable cache key
|
|
981
1040
|
const client = actions.getPublicClient(wagmiConfig);
|
|
982
1041
|
const chainId = client?.chain?.id;
|
|
983
1042
|
if (!chainId)
|
|
984
1043
|
throw new Error("Client chain ID is missing");
|
|
1044
|
+
const key = deploymentBlockKey(chainId, address, floor);
|
|
1045
|
+
const disableLocalStorage = options?.disableLocalStorage ?? false;
|
|
1046
|
+
// Seed cache from localStorage so fetchQuery returns immediately w/o running queryFn
|
|
1047
|
+
if (!disableLocalStorage) {
|
|
1048
|
+
const fromLS = readDeploymentBlockFromLS(chainId, address, floor);
|
|
1049
|
+
if (fromLS !== undefined) {
|
|
1050
|
+
queryClient.setQueryData(key, fromLS);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
985
1053
|
return queryClient.fetchQuery({
|
|
986
|
-
...getDeploymentBlockQueryOptionsX(address, floor, wagmiConfig
|
|
1054
|
+
...getDeploymentBlockQueryOptionsX(address, floor, wagmiConfig, {
|
|
1055
|
+
disableLocalStorage,
|
|
1056
|
+
}),
|
|
987
1057
|
// Ensure the final key includes a concrete chainId
|
|
988
|
-
queryKey:
|
|
1058
|
+
queryKey: key,
|
|
989
1059
|
// Reinstate metadata (in case your ensure/util merges)
|
|
990
1060
|
...queryConfig.metaDataQuery,
|
|
991
1061
|
});
|