11import { padHex , zeroAddress , zeroHash } from 'viem' ;
22import type { Address , Hex , PublicClient } from 'viem' ;
3+ import { getStorageAt , multicall } from 'viem/actions' ;
34
5+ import eip897ProxyAbi from '@/abi/eip897Proxy.js' ;
46import safeProxyAbi from '@/abi/safeProxy.js' ;
57
68// Storage slots for common proxy implementations
@@ -40,17 +42,6 @@ const slotMap: Record<string, Hex> = {
4042 // DIAMOND_STORAGE: '0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b',
4143} ;
4244
43- async function getStorage (
44- client : PublicClient ,
45- address : Address ,
46- slots : Hex [ ] ,
47- ) : Promise < Hex [ ] > {
48- const results = await Promise . all (
49- slots . map ( ( slot ) => client . getStorageAt ( { address, slot } ) ) ,
50- ) ;
51- return results . filter ( ( result ) => ! ! result ) ;
52- }
53-
5445function toAddress ( slotValue : Hex | null ) : Address | null {
5546 if ( ! slotValue ) {
5647 return null ;
@@ -66,40 +57,90 @@ function toAddress(slotValue: Hex | null): Address | null {
6657 return address ;
6758}
6859
69- // Attempts to get the proxy implementation address for a given address
70- // Note that this may not succeed even if the provided address is a proxy
71- async function getImplementation (
60+ async function getByImplementationCall (
7261 client : PublicClient ,
7362 address : Address ,
7463) : Promise < Address | null > {
75- // Call-based detection (Safe-like proxies)
76- const callResults = await client . multicall ( {
64+ const results = await multicall ( client , {
65+ contracts : [
66+ {
67+ address,
68+ abi : eip897ProxyAbi ,
69+ functionName : 'implementation' ,
70+ } ,
71+ ] ,
72+ } ) ;
73+ const result = results [ 0 ] ;
74+ if ( result . status === 'failure' ) {
75+ return null ;
76+ }
77+ const implementation = result . result . toLowerCase ( ) as Address ;
78+ if ( implementation === zeroAddress ) {
79+ return null ;
80+ }
81+ return implementation ;
82+ }
83+
84+ async function getByMasterCopyCall (
85+ client : PublicClient ,
86+ address : Address ,
87+ ) : Promise < Address | null > {
88+ const results = await multicall ( client , {
7789 contracts : [
7890 {
91+ address,
7992 abi : safeProxyAbi ,
80- address : address ,
8193 functionName : 'masterCopy' ,
82- args : [ ] ,
8394 } ,
8495 ] ,
8596 } ) ;
86- const masterCopyResult = callResults [ 0 ] ;
87- if ( masterCopyResult . status === 'success' ) {
88- const address = masterCopyResult . result . toLowerCase ( ) as Address ;
89- if ( address !== zeroAddress ) {
90- return address ;
91- }
97+ const result = results [ 0 ] ;
98+ if ( result . status === 'failure' ) {
99+ return null ;
100+ }
101+ const implementation = result . result . toLowerCase ( ) as Address ;
102+ if ( implementation === zeroAddress ) {
103+ return null ;
104+ }
105+ return implementation ;
106+ }
107+
108+ // Attempts to get the proxy implementation address for a given address
109+ // Note that this may not succeed even if the provided address is a proxy
110+ async function getImplementation (
111+ client : PublicClient ,
112+ address : Address ,
113+ ) : Promise < Address | null > {
114+ // Call-based detection (Safe-like proxies)
115+ const masterCopy = await getByMasterCopyCall ( client , address ) ;
116+ if ( masterCopy ) {
117+ return masterCopy ;
92118 }
93119 // Slot-based detection
94120 const slots = Object . values ( slotMap ) ;
95121 const addressSlot = padHex ( address ) ;
96122 slots . push ( addressSlot ) ;
97- const slotValues = await getStorage ( client , address , slots ) ;
98- for ( const slot of slotValues ) {
99- const slotAddress = toAddress ( slot ) ;
100- if ( slotAddress ) {
123+ const slotValues = await Promise . all (
124+ slots . map ( ( slot ) => getStorageAt ( client , { address, slot } ) ) ,
125+ ) ;
126+ for ( const slot of slots ) {
127+ const slotIndex = slots . indexOf ( slot ) ;
128+ const slotValue = slotValues [ slotIndex ] ;
129+ if ( ! slotValue ) {
130+ continue ;
131+ }
132+ const slotAddress = toAddress ( slotValue ) ;
133+ if ( ! slotAddress ) {
134+ continue ;
135+ }
136+ if ( slot !== slotMap [ 'EIP1967_BEACON' ] ) {
101137 return slotAddress ;
102138 }
139+ // Beacon proxies require a 2-step resolution
140+ const implementation = await getByImplementationCall ( client , slotAddress ) ;
141+ if ( implementation ) {
142+ return implementation ;
143+ }
103144 }
104145 // Bytecode-based detection
105146 // Credit to @banteg for the original list
0 commit comments