@@ -4,7 +4,11 @@ import {
44 useQuery ,
55} from "@tanstack/react-query" ;
66import type { Abi , AbiFunction , ExtractAbiFunctionNames } from "abitype" ;
7- import type { ThirdwebContract } from "../../../../contract/contract.js" ;
7+ import type {
8+ AbiOfLength ,
9+ AsyncGetAbiFunctionFromContract ,
10+ } from "../../../../contract/types.js" ;
11+ import type { Extension } from "../../../../extensions/types.js" ;
812import {
913 type ReadContractOptions ,
1014 type ReadContractResult ,
@@ -17,17 +21,16 @@ import type {
1721import type { PreparedMethod } from "../../../../utils/abi/prepare-method.js" ;
1822import { getFunctionId } from "../../../../utils/function-id.js" ;
1923import { stringify } from "../../../../utils/json.js" ;
20-
21- type PickedQueryOptions = {
22- enabled ?: boolean ;
23- refetchInterval ?: number ;
24- retry ?: number ;
25- } ;
24+ import type {
25+ PickedOnceQueryOptions ,
26+ WithPickedOnceQueryOptions ,
27+ } from "../types.js" ;
2628
2729/**
2830 * A hook to read state from a contract that automatically updates when the contract changes.
2931 *
30- * You can use raw read calls or read [extensions](https://portal.thirdweb.com/react/v5/extensions) to read from a contract.
32+ * You can use raw read calls or read [extensions](https://portal.thirdweb.com/react/v5/extensions) to read from a
33+ * contract.
3134 *
3235 * @param options - The options for reading from a contract
3336 * @returns a UseQueryResult object.
@@ -52,20 +55,19 @@ type PickedQueryOptions = {
5255 * @contract
5356 */
5457export function useReadContract <
55- const abi extends Abi ,
56- const method extends abi extends { length : 0 }
58+ const TAbi extends Abi ,
59+ const TMethod extends TAbi extends AbiOfLength < 0 >
5760 ? AbiFunction | string
58- : ExtractAbiFunctionNames < abi > ,
61+ : ExtractAbiFunctionNames < TAbi > ,
5962> (
60- options : ReadContractOptions < abi , method > & {
61- queryOptions ?: PickedQueryOptions ;
62- } ,
63+ options : WithPickedOnceQueryOptions < ReadContractOptions < TAbi , TMethod > > ,
6364) : UseQueryResult <
64- ReadContractResult < PreparedMethod < ParseMethod < abi , method > > [ 2 ] >
65+ ReadContractResult < PreparedMethod < ParseMethod < TAbi , TMethod > > [ 2 ] >
6566> ;
6667/**
6768 * A hook to read state from a contract that automatically updates when the contract changes.
68- * You can use raw read calls or read [extensions](https://portal.thirdweb.com/react/v5/extensions) to read from a contract.
69+ * You can use raw read calls or read [extensions](https://portal.thirdweb.com/react/v5/extensions) to read from a
70+ * contract.
6971 *
7072 * @param extension - An extension to call.
7173 * @param options - The read extension params.
@@ -82,38 +84,42 @@ export function useReadContract<
8284 * ```
8385 */
8486export function useReadContract <
85- const abi extends Abi ,
86- const params extends object ,
87- result ,
87+ const TAbi extends Abi ,
88+ const TParams extends object ,
89+ TResult ,
8890> (
89- extension : ( options : BaseTransactionOptions < params , abi > ) => Promise < result > ,
90- options : BaseTransactionOptions < params , abi > & {
91- queryOptions ?: PickedQueryOptions ;
92- } ,
93- ) : UseQueryResult < result > ;
91+ extension : Extension < TAbi , TParams , TResult > ,
92+ options : WithPickedOnceQueryOptions < BaseTransactionOptions < TParams , TAbi > > ,
93+ ) : UseQueryResult < TResult > ;
9494
9595export function useReadContract <
96- const abi extends Abi ,
97- const method extends abi extends {
98- length : 0 ;
99- }
100- ?
101- | AbiFunction
102- | `function ${string } `
103- | ( ( contract : ThirdwebContract < abi > ) => Promise < AbiFunction > )
104- : ExtractAbiFunctionNames < abi > ,
105- const params extends object ,
106- result ,
96+ const TAbi extends Abi ,
97+ const TMethod extends TAbi extends AbiOfLength < 0 >
98+ ? AbiFunction | `function ${string } ` | AsyncGetAbiFunctionFromContract < TAbi >
99+ : ExtractAbiFunctionNames < TAbi > ,
100+ const TParams extends object ,
101+ TResult ,
107102> (
108103 extensionOrOptions :
109- | ( ( options : BaseTransactionOptions < params , abi > ) => Promise < result > )
110- | ( ReadContractOptions < abi , method > & {
111- queryOptions ?: PickedQueryOptions ;
112- } ) ,
113- options ?: BaseTransactionOptions < params , abi > & {
114- queryOptions ?: PickedQueryOptions ;
115- } ,
104+ | Extension < TAbi , TParams , TResult >
105+ | WithPickedOnceQueryOptions < ReadContractOptions < TAbi , TMethod > > ,
106+ options ?: WithPickedOnceQueryOptions < BaseTransactionOptions < TParams , TAbi > > ,
116107) {
108+ type QueryKey = readonly [
109+ "readContract" ,
110+ number | string ,
111+ string ,
112+ string | PreparedMethod < ParseMethod < TAbi , TMethod > > ,
113+ string ,
114+ ] ;
115+ type QueryFn = ( ) => Promise <
116+ TResult | ReadContractResult < PreparedMethod < ParseMethod < TAbi , TMethod > > [ 2 ] >
117+ > ;
118+
119+ let queryKey : QueryKey | undefined ;
120+ let queryFn : QueryFn | undefined ;
121+ let queryOpts : PickedOnceQueryOptions | undefined ;
122+
117123 // extension case
118124 if ( typeof extensionOrOptions === "function" ) {
119125 if ( ! options ) {
@@ -122,46 +128,49 @@ export function useReadContract<
122128 ) as never ;
123129 }
124130 const { queryOptions, contract, ...params } = options ;
131+ queryOpts = queryOptions ;
125132
126- const query = defineQuery ( {
127- queryKey : [
128- "readContract" ,
129- contract . chain . id ,
130- contract . address ,
131- getFunctionId ( extensionOrOptions ) ,
132- stringify ( params ) ,
133- ] as const ,
134- // @ts -expect-error - TODO: clean up the type issues here
135- queryFn : ( ) => extensionOrOptions ( { ...params , contract } ) ,
136- ...queryOptions ,
137- } ) ;
133+ queryKey = [
134+ "readContract" ,
135+ contract . chain . id ,
136+ contract . address ,
137+ getFunctionId ( extensionOrOptions ) ,
138+ stringify ( params ) ,
139+ ] as const ;
138140
139- // TODO - FIX LATER
140- // biome-ignore lint/correctness/useHookAtTopLevel: <explanation>
141- return useQuery ( query ) ;
141+ queryFn = ( ) =>
142+ extensionOrOptions ( {
143+ ...( params as TParams ) ,
144+ contract,
145+ } ) ;
142146 }
143147 // raw tx case
144148 if ( "method" in extensionOrOptions ) {
145149 const { queryOptions, ...tx } = extensionOrOptions ;
150+ queryOpts = queryOptions ;
146151
147- const query = defineQuery ( {
148- queryKey : [
149- "readContract" ,
150- tx . contract . chain . id ,
151- tx . contract . address ,
152- tx . method ,
153- stringify ( tx . params ) ,
154- ] as const ,
155- queryFn : ( ) => readContract ( extensionOrOptions ) ,
156- ...queryOptions ,
157- } ) ;
152+ queryKey = [
153+ "readContract" ,
154+ tx . contract . chain . id ,
155+ tx . contract . address ,
156+ tx . method ,
157+ stringify ( tx . params ) ,
158+ ] as const ;
159+
160+ queryFn = ( ) => readContract ( extensionOrOptions ) ;
161+ }
158162
159- // TODO - FIX LATER
160- // biome-ignore lint/correctness/useHookAtTopLevel: <explanation>
161- return useQuery ( query ) ;
163+ if ( ! queryKey || ! queryFn ) {
164+ throw new Error (
165+ `Invalid "useReadContract" options. Expected either a read extension or a transaction object.` ,
166+ ) as never ;
162167 }
163168
164- throw new Error (
165- `Invalid "useReadContract" options. Expected either a read extension or a transaction object.` ,
166- ) as never ;
169+ return useQuery (
170+ defineQuery ( {
171+ queryKey : queryKey as QueryKey ,
172+ queryFn : queryFn as QueryFn ,
173+ ...( queryOpts ?? { } ) ,
174+ } ) ,
175+ ) ;
167176}
0 commit comments