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 }