Skip to content

Commit

Permalink
[Bridge] Support other vault metrics (#20431) (#20484)
Browse files Browse the repository at this point in the history
## Description 

As in title.

## Test plan 

How did you test the new or updated feature?

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:

## Description 

Describe the changes or additions included in this PR.

## Test plan 

How did you test the new or updated feature?

---

## Release notes

Check each box that your changes affect. If none of the boxes relate to
your changes, release notes aren't required.

For each box you select, include information after the relevant heading
that describes the impact of your changes that a user might notice and
any actions they must take to implement updates.

- [ ] Protocol: 
- [ ] Nodes (Validators and Full nodes): 
- [ ] Indexer: 
- [ ] JSON-RPC: 
- [ ] GraphQL: 
- [ ] CLI: 
- [ ] Rust SDK:
- [ ] REST API:

Co-authored-by: William Smith <williamprincesmith@gmail.com>
  • Loading branch information
Bridgerz and williampsmith authored Dec 2, 2024
1 parent b01806a commit 44dc210
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 56 deletions.
34 changes: 28 additions & 6 deletions crates/sui-bridge-indexer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -143,15 +146,33 @@ 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, &eth_provider).await?;
let (
_committee_address,
_limiter_address,
vault_address,
_config_address,
weth_address,
usdt_address,
) = get_eth_contract_addresses(eth_bridge_proxy_address, &eth_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(),
);
)
.await
.unwrap_or_else(|e| panic!("Failed to create eth vault balance: {}", e));
let usdt_vault_balance = EthereumVaultBalance::new(
eth_provider.clone(),
vault_address,
usdt_address,
VaultAsset::USDT,
watchdog_metrics.usdt_vault_balance.clone(),
)
.await
.unwrap_or_else(|e| panic!("Failed to create usdt vault balance: {}", e));

