@@ -11,9 +11,9 @@ import { MiniHeader } from "~~/components/MiniHeader";
11
11
import { formDataToChain , storeChainInLocalStorage } from "~~/components/NetworksDropdown/utils" ;
12
12
import { SwitchTheme } from "~~/components/SwitchTheme" ;
13
13
import { ContractUI } from "~~/components/scaffold-eth" ;
14
+ import useFetchContractAbi from "~~/hooks/useFetchContractAbi" ;
14
15
import { useAbiNinjaState , useGlobalState } from "~~/services/store/store" ;
15
- import { fetchContractABIFromAnyABI , fetchContractABIFromEtherscan , parseAndCorrectJSON } from "~~/utils/abi" ;
16
- import { detectProxyTarget } from "~~/utils/abi-ninja/proxyContracts" ;
16
+ import { parseAndCorrectJSON } from "~~/utils/abi" ;
17
17
import { notification } from "~~/utils/scaffold-eth" ;
18
18
19
19
interface ParsedQueryContractDetailsPage extends ParsedUrlQuery {
@@ -32,7 +32,6 @@ type ServerSideProps = {
32
32
} ;
33
33
34
34
export const getServerSideProps : GetServerSideProps = async context => {
35
- // Assume that 'contractAddress' and 'network' cannot be arrays.
36
35
const contractAddress = context . params ?. contractAddress as Address | undefined ;
37
36
const network = context . params ?. network as string | undefined ;
38
37
@@ -58,16 +57,14 @@ const toCamelCase = (str: string) => {
58
57
const ContractDetailPage = ( { addressFromUrl, chainIdFromUrl } : ServerSideProps ) => {
59
58
const router = useRouter ( ) ;
60
59
const { contractAddress, network } = router . query as ParsedQueryContractDetailsPage ;
61
- const [ contractData , setContractData ] = useState < ContractData > ( { abi : [ ] , address : contractAddress } ) ;
62
60
const [ localContractAbi , setLocalContractAbi ] = useState < string > ( "" ) ;
63
- const [ isLoading , setIsLoading ] = useState ( true ) ;
64
- const [ error , setError ] = useState < string | null > ( null ) ;
65
- const contractName = contractData . address ;
66
- const { setMainChainId, chainId, setImplementationAddress, contractAbi } = useAbiNinjaState ( state => ( {
61
+ const [ isUseLocalAbi , setIsUseLocalAbi ] = useState ( false ) ;
62
+ const [ contractData , setContractData ] = useState < ContractData | null > ( null ) ;
63
+ const contractName = contractAddress ;
64
+ const { setMainChainId, chainId, setImplementationAddress } = useAbiNinjaState ( state => ( {
67
65
setMainChainId : state . setMainChainId ,
68
66
chainId : state . mainChainId ,
69
67
setImplementationAddress : state . setImplementationAddress ,
70
- contractAbi : state . contractAbi ,
71
68
} ) ) ;
72
69
73
70
const { addChain, chains } = useGlobalState ( state => ( {
@@ -84,75 +81,47 @@ const ContractDetailPage = ({ addressFromUrl, chainIdFromUrl }: ServerSideProps)
84
81
return chain ? chain . name : "Unknown Network" ;
85
82
} ;
86
83
87
- useEffect ( ( ) => {
88
- if ( contractAbi . length > 0 ) {
89
- setContractData ( { abi : contractAbi , address : contractAddress } ) ;
90
- }
84
+ const {
85
+ contractData : fetchedContractData ,
86
+ error : fetchError ,
87
+ isLoading,
88
+ implementationAddress,
89
+ } = useFetchContractAbi ( contractAddress , parseInt ( network ) , publicClient ) ;
90
+
91
+ const effectiveContractData = isUseLocalAbi && contractData ? contractData : fetchedContractData ;
92
+
93
+ const error = isUseLocalAbi ? null : fetchError ;
91
94
95
+ useEffect ( ( ) => {
92
96
if ( network ) {
93
97
let normalizedNetwork = network . toLowerCase ( ) ;
94
98
if ( normalizedNetwork === "ethereum" || normalizedNetwork === "mainnet" ) {
95
- normalizedNetwork = "ethereum" ; // chain.network for mainnet in viem/chains
99
+ normalizedNetwork = "ethereum" ;
96
100
}
97
101
98
102
const chain = Object . values ( chains ) . find ( chain => toCamelCase ( chain . name ) === normalizedNetwork ) ;
99
-
100
- let parsedNetworkId = 1 ;
101
- if ( chain ) {
102
- parsedNetworkId = chain . id ;
103
- } else {
104
- parsedNetworkId = parseInt ( network ) ;
105
- }
106
-
103
+ const parsedNetworkId = chain ? chain . id : parseInt ( network ) ;
107
104
setMainChainId ( parsedNetworkId ) ;
105
+ }
108
106
109
- const fetchContractAbi = async ( ) => {
110
- setIsLoading ( true ) ;
111
-
112
- try {
113
- const implementationAddress = await detectProxyTarget ( contractAddress , publicClient ) ;
114
-
115
- if ( implementationAddress ) {
116
- setImplementationAddress ( implementationAddress ) ;
117
- }
118
- const abi = await fetchContractABIFromAnyABI ( implementationAddress || contractAddress , parsedNetworkId ) ;
119
- if ( ! abi ) throw new Error ( "Got empty or undefined ABI from AnyABI" ) ;
120
- setContractData ( { abi, address : contractAddress } ) ;
121
- setError ( null ) ;
122
- } catch ( error : any ) {
123
- console . error ( "Error fetching ABI from AnyABI: " , error ) ;
124
- console . log ( "Trying to fetch ABI from Etherscan..." ) ;
125
- try {
126
- const abiString = await fetchContractABIFromEtherscan ( contractAddress , parsedNetworkId ) ;
127
- const parsedAbi = JSON . parse ( abiString ) ;
128
- setContractData ( { abi : parsedAbi , address : contractAddress } ) ;
129
- setError ( null ) ;
130
- } catch ( etherscanError : any ) {
131
- console . error ( "Error fetching ABI from Etherscan: " , etherscanError ) ;
132
- setError ( etherscanError . message || "Error occurred while fetching ABI" ) ;
133
- }
134
- } finally {
135
- setIsLoading ( false ) ;
136
- }
137
- } ;
138
-
139
- if ( contractAddress && network ) {
140
- if ( isAddress ( contractAddress ) ) {
141
- fetchContractAbi ( ) ;
142
- } else {
143
- setIsLoading ( false ) ;
144
- setError ( "Please enter a valid address" ) ;
145
- }
146
- }
107
+ if ( implementationAddress ) {
108
+ setImplementationAddress ( implementationAddress ) ;
147
109
}
148
- } , [ contractAddress , network , setMainChainId , setImplementationAddress , publicClient , chains , contractAbi ] ) ;
110
+ } , [ network , implementationAddress , chains , setMainChainId , setImplementationAddress ] ) ;
149
111
150
112
const handleUserProvidedAbi = ( ) => {
151
113
try {
152
114
const parsedAbi = parseAndCorrectJSON ( localContractAbi ) ;
153
- setContractData ( { abi : parsedAbi , address : contractAddress } ) ;
154
- notification . success ( "ABI successfully loaded." ) ;
115
+ if ( parsedAbi ) {
116
+ setIsUseLocalAbi ( true ) ;
117
+ setContractData ( { abi : parsedAbi , address : contractAddress } ) ;
118
+ notification . success ( "ABI successfully loaded." ) ;
119
+ } else {
120
+ throw new Error ( "Parsed ABI is null or undefined" ) ;
121
+ }
155
122
} catch ( error ) {
123
+ console . error ( "Error parsing ABI:" , error ) ;
124
+ setIsUseLocalAbi ( false ) ;
156
125
notification . error ( "Invalid ABI format. Please ensure it is a valid JSON." ) ;
157
126
}
158
127
} ;
@@ -176,18 +145,18 @@ const ContractDetailPage = ({ addressFromUrl, chainIdFromUrl }: ServerSideProps)
176
145
< div className = "bg-base-100 h-screen flex flex-col" >
177
146
< MiniHeader />
178
147
< div className = "flex flex-col gap-y-6 lg:gap-y-8 flex-grow h-full overflow-hidden" >
179
- { isLoading ? (
148
+ { isLoading && ! isUseLocalAbi ? (
180
149
< div className = "flex justify-center h-full mt-14" >
181
150
< span className = "loading loading-spinner text-primary h-14 w-14" > </ span >
182
151
</ div >
183
- ) : contractData . abi ?. length > 0 ? (
184
- < ContractUI key = { contractName } initialContractData = { contractData } />
152
+ ) : effectiveContractData && effectiveContractData ? .abi ?. length > 0 ? (
153
+ < ContractUI key = { contractName } initialContractData = { effectiveContractData } />
185
154
) : (
186
155
< div className = "bg-base-200 flex flex-col border shadow-xl rounded-2xl px-6 lg:px-8 m-4 overflow-auto" >
187
156
< div className = "flex items-center" >
188
157
< ExclamationTriangleIcon className = "text-red-500 mt-4 h-20 w-20 pr-4" />
189
158
< div >
190
- < h2 className = "text-2xl pt-2 flex items-end" > { error } </ h2 >
159
+ < h2 className = "text-2xl pt-2 flex items-end" > { error ?. message } </ h2 >
191
160
< p className = "break-all" >
192
161
There was an error loading the contract < strong > { contractAddress } </ strong > on{ " " }
193
162
< strong > { getNetworkName ( chainId ) } </ strong > .
0 commit comments