github.com/ethereum-optimism/optimism@v1.7.2/packages/chain-mon/src/balance-mon/service.ts (about)

     1  import {
     2    BaseServiceV2,
     3    StandardOptions,
     4    Gauge,
     5    Counter,
     6    validators,
     7  } from '@eth-optimism/common-ts'
     8  import { Provider } from '@ethersproject/abstract-provider'
     9  
    10  import { version } from '../../package.json'
    11  
    12  type BalanceMonOptions = {
    13    rpc: Provider
    14    accounts: string
    15  }
    16  
    17  type BalanceMonMetrics = {
    18    balances: Gauge
    19    unexpectedRpcErrors: Counter
    20  }
    21  
    22  type BalanceMonState = {
    23    accounts: Array<{ address: string; nickname: string }>
    24  }
    25  
    26  export class BalanceMonService extends BaseServiceV2<
    27    BalanceMonOptions,
    28    BalanceMonMetrics,
    29    BalanceMonState
    30  > {
    31    constructor(options?: Partial<BalanceMonOptions & StandardOptions>) {
    32      super({
    33        version,
    34        name: 'balance-mon',
    35        loop: true,
    36        options: {
    37          loopIntervalMs: 60_000,
    38          ...options,
    39        },
    40        optionsSpec: {
    41          rpc: {
    42            validator: validators.provider,
    43            desc: 'Provider for network to monitor balances on',
    44          },
    45          accounts: {
    46            validator: validators.str,
    47            desc: 'JSON array of [{ address, nickname, safe }] to monitor balances and nonces of',
    48            public: true,
    49          },
    50        },
    51        metricsSpec: {
    52          balances: {
    53            type: Gauge,
    54            desc: 'Balances of addresses',
    55            labels: ['address', 'nickname'],
    56          },
    57          unexpectedRpcErrors: {
    58            type: Counter,
    59            desc: 'Number of unexpected RPC errors',
    60            labels: ['section', 'name'],
    61          },
    62        },
    63      })
    64    }
    65  
    66    protected async init(): Promise<void> {
    67      this.state.accounts = JSON.parse(this.options.accounts)
    68    }
    69  
    70    protected async main(): Promise<void> {
    71      for (const account of this.state.accounts) {
    72        try {
    73          const balance = await this.options.rpc.getBalance(account.address)
    74          this.logger.info(`got balance`, {
    75            address: account.address,
    76            nickname: account.nickname,
    77            balance: balance.toString(),
    78          })
    79  
    80          // Parse the balance as an integer instead of via toNumber() to avoid ethers throwing an
    81          // an error. We might get rounding errors but we don't need perfect precision here, just a
    82          // generally accurate sense for what the current balance is.
    83          this.metrics.balances.set(
    84            { address: account.address, nickname: account.nickname },
    85            parseInt(balance.toString(), 10)
    86          )
    87        } catch (err) {
    88          this.logger.info(`got unexpected RPC error`, {
    89            section: 'balances',
    90            name: 'getBalance',
    91            err,
    92          })
    93          this.metrics.unexpectedRpcErrors.inc({
    94            section: 'balances',
    95            name: 'getBalance',
    96          })
    97        }
    98      }
    99    }
   100  }
   101  
   102  if (require.main === module) {
   103    const service = new BalanceMonService()
   104    service.run()
   105  }