Skip to content

Commit d686e41

Browse files
Feat/validator check (#83)
* Check if the address searched is a validator * Fix beaconchain link * Remove console logs * Revert one bad change
1 parent 60bea75 commit d686e41

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

packages/nextjs/components/address-vision/ButtonsCard.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ export const ButtonsCard = () => {
3232
const [isGnosisSafe, setIsGnosisSafe] = useState<boolean>(false);
3333
const [safeOwners, setSafeOwners] = useState<Address[]>([]);
3434
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);
3541

3642
const { resolvedAddress: address } = useAddressStore();
3743
const client = usePublicClient();
@@ -43,6 +49,8 @@ export const ButtonsCard = () => {
4349
setIsGnosisSafe(false);
4450
setSafeOwners([]);
4551
setSafeThreshold(0);
52+
setIsValidator(false);
53+
setValidatorInfo(null);
4654
}, [address]);
4755

4856
useEffect(() => {
@@ -78,7 +86,54 @@ export const ButtonsCard = () => {
7886
}
7987
};
8088

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+
81135
checkGnosisSafe();
136+
checkValidator();
82137
}, [address, client]);
83138

84139
if (isContractAddress && !isGnosisSafe) {
@@ -97,6 +152,43 @@ export const ButtonsCard = () => {
97152
);
98153
}
99154

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+
100192
if (isContractAddress && isGnosisSafe) {
101193
return (
102194
<div className="card w-[370px] md:w-[425px] bg-base-100 shadow-xl">

0 commit comments

Comments
 (0)