@@ -7,6 +7,31 @@ import { withSpinner } from './spinner';
77
88const  logger  =  debugFactory ( 'graph-cli:abi-helpers' ) ; 
99
10+ const  fetchFromEtherscan  =  async  ( url : string ) : Promise < any  |  null >  =>  { 
11+   const  result  =  await  fetch ( url ) ; 
12+   let  json : any  =  { } ; 
13+ 
14+   if  ( result . ok )  { 
15+     json  =  await  result . json ( ) . catch ( error  =>  { 
16+       throw  new  Error ( `Failed to read JSON response from Etherscan: ${ error }  ` ) ; 
17+     } ) ; 
18+ 
19+     // Etherscan returns a JSON object that has a `status`, a `message` and 
20+     // a `result` field. The `status` is '0' in case of errors and '1' in 
21+     // case of success 
22+     if  ( json . status  ===  '1' )  return  json ; 
23+   } 
24+ 
25+   logger ( 
26+     'Failed to fetchFromEtherscan: [%s] %s (%s)\n%O' , 
27+     result . status , 
28+     result . statusText , 
29+     result . url , 
30+     json , 
31+   ) ; 
32+   return  null ; 
33+ } ; 
34+ 
1035export  const  loadAbiFromEtherscan  =  async  ( 
1136  ABICtor : typeof  ABI , 
1237  network : string , 
@@ -18,16 +43,14 @@ export const loadAbiFromEtherscan = async (
1843    `Warnings while fetching ABI from Etherscan` , 
1944    async  ( )  =>  { 
2045      const  scanApiUrl  =  getEtherscanLikeAPIUrl ( network ) ; 
21-       const  result  =  await  fetch ( `${ scanApiUrl }  ?module=contract&action=getabi&address=${ address }  ` ) ; 
22-       const  json  =  await  result . json ( ) ; 
46+       const  json  =  await  fetchFromEtherscan ( 
47+         `${ scanApiUrl }  ?module=contract&action=getabi&address=${ address }  ` , 
48+       ) ; 
2349
24-       // Etherscan returns a JSON object that has a `status`, a `message` and 
25-       // a `result` field. The `status` is '0' in case of errors and '1' in 
26-       // case of success 
27-       if  ( json . status  ===  '1' )  { 
50+       if  ( json ) 
2851        return  new  ABICtor ( 'Contract' ,  undefined ,  immutable . fromJS ( JSON . parse ( json . result ) ) ) ; 
29-        } 
30-       throw  new  Error ( 'ABI not found, try  loading it from a local file' ) ; 
52+ 
53+       throw  new  Error ( 'Try  loading it from a local file' ) ; 
3154    } , 
3255  ) ; 
3356
@@ -62,11 +85,11 @@ export const fetchDeployContractTransactionFromEtherscan = async (
6285  address : string , 
6386) : Promise < string >  =>  { 
6487  const  scanApiUrl  =  getEtherscanLikeAPIUrl ( network ) ; 
65-   const  json  =  await  fetchContractCreationHashWithRetry ( 
88+   const  json  =  await  fetchFromEtherscan ( 
6689    `${ scanApiUrl }  ?module=contract&action=getcontractcreation&contractaddresses=${ address }  ` , 
67-     5 , 
6890  ) ; 
69-   if  ( json . status  ===  '1' )  { 
91+ 
92+   if  ( json )  { 
7093    const  hash  =  json . result [ 0 ] . txHash ; 
7194    logger ( 'Successfully fetchDeployContractTransactionFromEtherscan. txHash: %s' ,  hash ) ; 
7295    return  hash ; 
@@ -75,33 +98,13 @@ export const fetchDeployContractTransactionFromEtherscan = async (
7598  throw  new  Error ( `Failed to fetch deploy contract transaction` ) ; 
7699} ; 
77100
78- export  const  fetchContractCreationHashWithRetry  =  async  ( 
79-   url : string , 
80-   retryCount : number , 
81- ) : Promise < any >  =>  { 
82-   let  json ; 
83-   for  ( let  i  =  0 ;  i  <  retryCount ;  i ++ )  { 
84-     try  { 
85-       const  result  =  await  fetch ( url ) ; 
86-       json  =  await  result . json ( ) ; 
87-       if  ( json . status  !==  '0' )  { 
88-         return  json ; 
89-       } 
90-     }  catch  ( error )  { 
91-       logger ( 'Failed to fetchContractCreationHashWithRetry: %O' ,  error ) ; 
92-       /* empty */ 
93-     } 
94-   } 
95-   throw  new  Error ( `Failed to fetch contract creation transaction hash` ) ; 
96- } ; 
97- 
98101export  const  fetchTransactionByHashFromRPC  =  async  ( 
99102  network : string , 
100103  transactionHash : string , 
101104) : Promise < any >  =>  { 
102105  let  json : any ; 
106+   const  RPCURL  =  getPublicRPCEndpoint ( network ) ; 
103107  try  { 
104-     const  RPCURL  =  getPublicRPCEndpoint ( network ) ; 
105108    if  ( ! RPCURL )  throw  new  Error ( `Unable to fetch RPC URL for ${ network }  ` ) ; 
106109    const  result  =  await  fetch ( String ( RPCURL ) ,  { 
107110      method : 'POST' , 
@@ -120,7 +123,9 @@ export const fetchTransactionByHashFromRPC = async (
120123    return  json ; 
121124  }  catch  ( error )  { 
122125    logger ( 'Failed to fetchTransactionByHashFromRPC: %O' ,  error ) ; 
123-     throw  new  Error ( 'Failed to fetch contract creation transaction' ) ; 
126+     throw  new  Error ( 
127+       `Failed to run \`eth_getTransactionByHash\` on RPC (${ RPCURL }  ) (run with env \`DEBUG=*\` for full error).` , 
128+     ) ; 
124129  } 
125130} ; 
126131
@@ -129,14 +134,15 @@ export const fetchSourceCodeFromEtherscan = async (
129134  address : string , 
130135) : Promise < any >  =>  { 
131136  const  scanApiUrl  =  getEtherscanLikeAPIUrl ( network ) ; 
132-   const  result  =  await  fetch ( 
137+   const  json  =  await  fetchFromEtherscan ( 
133138    `${ scanApiUrl }  ?module=contract&action=getsourcecode&address=${ address }  ` , 
134139  ) ; 
135-   const  json  =  await  result . json ( ) ; 
136-   if  ( json . status  ===  '1' )  { 
137-     return  json ; 
138-   } 
139-   throw  new  Error ( 'Failed to fetch contract source code' ) ; 
140+ 
141+   // Have to check that the SourceCode response is not empty due to Etherscan API bug responding with 
142+   // 1 - OK on non-valid contracts. 
143+   if  ( json . result [ 0 ] . SourceCode )  return  json ; 
144+ 
145+   throw  new  Error ( `Failed to fetch contract source code: ${ json . result [ 0 ] . ABI }  ` ) ; 
140146} ; 
141147
142148export  const  getContractNameForAddress  =  async  ( 
@@ -145,7 +151,13 @@ export const getContractNameForAddress = async (
145151) : Promise < string >  =>  { 
146152  try  { 
147153    const  contractSourceCode  =  await  fetchSourceCodeFromEtherscan ( network ,  address ) ; 
148-     const  contractName  =  contractSourceCode . result [ 0 ] . ContractName ; 
154+     let  contractName : string  =  contractSourceCode . result [ 0 ] . ContractName ; 
155+ 
156+     // Some explorers will return the full path of the contract instead of just the name 
157+     // Example: contracts/SyncSwapRouter.sol:SyncSwapRouter 
158+     if  ( contractName . includes ( ':' ) ) 
159+       contractName  =  contractName . substring ( contractName . lastIndexOf ( ':' )  +  1 ) ; 
160+ 
149161    logger ( 'Successfully getContractNameForAddress. contractName: %s' ,  contractName ) ; 
150162    return  contractName ; 
151163  }  catch  ( error )  { 
@@ -319,7 +331,7 @@ const getEtherscanLikeAPIUrl = (network: string) => {
319331    case  'arbitrum-nova' :
320332      return  'https://arbitrum-nova.abi.pinax.network/api' ; 
321333    case  'soneium-testnet' :
322-       return  'https://explorer-testnet.soneium.org /api' ; 
334+       return  'https://soneium-minato.blockscout.com /api' ; 
323335    case  'chiliz' :
324336      return  'https://scan.chiliz.com/api' ; 
325337    case  'chiliz-testnet' :
@@ -338,6 +350,14 @@ const getEtherscanLikeAPIUrl = (network: string) => {
338350      return  'https://rootstock-testnet.blockscout.com/api' ; 
339351    case  'unichain-testnet' :
340352      return  'https://unichain-sepolia.blockscout.com/api' ; 
353+     case  'lens-testnet' :
354+       return  'https://block-explorer-api.testnet.lens.dev/api' ; 
355+     case  'abstract-testnet' :
356+       return  'https://block-explorer-api.testnet.abs.xyz/api' ; 
357+     case  'corn' :
358+       return  'https://maizenet-explorer.usecorn.com/api' ; 
359+     case  'corn-testnet' :
360+       return  'https://testnet-explorer.usecorn.com/api' ; 
341361    default :
342362      return  `https://api-${ network }  .etherscan.io/api` ; 
343363  } 
@@ -383,11 +403,11 @@ const getPublicRPCEndpoint = (network: string) => {
383403    case  'goerli' :
384404      return  'https://rpc.ankr.com/eth_goerli' ; 
385405    case  'gnosis' :
386-       return  'https://safe-transaction.gnosis.io ' ; 
406+       return  'https://rpc.gnosischain.com ' ; 
387407    case  'mainnet' :
388408      return  'https://rpc.ankr.com/eth' ; 
389409    case  'matic' :
390-       return  'https://rpc-mainnet.maticvigil. com' ; 
410+       return  'https://polygon-rpc. com/ ' ; 
391411    case  'mbase' :
392412      return  'https://rpc.moonbase.moonbeam.network' ; 
393413    case  'mumbai' :
@@ -494,6 +514,14 @@ const getPublicRPCEndpoint = (network: string) => {
494514      return  'https://public-en.kairos.node.kaia.io' ; 
495515    case  'unichain-testnet' :
496516      return  'https://sepolia.unichain.org' ; 
517+     case  'lens-testnet' :
518+       return  'https://api.staging.lens.zksync.dev' ; 
519+     case  'abstract-testnet' :
520+       return  'https://api.testnet.abs.xyz' ; 
521+     case  'corn' :
522+       return  'https://maizenet-rpc.usecorn.com' ; 
523+     case  'corn-testnet' :
524+       return  'https://testnet-rpc.usecorn.com' ; 
497525    default :
498526      throw  new  Error ( `Unknown network: ${ network }  ` ) ; 
499527  } 
0 commit comments