sui_bridge/sui_bridge_watchdog/
eth_vault_balance.rs1use crate::abi::EthERC20;
5use crate::metered_eth_provider::MeteredEthHttpProvier;
6use crate::sui_bridge_watchdog::Observable;
7use async_trait::async_trait;
8use ethers::providers::Provider;
9use ethers::types::{Address as EthAddress, U256};
10use prometheus::IntGauge;
11use std::sync::Arc;
12use tokio::time::Duration;
13use tracing::{error, info};
14
15#[derive(Debug)]
16pub enum VaultAsset {
17 WETH,
18 USDT,
19 WBTC,
20 LBTC,
21}
22
23pub struct EthereumVaultBalance {
24 coin_contract: EthERC20<Provider<MeteredEthHttpProvier>>,
25 asset: VaultAsset,
26 decimals: u8,
27 vault_address: EthAddress,
28 metric: IntGauge,
29}
30
31impl EthereumVaultBalance {
32 pub async fn new(
33 provider: Arc<Provider<MeteredEthHttpProvier>>,
34 vault_address: EthAddress,
35 coin_address: EthAddress, asset: VaultAsset,
37 metric: IntGauge,
38 ) -> anyhow::Result<Self> {
39 let coin_contract = EthERC20::new(coin_address, provider);
40 let decimals = coin_contract
41 .decimals()
42 .call()
43 .await
44 .map_err(|e| anyhow::anyhow!("Failed to get decimals from token contract: {e}"))?;
45 Ok(Self {
46 coin_contract,
47 vault_address,
48 decimals,
49 asset,
50 metric,
51 })
52 }
53}
54
55#[async_trait]
56impl Observable for EthereumVaultBalance {
57 fn name(&self) -> &str {
58 "EthereumVaultBalance"
59 }
60 async fn observe_and_report(&self) {
61 let balance: Result<
62 U256,
63 ethers::contract::ContractError<Provider<MeteredEthHttpProvier>>,
64 > = self
65 .coin_contract
66 .balance_of(self.vault_address)
67 .call()
68 .await;
69 match balance {
70 Ok(balance) => {
71 let normalized_balance: U256 = match self.decimals.checked_sub(8) {
81 Some(delta) if delta > 0 => balance
84 .checked_div(U256::from(10).pow(U256::from(delta)))
85 .expect("Division by zero should be impossible here"),
86 None => {
89 let delta = 8 - self.decimals;
90 balance
91 .checked_mul(U256::from(10).pow(U256::from(delta)))
92 .expect("Integer overflow")
93 }
94 Some(_) => balance,
97 };
98 self.metric.set(normalized_balance.as_u128() as i64);
99
100 info!("{:?} Vault Balance: {:?}", self.asset, normalized_balance,);
101 }
102 Err(e) => {
103 error!("Error getting balance from vault: {:?}", e);
104 }
105 }
106 }
107
108 fn interval(&self) -> Duration {
109 Duration::from_secs(10)
110 }
111}