瀏覽代碼

Merge pull request #2506 from pyth-network/more-network-metrics

feat(apps/price_pusher): add support for aptos and sui metrics
Daniel Chew 8 月之前
父節點
當前提交
87150c9f44

+ 172 - 35
apps/price_pusher/grafana-dashboard.sample.json

@@ -18,7 +18,7 @@
   "editable": true,
   "fiscalYearStartMonth": 0,
   "graphTooltip": 0,
-  "id": 1,
+  "id": 86,
   "links": [],
   "panels": [
     {
@@ -60,7 +60,7 @@
       },
       "gridPos": {
         "h": 6,
-        "w": 6,
+        "w": 12,
         "x": 0,
         "y": 1
       },
@@ -82,7 +82,7 @@
         "textMode": "value",
         "wideLayout": true
       },
-      "pluginVersion": "11.5.2",
+      "pluginVersion": "11.1.0",
       "repeat": "chain",
       "repeatDirection": "h",
       "targets": [
@@ -141,7 +141,7 @@
       },
       "gridPos": {
         "h": 6,
-        "w": 6,
+        "w": 12,
         "x": 0,
         "y": 8
       },
@@ -163,7 +163,7 @@
         "textMode": "value",
         "wideLayout": true
       },
-      "pluginVersion": "11.5.2",
+      "pluginVersion": "11.1.0",
       "repeat": "chain",
       "repeatDirection": "h",
       "targets": [
@@ -226,7 +226,7 @@
       },
       "gridPos": {
         "h": 6,
-        "w": 8,
+        "w": 12,
         "x": 0,
         "y": 15
       },
@@ -248,7 +248,7 @@
         "textMode": "auto",
         "wideLayout": true
       },
-      "pluginVersion": "11.5.2",
+      "pluginVersion": "11.1.0",
       "repeat": "chain",
       "repeatDirection": "h",
       "targets": [
@@ -381,7 +381,7 @@
           }
         ]
       },
-      "pluginVersion": "11.5.2",
+      "pluginVersion": "11.1.0",
       "repeat": "chain",
       "repeatDirection": "v",
       "targets": [
@@ -440,6 +440,7 @@
             "excludeByName": {
               "Time": true,
               "Value #A": true,
+              "Value #C": false,
               "__name__": true,
               "__name__#B": true,
               "__name__#C": true,
@@ -458,14 +459,13 @@
               "job#C": true,
               "price_id": false,
               "price_id#B": true,
-              "price_id#C": true,
-              "Value #C": false
+              "price_id#C": true
             },
             "includeByName": {
-              "alias 1": true,
-              "price_id": true,
               "Value #B": true,
-              "Value #C": true
+              "Value #C": true,
+              "alias 1": true,
+              "price_id": true
             },
             "indexByName": {
               "Time 1": 4,
@@ -504,7 +504,7 @@
         "h": 1,
         "w": 24,
         "x": 0,
-        "y": 30
+        "y": 38
       },
       "id": 19,
       "panels": [],
@@ -528,7 +528,6 @@
             "axisLabel": "",
             "axisPlacement": "auto",
             "barAlignment": 0,
-            "barWidthFactor": 0.6,
             "drawStyle": "line",
             "fillOpacity": 0,
             "gradientMode": "none",
@@ -589,7 +588,7 @@
         "h": 8,
         "w": 12,
         "x": 0,
-        "y": 31
+        "y": 39
       },
       "id": 3,
       "options": {
@@ -630,7 +629,7 @@
         "h": 1,
         "w": 24,
         "x": 0,
-        "y": 39
+        "y": 47
       },
       "id": 14,
       "panels": [],
@@ -706,9 +705,9 @@
       },
       "gridPos": {
         "h": 8,
-        "w": 8,
+        "w": 12,
         "x": 0,
-        "y": 40
+        "y": 48
       },
       "id": 13,
       "options": {
@@ -765,7 +764,7 @@
         "h": 1,
         "w": 24,
         "x": 0,
-        "y": 48
+        "y": 56
       },
       "id": 20,
       "panels": [],
@@ -806,9 +805,9 @@
       },
       "gridPos": {
         "h": 6,
-        "w": 8,
+        "w": 12,
         "x": 0,
-        "y": 49
+        "y": 57
       },
       "id": 10,
       "options": {
@@ -828,7 +827,7 @@
         "textMode": "value_and_name",
         "wideLayout": true
       },
