@@ -11,6 +11,8 @@ import {
1111  PublicClient , 
1212  createPublicClient , 
1313  http , 
14+   size , 
15+   slice , 
1416  toEventSelector , 
1517  toFunctionSelector , 
1618}  from  'viem' ; 
@@ -33,6 +35,8 @@ const DAY = 1000 * 60 * 60 * 24;
3335const  NO_SOURCE_CACHE_DURATION  =  7  *  DAY ; 
3436const  IMPLEMENTATION_CACHE_DURATION  =  DAY ; 
3537const  NO_IMPLEMENTATION_CACHE_DURATION  =  30  *  DAY ; 
38+ const  DELEGATION_CACHE_DURATION  =  DAY ; 
39+ const  NO_DELEGATION_CACHE_DURATION  =  DAY ; 
3640const  NO_DEPLOYMENT_CACHE_DURATION  =  30  *  DAY ; 
3741
3842interface  SourceCodeResponse  { 
@@ -43,6 +47,11 @@ interface SourceCodeResponse {
4347    abi : Abi  |  null ; 
4448    source : SourceCode  |  null ; 
4549  }  |  null ; 
50+   delegation : { 
51+     address : Address ; 
52+     abi : Abi  |  null ; 
53+     source : SourceCode  |  null ; 
54+   }  |  null ; 
4655} 
4756
4857interface  DeploymentResponse  { 
@@ -122,7 +131,41 @@ async function getSource(
122131  if  ( ! contract . value )  { 
123132    return  null ; 
124133  } 
125-   // Fetch impl address if there's no contract or if there is a contract but there is no implementation cached (unless we did that already recently) 
134+   const  implementation  =  await  fetchImplementation ( 
135+     chain , 
136+     client , 
137+     contract , 
138+     address , 
139+   ) ; 
140+   const  delegation  =  await  fetchDelegation ( chain ,  client ,  contract ,  address ) ; 
141+   return  { 
142+     abi : contract . value . abi , 
143+     source : contract . value . source , 
144+     implementation, 
145+     delegation, 
146+   } ; 
147+ } 
148+ 
149+ // Fetch implementation if there's no contract or if there is a contract but there is no implementation cached (unless we did that already recently) 
150+ async  function  fetchImplementation ( 
151+   chain : ChainId , 
152+   client : PublicClient , 
153+   contract : OptionalContractSourceCache , 
154+   address : Address , 
155+ ) : Promise < { 
156+   address : Address ; 
157+   abi : Abi  |  null ; 
158+   source : SourceCode  |  null ; 
159+ }  |  null >  { 
160+   const  minioService  =  new  MinioService ( 
161+     minioPublicEndpoint , 
162+     minioAccessKey , 
163+     minioSecretKey , 
164+     minioBucket , 
165+   ) ; 
166+   if  ( ! contract . value )  { 
167+     return  null ; 
168+   } 
126169  const  useCachedImplementation  = 
127170    contract . timestamp  ===  null 
128171      ? contract . value . implementation  !==  null 
@@ -150,17 +193,84 @@ async function getSource(
150193    implementationContract  &&  implementationContract . value 
151194      ? implementationContract . value . source 
152195      : null ; 
153-   return  { 
154-     abi : contract . value . abi , 
155-     source : contract . value . source , 
156-     implementation : implementation 
157-       ? { 
158-           address : implementation , 
159-           abi : implementationAbi , 
160-           source : implementationSource , 
161-         } 
162-       : null , 
163-   } ; 
196+   return  implementation 
197+     ? { 
198+         address : implementation , 
199+         abi : implementationAbi , 
200+         source : implementationSource , 
201+       } 
202+     : null ; 
203+ } 
204+ 
205+ // Fetch delegation if there's no contract or if there is a contract but there is no delegation cached (unless we did that already recently) 
206+ async  function  fetchDelegation ( 
207+   chain : ChainId , 
208+   client : PublicClient , 
209+   contract : OptionalContractSourceCache , 
210+   address : Address , 
211+ ) : Promise < { 
212+   address : Address ; 
213+   abi : Abi  |  null ; 
214+   source : SourceCode  |  null ; 
215+ }  |  null >  { 
216+   async  function  getDelegation ( 
217+     client : PublicClient , 
218+     address : Address , 
219+   ) : Promise < Address  |  null >  { 
220+     const  code  =  await  client . getCode ( { 
221+       address, 
222+     } ) ; 
223+     if  ( code  ===  undefined )  { 
224+       return  null ; 
225+     } 
226+     if  ( code . length  ===  0 )  { 
227+       return  null ; 
228+     } 
229+     if  ( size ( code )  !==  23 )  { 
230+       return  null ; 
231+     } 
232+     return  slice ( code ,  3 ) ; 
233+   } 
234+ 
235+   const  minioService  =  new  MinioService ( 
236+     minioPublicEndpoint , 
237+     minioAccessKey , 
238+     minioSecretKey , 
239+     minioBucket , 
240+   ) ; 
241+   if  ( ! contract . value )  { 
242+     return  null ; 
243+   } 
244+   const  useCachedDelegation  = 
245+     contract . timestamp  ===  null 
246+       ? contract . value . delegation  !==  null 
247+       : contract . value . delegation  ===  null 
248+         ? contract . timestamp  >  Date . now ( )  -  NO_DELEGATION_CACHE_DURATION 
249+         : contract . timestamp  >  Date . now ( )  -  DELEGATION_CACHE_DURATION ; 
250+   const  delegation  =  useCachedDelegation 
251+     ? contract . value . delegation 
252+     : await  getDelegation ( client ,  address ) ; 
253+   // Store the delegation in the cache 
254+   if  ( ! useCachedDelegation )  { 
255+     await  minioService . setSource ( chain ,  address ,  { 
256+       ...contract . value , 
257+       delegation, 
258+     } ) ; 
259+   } 
260+   const  delegationContract  =  delegation 
261+     ? await  fetchContract ( minioService ,  chain ,  delegation ) 
262+     : null ; 
263+   const  delegationAbi  = 
264+     delegationContract  &&  delegationContract . value 
265+       ? delegationContract . value . abi 
266+       : null ; 
267+   const  delegationSource  = 
268+     delegationContract  &&  delegationContract . value 
269+       ? delegationContract . value . source 
270+       : null ; 
271+   return  delegation 
272+     ? {  address : delegation ,  abi : delegationAbi ,  source : delegationSource  } 
273+     : null ; 
164274} 
165275
166276async  function  getAbi ( 
@@ -335,16 +445,25 @@ async function extractContractAbi(
335445    return  null ; 
336446  } 
337447  const  implementation  =  contract . implementation ; 
338-   if  ( ! implementation )  { 
339-     if  ( contract . abi  !==  null )  { 
340-       return  contract . abi ; 
448+   if  ( implementation )  { 
449+     if  ( implementation . abi  !==  null )  { 
450+       return  implementation . abi ; 
341451    } 
342-     return  guessAbi ( chain ,  address ) ; 
452+     return  guessAbi ( chain ,  implementation . address ) ; 
343453  } 
344-   if  ( implementation . abi  !==  null )  { 
345-     return  implementation . abi ; 
454+ 
455+   const  delegation  =  contract . delegation ; 
456+   if  ( delegation )  { 
457+     if  ( delegation . abi  !==  null )  { 
458+       return  delegation . abi ; 
459+     } 
460+     return  guessAbi ( chain ,  delegation . address ) ; 
461+   } 
462+ 
463+   if  ( contract . abi  !==  null )  { 
464+     return  contract . abi ; 
346465  } 
347-   return  guessAbi ( chain ,  implementation . address ) ; 
466+   return  guessAbi ( chain ,  address ) ; 
348467} 
349468
350469async  function  fetchContract ( 
@@ -379,6 +498,7 @@ async function fetchContract(
379498    // Don't use the implementation address from etherscan 
380499    // Too many false positives 
381500    implementation : null , 
501+     delegation : null , 
382502  } ; 
383503  await  minioService . setSource ( chain ,  address ,  contract ) ; 
384504  return  { 
0 commit comments