Relayer balance metrics (#2976)
Done: - scaffolding for fetching custom agent metrics - abstractions for building a metrics fetcher for a given VM - querying cosmos balances; e2e tested. - querying evm balances; e2e tested. - **Note that as a result, evm addresses are now no longer zero-padded when printed in the logs. This may break existing log queries** - fixed a nasty bug on ubuntu where wasmd (osmosisd dependency, part of the grpc query flow) would panic when a block is specified via `x-cosmos-block-height`. The fix was to bump the version of osmosisd from `19.0.0` to `20.5.0`. **Note that when running e2e on Mac OS, the osmosis version in use is still 19.0.0**. That's because we need a fork that publishes a darwin target binary (currently pointing [here](https://github.com/hashableric/osmosis/releases/download/v19.0.0-mnts/osmosisd-19.0.0-mnts-darwin-arm64.tar.gz)) For follow up PR: - sealevel balance querying I'm open to all renaming suggestions, I just tried to speed through and didn't ponder names too much Relates to https://github.com/hyperlane-xyz/issues/issues/701 Closes https://github.com/hyperlane-xyz/issues/issues/702 (because the balance becomes available in the metrics endpoint for polling)pull/3025/head
parent
2da6ccebe8
commit
77aa58c581
@ -0,0 +1,120 @@ |
|||||||
|
use std::time::Duration; |
||||||
|
|
||||||
|
use derive_builder::Builder; |
||||||
|
use derive_new::new; |
||||||
|
use eyre::Result; |
||||||
|
use hyperlane_core::metrics::agent::u256_as_scaled_f64; |
||||||
|
use hyperlane_core::HyperlaneDomain; |
||||||
|
use hyperlane_core::HyperlaneProvider; |
||||||
|
use maplit::hashmap; |
||||||
|
use prometheus::GaugeVec; |
||||||
|
use tokio::time::MissedTickBehavior; |
||||||
|
use tracing::{trace, warn}; |
||||||
|
|
||||||
|
use crate::CoreMetrics; |
||||||
|
|
||||||
|
/// Expected label names for the `wallet_balance` metric.
|
||||||
|
pub const WALLET_BALANCE_LABELS: &[&str] = &[ |
||||||
|
"chain", |
||||||
|
"wallet_address", |
||||||
|
"wallet_name", |
||||||
|
"token_address", |
||||||
|
"token_symbol", |
||||||
|
"token_name", |
||||||
|
]; |
||||||
|
/// Help string for the metric.
|
||||||
|
pub const WALLET_BALANCE_HELP: &str = |
||||||
|
"Current native token balance for the wallet addresses in the `wallets` set"; |
||||||
|
|
||||||
|
/// Agent-specific metrics
|
||||||
|
#[derive(Clone, Builder)] |
||||||
|
pub struct AgentMetrics { |
||||||
|
/// Current balance of native tokens for the
|
||||||
|
/// wallet address.
|
||||||
|
/// - `chain`: the chain name (or chain ID if the name is unknown) of the
|
||||||
|
/// chain the tx occurred on.
|
||||||
|
/// - `wallet_address`: Address of the wallet holding the funds.
|
||||||
|
/// - `wallet_name`: Name of the address holding the funds.
|
||||||
|
/// - `token_address`: Address of the token.
|
||||||
|
/// - `token_symbol`: Symbol of the token.
|
||||||
|
/// - `token_name`: Full name of the token.
|
||||||
|
#[builder(setter(into, strip_option), default)] |
||||||
|
wallet_balance: Option<GaugeVec>, |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn create_agent_metrics(metrics: &CoreMetrics) -> Result<AgentMetrics> { |
||||||
|
Ok(AgentMetricsBuilder::default() |
||||||
|
.wallet_balance(metrics.new_gauge( |
||||||
|
"wallet_balance", |
||||||
|
WALLET_BALANCE_HELP, |
||||||
|
WALLET_BALANCE_LABELS, |
||||||
|
)?) |
||||||
|
.build()?) |
||||||
|
} |
||||||
|
|
||||||
|
/// Configuration for the prometheus middleware. This can be loaded via serde.
|
||||||
|
#[derive(Clone, Debug)] |
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize))] |
||||||
|
#[cfg_attr(feature = "serde", serde(tag = "type", rename_all = "camelCase"))] |
||||||
|
pub struct AgentMetricsConf { |
||||||
|
/// The account to track
|
||||||
|
#[cfg_attr(feature = "serde", serde(default))] |
||||||
|
pub address: Option<String>, |
||||||
|
|
||||||
|
/// Information about the chain this metric is for
|
||||||
|
pub domain: HyperlaneDomain, |
||||||
|
|
||||||
|
/// Name of the agent the metrics are about
|
||||||
|
pub name: String, |
||||||
|
} |
||||||
|
|
||||||
|
/// Utility struct to update agent metrics for a given chain
|
||||||
|
#[derive(new)] |
||||||
|
pub struct AgentMetricsUpdater { |
||||||
|
metrics: AgentMetrics, |
||||||
|
conf: AgentMetricsConf, |
||||||
|
provider: Box<dyn HyperlaneProvider>, |
||||||
|
} |
||||||
|
|
||||||
|
impl AgentMetricsUpdater { |
||||||
|
async fn update_wallet_balances(&self) { |
||||||
|
let Some(wallet_addr) = self.conf.address.clone() else { |
||||||
|
return; |
||||||
|
}; |
||||||
|
let wallet_name = self.conf.name.clone(); |
||||||
|
let Some(wallet_balance_metric) = self.metrics.wallet_balance.clone() else { |
||||||
|
return; |
||||||
|
}; |
||||||
|
let chain = self.conf.domain.name(); |
||||||
|
|
||||||
|
match self.provider.get_balance(wallet_addr.clone()).await { |
||||||
|
Ok(balance) => { |
||||||
|
// Okay, so the native type is not a token, but whatever, close enough.
|
||||||
|
// Note: This is ETH for many chains, but not all so that is why we use `N` and `Native`
|
||||||
|
// TODO: can we get away with scaling as 18 in all cases here? I am guessing not.
|
||||||
|
let balance = u256_as_scaled_f64(balance, self.conf.domain.domain_protocol()); |
||||||
|
trace!("Wallet {wallet_name} ({wallet_addr}) on chain {chain} balance is {balance} of the native currency"); |
||||||
|
wallet_balance_metric |
||||||
|
.with(&hashmap! { |
||||||
|
"chain" => chain, |
||||||
|
"wallet_address" => wallet_addr.as_str(), |
||||||
|
"wallet_name" => wallet_name.as_str(), |
||||||
|
"token_address" => "none", |
||||||
|
"token_symbol" => "Native", |
||||||
|
"token_name" => "Native" |
||||||
|
}).set(balance) |
||||||
|
}, |
||||||
|
Err(e) => warn!("Metric update failed for wallet {wallet_name} ({wallet_addr}) on chain {chain} balance for native currency; {e}") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Periodically updates the metrics
|
||||||
|
pub async fn start_updating_on_interval(self, period: Duration) { |
||||||
|
let mut interval = tokio::time::interval(period); |
||||||
|
interval.set_missed_tick_behavior(MissedTickBehavior::Skip); |
||||||
|
loop { |
||||||
|
self.update_wallet_balances().await; |
||||||
|
interval.tick().await; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,10 +1,14 @@ |
|||||||
//! Useful metrics that all agents should track.
|
//! Useful metrics that all agents should track.
|
||||||
|
|
||||||
|
pub use self::core::*; |
||||||
|
|
||||||
/// The metrics namespace prefix. All metric names will start with `{NAMESPACE}_`.
|
/// The metrics namespace prefix. All metric names will start with `{NAMESPACE}_`.
|
||||||
pub const NAMESPACE: &str = "hyperlane"; |
pub const NAMESPACE: &str = "hyperlane"; |
||||||
|
|
||||||
mod core; |
mod core; |
||||||
pub use self::core::*; |
|
||||||
|
|
||||||
|
mod agent_metrics; |
||||||
mod json_rpc_client; |
mod json_rpc_client; |
||||||
mod provider; |
mod provider; |
||||||
|
|
||||||
|
pub use self::agent_metrics::*; |
||||||
|
@ -0,0 +1,29 @@ |
|||||||
|
use crate::HyperlaneDomainProtocol; |
||||||
|
use std::time::Duration; |
||||||
|
|
||||||
|
use crate::U256; |
||||||
|
|
||||||
|
const ETHEREUM_DECIMALS: u8 = 18; |
||||||
|
const COSMOS_DECIMALS: u8 = 6; |
||||||
|
const SOLANA_DECIMALS: u8 = 9; |
||||||
|
|
||||||
|
/// Interval for querying the prometheus metrics endpoint.
|
||||||
|
/// This should be whatever the prometheus scrape interval is
|
||||||
|
pub const METRICS_SCRAPE_INTERVAL: Duration = Duration::from_secs(60); |
||||||
|
|
||||||
|
/// Convert a u256 scaled integer value into the corresponding f64 value.
|
||||||
|
#[cfg(feature = "float")] |
||||||
|
pub fn u256_as_scaled_f64(value: U256, domain: HyperlaneDomainProtocol) -> f64 { |
||||||
|
let decimals = decimals_by_protocol(domain); |
||||||
|
value.to_f64_lossy() / (10u64.pow(decimals as u32) as f64) |
||||||
|
} |
||||||
|
|
||||||
|
/// Get the decimals each protocol typically uses for its lowest denomination
|
||||||
|
/// of the native token
|
||||||
|
pub fn decimals_by_protocol(protocol: HyperlaneDomainProtocol) -> u8 { |
||||||
|
match protocol { |
||||||
|
HyperlaneDomainProtocol::Cosmos => COSMOS_DECIMALS, |
||||||
|
HyperlaneDomainProtocol::Sealevel => SOLANA_DECIMALS, |
||||||
|
_ => ETHEREUM_DECIMALS, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,2 @@ |
|||||||
|
/// Agent metrics utils
|
||||||
|
pub mod agent; |
Loading…
Reference in new issue