-      "pluginVersion": "11.5.2",
+      "pluginVersion": "11.1.0",
       "repeat": "chain",
       "repeatDirection": "h",
       "targets": [
@@ -865,7 +864,6 @@
             "axisLabel": "",
             "axisPlacement": "auto",
             "barAlignment": 0,
-            "barWidthFactor": 0.6,
             "drawStyle": "line",
             "fillOpacity": 0,
             "gradientMode": "none",
@@ -906,10 +904,10 @@
         "overrides": []
       },
       "gridPos": {
-        "h": 8,
+        "h": 6,
         "w": 12,
         "x": 0,
-        "y": 55
+        "y": 63
       },
       "id": 11,
       "options": {
@@ -950,7 +948,7 @@
         "h": 1,
         "w": 24,
         "x": 0,
-        "y": 63
+        "y": 69
       },
       "id": 21,
       "panels": [],
@@ -974,7 +972,6 @@
             "axisLabel": "",
             "axisPlacement": "auto",
             "barAlignment": 0,
-            "barWidthFactor": 0.6,
             "drawStyle": "line",
             "fillOpacity": 0,
             "gradientMode": "none",
@@ -1019,7 +1016,7 @@
         "h": 8,
         "w": 12,
         "x": 0,
-        "y": 64
+        "y": 70
       },
       "id": 6,
       "options": {
@@ -1053,11 +1050,149 @@
       ],
       "title": "Failed Updates: ${chain}",
       "type": "timeseries"
+    },
+    {
+      "collapsed": false,
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 78
+      },
+      "id": 188,
+      "panels": [],
+      "title": "Tx Hash",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "loki",
+        "uid": "ads9ouz3jh4hsa"
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 79
+      },
+      "id": 187,
+      "options": {
+        "dedupStrategy": "none",
+        "enableLogDetails": true,
+        "prettifyLogMessage": false,
+        "showCommonLabels": false,
+        "showLabels": false,
+        "showTime": true,
+        "sortOrder": "Descending",
+        "wrapLogMessage": false
+      },
+      "repeat": "chain",
+      "repeatDirection": "h",
+      "targets": [
+        {
+          "datasource": {
+            "type": "loki",
+            "uid": "ads9ouz3jh4hsa"
+          },
+          "editorMode": "code",
+          "expr": "{namespace=~\"$chain\"} | logfmt | json | msg =~ `.*(Price update successful|Transaction confirmed|Successfully updated price).*` | line_format `Tx Hash: {{.hash}}`",
+          "queryType": "range",
+          "refId": "A"
+        }
+      ],
+      "title": "Tx Hash: ${chain}",
+      "type": "logs"
+    },
+    {
+      "collapsed": false,
+      "gridPos": {
+        "h": 1,
+        "w": 24,
+        "x": 0,
+        "y": 87
+      },
+      "id": 31,
+      "panels": [],
+      "title": "Logs",
+      "type": "row"
+    },
+    {
+      "datasource": {
+        "type": "loki",
+        "uid": "ads9ouz3jh4hsa"
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 0,
+        "y": 88
+      },
+      "id": 41,
+      "options": {
+        "dedupStrategy": "none",
+        "enableLogDetails": true,
+        "prettifyLogMessage": false,
+        "showCommonLabels": false,
+        "showLabels": false,
+        "showTime": true,
+        "sortOrder": "Descending",
+        "wrapLogMessage": false
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "loki",
+            "uid": "ads9ouz3jh4hsa"
+          },
+          "editorMode": "code",
+          "expr": "{namespace=~\"$chain\"} | logfmt",
+          "queryType": "range",
+          "refId": "A"
+        }
+      ],
+      "title": "All Logs",
+      "type": "logs"
+    },
+    {
+      "datasource": {
+        "type": "loki",
+        "uid": "ads9ouz3jh4hsa"
+      },
+      "gridPos": {
+        "h": 8,
+        "w": 12,
+        "x": 12,
+        "y": 88
+      },
+      "id": 42,
+      "options": {
+        "dedupStrategy": "none",
+        "enableLogDetails": true,
+        "prettifyLogMessage": false,
+        "showCommonLabels": false,
+        "showLabels": false,
+        "showTime": true,
+        "sortOrder": "Descending",
+        "wrapLogMessage": false
+      },
+      "targets": [
+        {
+          "datasource": {
+            "type": "loki",
+            "uid": "ads9ouz3jh4hsa"
+          },
+          "editorMode": "builder",
+          "expr": "{namespace=~\"$chain\"} | logfmt | detected_level = `error`",
+          "queryType": "range",
+          "refId": "A"
+        }
+      ],
+      "title": "Error Logs",
+      "type": "logs"
     }
   ],