let eth_bridge_status = EthBridgeStatus::new(
eth_provider,
Expand All @@ -163,6 +184,7 @@ async fn start_watchdog(
SuiBridgeStatus::new(sui_client, watchdog_metrics.sui_bridge_paused.clone());
let observables: Vec<Box<dyn Observable + Send + Sync>> = vec![
Box::new(eth_vault_balance),
Box::new(usdt_vault_balance),
Box::new(eth_bridge_status),
Box::new(sui_bridge_status),
];
Expand Down
69 changes: 51 additions & 18 deletions crates/sui-bridge-watchdog/eth_vault_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,89 @@ use sui_bridge::metered_eth_provider::MeteredEthHttpProvier;
use tokio::time::Duration;
use tracing::{error, info};

const TEN_ZEROS: u64 = 10_u64.pow(10);
#[derive(Debug)]
pub enum VaultAsset {
WETH,
USDT,
}

pub struct EthVaultBalance {
pub struct EthereumVaultBalance {
coin_contract: EthERC20<Provider<MeteredEthHttpProvier>>,
asset: VaultAsset,
decimals: u8,
vault_address: EthAddress,
ten_zeros: U256,
metric: IntGauge,
}

impl EthVaultBalance {
impl EthereumVaultBalance {
pub fn new(
provider: Arc<Provider<MeteredEthHttpProvier>>,
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);
) -> anyhow::Result<Self> {
let coin_contract = EthERC20::new(coin_address, provider);
Self {
let decimals = coin_contract
.decimals()
.call()
.await
.map_err(|e| anyhow::anyhow!("Failed to get decimals from token contract: {e}"))?;
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) {
match self
let balance: Result<U256> = self
.coin_contract
.balance_of(self.vault_address)
.call()
.await
{
.await;
match balance {
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 normalized_balance: U256 = match self.decimals.checked_sub(8) {
// In this case, there are more decimals than needed, so we need to
// remove trailing decimals.
Some(delta) if delta > 0 => balance
.checked_div(U256::from(10).pow(U256::from(delta)))
.expect("Division by zero should be impossible here"),
// In this case, there are fewer decimals than needed, so we need to
// add zeroes.
None => {
// this should be guaranteed to be positive
let delta = 8 - self.decimals;
balance
.checked_mul(U256::from(10).pow(U256::from(delta)))
.expect("Integer overflow")
}
// in this case, the token contract has the target precision
// so we don't need to do anything.
Some(_) => balance,
};
self.metric.set(normalized_balance.as_u128() as i64);

info!("{:?} Vault Balance: {:?}", self.asset, normalized_balance,);
}
Err(e) => {
error!("Error getting balance from vault: {:?}", e);
Expand Down
10 changes: 8 additions & 2 deletions crates/sui-bridge/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,14 @@ impl BridgeNodeConfig {
.interval(std::time::Duration::from_millis(2000)),
);
let chain_id = provider.get_chainid().await?;
let (committee_address, limiter_address, vault_address, config_address, _weth_address) =
get_eth_contract_addresses(bridge_proxy_address, &provider).await?;
let (
committee_address,
limiter_address,
vault_address,
config_address,
_weth_address,
_usdt_address,
) = get_eth_contract_addresses(bridge_proxy_address, &provider).await?;
let config = EthBridgeConfig::new(config_address, provider.clone());

if self.run_client && self.eth.eth_contracts_start_block_fallback.is_none() {
Expand Down
33 changes: 26 additions & 7 deletions crates/sui-bridge/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -158,17 +158,35 @@ async fn start_watchdog(
sui_client: Arc<SuiBridgeClient>,
) {
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, &eth_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, &eth_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(),
);
)
.await
.unwrap_or_else(|e| panic!("Failed to create eth vault balance: {}", e));
let usdt_vault_balance = EthereumVaultBalance::new(
eth_provider.clone(),
vault_address,
usdt_address,
VaultAsset::USDT,
watchdog_metrics.usdt_vault_balance.clone(),
)
.await
.unwrap_or_else(|e| panic!("Failed to create usdt vault balance: {}", e));

let eth_bridge_status = EthBridgeStatus::new(
eth_provider,
Expand All @@ -183,6 +201,7 @@ async fn start_watchdog(

let mut observables: Vec<Box<dyn Observable + Send + Sync>> = vec![
Box::new(eth_vault_balance),
Box::new(usdt_vault_balance),
Box::new(eth_bridge_status),
Box::new(sui_bridge_status),
];
Expand Down
74 changes: 54 additions & 20 deletions crates/sui-bridge/src/sui_bridge_watchdog/eth_vault_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,90 @@ use std::sync::Arc;
use tokio::time::Duration;
use tracing::{error, info};

const TEN_ZEROS: u64 = 10_u64.pow(10);
#[derive(Debug)]
pub enum VaultAsset {
WETH,
USDT,
}

pub struct EthVaultBalance {
pub struct EthereumVaultBalance {
coin_contract: EthERC20<Provider<MeteredEthHttpProvier>>,
asset: VaultAsset,
decimals: u8,
vault_address: EthAddress,
ten_zeros: U256,
metric: IntGauge,
}

impl EthVaultBalance {
pub fn new(
impl EthereumVaultBalance {
pub async fn new(
provider: Arc<Provider<MeteredEthHttpProvier>>,
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);
) -> anyhow::Result<Self> {
let coin_contract = EthERC20::new(coin_address, provider);
Self {
let decimals = coin_contract
.decimals()
.call()
.await
.map_err(|e| anyhow::anyhow!("Failed to get decimals from token contract: {e}"))?;
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) {
match self
let balance: Result<
U256,
ethers::contract::ContractError<Provider<MeteredEthHttpProvier>>,
> = self
.coin_contract
.balance_of(self.vault_address)
.call()
.await
{
.await;
match balance {
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 normalized_balance: U256 = match self.decimals.checked_sub(8) {
// In this case, there are more decimals than needed, so we need to
// remove trailing decimals.
Some(delta) if delta > 0 => balance
.checked_div(U256::from(10).pow(U256::from(delta)))
.expect("Division by zero should be impossible here"),
// In this case, there are fewer decimals than needed, so we need to
// add zeroes.
None => {
let delta = 8 - self.decimals;
balance
.checked_mul(U256::from(10).pow(U256::from(delta)))
.expect("Integer overflow")
}
// in this case, the token contract has the target precision
// so we don't need to do anything.
Some(_) => balance,
};
self.metric.set(normalized_balance.as_u128() as i64);

info!("{:?} Vault Balance: {:?}", self.asset, normalized_balance,);
}
Err(e) => {
error!("Error getting balance from vault: {:?}", e);
Expand Down
7 changes: 7 additions & 0 deletions crates/sui-bridge/src/sui_bridge_watchdog/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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",
Expand Down
Loading

0 comments on commit 44dc210

Please sign in to comment.