Skip to content

Commit 74c7367

Browse files
committed
feat: integrate validator checks into proof creation and dashboard error handling
1 parent a4edcff commit 74c7367

File tree

8 files changed

+122
-31
lines changed

8 files changed

+122
-31
lines changed

abi/Dashboard.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { StakingVaultErrorsAbi } from './StakingVault.js';
22
import { OperatorGridErrorsAbi } from './OperatorGrid.js';
33
import { VaultHubErrorsAbi } from './VaultHub.js';
4+
import { PredepositGuaranteeErrorsAbi } from './PredepositGuarantee.js';
45

56
export const DashboardErrorsAbi = [
67
{
@@ -216,6 +217,7 @@ export const DashboardAbi = [
216217
...StakingVaultErrorsAbi,
217218
...OperatorGridErrorsAbi,
218219
...VaultHubErrorsAbi,
220+
...PredepositGuaranteeErrorsAbi,
219221
{
220222
inputs: [
221223
{

abi/PredepositGuarantee.ts

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,6 @@
11
import { StakingVaultErrorsAbi } from './StakingVault.js';
22

3-
export const PredepositGuaranteeAbi = [
4-
...StakingVaultErrorsAbi,
5-
{
6-
inputs: [
7-
{
8-
internalType: 'bytes4',
9-
name: '_genesisForkVersion',
10-
type: 'bytes4',
11-
},
12-
{
13-
internalType: 'GIndex',
14-
name: '_gIFirstValidator',
15-
type: 'bytes32',
16-
},
17-
{
18-
internalType: 'GIndex',
19-
name: '_gIFirstValidatorAfterChange',
20-
type: 'bytes32',
21-
},
22-
{
23-
internalType: 'uint64',
24-
name: '_pivotSlot',
25-
type: 'uint64',
26-
},
27-
],
28-
stateMutability: 'nonpayable',
29-
type: 'constructor',
30-
},
3+
export const PredepositGuaranteeErrorsAbi = [
314
{
325
inputs: [],
336
name: 'AccessControlBadConfirmation',
@@ -379,6 +352,37 @@ export const PredepositGuaranteeAbi = [
379352
name: 'ZeroPauseDuration',
380353
type: 'error',
381354
},
355+
] as const;
356+
357+
export const PredepositGuaranteeAbi = [
358+
...PredepositGuaranteeErrorsAbi,
359+
...StakingVaultErrorsAbi,
360+
{
361+
inputs: [
362+
{
363+
internalType: 'bytes4',
364+
name: '_genesisForkVersion',
365+
type: 'bytes4',
366+
},
367+
{
368+
internalType: 'GIndex',
369+
name: '_gIFirstValidator',
370+
type: 'bytes32',
371+
},
372+
{
373+
internalType: 'GIndex',
374+
name: '_gIFirstValidatorAfterChange',
375+
type: 'bytes32',
376+
},
377+
{
378+
internalType: 'uint64',
379+
name: '_pivotSlot',
380+
type: 'uint64',
381+
},
382+
],
383+
stateMutability: 'nonpayable',
384+
type: 'constructor',
385+
},
382386
{
383387
anonymous: false,
384388
inputs: [

features/deposits/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './check-bls-deposits.js';
22
export * from './make-pdg-proof.js';
33
export * from './no-pdg.js';
44
export * from './pdg.js';
5+
export * from './validators.js';

features/deposits/validators.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { formatEther, Hex } from 'viem';
2+
import { confirmOperation, logError, logInfo } from 'utils';
3+
4+
type ValidatorInfo = {
5+
pubkey: Hex;
6+
effectiveBalance: number;
7+
activationEpoch: number;
8+
slashed: boolean;
9+
};
10+
11+
const MIN_EFFECTIVE_BALANCE_GWEI = 32000000000;
12+
const NOT_ACTIVATED_EPOCH = Infinity;
13+
14+
export const checkValidatorInfo = async (validator: ValidatorInfo) => {
15+
let message = '';
16+
let isValid = true;
17+
if (validator.effectiveBalance < MIN_EFFECTIVE_BALANCE_GWEI) {
18+
message += `Validator effective balance is less than ${formatEther(BigInt(MIN_EFFECTIVE_BALANCE_GWEI), 'gwei')} ETH (current: ${formatEther(BigInt(validator.effectiveBalance), 'gwei')} ETH)\n`;
19+
isValid = false;
20+
}
21+
22+
if (validator.activationEpoch === NOT_ACTIVATED_EPOCH) {
23+
message += `Validator is not activated. (activationEpoch: ${validator.activationEpoch})\n`;
24+
isValid = false;
25+
}
26+
27+
if (validator.slashed) {
28+
message += `Validator is slashed. (slashed: ${validator.slashed})\n`;
29+
isValid = false;
30+
}
31+
32+
logError(message);
33+
34+
const confirm = await confirmOperation(
35+
`Do you want to skip this validator (pubkey: ${validator.pubkey})?`,
36+
);
37+
38+
if (!confirm) {
39+
throw new Error('Operation cancelled');
40+
}
41+
42+
if (!isValid) {
43+
logInfo('Validator is not valid. Skipping...');
44+
}
45+
46+
return { isValid, skip: confirm };
47+
};

programs/contracts/dashboard/write.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
checkBLSDeposits,
1616
checkPdgIsPaused,
1717
callWriteMethodsWithReportFresh,
18+
checkValidatorInfo,
1819
} from 'features';
1920
import {
2021
callReadMethod,
@@ -655,10 +656,19 @@ dashboardWrite
655656
message: `Making proof for validator ${validatorIndex}...`,
656657
});
657658
const packageProof = await createPDGProof(Number(validatorIndex));
658-
const { proof, pubkey, childBlockTimestamp, slot, proposerIndex } =
659-
packageProof;
659+
const {
660+
proof,
661+
pubkey,
662+
childBlockTimestamp,
663+
slot,
664+
proposerIndex,
665+
validator,
666+
} = packageProof;
660667
hideSpinner();
661668

669+
const { skip } = await checkValidatorInfo({ pubkey, ...validator });
670+
if (skip) continue;
671+
662672
const confirm = await confirmOperation(
663673
`Are you sure you want to prove ${pubkey} validator (${validatorIndex}) to the Predeposit Guarantee contract ${pdgContractAddress} in the staking vault ${vault}?
664674
Witnesses length: ${proof.length}`,
@@ -676,6 +686,16 @@ dashboardWrite
676686
payload.push(proofItem);
677687
}
678688

689+
if (payload.length === 0) {
690+
logInfo('No validators to prove. Exiting...');
691+
return;
692+
}
693+
694+
const confirmProof = await confirmOperation(
695+
`Validators to prove (${payload.length}): ${payload.map((i) => i.pubkey).join(', ')}. Continue?`,
696+
);
697+
if (!confirmProof) return;
698+
679699
await callWriteMethodWithReceipt({
680700
contract: dashboardContract,
681701
methodName: 'proveUnknownValidatorsToPDG',

programs/contracts/hub/write.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Address, formatEther, Hex, parseEther } from 'viem';
22
import { Option } from 'commander';
33

4+
import { checkValidatorInfo } from 'features';
45
import {
56
getOperatorGridContract,
67
getStakingVaultContract,
@@ -434,8 +435,12 @@ VaultHubWrite.command('prove-unknown-validator-to-pdg')
434435
withdrawalCredentials,
435436
slot,
436437
proposerIndex,
438+
validator,
437439
} = packageProof;
438440

441+
const { skip } = await checkValidatorInfo({ pubkey, ...validator });
442+
if (skip) return;
443+
439444
logResult({});
440445
logInfo('----------------------proof----------------------');
441446
logInfo(proof);

utils/interrupt-handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export const withInterruptHandling = (action: ActionHandler) => {
2727

2828
return result;
2929
} catch (err) {
30-
logError('Command failed:', err);
30+
if (err instanceof Error) logError('Command failed:', err.message);
31+
else logError('Command failed:', err);
32+
3133
await disconnectWalletConnect();
3234
process.exit(1);
3335
} finally {

utils/proof/create-proof.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ export interface ValidatorWitnessWithWC extends ValidatorWitness {
2727
withdrawalCredentials: Hex;
2828
slot: bigint;
2929
proposerIndex: bigint;
30+
validator: {
31+
effectiveBalance: number;
32+
activationEpoch: number;
33+
slashed: boolean;
34+
};
3035
}
3136

3237
const slotToTimestamp = (slot: number, genesisTimestamp: number): number => {
@@ -88,6 +93,11 @@ export const createPDGProof = async (
8893
childBlockTimestamp: BigInt(headerByParentTimestamp),
8994
slot: BigInt(beaconHeader.slot),
9095
proposerIndex: BigInt(beaconHeader.proposer_index),
96+
validator: {
97+
effectiveBalance: validator.effectiveBalance,
98+
activationEpoch: validator.activationEpoch,
99+
slashed: validator.slashed,
100+
},
91101
};
92102

93103
return result;

0 commit comments

Comments
 (0)