-  "preload": false,
   "refresh": "5s",
-  "schemaVersion": 40,
+  "schemaVersion": 39,
   "tags": [],
   "templating": {
     "list": [
@@ -1065,10 +1200,12 @@
         "current": {
           "selected": true,
           "text": [
-            "All"
+            "optimism-sepolia-price-pusher-testnet",
+            "sui-price-pusher-mainnet"
           ],
           "value": [
-            "$__all"
+            "optimism-sepolia-price-pusher-testnet",
+            "sui-price-pusher-mainnet"
           ]
         },
         "datasource": {
@@ -1097,13 +1234,13 @@
     ]
   },
   "time": {
-    "from": "now-1h",
+    "from": "now-3h",
     "to": "now"
   },
   "timepicker": {},
   "timezone": "",
   "title": "Pyth Price Pusher Dashboard",
   "uid": "pyth-price-pusher",
-  "version": 44,
+  "version": 12,
   "weekStart": ""
 }

+ 1 - 1
apps/price_pusher/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/price-pusher",
-  "version": "9.1.2",
+  "version": "9.1.3",
   "description": "Pyth Price Pusher",
   "homepage": "https://pyth.network",
   "main": "lib/index.js",

+ 111 - 0
apps/price_pusher/src/aptos/balance-tracker.ts

@@ -0,0 +1,111 @@
+import { AptosClient } from "aptos";
+import {
+  BaseBalanceTracker,
+  BaseBalanceTrackerConfig,
+  IBalanceTracker,
+} from "../interface";
+import { DurationInSeconds } from "../utils";
+import { PricePusherMetrics } from "../metrics";
+import { Logger } from "pino";
+
+/**
+ * Aptos-specific configuration for balance tracker
+ */
+export interface AptosBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+  /** Aptos node endpoint URL */
+  endpoint: string;
+  /** Aptos account address */
+  address: string;
+  /** Optional decimal places for APT token (default: 8) */
+  decimals?: number;
+}
+
+/**
+ * Aptos-specific implementation of the balance tracker
+ */
+export class AptosBalanceTracker extends BaseBalanceTracker {
+  private client: AptosClient;
+  private aptosAddress: string;
+  private decimals: number;
+
+  constructor(config: AptosBalanceTrackerConfig) {
+    super({
+      ...config,
+      logger: config.logger.child({ module: "AptosBalanceTracker" }),
+    });
+
+    this.client = new AptosClient(config.endpoint);
+    this.aptosAddress = config.address;
+    // APT has 8 decimal places by default
+    this.decimals = config.decimals ?? 8;
+  }
+
+  /**
+   * Aptos-specific implementation of balance update
+   * Fetches the native APT balance for the configured address
+   */
+  protected async updateBalance(): Promise<void> {
+    try {
+      // Get account resource to check the balance
+      const accountResource = await this.client.getAccountResource(
+        this.aptosAddress,
+        "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>",
+      );
+
+      // Extract the balance value from the account resource
+      const rawBalance = (accountResource.data as any).coin.value;
+
+      // Convert the balance to a bigint
+      const balance = BigInt(rawBalance);
+
+      // Calculate the normalized balance for display
+      const normalizedBalance = Number(balance) / Math.pow(10, this.decimals);
+
+      // Update metrics with the new balance
+      this.metrics.updateWalletBalance(
+        this.address,
+        this.network,
+        normalizedBalance,
+      );
+
+      this.logger.debug(
+        `Updated Aptos wallet balance: ${this.address} = ${normalizedBalance.toString()} APT (raw: ${balance.toString()})`,
+      );
+    } catch (error) {
+      this.logger.error(
+        { error },
+        "Error fetching Aptos wallet balance for metrics",
+      );
+    }
+  }
+}
+
+/**
+ * Parameters for creating an Aptos balance tracker
+ */
+export interface CreateAptosBalanceTrackerParams {
+  endpoint: string;
+  address: string;
+  network: string;
+  updateInterval: DurationInSeconds;
+  metrics: PricePusherMetrics;
+  logger: Logger;
+  decimals?: number;
+}
+
+/**
+ * Factory function to create a balance tracker for Aptos chain
+ */
+export function createAptosBalanceTracker(
+  params: CreateAptosBalanceTrackerParams,
+): IBalanceTracker {
+  return new AptosBalanceTracker({
+    endpoint: params.endpoint,
+    address: params.address,
+    network: params.network,
+    updateInterval: params.updateInterval,
+    metrics: params.metrics,
+    logger: params.logger,
+    decimals: params.decimals,
+  });
+}

