staking_cli/
info.rs

1use alloy::{
2    primitives::{utils::format_ether, Address},
3    providers::ProviderBuilder,
4};
5use anyhow::{Context as _, Result};
6use espresso_types::{
7    v0_3::{Fetcher, Validator},
8    L1Client,
9};
10use hotshot_contract_adapter::sol_types::StakeTableV2;
11use hotshot_types::signature_key::BLSPubKey;
12use url::Url;
13
14use crate::{output::output_success, parse::Commission};
15
16pub async fn stake_table_info(
17    l1_url: Url,
18    stake_table_address: Address,
19    l1_block_number: u64,
20) -> Result<Vec<Validator<BLSPubKey>>> {
21    let l1 = L1Client::new(vec![l1_url])?;
22    let (validators, _) =
23        Fetcher::fetch_all_validators_from_contract(l1, stake_table_address, l1_block_number)
24            .await?;
25
26    Ok(validators
27        .into_iter()
28        .map(|(_address, validator)| validator)
29        .collect())
30}
31
32pub fn display_stake_table(stake_table: Vec<Validator<BLSPubKey>>, compact: bool) -> Result<()> {
33    let mut stake_table = stake_table.clone();
34    stake_table.sort_by(|a, b| a.stake.cmp(&b.stake));
35
36    for validator in stake_table.iter() {
37        let comm: Commission = validator.commission.try_into()?;
38        let bls_key = validator.stake_table_key.to_string();
39        let key_str = if compact {
40            let end = bls_key.chars().map(|c| c.len_utf8()).take(40).sum();
41            format!("{}..", &bls_key[..end])
42        } else {
43            bls_key.to_string()
44        };
45        output_success(format!(
46            "Validator {}: {key_str} comm={comm} stake={} ESP",
47            validator.account,
48            format_ether(validator.stake),
49        ));
50
51        if validator.delegators.is_empty() {
52            output_success(" - No delegators");
53            continue;
54        }
55
56        // sort delegators by address for easier reading
57        let mut delegators = validator.delegators.iter().collect::<Vec<_>>();
58        delegators.sort_by(|a, b| a.0.cmp(b.0));
59        for (delegator, stake) in delegators {
60            output_success(format!(
61                " - Delegator {delegator}: stake={} ESP",
62                format_ether(*stake)
63            ));
64        }
65    }
66    Ok(())
67}
68
69pub async fn fetch_token_address(rpc_url: Url, stake_table_address: Address) -> Result<Address> {
70    let provider = ProviderBuilder::new().connect_http(rpc_url);
71    StakeTableV2::new(stake_table_address, provider)
72        .token()
73        .call()
74        .await
75        .with_context(|| {
76            format!(
77                "Failed to fetch token address from stake table contract at {stake_table_address}"
78            )
79        })
80}