@@ -8,6 +8,7 @@ import { usePublicClient } from "wagmi";
88import { useAddressStore } from "~~/services/store/store" ;
99
1010const GNOSIS_SAFE_BYTECODE_PATTERN = "0x608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e" ;
11+ const EOF_SIGNATURE = "0xef01" ;
1112
1213const SAFE_ABI = [
1314 {
@@ -31,6 +32,12 @@ export const ButtonsCard = () => {
3132 const [ isGnosisSafe , setIsGnosisSafe ] = useState < boolean > ( false ) ;
3233 const [ safeOwners , setSafeOwners ] = useState < Address [ ] > ( [ ] ) ;
3334 const [ safeThreshold , setSafeThreshold ] = useState < number > ( 0 ) ;
35+ const [ isValidator , setIsValidator ] = useState < boolean > ( false ) ;
36+ const [ validatorInfo , setValidatorInfo ] = useState < {
37+ totalDeposits : number ;
38+ totalETHStaked : string ;
39+ pubkeys : string [ ] ;
40+ } | null > ( null ) ;
3441
3542 const { resolvedAddress : address } = useAddressStore ( ) ;
3643 const client = usePublicClient ( ) ;
@@ -42,14 +49,16 @@ export const ButtonsCard = () => {
4249 setIsGnosisSafe ( false ) ;
4350 setSafeOwners ( [ ] ) ;
4451 setSafeThreshold ( 0 ) ;
52+ setIsValidator ( false ) ;
53+ setValidatorInfo ( null ) ;
4554 } , [ address ] ) ;
4655
4756 useEffect ( ( ) => {
4857 const checkGnosisSafe = async ( ) => {
4958 if ( ! address || ! client ) return ;
5059 try {
5160 const bytecode = await client . getCode ( { address } ) ;
52- const isContract = bytecode && bytecode . length > 2 ;
61+ const isContract = bytecode && bytecode . length > 2 && ! bytecode . startsWith ( EOF_SIGNATURE ) ;
5362
5463 setIsContractAddress ( isContract || false ) ;
5564
@@ -77,7 +86,54 @@ export const ButtonsCard = () => {
7786 }
7887 } ;
7988
89+ const checkValidator = async ( ) => {
90+ if ( ! address ) return ;
91+ try {
92+ const response = await fetch ( `https://beaconcha.in/api/v1/validator/eth1/${ address } ` ) ;
93+
94+ if ( response . ok ) {
95+ const data = await response . json ( ) ;
96+
97+ if ( data . status === "OK" && data . data && data . data . length > 0 ) {
98+ const validatorIndices = data . data . map ( ( validator : any ) => validator . validatorindex ) ;
99+
100+ const validatorPromises = validatorIndices . map ( async ( index : number ) => {
101+ try {
102+ const validatorResponse = await fetch ( `https://beaconcha.in/api/v1/validator/${ index } ` ) ;
103+ if ( validatorResponse . ok ) {
104+ const validatorData = await validatorResponse . json ( ) ;
105+ if ( validatorData . status === "OK" && validatorData . data ) {
106+ return {
107+ balance : parseFloat ( validatorData . data . balance ) / 1e9 ,
108+ pubkey : validatorData . data . pubkey ,
109+ } ;
110+ }
111+ }
112+ return { balance : 0 , pubkey : "" } ;
113+ } catch ( error ) {
114+ return { balance : 0 , pubkey : "" } ;
115+ }
116+ } ) ;
117+
118+ const validators = await Promise . all ( validatorPromises ) ;
119+ const totalBalance = validators . reduce ( ( sum , validator ) => sum + validator . balance , 0 ) ;
120+ const pubkeys = validators . map ( validator => validator . pubkey ) . filter ( pubkey => pubkey !== "" ) ;
121+
122+ setIsValidator ( true ) ;
123+ setValidatorInfo ( {
124+ totalDeposits : data . data . length ,
125+ totalETHStaked : totalBalance . toFixed ( 4 ) ,
126+ pubkeys : pubkeys ,
127+ } ) ;
128+ }
129+ }
130+ } catch ( error ) {
131+ console . error ( "Validator check failed:" , error ) ;
132+ }
133+ } ;
134+
80135 checkGnosisSafe ( ) ;
136+ checkValidator ( ) ;
81137 } , [ address , client ] ) ;
82138
83139 if ( isContractAddress && ! isGnosisSafe ) {
@@ -96,6 +152,43 @@ export const ButtonsCard = () => {
96152 ) ;
97153 }
98154
155+ if ( isValidator ) {
156+ return (
157+ < div className = "card w-[370px] md:w-[425px] bg-base-100 shadow-xl" >
158+ < div className = "card-body" >
159+ < h2 className = "card-title" >
160+ < span className = "text-2xl mr-2" > 🔗</ span >
161+ Ethereum Validator
162+ </ h2 >
163+ < div className = "space-y-2" >
164+ < div className = "flex justify-between items-center" >
165+ < p className = "m-0 font-semibold" > Validators:</ p >
166+ < p className = "m-0" > { validatorInfo ?. totalDeposits || 0 } </ p >
167+ </ div >
168+ < div className = "flex justify-between items-center" >
169+ < p className = "m-0 font-semibold" > Total Balance:</ p >
170+ < p className = "m-0" > { validatorInfo ?. totalETHStaked || "0" } ETH</ p >
171+ </ div >
172+ < div className = "mt-4" >
173+ < Link
174+ href = {
175+ validatorInfo ?. pubkeys && validatorInfo . pubkeys . length === 1
176+ ? `https://beaconcha.in/validator/${ validatorInfo . pubkeys [ 0 ] } `
177+ : `https://beaconcha.in/validators/eth1deposits?q=${ address } `
178+ }
179+ className = "btn btn-primary btn-sm rounded-full w-full"
180+ target = "_blank"
181+ rel = "noopener noreferrer"
182+ >
183+ View on Beaconcha.in
184+ </ Link >
185+ </ div >
186+ </ div >
187+ </ div >
188+ </ div >
189+ ) ;
190+ }
191+
99192 if ( isContractAddress && isGnosisSafe ) {
100193 return (
101194 < div className = "card w-[370px] md:w-[425px] bg-base-100 shadow-xl" >
0 commit comments