From 8002ff0bc263e9f138e882d46f95332e6e3fe967 Mon Sep 17 00:00:00 2001 From: William Smith Date: Mon, 25 Nov 2024 16:20:33 -0800 Subject: [PATCH] [Native Bridge] Support other vault balances --- crates/sui-bridge-indexer/src/main.rs | 28 +++++++++--- .../sui-bridge-watchdog/eth_vault_balance.rs | 37 ++++++++++----- crates/sui-bridge/src/node.rs | 27 ++++++++--- .../sui_bridge_watchdog/eth_vault_balance.rs | 45 ++++++++++++------- .../src/sui_bridge_watchdog/metrics.rs | 7 +++ crates/sui-bridge/src/utils.rs | 11 ++++- 6 files changed, 115 insertions(+), 40 deletions(-) diff --git a/crates/sui-bridge-indexer/src/main.rs b/crates/sui-bridge-indexer/src/main.rs index a42fc23b04ab75..55a85ab2c7eb2c 100644 --- a/crates/sui-bridge-indexer/src/main.rs +++ b/crates/sui-bridge-indexer/src/main.rs @@ -27,8 +27,11 @@ use mysten_metrics::start_prometheus_server; use sui_bridge::metrics::BridgeMetrics; use sui_bridge::sui_bridge_watchdog::{ - eth_bridge_status::EthBridgeStatus, eth_vault_balance::EthVaultBalance, - metrics::WatchdogMetrics, sui_bridge_status::SuiBridgeStatus, BridgeWatchDog, + eth_bridge_status::EthBridgeStatus, + eth_vault_balance::{EthereumVaultBalance, VaultAsset}, + metrics::WatchdogMetrics, + sui_bridge_status::SuiBridgeStatus, + BridgeWatchDog, }; use sui_bridge_indexer::config::IndexerConfig; use sui_bridge_indexer::metrics::BridgeIndexerMetrics; @@ -143,15 +146,29 @@ async fn start_watchdog( let watchdog_metrics = WatchdogMetrics::new(registry); let eth_provider = Arc::new(new_metered_eth_provider(&config.eth_rpc_url, bridge_metrics.clone()).unwrap()); - let (_committee_address, _limiter_address, vault_address, _config_address, weth_address) = - get_eth_contract_addresses(eth_bridge_proxy_address, ð_provider).await?; + let ( + _committee_address, + _limiter_address, + vault_address, + _config_address, + weth_address, + usdt_address, + ) = get_eth_contract_addresses(eth_bridge_proxy_address, ð_provider).await?; - let eth_vault_balance = EthVaultBalance::new( + let eth_vault_balance = EthereumVaultBalance::new( eth_provider.clone(), vault_address, weth_address, + VaultAsset::WETH, watchdog_metrics.eth_vault_balance.clone(), ); + let usdt_vault_balance = EthereumVaultBalance::new( + eth_provider.clone(), + vault_address, + usdt_address, + VaultAsset::USDT, + watchdog_metrics.usdt_vault_balance.clone(), + ); let eth_bridge_status = EthBridgeStatus::new( eth_provider, @@ -163,6 +180,7 @@ async fn start_watchdog( SuiBridgeStatus::new(sui_client, watchdog_metrics.sui_bridge_paused.clone()); let observables: Vec> = vec![ Box::new(eth_vault_balance), + Box::new(usdt_vault_balance), Box::new(eth_bridge_status), Box::new(sui_bridge_status), ]; diff --git a/crates/sui-bridge-watchdog/eth_vault_balance.rs b/crates/sui-bridge-watchdog/eth_vault_balance.rs index dfc359e0cb393c..fea40b161d9c45 100644 --- a/crates/sui-bridge-watchdog/eth_vault_balance.rs +++ b/crates/sui-bridge-watchdog/eth_vault_balance.rs @@ -12,37 +12,43 @@ use sui_bridge::metered_eth_provider::MeteredEthHttpProvier; use tokio::time::Duration; use tracing::{error, info}; -const TEN_ZEROS: u64 = 10_u64.pow(10); +pub enum VaultAsset { + WETH, + USDT, +} -pub struct EthVaultBalance { +pub struct EthereumVaultBalance { coin_contract: EthERC20>, + asset: VaultAsset, + decimals: u32, vault_address: EthAddress, - ten_zeros: U256, metric: IntGauge, } -impl EthVaultBalance { +impl EthereumVaultBalance { pub fn new( provider: Arc>, vault_address: EthAddress, coin_address: EthAddress, // for now this only support one coin which is WETH + asset: VaultAsset, metric: IntGauge, ) -> Self { - let ten_zeros = U256::from(TEN_ZEROS); let coin_contract = EthERC20::new(coin_address, provider); + let decimals = coin_contract.decimals().call().await?; Self { coin_contract, vault_address, - ten_zeros, + decimals, + asset, metric, } } } #[async_trait] -impl Observable for EthVaultBalance { +impl Observable for EthereumVaultBalance { fn name(&self) -> &str { - "EthVaultBalance" + "EthereumVaultBalance" } async fn observe_and_report(&self) { @@ -55,13 +61,20 @@ impl Observable for EthVaultBalance { Ok(balance) => { // Why downcasting is safe: // 1. On Ethereum we only take the first 8 decimals into account, - // meaning the trailing 10 digits can be ignored + // meaning the trailing 10 digits can be ignored. For other assets, + // we will also assume this max level of precision for metrics purposes. // 2. i64::MAX is 9_223_372_036_854_775_807, with 8 decimal places is // 92_233_720_368. We likely won't see any balance higher than this // in the next 12 months. - let balance = (balance / self.ten_zeros).as_u64() as i64; - self.metric.set(balance); - info!("Eth Vault Balance: {:?}", balance); + // For USDT, for example, this will be 10^6 - 8 = 10^(-2) = 0.01, + // therefore we will add 2 zeroes of precision. + let denom = U256::from(10).pow(self.decimals - 8); + let normalized_balance = (balance / denom).as_u64() as i64; + self.metric.set(normalized_balance); + info!( + "{} Vault Balance: {:?} (${:?} {})", + self.asset, balance, normalized_balance, denom + ); } Err(e) => { error!("Error getting balance from vault: {:?}", e); diff --git a/crates/sui-bridge/src/node.rs b/crates/sui-bridge/src/node.rs index 671f2b358f3c03..ffcee8e8ea65a7 100644 --- a/crates/sui-bridge/src/node.rs +++ b/crates/sui-bridge/src/node.rs @@ -5,7 +5,7 @@ use crate::config::WatchdogConfig; use crate::crypto::BridgeAuthorityPublicKeyBytes; use crate::metered_eth_provider::MeteredEthHttpProvier; use crate::sui_bridge_watchdog::eth_bridge_status::EthBridgeStatus; -use crate::sui_bridge_watchdog::eth_vault_balance::EthVaultBalance; +use crate::sui_bridge_watchdog::eth_vault_balance::{EthereumVaultBalance, VaultAsset}; use crate::sui_bridge_watchdog::metrics::WatchdogMetrics; use crate::sui_bridge_watchdog::sui_bridge_status::SuiBridgeStatus; use crate::sui_bridge_watchdog::total_supplies::TotalSupplies; @@ -158,17 +158,31 @@ async fn start_watchdog( sui_client: Arc, ) { let watchdog_metrics = WatchdogMetrics::new(registry); - let (_committee_address, _limiter_address, vault_address, _config_address, weth_address) = - get_eth_contract_addresses(eth_bridge_proxy_address, ð_provider) - .await - .unwrap_or_else(|e| panic!("get_eth_contract_addresses should not fail: {}", e)); + let ( + _committee_address, + _limiter_address, + vault_address, + _config_address, + weth_address, + usdt_address, + ) = get_eth_contract_addresses(eth_bridge_proxy_address, ð_provider) + .await + .unwrap_or_else(|e| panic!("get_eth_contract_addresses should not fail: {}", e)); - let eth_vault_balance = EthVaultBalance::new( + let eth_vault_balance = EthereumVaultBalance::new( eth_provider.clone(), vault_address, weth_address, + VaultAsset::WETH, watchdog_metrics.eth_vault_balance.clone(), ); + let usdt_vault_balance = EthereumVaultBalance::new( + eth_provider.clone(), + vault_address, + usdt_address, + VaultAsset::USDT, + watchdog_metrics.usdt_vault_balance.clone(), + ); let eth_bridge_status = EthBridgeStatus::new( eth_provider, @@ -183,6 +197,7 @@ async fn start_watchdog( let mut observables: Vec> = vec![ Box::new(eth_vault_balance), + Box::new(usdt_vault_balance), Box::new(eth_bridge_status), Box::new(sui_bridge_status), ]; diff --git a/crates/sui-bridge/src/sui_bridge_watchdog/eth_vault_balance.rs b/crates/sui-bridge/src/sui_bridge_watchdog/eth_vault_balance.rs index b43b7538067d40..9073a1b89e5f8e 100644 --- a/crates/sui-bridge/src/sui_bridge_watchdog/eth_vault_balance.rs +++ b/crates/sui-bridge/src/sui_bridge_watchdog/eth_vault_balance.rs @@ -12,37 +12,43 @@ use std::sync::Arc; use tokio::time::Duration; use tracing::{error, info}; -const TEN_ZEROS: u64 = 10_u64.pow(10); +pub enum VaultAsset { + WETH, + USDT, +} -pub struct EthVaultBalance { +pub struct EthereumVaultBalance { coin_contract: EthERC20>, + asset: VaultAsset, + decimals: u32, vault_address: EthAddress, - ten_zeros: U256, metric: IntGauge, } -impl EthVaultBalance { - pub fn new( +impl EthereumVaultBalance { + pub async fn new( provider: Arc>, vault_address: EthAddress, coin_address: EthAddress, // for now this only support one coin which is WETH + asset: VaultAsset, metric: IntGauge, - ) -> Self { - let ten_zeros = U256::from(TEN_ZEROS); + ) -> Result { let coin_contract = EthERC20::new(coin_address, provider); - Self { + let decimals = coin_contract.decimals().call().await?; + Ok(Self { coin_contract, vault_address, - ten_zeros, + decimals, + asset, metric, - } + }) } } #[async_trait] -impl Observable for EthVaultBalance { +impl Observable for EthereumVaultBalance { fn name(&self) -> &str { - "EthVaultBalance" + "EthereumVaultBalance" } async fn observe_and_report(&self) { @@ -55,13 +61,20 @@ impl Observable for EthVaultBalance { Ok(balance) => { // Why downcasting is safe: // 1. On Ethereum we only take the first 8 decimals into account, - // meaning the trailing 10 digits can be ignored + // meaning the trailing 10 digits can be ignored. For other assets, + // we will also assume this max level of precision for metrics purposes. // 2. i64::MAX is 9_223_372_036_854_775_807, with 8 decimal places is // 92_233_720_368. We likely won't see any balance higher than this // in the next 12 months. - let balance = (balance / self.ten_zeros).as_u64() as i64; - self.metric.set(balance); - info!("Eth Vault Balance: {:?}", balance); + // For USDT, for example, this will be 10^6 - 8 = 10^(-2) = 0.01, + // therefore we will add 2 zeroes of precision. + let denom = U256::from(10).pow(self.decimals - 8); + let normalized_balance = (balance / denom).as_u64() as i64; + self.metric.set(normalized_balance); + info!( + "{} Vault Balance: {:?} (${:?} {})", + self.asset, balance, normalized_balance, denom + ); } Err(e) => { error!("Error getting balance from vault: {:?}", e); diff --git a/crates/sui-bridge/src/sui_bridge_watchdog/metrics.rs b/crates/sui-bridge/src/sui_bridge_watchdog/metrics.rs index 8fea209d7f43f2..58684a75e70175 100644 --- a/crates/sui-bridge/src/sui_bridge_watchdog/metrics.rs +++ b/crates/sui-bridge/src/sui_bridge_watchdog/metrics.rs @@ -9,6 +9,7 @@ use prometheus::{ #[derive(Clone, Debug)] pub struct WatchdogMetrics { pub eth_vault_balance: IntGauge, + pub usdt_vault_balance: IntGauge, pub total_supplies: IntGaugeVec, pub eth_bridge_paused: IntGauge, pub sui_bridge_paused: IntGauge, @@ -23,6 +24,12 @@ impl WatchdogMetrics { registry, ) .unwrap(), + usdt_vault_balance: register_int_gauge_with_registry!( + "bridge_usdt_vault_balance", + "Current balance of usdt eth vault", + registry, + ) + .unwrap(), total_supplies: register_int_gauge_vec_with_registry!( "bridge_total_supplies", "Current total supplies of coins on Sui based on Treasury Cap", diff --git a/crates/sui-bridge/src/utils.rs b/crates/sui-bridge/src/utils.rs index d6f7ca487e191a..74a965e3a35b6c 100644 --- a/crates/sui-bridge/src/utils.rs +++ b/crates/sui-bridge/src/utils.rs @@ -107,13 +107,21 @@ pub fn generate_bridge_client_key_and_write_to_file( pub async fn get_eth_contract_addresses( bridge_proxy_address: EthAddress, provider: &Arc>, -) -> anyhow::Result<(EthAddress, EthAddress, EthAddress, EthAddress, EthAddress)> { +) -> anyhow::Result<( + EthAddress, + EthAddress, + EthAddress, + EthAddress, + EthAddress, + EthAddress, +)> { let sui_bridge = EthSuiBridge::new(bridge_proxy_address, provider.clone()); let committee_address: EthAddress = sui_bridge.committee().call().await?; let limiter_address: EthAddress = sui_bridge.limiter().call().await?; let vault_address: EthAddress = sui_bridge.vault().call().await?; let vault = EthBridgeVault::new(vault_address, provider.clone()); let weth_address: EthAddress = vault.w_eth().call().await?; + let usdt_address: EthAddress = vault.usdt().call().await?; let committee = EthBridgeCommittee::new(committee_address, provider.clone()); let config_address: EthAddress = committee.config().call().await?; @@ -123,6 +131,7 @@ pub async fn get_eth_contract_addresses