csm-dashboard 0.3.6__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- {csm_dashboard-0.3.6.dist-info → csm_dashboard-0.4.0.dist-info}/METADATA +2 -1
- csm_dashboard-0.4.0.dist-info/RECORD +35 -0
- src/cli/commands.py +8 -0
- src/core/config.py +7 -0
- src/core/types.py +3 -6
- src/data/beacon.py +23 -9
- src/data/cache.py +53 -8
- src/data/database.py +189 -0
- src/data/etherscan.py +33 -7
- src/data/ipfs_logs.py +29 -20
- src/data/lido_api.py +38 -12
- src/data/onchain.py +111 -58
- src/data/price.py +46 -0
- src/data/rewards_tree.py +18 -3
- src/data/strikes.py +35 -13
- src/main.py +12 -0
- src/services/operator_service.py +76 -52
- src/web/app.py +794 -72
- src/web/routes.py +372 -0
- csm_dashboard-0.3.6.dist-info/RECORD +0 -33
- {csm_dashboard-0.3.6.dist-info → csm_dashboard-0.4.0.dist-info}/WHEEL +0 -0
- {csm_dashboard-0.3.6.dist-info → csm_dashboard-0.4.0.dist-info}/entry_points.txt +0 -0
src/web/routes.py
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
"""API endpoints for the web interface."""
|
|
2
2
|
|
|
3
|
+
import logging
|
|
4
|
+
|
|
3
5
|
from fastapi import APIRouter, HTTPException, Query
|
|
4
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
from ..data.database import (
|
|
10
|
+
delete_operator,
|
|
11
|
+
get_saved_operators,
|
|
12
|
+
is_operator_saved,
|
|
13
|
+
save_operator,
|
|
14
|
+
update_operator_data,
|
|
15
|
+
)
|
|
16
|
+
from ..data.price import get_eth_price
|
|
5
17
|
from ..services.operator_service import OperatorService
|
|
6
18
|
|
|
7
19
|
router = APIRouter()
|
|
@@ -23,6 +35,7 @@ async def get_operator(
|
|
|
23
35
|
- Add ?history=true to include all historical distribution frames
|
|
24
36
|
- Add ?withdrawals=true to include withdrawal/claim history
|
|
25
37
|
"""
|
|
38
|
+
logger.info(f"Get operator: {identifier}, detailed={detailed}, history={history}, withdrawals={withdrawals}")
|
|
26
39
|
service = OperatorService()
|
|
27
40
|
|
|
28
41
|
# Determine if this is an ID or address
|
|
@@ -128,6 +141,8 @@ async def get_operator(
|
|
|
128
141
|
"duration_days": f.duration_days,
|
|
129
142
|
"validator_count": f.validator_count,
|
|
130
143
|
"apy": f.apy,
|
|
144
|
+
"bond_apy": f.bond_apy,
|
|
145
|
+
"net_apy": f.net_apy,
|
|
131
146
|
}
|
|
132
147
|
for f in rewards.apy.frames
|
|
133
148
|
]
|
|
@@ -240,7 +255,364 @@ async def get_operator_strikes(identifier: str):
|
|
|
240
255
|
}
|
|
241
256
|
|
|
242
257
|
|
|
258
|
+
@router.get("/saved-operators")
|
|
259
|
+
async def list_saved_operators():
|
|
260
|
+
"""Get all saved operators with their cached data."""
|
|
261
|
+
logger.info("Loading saved operators from database")
|
|
262
|
+
try:
|
|
263
|
+
operators = await get_saved_operators()
|
|
264
|
+
# Log what data each operator has
|
|
265
|
+
for op in operators:
|
|
266
|
+
op_id = op.get("operator_id", "?")
|
|
267
|
+
frames = len(op.get("apy", {}).get("frames", []))
|
|
268
|
+
withdrawals = len(op.get("withdrawals", []))
|
|
269
|
+
logger.info(f" Operator {op_id}: {frames} frames, {withdrawals} withdrawals in cache")
|
|
270
|
+
logger.info(f"Loaded {len(operators)} saved operators")
|
|
271
|
+
return {"operators": operators}
|
|
272
|
+
except Exception as e:
|
|
273
|
+
logger.error(f"Error loading saved operators: {e}", exc_info=True)
|
|
274
|
+
return {"operators": [], "error": str(e)}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@router.post("/operator/{identifier}/save")
|
|
278
|
+
async def save_operator_endpoint(identifier: str):
|
|
279
|
+
"""Save an operator to the follow list.
|
|
280
|
+
|
|
281
|
+
Fetches current operator data (including history and withdrawals) and stores it in the database.
|
|
282
|
+
"""
|
|
283
|
+
logger.info(f"Saving operator: {identifier}")
|
|
284
|
+
service = OperatorService()
|
|
285
|
+
|
|
286
|
+
# Determine operator ID
|
|
287
|
+
if identifier.isdigit():
|
|
288
|
+
operator_id = int(identifier)
|
|
289
|
+
elif identifier.startswith("0x"):
|
|
290
|
+
operator_id = await service.onchain.find_operator_by_address(identifier)
|
|
291
|
+
if operator_id is None:
|
|
292
|
+
raise HTTPException(status_code=404, detail="Operator not found")
|
|
293
|
+
else:
|
|
294
|
+
raise HTTPException(status_code=400, detail="Invalid identifier format")
|
|
295
|
+
|
|
296
|
+
# Fetch current operator data with history and withdrawals
|
|
297
|
+
rewards = await service.get_operator_by_id(
|
|
298
|
+
operator_id,
|
|
299
|
+
include_validators=True,
|
|
300
|
+
include_history=True,
|
|
301
|
+
include_withdrawals=True,
|
|
302
|
+
)
|
|
303
|
+
if rewards is None:
|
|
304
|
+
raise HTTPException(status_code=404, detail="Operator not found")
|
|
305
|
+
|
|
306
|
+
# Build the data dict (same format as get_operator endpoint)
|
|
307
|
+
data = {
|
|
308
|
+
"operator_id": rewards.node_operator_id,
|
|
309
|
+
"manager_address": rewards.manager_address,
|
|
310
|
+
"reward_address": rewards.reward_address,
|
|
311
|
+
"curve_id": rewards.curve_id,
|
|
312
|
+
"operator_type": rewards.operator_type,
|
|
313
|
+
"rewards": {
|
|
314
|
+
"current_bond_eth": str(rewards.current_bond_eth),
|
|
315
|
+
"required_bond_eth": str(rewards.required_bond_eth),
|
|
316
|
+
"excess_bond_eth": str(rewards.excess_bond_eth),
|
|
317
|
+
"cumulative_rewards_shares": rewards.cumulative_rewards_shares,
|
|
318
|
+
"cumulative_rewards_eth": str(rewards.cumulative_rewards_eth),
|
|
319
|
+
"distributed_shares": rewards.distributed_shares,
|
|
320
|
+
"distributed_eth": str(rewards.distributed_eth),
|
|
321
|
+
"unclaimed_shares": rewards.unclaimed_shares,
|
|
322
|
+
"unclaimed_eth": str(rewards.unclaimed_eth),
|
|
323
|
+
"total_claimable_eth": str(rewards.total_claimable_eth),
|
|
324
|
+
},
|
|
325
|
+
"validators": {
|
|
326
|
+
"total": rewards.total_validators,
|
|
327
|
+
"active": rewards.active_validators,
|
|
328
|
+
"exited": rewards.exited_validators,
|
|
329
|
+
},
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if rewards.validators_by_status:
|
|
333
|
+
data["validators"]["by_status"] = rewards.validators_by_status
|
|
334
|
+
|
|
335
|
+
if rewards.avg_effectiveness is not None:
|
|
336
|
+
data["performance"] = {
|
|
337
|
+
"avg_effectiveness": round(rewards.avg_effectiveness, 2),
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if rewards.active_since:
|
|
341
|
+
data["active_since"] = rewards.active_since.isoformat()
|
|
342
|
+
|
|
343
|
+
if rewards.apy:
|
|
344
|
+
data["apy"] = {
|
|
345
|
+
"historical_reward_apy_28d": rewards.apy.historical_reward_apy_28d,
|
|
346
|
+
"historical_reward_apy_ltd": rewards.apy.historical_reward_apy_ltd,
|
|
347
|
+
"bond_apy": rewards.apy.bond_apy,
|
|
348
|
+
"net_apy_28d": rewards.apy.net_apy_28d,
|
|
349
|
+
"net_apy_ltd": rewards.apy.net_apy_ltd,
|
|
350
|
+
"next_distribution_date": rewards.apy.next_distribution_date,
|
|
351
|
+
"next_distribution_est_eth": rewards.apy.next_distribution_est_eth,
|
|
352
|
+
"lifetime_reward_apy": rewards.apy.lifetime_reward_apy,
|
|
353
|
+
"lifetime_bond_apy": rewards.apy.lifetime_bond_apy,
|
|
354
|
+
"lifetime_net_apy": rewards.apy.lifetime_net_apy,
|
|
355
|
+
}
|
|
356
|
+
# Include distribution history frames
|
|
357
|
+
if rewards.apy.frames:
|
|
358
|
+
data["apy"]["frames"] = [
|
|
359
|
+
{
|
|
360
|
+
"frame_number": f.frame_number,
|
|
361
|
+
"start_date": f.start_date,
|
|
362
|
+
"end_date": f.end_date,
|
|
363
|
+
"rewards_eth": f.rewards_eth,
|
|
364
|
+
"rewards_shares": f.rewards_shares,
|
|
365
|
+
"duration_days": f.duration_days,
|
|
366
|
+
"validator_count": f.validator_count,
|
|
367
|
+
"apy": f.apy,
|
|
368
|
+
"bond_apy": f.bond_apy,
|
|
369
|
+
"net_apy": f.net_apy,
|
|
370
|
+
}
|
|
371
|
+
for f in rewards.apy.frames
|
|
372
|
+
]
|
|
373
|
+
|
|
374
|
+
if rewards.health:
|
|
375
|
+
data["health"] = {
|
|
376
|
+
"bond_healthy": rewards.health.bond_healthy,
|
|
377
|
+
"bond_deficit_eth": str(rewards.health.bond_deficit_eth),
|
|
378
|
+
"stuck_validators_count": rewards.health.stuck_validators_count,
|
|
379
|
+
"slashed_validators_count": rewards.health.slashed_validators_count,
|
|
380
|
+
"validators_at_risk_count": rewards.health.validators_at_risk_count,
|
|
381
|
+
"strikes": {
|
|
382
|
+
"total_validators_with_strikes": rewards.health.strikes.total_validators_with_strikes,
|
|
383
|
+
"validators_at_risk": rewards.health.strikes.validators_at_risk,
|
|
384
|
+
"validators_near_ejection": rewards.health.strikes.validators_near_ejection,
|
|
385
|
+
"total_strikes": rewards.health.strikes.total_strikes,
|
|
386
|
+
"max_strikes": rewards.health.strikes.max_strikes,
|
|
387
|
+
"strike_threshold": rewards.health.strikes.strike_threshold,
|
|
388
|
+
},
|
|
389
|
+
"has_issues": rewards.health.has_issues,
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
# Include withdrawal history
|
|
393
|
+
if rewards.withdrawals:
|
|
394
|
+
data["withdrawals"] = [
|
|
395
|
+
{
|
|
396
|
+
"block_number": w.block_number,
|
|
397
|
+
"timestamp": w.timestamp,
|
|
398
|
+
"shares": w.shares,
|
|
399
|
+
"eth_value": w.eth_value,
|
|
400
|
+
"tx_hash": w.tx_hash,
|
|
401
|
+
"withdrawal_type": w.withdrawal_type,
|
|
402
|
+
"request_id": w.request_id,
|
|
403
|
+
"status": w.status,
|
|
404
|
+
"claimed_eth": w.claimed_eth,
|
|
405
|
+
"claim_tx_hash": w.claim_tx_hash,
|
|
406
|
+
"claim_timestamp": w.claim_timestamp,
|
|
407
|
+
}
|
|
408
|
+
for w in rewards.withdrawals
|
|
409
|
+
]
|
|
410
|
+
|
|
411
|
+
# Save to database
|
|
412
|
+
frames_count = len(data.get("apy", {}).get("frames", []))
|
|
413
|
+
withdrawals_count = len(data.get("withdrawals", []))
|
|
414
|
+
logger.info(f"Saving operator {operator_id}: {frames_count} frames, {withdrawals_count} withdrawals")
|
|
415
|
+
await save_operator(operator_id, data)
|
|
416
|
+
|
|
417
|
+
return {"status": "saved", "operator_id": operator_id}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
@router.delete("/operator/{identifier}/save")
|
|
421
|
+
async def unsave_operator_endpoint(identifier: str):
|
|
422
|
+
"""Remove an operator from the follow list."""
|
|
423
|
+
# Determine operator ID
|
|
424
|
+
if identifier.isdigit():
|
|
425
|
+
operator_id = int(identifier)
|
|
426
|
+
elif identifier.startswith("0x"):
|
|
427
|
+
service = OperatorService()
|
|
428
|
+
operator_id = await service.onchain.find_operator_by_address(identifier)
|
|
429
|
+
if operator_id is None:
|
|
430
|
+
raise HTTPException(status_code=404, detail="Operator not found")
|
|
431
|
+
else:
|
|
432
|
+
raise HTTPException(status_code=400, detail="Invalid identifier format")
|
|
433
|
+
|
|
434
|
+
deleted = await delete_operator(operator_id)
|
|
435
|
+
if not deleted:
|
|
436
|
+
raise HTTPException(status_code=404, detail="Operator not in saved list")
|
|
437
|
+
|
|
438
|
+
return {"status": "removed", "operator_id": operator_id}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@router.get("/operator/{identifier}/saved")
|
|
442
|
+
async def check_operator_saved(identifier: str):
|
|
443
|
+
"""Check if an operator is in the saved list."""
|
|
444
|
+
if identifier.isdigit():
|
|
445
|
+
operator_id = int(identifier)
|
|
446
|
+
elif identifier.startswith("0x"):
|
|
447
|
+
service = OperatorService()
|
|
448
|
+
operator_id = await service.onchain.find_operator_by_address(identifier)
|
|
449
|
+
if operator_id is None:
|
|
450
|
+
return {"saved": False}
|
|
451
|
+
else:
|
|
452
|
+
raise HTTPException(status_code=400, detail="Invalid identifier format")
|
|
453
|
+
|
|
454
|
+
saved = await is_operator_saved(operator_id)
|
|
455
|
+
return {"saved": saved, "operator_id": operator_id}
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
@router.post("/operator/{identifier}/refresh")
|
|
459
|
+
async def refresh_operator_endpoint(identifier: str):
|
|
460
|
+
"""Refresh the cached data for a saved operator.
|
|
461
|
+
|
|
462
|
+
Fetches fresh data (including history and withdrawals) from APIs and updates the database.
|
|
463
|
+
"""
|
|
464
|
+
logger.info(f"Refreshing operator: {identifier}")
|
|
465
|
+
service = OperatorService()
|
|
466
|
+
|
|
467
|
+
# Determine operator ID
|
|
468
|
+
if identifier.isdigit():
|
|
469
|
+
operator_id = int(identifier)
|
|
470
|
+
elif identifier.startswith("0x"):
|
|
471
|
+
operator_id = await service.onchain.find_operator_by_address(identifier)
|
|
472
|
+
if operator_id is None:
|
|
473
|
+
raise HTTPException(status_code=404, detail="Operator not found")
|
|
474
|
+
else:
|
|
475
|
+
raise HTTPException(status_code=400, detail="Invalid identifier format")
|
|
476
|
+
|
|
477
|
+
# Check if operator is saved
|
|
478
|
+
if not await is_operator_saved(operator_id):
|
|
479
|
+
raise HTTPException(status_code=404, detail="Operator not in saved list")
|
|
480
|
+
|
|
481
|
+
# Fetch fresh data with history and withdrawals
|
|
482
|
+
rewards = await service.get_operator_by_id(
|
|
483
|
+
operator_id,
|
|
484
|
+
include_validators=True,
|
|
485
|
+
include_history=True,
|
|
486
|
+
include_withdrawals=True,
|
|
487
|
+
)
|
|
488
|
+
if rewards is None:
|
|
489
|
+
raise HTTPException(status_code=404, detail="Operator not found")
|
|
490
|
+
|
|
491
|
+
# Build the data dict
|
|
492
|
+
data = {
|
|
493
|
+
"operator_id": rewards.node_operator_id,
|
|
494
|
+
"manager_address": rewards.manager_address,
|
|
495
|
+
"reward_address": rewards.reward_address,
|
|
496
|
+
"curve_id": rewards.curve_id,
|
|
497
|
+
"operator_type": rewards.operator_type,
|
|
498
|
+
"rewards": {
|
|
499
|
+
"current_bond_eth": str(rewards.current_bond_eth),
|
|
500
|
+
"required_bond_eth": str(rewards.required_bond_eth),
|
|
501
|
+
"excess_bond_eth": str(rewards.excess_bond_eth),
|
|
502
|
+
"cumulative_rewards_shares": rewards.cumulative_rewards_shares,
|
|
503
|
+
"cumulative_rewards_eth": str(rewards.cumulative_rewards_eth),
|
|
504
|
+
"distributed_shares": rewards.distributed_shares,
|
|
505
|
+
"distributed_eth": str(rewards.distributed_eth),
|
|
506
|
+
"unclaimed_shares": rewards.unclaimed_shares,
|
|
507
|
+
"unclaimed_eth": str(rewards.unclaimed_eth),
|
|
508
|
+
"total_claimable_eth": str(rewards.total_claimable_eth),
|
|
509
|
+
},
|
|
510
|
+
"validators": {
|
|
511
|
+
"total": rewards.total_validators,
|
|
512
|
+
"active": rewards.active_validators,
|
|
513
|
+
"exited": rewards.exited_validators,
|
|
514
|
+
},
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if rewards.validators_by_status:
|
|
518
|
+
data["validators"]["by_status"] = rewards.validators_by_status
|
|
519
|
+
|
|
520
|
+
if rewards.avg_effectiveness is not None:
|
|
521
|
+
data["performance"] = {
|
|
522
|
+
"avg_effectiveness": round(rewards.avg_effectiveness, 2),
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if rewards.active_since:
|
|
526
|
+
data["active_since"] = rewards.active_since.isoformat()
|
|
527
|
+
|
|
528
|
+
if rewards.apy:
|
|
529
|
+
data["apy"] = {
|
|
530
|
+
"historical_reward_apy_28d": rewards.apy.historical_reward_apy_28d,
|
|
531
|
+
"historical_reward_apy_ltd": rewards.apy.historical_reward_apy_ltd,
|
|
532
|
+
"bond_apy": rewards.apy.bond_apy,
|
|
533
|
+
"net_apy_28d": rewards.apy.net_apy_28d,
|
|
534
|
+
"net_apy_ltd": rewards.apy.net_apy_ltd,
|
|
535
|
+
"next_distribution_date": rewards.apy.next_distribution_date,
|
|
536
|
+
"next_distribution_est_eth": rewards.apy.next_distribution_est_eth,
|
|
537
|
+
"lifetime_reward_apy": rewards.apy.lifetime_reward_apy,
|
|
538
|
+
"lifetime_bond_apy": rewards.apy.lifetime_bond_apy,
|
|
539
|
+
"lifetime_net_apy": rewards.apy.lifetime_net_apy,
|
|
540
|
+
}
|
|
541
|
+
# Include distribution history frames
|
|
542
|
+
if rewards.apy.frames:
|
|
543
|
+
data["apy"]["frames"] = [
|
|
544
|
+
{
|
|
545
|
+
"frame_number": f.frame_number,
|
|
546
|
+
"start_date": f.start_date,
|
|
547
|
+
"end_date": f.end_date,
|
|
548
|
+
"rewards_eth": f.rewards_eth,
|
|
549
|
+
"rewards_shares": f.rewards_shares,
|
|
550
|
+
"duration_days": f.duration_days,
|
|
551
|
+
"validator_count": f.validator_count,
|
|
552
|
+
"apy": f.apy,
|
|
553
|
+
"bond_apy": f.bond_apy,
|
|
554
|
+
"net_apy": f.net_apy,
|
|
555
|
+
}
|
|
556
|
+
for f in rewards.apy.frames
|
|
557
|
+
]
|
|
558
|
+
|
|
559
|
+
if rewards.health:
|
|
560
|
+
data["health"] = {
|
|
561
|
+
"bond_healthy": rewards.health.bond_healthy,
|
|
562
|
+
"bond_deficit_eth": str(rewards.health.bond_deficit_eth),
|
|
563
|
+
"stuck_validators_count": rewards.health.stuck_validators_count,
|
|
564
|
+
"slashed_validators_count": rewards.health.slashed_validators_count,
|
|
565
|
+
"validators_at_risk_count": rewards.health.validators_at_risk_count,
|
|
566
|
+
"strikes": {
|
|
567
|
+
"total_validators_with_strikes": rewards.health.strikes.total_validators_with_strikes,
|
|
568
|
+
"validators_at_risk": rewards.health.strikes.validators_at_risk,
|
|
569
|
+
"validators_near_ejection": rewards.health.strikes.validators_near_ejection,
|
|
570
|
+
"total_strikes": rewards.health.strikes.total_strikes,
|
|
571
|
+
"max_strikes": rewards.health.strikes.max_strikes,
|
|
572
|
+
"strike_threshold": rewards.health.strikes.strike_threshold,
|
|
573
|
+
},
|
|
574
|
+
"has_issues": rewards.health.has_issues,
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# Include withdrawal history
|
|
578
|
+
if rewards.withdrawals:
|
|
579
|
+
data["withdrawals"] = [
|
|
580
|
+
{
|
|
581
|
+
"block_number": w.block_number,
|
|
582
|
+
"timestamp": w.timestamp,
|
|
583
|
+
"shares": w.shares,
|
|
584
|
+
"eth_value": w.eth_value,
|
|
585
|
+
"tx_hash": w.tx_hash,
|
|
586
|
+
"withdrawal_type": w.withdrawal_type,
|
|
587
|
+
"request_id": w.request_id,
|
|
588
|
+
"status": w.status,
|
|
589
|
+
"claimed_eth": w.claimed_eth,
|
|
590
|
+
"claim_tx_hash": w.claim_tx_hash,
|
|
591
|
+
"claim_timestamp": w.claim_timestamp,
|
|
592
|
+
}
|
|
593
|
+
for w in rewards.withdrawals
|
|
594
|
+
]
|
|
595
|
+
|
|
596
|
+
# Update in database
|
|
597
|
+
frames_count = len(data.get("apy", {}).get("frames", []))
|
|
598
|
+
withdrawals_count = len(data.get("withdrawals", []))
|
|
599
|
+
logger.info(f"Refreshing operator {operator_id}: {frames_count} frames, {withdrawals_count} withdrawals")
|
|
600
|
+
await update_operator_data(operator_id, data)
|
|
601
|
+
|
|
602
|
+
return {"status": "refreshed", "operator_id": operator_id, "data": data}
|
|
603
|
+
|
|
604
|
+
|
|
243
605
|
@router.get("/health")
|
|
244
606
|
async def health_check():
|
|
245
607
|
"""Health check endpoint."""
|
|
608
|
+
logger.debug("Health check called")
|
|
246
609
|
return {"status": "healthy"}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
@router.get("/price/eth")
|
|
613
|
+
async def get_eth_price_endpoint():
|
|
614
|
+
"""Get current ETH price in USD from CoinGecko."""
|
|
615
|
+
price = await get_eth_price()
|
|
616
|
+
if price is None:
|
|
617
|
+
return {"price": None, "error": "Failed to fetch price"}
|
|
618
|
+
return {"price": price, "currency": "USD"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
src/__init__.py,sha256=Bfqpjo9Q1XaV8DNqkf1sADzWg2ACpfZWSGLtahZE3iU,35
|
|
2
|
-
src/main.py,sha256=tj7C09FVBGBVyjKYwmElpX5M92xfydDm8RJ6-MSFdMk,951
|
|
3
|
-
src/abis/CSAccounting.json,sha256=-eBMqw3XqgMDzlVrG8mOrd7IYLf8nfsBIpjYqaKPYno,1281
|
|
4
|
-
src/abis/CSFeeDistributor.json,sha256=unLBacJcCHq4xsmB4xOPlVXcOrxGWNf6KDmC3Ld5u-c,1517
|
|
5
|
-
src/abis/CSModule.json,sha256=T6D6aInBoqVH3ZD6U6p3lrPa3t_ucA9V83IwE80kOuU,1687
|
|
6
|
-
src/abis/WithdrawalQueueERC721.json,sha256=b25a5RQW5HGPH_KQcolecVToHTgYwqeikz0ZEZ9MGxU,1424
|
|
7
|
-
src/abis/__init__.py,sha256=9HV2hKMGSoNAi8evsjzymTr4V4kowITNsX1-LPu6l98,20
|
|
8
|
-
src/abis/stETH.json,sha256=ldxbIRrtt8ePVFewJ9Tnz4qUGFmuOXa41GN1t3tnWEg,1106
|
|
9
|
-
src/cli/__init__.py,sha256=mgHAwomqzAhOHJnlWrWtCguhGzhDWlkCvkzKsfoFsds,35
|
|
10
|
-
src/cli/commands.py,sha256=JNbn74H3nbwk5IT515OB4Muw-edCG9m7imub16geEyA,37506
|
|
11
|
-
src/core/__init__.py,sha256=ZDHZojANK1ZFpn5lQROERTo97MYQFAxqA9APvs7ruEQ,57
|
|
12
|
-
src/core/config.py,sha256=KYYlJv383RtHo_CpaCt2DM7yaTr3C267cY4T8_ZRiEc,1613
|
|
13
|
-
src/core/contracts.py,sha256=u1KW0z-9V21Zpf7qxGBvRy2Ffdo4YfGusUdT4K_ggP4,582
|
|
14
|
-
src/core/types.py,sha256=pirEDIR6csbGWWUH2s3RDCG2ce-oqgEvdsamAI-rurM,7807
|
|
15
|
-
src/data/__init__.py,sha256=DItA8aEp8Lbr0uFlJVppMaTtVEEznoA1hkRpH-vfHhk,57
|
|
16
|
-
src/data/beacon.py,sha256=jICauXdee4efYj8mj1GIoGsY5_uWmgHB57XhqkXvwS4,15439
|
|
17
|
-
src/data/cache.py,sha256=w1iv4rM-FVgFlaSNYdQA3CfqyhZo9-gqbZwKmzKY0Ps,2134
|
|
18
|
-
src/data/etherscan.py,sha256=JcO6H2dFFZFOV-qCaNhoJvyza2ymd2GhgmJo7vxvTPM,10938
|
|
19
|
-
src/data/ipfs_logs.py,sha256=gXUTP9dmZ_e7gW6ouotJ1r_72ixq2q_32y2evYQf0DM,10709
|
|
20
|
-
src/data/known_cids.py,sha256=onwTTNvAv4h0-3LZgLhqMlKzuNH2VhBqQGZ9fm8GyoE,1705
|
|
21
|
-
src/data/lido_api.py,sha256=477-1vqlMAwLaisaa61T9AxJuSUV6W7NLg1cxvdmheY,4884
|
|
22
|
-
src/data/onchain.py,sha256=2eLGAI0URz1JuNZi2JmLWN-h_UDfkDlbT30rXpoqbts,25252
|
|
23
|
-
src/data/rewards_tree.py,sha256=a-MO14b4COjOvy59FPd0jaf1dkgidlqCQKAFDinwRJU,1894
|
|
24
|
-
src/data/strikes.py,sha256=b68exKntZpzjiEBftiI3AcvEclHp7vg5lsOiG2a7Kzo,9046
|
|
25
|
-
src/services/__init__.py,sha256=MC7blFLAMazErCWuyYXvS6sO3uZm1z_RUOtnlIK0kSo,38
|
|
26
|
-
src/services/operator_service.py,sha256=xncq7Z3DDUcr8cohcehvL9nqaONRUPRuvjBliEGYNd8,29985
|
|
27
|
-
src/web/__init__.py,sha256=iI2c5xxXmzsNxIetm0P2qE3uVsT-ClsMfzn620r5YTU,40
|
|
28
|
-
src/web/app.py,sha256=AvNKmY6mNRDQaHQLDaLmf9LJxtcB0Mc-YG83XyJ7K9g,47944
|
|
29
|
-
src/web/routes.py,sha256=UydCD7DMWBYv_lbQQjeHI82l8gLZkZ2lEy4OayPxNiU,10716
|
|
30
|
-
csm_dashboard-0.3.6.dist-info/METADATA,sha256=7rgyMM56MfOPeTBY4M8TitFJOPMT70SuxqHMQhxGIwA,12552
|
|
31
|
-
csm_dashboard-0.3.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
32
|
-
csm_dashboard-0.3.6.dist-info/entry_points.txt,sha256=P1Ul8ALIPBwDlVlXqTPuzJ64xxRpIJsYW8U73Tyjgtg,37
|
|
33
|
-
csm_dashboard-0.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|