+ 34 - 1
apps/price_pusher/src/aptos/command.ts

@@ -13,6 +13,9 @@ import {
 import { AptosAccount } from "aptos";
 import pino from "pino";
 import { filterInvalidPriceItems } from "../utils";
+import { PricePusherMetrics } from "../metrics";
+import { createAptosBalanceTracker } from "./balance-tracker";
+
 export default {
   command: "aptos",
   describe: "run price pusher for aptos",
@@ -40,6 +43,8 @@ export default {
     ...options.pushingFrequency,
     ...options.logLevel,
     ...options.controllerLogLevel,
+    ...options.enableMetrics,
+    ...options.metricsPort,
   },
   handler: async function (argv: any) {
     // FIXME: type checks for this
@@ -54,6 +59,8 @@ export default {
       overrideGasPriceMultiplier,
       logLevel,
       controllerLogLevel,
+      enableMetrics,
+      metricsPort,
     } = argv;
 
     const logger = pino({ level: logLevel });
@@ -61,6 +68,14 @@ export default {
     const priceConfigs = readPriceConfigFile(priceConfigFile);
     const hermesClient = new HermesClient(priceServiceEndpoint);
 
+    // Initialize metrics if enabled
+    let metrics: PricePusherMetrics | undefined;
+    if (enableMetrics) {
+      metrics = new PricePusherMetrics(logger.child({ module: "Metrics" }));
+      metrics.start(metricsPort);
+      logger.info(`Metrics server started on port ${metricsPort}`);
+    }
+
     const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
     const account = AptosAccount.fromDerivePath(
       APTOS_ACCOUNT_HD_PATH,
@@ -113,9 +128,27 @@ export default {
       aptosListener,
       aptosPusher,
       logger.child({ module: "Controller" }, { level: controllerLogLevel }),
-      { pushingFrequency },
+      {
+        pushingFrequency,
+        metrics,
+      },
     );
 
+    // Create and start the balance tracker if metrics are enabled
+    if (metrics) {
+      const balanceTracker = createAptosBalanceTracker({
+        address: account.address().toString(),
+        endpoint,
+        network: "aptos",
+        updateInterval: pushingFrequency,
+        metrics,
+        logger: logger.child({ module: "AptosBalanceTracker" }),
+      });
+
+      // Start the balance tracker
+      await balanceTracker.start();
+    }
+
     controller.start();
   },
 };

+ 0 - 38
apps/price_pusher/src/balance-tracker.ts

@@ -1,38 +0,0 @@
-import { PricePusherMetrics } from "./metrics";
-import { Logger } from "pino";
-import { DurationInSeconds } from "./utils";
-import { IBalanceTracker } from "./interface";
-import { EvmBalanceTracker } from "./evm/balance-tracker";
-import { SuperWalletClient } from "./evm/super-wallet";
-
-/**
- * Parameters for creating an EVM balance tracker
- */
-export interface CreateEvmBalanceTrackerParams {
-  client: SuperWalletClient;
-  address: `0x${string}`;
-  network: string;
-  updateInterval: DurationInSeconds;
-  metrics: PricePusherMetrics;
-  logger: Logger;
-}
-
-/**
- * Factory function to create a balance tracker for EVM chains
- */
-export function createEvmBalanceTracker(
-  params: CreateEvmBalanceTrackerParams,
-): IBalanceTracker {
-  return new EvmBalanceTracker({
-    client: params.client,
-    address: params.address,
-    network: params.network,
-    updateInterval: params.updateInterval,
-    metrics: params.metrics,
-    logger: params.logger,
-  });
-}
-
-// Additional factory functions for other chains would follow the same pattern:
-// export function createSuiBalanceTracker(params: CreateSuiBalanceTrackerParams): IBalanceTracker { ... }
-// export function createSolanaBalanceTracker(params: CreateSolanaBalanceTrackerParams): IBalanceTracker { ... }

+ 36 - 1
apps/price_pusher/src/evm/balance-tracker.ts

@@ -1,5 +1,12 @@
 import { SuperWalletClient } from "./super-wallet";
-import { BaseBalanceTracker, BaseBalanceTrackerConfig } from "../interface";
+import {
+  BaseBalanceTracker,
+  BaseBalanceTrackerConfig,
+  IBalanceTracker,
+} from "../interface";
+import { DurationInSeconds } from "../utils";
+import { PricePusherMetrics } from "../metrics";
+import { Logger } from "pino";
 
 /**
  * EVM-specific configuration for balance tracker
@@ -49,3 +56,31 @@ export class EvmBalanceTracker extends BaseBalanceTracker {
     }
   }
 }
+
+/**
+ * Parameters for creating an EVM balance tracker
+ */
+export interface CreateEvmBalanceTrackerParams {
+  client: SuperWalletClient;
+  address: `0x${string}`;
+  network: string;
+  updateInterval: DurationInSeconds;
+  metrics: PricePusherMetrics;
+  logger: Logger;
+}
+
+/**
+ * Factory function to create a balance tracker for EVM chains
+ */
+export function createEvmBalanceTracker(
+  params: CreateEvmBalanceTrackerParams,
+): IBalanceTracker {
+  return new EvmBalanceTracker({
+    client: params.client,
+    address: params.address,
+    network: params.network,
+    updateInterval: params.updateInterval,
+    metrics: params.metrics,
+    logger: params.logger,
+  });
+}

+ 1 - 1
apps/price_pusher/src/evm/command.ts

@@ -12,7 +12,7 @@ import { createClient } from "./super-wallet";
 import { createPythContract } from "./pyth-contract";
 import { isWsEndpoint, filterInvalidPriceItems } from "../utils";
 import { PricePusherMetrics } from "../metrics";
-import { createEvmBalanceTracker } from "../balance-tracker";
+import { createEvmBalanceTracker } from "./balance-tracker";
 
 export default {
   command: "evm",

+ 96 - 0
apps/price_pusher/src/sui/balance-tracker.ts

@@ -0,0 +1,96 @@
+import { SuiClient } from "@mysten/sui/client";
+import {
+  BaseBalanceTracker,
+  BaseBalanceTrackerConfig,
+  IBalanceTracker,
+} from "../interface";
+import { DurationInSeconds } from "../utils";
+import { PricePusherMetrics } from "../metrics";
+import { Logger } from "pino";
+
+/**
+ * Sui-specific configuration for balance tracker
+ */
+export interface SuiBalanceTrackerConfig extends BaseBalanceTrackerConfig {
+  /** Sui client instance */
+  client: SuiClient;
+}
+
+/**
+ * Sui-specific implementation of the balance tracker
+ */
+export class SuiBalanceTracker extends BaseBalanceTracker {
+  private client: SuiClient;
+
+  constructor(config: SuiBalanceTrackerConfig) {
+    super({
+      ...config,
+      logger: config.logger.child({ module: "SuiBalanceTracker" }),
+    });
+
+    this.client = config.client;
+  }
+
+  /**
+   * Sui-specific implementation of balance update
+   */
+  protected async updateBalance(): Promise<void> {
+    try {
+      // Get all coins owned by the address
+      const { data: coins } = await this.client.getCoins({
+        owner: this.address,
+      });
+
+      // Sum up all coin balances
+      const totalBalance = coins.reduce((acc, coin) => {
+        return acc + BigInt(coin.balance);
+      }, BigInt(0));
+
+      // Convert to a normalized number for reporting (SUI has 9 decimals)
+      const normalizedBalance = Number(totalBalance) / 1e9;
+
+      this.metrics.updateWalletBalance(
+        this.address,
+        this.network,
+        normalizedBalance,
+      );
+
+      this.logger.debug(
+        `Updated Sui wallet balance: ${this.address} = ${normalizedBalance} SUI`,
+      );
+    } catch (error) {
+      this.logger.error(
+        { error },
+        "Error fetching Sui wallet balance for metrics",
+      );
+    }
+  }
+}
+
+/**
+ * Parameters for creating a Sui balance tracker
+ */
+export interface CreateSuiBalanceTrackerParams {
+  client: SuiClient;
+  address: string;
+  network: string;
+  updateInterval: DurationInSeconds;
+  metrics: PricePusherMetrics;
+  logger: Logger;
+}
+
+/**
+ * Factory function to create a balance tracker for Sui chain
+ */
+export function createSuiBalanceTracker(
+  params: CreateSuiBalanceTrackerParams,
+): IBalanceTracker {
+  return new SuiBalanceTracker({
+    client: params.client,
+    address: params.address,
+    network: params.network,
+    updateInterval: params.updateInterval,
+    metrics: params.metrics,
+    logger: params.logger,
+  });
+}

+ 39 - 6
apps/price_pusher/src/sui/command.ts

@@ -9,6 +9,9 @@ import { SuiPriceListener, SuiPricePusher } from "./sui";
 import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
 import pino from "pino";
 import { filterInvalidPriceItems } from "../utils";
+import { PricePusherMetrics } from "../metrics";
+import { createSuiBalanceTracker } from "./balance-tracker";
+import { SuiClient } from "@mysten/sui/client";
 
 export default {
   command: "sui",
@@ -72,6 +75,8 @@ export default {
     ...options.pushingFrequency,
     ...options.logLevel,
     ...options.controllerLogLevel,
+    ...options.enableMetrics,
+    ...options.metricsPort,
   },
   handler: async function (argv: any) {
     const {
@@ -89,6 +94,8 @@ export default {
       accountIndex,
       logLevel,
       controllerLogLevel,
+      enableMetrics,
+      metricsPort,
     } = argv;
 
     const logger = pino({ level: logLevel });
@@ -101,11 +108,8 @@ export default {
       mnemonic,
       `m/44'/784'/${accountIndex}'/0'/0'`,
     );
-    logger.info(
-      `Pushing updates from wallet address: ${keypair
-        .getPublicKey()
-        .toSuiAddress()}`,
-    );
+    const suiAddress = keypair.getPublicKey().toSuiAddress();
+    logger.info(`Pushing updates from wallet address: ${suiAddress}`);
 
     let priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
 
@@ -123,12 +127,22 @@ export default {
 
     priceItems = existingPriceItems;
 
+    // Initialize metrics if enabled
+    let metrics: PricePusherMetrics | undefined;
+    if (enableMetrics) {
+      metrics = new PricePusherMetrics(logger.child({ module: "Metrics" }));
+      metrics.start(metricsPort);
+      logger.info(`Metrics server started on port ${metricsPort}`);
+    }
+
     const pythListener = new PythPriceListener(
       hermesClient,
       priceItems,
       logger.child({ module: "PythPriceListener" }),
     );
 
+    const suiClient = new SuiClient({ url: endpoint });
+
     const suiListener = new SuiPriceListener(
       pythStateId,
       wormholeStateId,
@@ -137,6 +151,7 @@ export default {
       logger.child({ module: "SuiPriceListener" }),
       { pollingFrequency },
     );
+
     const suiPusher = await SuiPricePusher.createWithAutomaticGasPool(
       hermesClient,
       logger.child({ module: "SuiPricePusher" }),
@@ -155,9 +170,27 @@ export default {
       suiListener,
       suiPusher,
       logger.child({ module: "Controller" }, { level: controllerLogLevel }),
-      { pushingFrequency },
+      {
+        pushingFrequency,
+        metrics,
+      },
     );
 
+    // Create and start the balance tracker if metrics are enabled
+    if (metrics) {
+      const balanceTracker = createSuiBalanceTracker({
+        client: suiClient,
+        address: suiAddress,
+        network: "sui",
+        updateInterval: pushingFrequency,
+        metrics,
+        logger,
+      });
+
+      // Start the balance tracker
+      await balanceTracker.start();
+    }
+
     controller.start();
   },
 };