protoscan 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ import type { ProtocolDef } from './schema.js';
2
+ import { aaveV3 } from './aave-v3.js';
3
+ import { compoundV3 } from './compound-v3.js';
4
+ import { uniswapV3 } from './uniswap-v3.js';
5
+ import { maker } from './maker.js';
6
+ import { eigenLayer } from './eigenlayer.js';
7
+ import { pendle } from './pendle.js';
8
+ import { lido } from './lido.js';
9
+ import { morphoBlue } from './morpho-blue.js';
10
+
11
+ const protocols: Record<string, ProtocolDef> = {
12
+ 'aave-v3': aaveV3,
13
+ 'compound-v3': compoundV3,
14
+ 'uniswap-v3': uniswapV3,
15
+ 'maker': maker,
16
+ 'eigenlayer': eigenLayer,
17
+ 'pendle': pendle,
18
+ 'lido': lido,
19
+ 'morpho-blue': morphoBlue,
20
+ };
21
+
22
+ export function getProtocol(id: string): ProtocolDef | undefined {
23
+ return protocols[id];
24
+ }
25
+
26
+ export function listProtocols(): ProtocolDef[] {
27
+ return Object.values(protocols);
28
+ }
29
+
30
+ export function getProtocolForChain(id: string, chain: string): { protocol: ProtocolDef; addresses: Record<string, string> } | undefined {
31
+ const protocol = protocols[id];
32
+ if (!protocol) return undefined;
33
+ const addresses = protocol.deployments[chain];
34
+ if (!addresses) return undefined;
35
+ return { protocol, addresses };
36
+ }
37
+
38
+ export type { ProtocolDef, ParamDef, AccessDef, ContractDef } from './schema.js';
@@ -0,0 +1,557 @@
1
+ import { parseAbi } from 'viem';
2
+ import type { ProtocolDef } from './schema.js';
3
+
4
+ const lidoAbi = parseAbi([
5
+ 'function totalSupply() view returns (uint256)',
6
+ 'function getTotalPooledEther() view returns (uint256)',
7
+ 'function getTotalShares() view returns (uint256)',
8
+ 'function getBufferedEther() view returns (uint256)',
9
+ 'function isStakingPaused() view returns (bool)',
10
+ 'function getCurrentStakeLimit() view returns (uint256)',
11
+ 'function getStakeLimitFullInfo() view returns (bool isStakingPaused, bool isStakeLimitSet, uint256 currentStakeLimit, uint256 maxStakeLimit, uint256 maxStakeLimitGrowthBlocks, uint256 prevStakeLimit, uint256 prevStakeBlockNumber)',
12
+ 'function getFee() view returns (uint16)',
13
+ 'function getFeeDistribution() view returns (uint16 treasuryFeeBasisPoints, uint16 insuranceFeeBasisPoints, uint16 operatorsFeeBasisPoints)',
14
+ 'function getWithdrawalCredentials() view returns (bytes32)',
15
+ 'function getTreasury() view returns (address)',
16
+ 'function getLidoLocator() view returns (address)',
17
+ 'function getBeaconStat() view returns (uint256 depositedValidators, uint256 beaconValidators, uint256 beaconBalance)',
18
+ ]);
19
+
20
+ const wstEthAbi = parseAbi([
21
+ 'function stEthPerToken() view returns (uint256)',
22
+ 'function tokensPerStEth() view returns (uint256)',
23
+ 'function getStETHByWstETH(uint256 _wstETHAmount) view returns (uint256)',
24
+ 'function getWstETHByStETH(uint256 _stETHAmount) view returns (uint256)',
25
+ 'function stETH() view returns (address)',
26
+ ]);
27
+
28
+ const norAbi = parseAbi([
29
+ 'function getNodeOperatorsCount() view returns (uint256)',
30
+ 'function getActiveNodeOperatorsCount() view returns (uint256)',
31
+ 'function getNodeOperator(uint256 _nodeOperatorId, bool _fullInfo) view returns (bool active, string name, address rewardAddress, uint64 totalVettedValidators, uint64 totalExitedValidators, uint64 totalAddedValidators, uint64 totalDepositedValidators)',
32
+ 'function getNodeOperatorIsActive(uint256 _nodeOperatorId) view returns (bool)',
33
+ 'function getNonce() view returns (uint256)',
34
+ 'function getType() view returns (bytes32)',
35
+ 'function getLocator() view returns (address)',
36
+ ]);
37
+
38
+ const withdrawalQueueAbi = parseAbi([
39
+ 'function isBunkerModeActive() view returns (bool)',
40
+ 'function bunkerModeSinceTimestamp() view returns (uint256)',
41
+ 'function getLastRequestId() view returns (uint256)',
42
+ 'function getLastFinalizedRequestId() view returns (uint256)',
43
+ 'function getLastCheckpointIndex() view returns (uint256)',
44
+ 'function MIN_STETH_WITHDRAWAL_AMOUNT() view returns (uint256)',
45
+ 'function MAX_STETH_WITHDRAWAL_AMOUNT() view returns (uint256)',
46
+ ]);
47
+
48
+ const dsmAbi = parseAbi([
49
+ 'function getOwner() view returns (address)',
50
+ 'function getGuardianQuorum() view returns (uint256)',
51
+ 'function getGuardians() view returns (address[])',
52
+ 'function isGuardian(address addr) view returns (bool)',
53
+ 'function canDeposit(uint256 stakingModuleId) view returns (bool)',
54
+ 'function getPauseIntentValidityPeriodBlocks() view returns (uint256)',
55
+ 'function getMaxOperatorsPerUnvetting() view returns (uint256)',
56
+ 'function getLastDepositBlock() view returns (uint256)',
57
+ ]);
58
+
59
+ const accountingOracleAbi = parseAbi([
60
+ 'function getConsensusContract() view returns (address)',
61
+ 'function getConsensusVersion() view returns (uint256)',
62
+ 'function getLastProcessingRefSlot() view returns (uint256)',
63
+ 'function getConsensusReport() view returns (bytes32 hash, uint256 refSlot, uint256 processingDeadlineTime, bool processingStarted)',
64
+ 'function SECONDS_PER_SLOT() view returns (uint256)',
65
+ 'function GENESIS_TIME() view returns (uint256)',
66
+ ]);
67
+
68
+ const hashConsensusAbi = parseAbi([
69
+ 'function getQuorum() view returns (uint256)',
70
+ 'function getMembers() view returns (address[] addresses, uint256[] lastReportedRefSlots)',
71
+ 'function getFastLaneMembers() view returns (address[] addresses, uint256[] lastReportedRefSlots)',
72
+ 'function getCurrentFrame() view returns (uint256 refSlot, uint256 reportProcessingDeadlineSlot)',
73
+ 'function getChainConfig() view returns (uint256 slotsPerEpoch, uint256 secondsPerSlot, uint256 genesisTime)',
74
+ 'function getFrameConfig() view returns (uint256 initialEpoch, uint256 epochsPerFrame, uint256 fastLaneLengthSlots)',
75
+ 'function getReportProcessor() view returns (address)',
76
+ ]);
77
+
78
+ const locatorAbi = parseAbi([
79
+ 'function lido() view returns (address)',
80
+ 'function accountingOracle() view returns (address)',
81
+ 'function stakingRouter() view returns (address)',
82
+ 'function withdrawalQueue() view returns (address)',
83
+ 'function withdrawalVault() view returns (address)',
84
+ 'function elRewardsVault() view returns (address)',
85
+ 'function treasury() view returns (address)',
86
+ 'function oracleReportSanityChecker() view returns (address)',
87
+ 'function burner() view returns (address)',
88
+ 'function validatorsExitBusOracle() view returns (address)',
89
+ 'function depositSecurityModule() view returns (address)',
90
+ 'function oracleDaemonConfig() view returns (address)',
91
+ 'function postTokenRebaseReceiver() view returns (address)',
92
+ ]);
93
+
94
+ const votingAbi = parseAbi([
95
+ 'function supportRequiredPct() view returns (uint64)',
96
+ 'function minAcceptQuorumPct() view returns (uint64)',
97
+ 'function voteTime() view returns (uint64)',
98
+ 'function objectionPhaseTime() view returns (uint64)',
99
+ 'function votesLength() view returns (uint256)',
100
+ ]);
101
+
102
+ const agentAbi = parseAbi([
103
+ 'function kernel() view returns (address)',
104
+ 'function isForwarder() view returns (bool)',
105
+ ]);
106
+
107
+ export const lido: ProtocolDef = {
108
+ id: 'lido',
109
+ name: 'Lido',
110
+ version: '2.0',
111
+ website: 'https://lido.fi',
112
+ description: 'Liquid staking protocol for Ethereum — stake ETH, receive rebasing stETH',
113
+
114
+ contracts: {
115
+ Lido: { name: 'Lido (stETH)', role: 'Core staking pool + rebasing stETH token — accepts ETH deposits, mints stETH, tracks total pooled ether', type: 'proxy' },
116
+ WstETH: { name: 'wstETH', role: 'Non-rebasing wrapped stETH — fixed balance, accrues value via exchange rate', type: 'raw' },
117
+ NodeOperatorsRegistry: { name: 'NodeOperatorsRegistry', role: 'Curated staking module — manages node operators and validator signing keys', type: 'proxy' },
118
+ WithdrawalQueue: { name: 'WithdrawalQueueERC721', role: 'stETH withdrawal request queue — NFT claim tickets, bunker mode tracking', type: 'proxy' },
119
+ DepositSecurityModule: { name: 'DepositSecurityModule', role: 'Guardian committee gate for validator deposits — prevents deposit front-running/DoS', type: 'raw' },
120
+ AccountingOracle: { name: 'AccountingOracle', role: 'Reports consensus-layer state (balances, exited validators) each epoch, drives stETH rebase', type: 'proxy' },
121
+ HashConsensus: { name: 'HashConsensus (Accounting)', role: 'Oracle committee consensus module — quorum + member management for AccountingOracle reports', type: 'raw' },
122
+ LidoLocator: { name: 'LidoLocator', role: 'DAO-controlled pointer registry for every core protocol component — repointing it repoints the whole protocol', type: 'proxy' },
123
+ Voting: { name: 'Aragon Voting', role: 'Lido DAO governance — token-weighted voting, approves everything Agent executes', type: 'proxy' },
124
+ Agent: { name: 'Aragon Agent', role: 'DAO treasury + privileged executor — holds funds, executes DAO-approved calls against any contract', type: 'proxy' },
125
+ },
126
+
127
+ deployments: {
128
+ ethereum: {
129
+ Lido: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84',
130
+ WstETH: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0',
131
+ NodeOperatorsRegistry: '0x55032650b14df07b85bF18A3a3eC8E0Af2e028d5',
132
+ WithdrawalQueue: '0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1',
133
+ DepositSecurityModule: '0xC77F8768774E1c9244BEed705C4354f2113CFc09',
134
+ AccountingOracle: '0x852deD011285fe67063a08005c71a85690503Cee',
135
+ HashConsensus: '0xD624B08C83bAECF0807Dd2c6880C3154a5F0B288',
136
+ LidoLocator: '0xC1d0b3DE6792Bf6b4b37EccdcC24e45978Cfd2Eb',
137
+ Voting: '0x2e59A20f205bB85a89C53f1936454680651E618e',
138
+ Agent: '0x3e40D73EB977Dc6a537aF587D48316feE66E9C8c',
139
+ },
140
+ },
141
+
142
+ params: [
143
+ // -- core pool state --
144
+ {
145
+ name: 'totalPooledEther',
146
+ description: 'Total ETH controlled by the protocol (buffered + beacon chain + EL rewards)',
147
+ contract: 'Lido',
148
+ functionName: 'getTotalPooledEther',
149
+ abi: lidoAbi,
150
+ decode: 'uint',
151
+ category: 'state',
152
+ severity: 'info',
153
+ },
154
+ {
155
+ name: 'totalShares',
156
+ description: 'Total stETH shares outstanding — denominator used to compute every account balance via rebase',
157
+ contract: 'Lido',
158
+ functionName: 'getTotalShares',
159
+ abi: lidoAbi,
160
+ decode: 'uint',
161
+ category: 'state',
162
+ severity: 'info',
163
+ },
164
+ {
165
+ name: 'bufferedEther',
166
+ description: 'Unstaked ETH sitting in the Lido contract awaiting deposit to validators',
167
+ contract: 'Lido',
168
+ functionName: 'getBufferedEther',
169
+ abi: lidoAbi,
170
+ decode: 'uint',
171
+ category: 'state',
172
+ severity: 'info',
173
+ },
174
+ {
175
+ name: 'isStakingPaused',
176
+ description: 'Whether new ETH deposits into Lido are paused',
177
+ contract: 'Lido',
178
+ functionName: 'isStakingPaused',
179
+ abi: lidoAbi,
180
+ decode: 'bool',
181
+ category: 'state',
182
+ severity: 'critical',
183
+ },
184
+ {
185
+ name: 'currentStakeLimit',
186
+ description: 'Remaining ETH that can be staked right now before hitting the anti-frontrun stake rate limit',
187
+ contract: 'Lido',
188
+ functionName: 'getCurrentStakeLimit',
189
+ abi: lidoAbi,
190
+ decode: 'uint',
191
+ category: 'risk',
192
+ severity: 'warning',
193
+ },
194
+
195
+ // -- fees --
196
+ {
197
+ name: 'protocolFee',
198
+ description: 'Legacy total protocol fee taken from staking rewards (treasury + node operators), in bps',
199
+ contract: 'Lido',
200
+ functionName: 'getFee',
201
+ abi: lidoAbi,
202
+ decode: 'bps',
203
+ category: 'config',
204
+ severity: 'warning',
205
+ },
206
+ {
207
+ name: 'feeDistribution',
208
+ description: 'Legacy fee split between treasury, insurance and node operators (bps each) — module-level splits now live on StakingRouter',
209
+ contract: 'Lido',
210
+ functionName: 'getFeeDistribution',
211
+ abi: lidoAbi,
212
+ category: 'config',
213
+ severity: 'info',
214
+ },
215
+
216
+ // -- withdrawal credentials / treasury / locator pointer --
217
+ {
218
+ name: 'withdrawalCredentials',
219
+ description: 'Withdrawal credentials validators are deposited with — controls where exited/skimmed ETH lands',
220
+ contract: 'Lido',
221
+ functionName: 'getWithdrawalCredentials',
222
+ abi: lidoAbi,
223
+ decode: 'string',
224
+ category: 'config',
225
+ severity: 'critical',
226
+ },
227
+ {
228
+ name: 'treasury',
229
+ description: 'Address receiving the treasury share of staking rewards (Aragon Agent)',
230
+ contract: 'Lido',
231
+ functionName: 'getTreasury',
232
+ abi: lidoAbi,
233
+ decode: 'address',
234
+ category: 'access',
235
+ severity: 'critical',
236
+ },
237
+ {
238
+ name: 'lidoLocator',
239
+ description: 'LidoLocator address the core contract resolves every peer component through',
240
+ contract: 'Lido',
241
+ functionName: 'getLidoLocator',
242
+ abi: lidoAbi,
243
+ decode: 'address',
244
+ category: 'config',
245
+ severity: 'info',
246
+ },
247
+
248
+ // -- wstETH --
249
+ {
250
+ name: 'wstEthExchangeRate',
251
+ description: 'stETH redeemable for 1 wstETH — reflects accumulated staking rewards since wrap',
252
+ contract: 'WstETH',
253
+ functionName: 'stEthPerToken',
254
+ abi: wstEthAbi,
255
+ decode: 'wad',
256
+ category: 'rate',
257
+ severity: 'info',
258
+ },
259
+
260
+ // -- node operators --
261
+ {
262
+ name: 'nodeOperatorsCount',
263
+ description: 'Total registered node operators in the curated staking module',
264
+ contract: 'NodeOperatorsRegistry',
265
+ functionName: 'getNodeOperatorsCount',
266
+ abi: norAbi,
267
+ decode: 'uint',
268
+ category: 'state',
269
+ severity: 'info',
270
+ },
271
+ {
272
+ name: 'activeNodeOperatorsCount',
273
+ description: 'Node operators currently active and eligible for new deposits',
274
+ contract: 'NodeOperatorsRegistry',
275
+ functionName: 'getActiveNodeOperatorsCount',
276
+ abi: norAbi,
277
+ decode: 'uint',
278
+ category: 'state',
279
+ severity: 'info',
280
+ },
281
+ {
282
+ name: 'norNonce',
283
+ description: 'Increments on any change to operator keys/limits — used to detect stale/front-run deposit data',
284
+ contract: 'NodeOperatorsRegistry',
285
+ functionName: 'getNonce',
286
+ abi: norAbi,
287
+ decode: 'uint',
288
+ category: 'state',
289
+ severity: 'info',
290
+ },
291
+
292
+ // -- withdrawal queue --
293
+ {
294
+ name: 'isBunkerModeActive',
295
+ description: 'Whether the withdrawal queue is in bunker mode (CL-level slashing/penalty event) — changes stETH:ETH withdrawal accounting',
296
+ contract: 'WithdrawalQueue',
297
+ functionName: 'isBunkerModeActive',
298
+ abi: withdrawalQueueAbi,
299
+ decode: 'bool',
300
+ category: 'risk',
301
+ severity: 'critical',
302
+ },
303
+ {
304
+ name: 'bunkerModeSinceTimestamp',
305
+ description: 'Timestamp bunker mode was activated',
306
+ contract: 'WithdrawalQueue',
307
+ functionName: 'bunkerModeSinceTimestamp',
308
+ abi: withdrawalQueueAbi,
309
+ decode: 'uint',
310
+ category: 'state',
311
+ severity: 'warning',
312
+ },
313
+ {
314
+ name: 'lastRequestId',
315
+ description: 'Highest withdrawal request id issued',
316
+ contract: 'WithdrawalQueue',
317
+ functionName: 'getLastRequestId',
318
+ abi: withdrawalQueueAbi,
319
+ decode: 'uint',
320
+ category: 'state',
321
+ severity: 'info',
322
+ },
323
+ {
324
+ name: 'lastFinalizedRequestId',
325
+ description: 'Highest withdrawal request id finalized and claimable — gap vs lastRequestId is the unfinalized withdrawal backlog',
326
+ contract: 'WithdrawalQueue',
327
+ functionName: 'getLastFinalizedRequestId',
328
+ abi: withdrawalQueueAbi,
329
+ decode: 'uint',
330
+ category: 'state',
331
+ severity: 'warning',
332
+ },
333
+ {
334
+ name: 'minWithdrawalAmount',
335
+ description: 'Minimum stETH amount per withdrawal request',
336
+ contract: 'WithdrawalQueue',
337
+ functionName: 'MIN_STETH_WITHDRAWAL_AMOUNT',
338
+ abi: withdrawalQueueAbi,
339
+ decode: 'uint',
340
+ category: 'config',
341
+ severity: 'info',
342
+ },
343
+ {
344
+ name: 'maxWithdrawalAmount',
345
+ description: 'Maximum stETH amount per withdrawal request',
346
+ contract: 'WithdrawalQueue',
347
+ functionName: 'MAX_STETH_WITHDRAWAL_AMOUNT',
348
+ abi: withdrawalQueueAbi,
349
+ decode: 'uint',
350
+ category: 'config',
351
+ severity: 'info',
352
+ },
353
+
354
+ // -- deposit security module (guardian committee) --
355
+ {
356
+ name: 'guardianQuorum',
357
+ description: 'Guardian signatures required for DepositSecurityModule to authorize a deposit or emergency pause',
358
+ contract: 'DepositSecurityModule',
359
+ functionName: 'getGuardianQuorum',
360
+ abi: dsmAbi,
361
+ decode: 'uint',
362
+ category: 'access',
363
+ severity: 'critical',
364
+ },
365
+ {
366
+ name: 'dsmOwner',
367
+ description: 'Can add/remove guardians and change the quorum — controls who gates validator deposits',
368
+ contract: 'DepositSecurityModule',
369
+ functionName: 'getOwner',
370
+ abi: dsmAbi,
371
+ decode: 'address',
372
+ category: 'access',
373
+ severity: 'critical',
374
+ },
375
+ {
376
+ name: 'pauseIntentValidityPeriodBlocks',
377
+ description: 'Block window during which a guardian pause signature remains valid',
378
+ contract: 'DepositSecurityModule',
379
+ functionName: 'getPauseIntentValidityPeriodBlocks',
380
+ abi: dsmAbi,
381
+ decode: 'uint',
382
+ category: 'config',
383
+ severity: 'info',
384
+ },
385
+ {
386
+ name: 'maxOperatorsPerUnvetting',
387
+ description: 'Max node operators guardians can unvet keys for in a single report',
388
+ contract: 'DepositSecurityModule',
389
+ functionName: 'getMaxOperatorsPerUnvetting',
390
+ abi: dsmAbi,
391
+ decode: 'uint',
392
+ category: 'config',
393
+ severity: 'info',
394
+ },
395
+
396
+ // -- accounting oracle / consensus --
397
+ {
398
+ name: 'accountingConsensusVersion',
399
+ description: 'Report schema version AccountingOracle expects from HashConsensus reports — bumping it invalidates old reporter software',
400
+ contract: 'AccountingOracle',
401
+ functionName: 'getConsensusVersion',
402
+ abi: accountingOracleAbi,
403
+ decode: 'uint',
404
+ category: 'config',
405
+ severity: 'info',
406
+ },
407
+ {
408
+ name: 'accountingLastProcessingRefSlot',
409
+ description: 'Last beacon chain reference slot processed by AccountingOracle — staleness indicator for the whole rebase pipeline',
410
+ contract: 'AccountingOracle',
411
+ functionName: 'getLastProcessingRefSlot',
412
+ abi: accountingOracleAbi,
413
+ decode: 'uint',
414
+ category: 'state',
415
+ severity: 'warning',
416
+ },
417
+ {
418
+ name: 'oracleQuorum',
419
+ description: 'Oracle committee members required to agree before an AccountingOracle report is considered final — directly controls the reported stETH rebase',
420
+ contract: 'HashConsensus',
421
+ functionName: 'getQuorum',
422
+ abi: hashConsensusAbi,
423
+ decode: 'uint',
424
+ category: 'access',
425
+ severity: 'critical',
426
+ },
427
+
428
+ // -- LidoLocator pointer registry (god-pointer) --
429
+ {
430
+ name: 'locatorLido',
431
+ description: 'Lido core contract the locator resolves to — every module trusts this pointer',
432
+ contract: 'LidoLocator',
433
+ functionName: 'lido',
434
+ abi: locatorAbi,
435
+ decode: 'address',
436
+ category: 'access',
437
+ severity: 'critical',
438
+ },
439
+ {
440
+ name: 'locatorAccountingOracle',
441
+ description: 'AccountingOracle the locator resolves to — repointing it swaps out the entire rebase reporting pipeline',
442
+ contract: 'LidoLocator',
443
+ functionName: 'accountingOracle',
444
+ abi: locatorAbi,
445
+ decode: 'address',
446
+ category: 'access',
447
+ severity: 'critical',
448
+ },
449
+ {
450
+ name: 'locatorStakingRouter',
451
+ description: 'StakingRouter the locator resolves to — owns module registration and per-module fee routing',
452
+ contract: 'LidoLocator',
453
+ functionName: 'stakingRouter',
454
+ abi: locatorAbi,
455
+ decode: 'address',
456
+ category: 'access',
457
+ severity: 'critical',
458
+ },
459
+ {
460
+ name: 'locatorDepositSecurityModule',
461
+ description: 'DepositSecurityModule the locator resolves to — repointing it swaps out the guardian committee gating deposits',
462
+ contract: 'LidoLocator',
463
+ functionName: 'depositSecurityModule',
464
+ abi: locatorAbi,
465
+ decode: 'address',
466
+ category: 'access',
467
+ severity: 'critical',
468
+ },
469
+ {
470
+ name: 'locatorTreasury',
471
+ description: 'Treasury address the locator resolves to — where protocol fee revenue ultimately flows',
472
+ contract: 'LidoLocator',
473
+ functionName: 'treasury',
474
+ abi: locatorAbi,
475
+ decode: 'address',
476
+ category: 'access',
477
+ severity: 'critical',
478
+ },
479
+
480
+ // -- DAO governance parameters --
481
+ {
482
+ name: 'votingSupportRequiredPct',
483
+ description: 'Percentage of yes-votes required for a DAO vote to pass',
484
+ contract: 'Voting',
485
+ functionName: 'supportRequiredPct',
486
+ abi: votingAbi,
487
+ decode: 'percent',
488
+ category: 'access',
489
+ severity: 'critical',
490
+ },
491
+ {
492
+ name: 'votingMinAcceptQuorumPct',
493
+ description: 'Minimum participation quorum required for a DAO vote to be valid',
494
+ contract: 'Voting',
495
+ functionName: 'minAcceptQuorumPct',
496
+ abi: votingAbi,
497
+ decode: 'percent',
498
+ category: 'access',
499
+ severity: 'critical',
500
+ },
501
+ {
502
+ name: 'voteTime',
503
+ description: 'Duration a vote remains open before it can be executed',
504
+ contract: 'Voting',
505
+ functionName: 'voteTime',
506
+ abi: votingAbi,
507
+ decode: 'uint',
508
+ category: 'config',
509
+ severity: 'info',
510
+ },
511
+ {
512
+ name: 'objectionPhaseTime',
513
+ description: 'Trailing window of a vote during which only "no" votes are accepted — guards against last-block vote flipping',
514
+ contract: 'Voting',
515
+ functionName: 'objectionPhaseTime',
516
+ abi: votingAbi,
517
+ decode: 'uint',
518
+ category: 'config',
519
+ severity: 'warning',
520
+ },
521
+ ],
522
+
523
+ access: [
524
+ {
525
+ role: 'Aragon Voting',
526
+ description: 'Token-weighted DAO vote — required to trigger any Agent-executed protocol change (fees, oracle members, guardians, LidoLocator updates)',
527
+ contract: 'Voting',
528
+ functionName: 'supportRequiredPct',
529
+ abi: votingAbi,
530
+ severity: 'critical',
531
+ },
532
+ {
533
+ role: 'Aragon Agent (Treasury/Executor)',
534
+ description: 'Executes DAO-approved calls against every contract resolved via LidoLocator — holds treasury funds and is the ultimate admin of the protocol',
535
+ contract: 'Agent',
536
+ functionName: 'kernel',
537
+ abi: agentAbi,
538
+ severity: 'critical',
539
+ },
540
+ {
541
+ role: 'DepositSecurityModule Owner',
542
+ description: 'Can add/remove guardians and change the guardian quorum — controls who gates validator deposits and emergency pauses',
543
+ contract: 'DepositSecurityModule',
544
+ functionName: 'getOwner',
545
+ abi: dsmAbi,
546
+ severity: 'critical',
547
+ },
548
+ {
549
+ role: 'Oracle Committee (HashConsensus)',
550
+ description: 'Guardian-style committee whose quorum determines the accepted consensus-layer report — directly drives the stETH rebase rate every day',
551
+ contract: 'HashConsensus',
552
+ functionName: 'getQuorum',
553
+ abi: hashConsensusAbi,
554
+ severity: 'critical',
555
+ },
556
+ ],
557
+ };