Commit fa36d8b4 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #5988 from ethereum-optimism/cleanup/delete-legacy-balance-monitor

cleanup: delete legacy balance monitor
parents 008db717 eb99ec54
...@@ -79,7 +79,6 @@ jobs: ...@@ -79,7 +79,6 @@ jobs:
key: yarn-packages-v2-{{ checksum "yarn.lock" }} key: yarn-packages-v2-{{ checksum "yarn.lock" }}
paths: paths:
- "node_modules" - "node_modules"
- "packages/balance-monitor/node_modules"
- "packages/chain-mon/node_modules" - "packages/chain-mon/node_modules"
- "packages/common-ts/node_modules" - "packages/common-ts/node_modules"
- "packages/contracts-bedrock/node_modules" - "packages/contracts-bedrock/node_modules"
...@@ -1154,12 +1153,6 @@ workflows: ...@@ -1154,12 +1153,6 @@ workflows:
dependencies: "(contracts|core-utils)" dependencies: "(contracts|core-utils)"
requires: requires:
- yarn-monorepo - yarn-monorepo
- js-lint-test:
name: balance-monitor-tests
coverage_flag: balance-monitor-tests
package_name: balance-monitor
requires:
- yarn-monorepo
- depcheck: - depcheck:
requires: requires:
- yarn-monorepo - yarn-monorepo
......
...@@ -21,7 +21,6 @@ jobs: ...@@ -21,7 +21,6 @@ jobs:
wd-mon: ${{ steps.packages.outputs.wd-mon }} wd-mon: ${{ steps.packages.outputs.wd-mon }}
contracts: ${{ steps.packages.outputs.contracts }} contracts: ${{ steps.packages.outputs.contracts }}
contracts-bedrock: ${{ steps.packages.outputs.contracts-bedrock }} contracts-bedrock: ${{ steps.packages.outputs.contracts-bedrock }}
balance-monitor: ${{ steps.packages.outputs.balance-monitor }}
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }} replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
hardhat-node: ${{ steps.packages.outputs.hardhat-node }} hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter: ${{ steps.packages.outputs.op-exporter }} op-exporter: ${{ steps.packages.outputs.op-exporter }}
...@@ -358,33 +357,6 @@ jobs: ...@@ -358,33 +357,6 @@ jobs:
push: true push: true
tags: ethereumoptimism/deployer-bedrock:${{ needs.release.outputs.contracts-bedrock }},ethereumoptimism/deployer-bedrock:latest tags: ethereumoptimism/deployer-bedrock:${{ needs.release.outputs.contracts-bedrock }},ethereumoptimism/deployer-bedrock:latest
balance-monitor:
name: Publish balance-monitor Version ${{ needs.release.outputs.balance-monitor }}
needs: release
if: needs.release.outputs.balance-monitor != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: balance-monitor
push: true
tags: ethereumoptimism/balance-monitor:${{ needs.release.outputs.balance-monitor }},ethereumoptimism/balance-monitor:latest
replica-healthcheck: replica-healthcheck:
name: Publish Replica Healthcheck Version ${{ needs.release.outputs.replica-healthcheck }} name: Publish Replica Healthcheck Version ${{ needs.release.outputs.replica-healthcheck }}
needs: release needs: release
......
...@@ -86,10 +86,6 @@ FROM base as drippie-mon ...@@ -86,10 +86,6 @@ FROM base as drippie-mon
WORKDIR /opt/optimism/packages/chain-mon WORKDIR /opt/optimism/packages/chain-mon
ENTRYPOINT ["npm", "run", "start:drippie-mon"] ENTRYPOINT ["npm", "run", "start:drippie-mon"]
FROM base as balance-monitor
WORKDIR /opt/optimism/packages/balance-monitor
ENTRYPOINT ["yarn", "run", "start:prod"]
FROM base as wd-mon FROM base as wd-mon
WORKDIR /opt/optimism/packages/chain-mon WORKDIR /opt/optimism/packages/chain-mon
ENTRYPOINT ["yarn", "run", "start:wd-mon"] ENTRYPOINT ["yarn", "run", "start:wd-mon"]
export L1_RPC_URL=
export OPS_GENIE_KEY=
export OPS_GENIE_TEAM=
export OPS_GENIE_HEARTBEAT_NAME=
# Threshold values are denominated in ETH.
export SEQUENCER_ADDRESS=
export SEQUENCER_DANGER_THRESHOLD=10
export PROPOSER_ADDRESS=
export PROPOSER_DANGER_THRESHOLD=10
module.exports = {
extends: '../../.eslintrc.js',
ignorePatterns: ['src/contract-artifacts.ts'],
}
node_modules
dist
forta.config.json
module.exports = {
...require('../../.prettierrc.js'),
}
# @eth-optimism/balance-monitor
## 0.0.4
### Patch Changes
- 013bd456f: Fixed the name in Dockerfile.packages
## 0.0.3
### Patch Changes
- 082a4ff00: Fix balance monitor package json
## 0.0.2
### Patch Changes
- 63ac3a63c: Added basic balance monitoring
- 2b931bc36: Created the Balance Monitoring package
# Minimum Balance Agent
## Description
A forta agent which detects when a specified account balance is below the
specified threshold.
## Installing and building
`yarn && yarn build`
## Running
1. Copy `.env.example` into `.env` and set the appropriate values.
2. Copy `forta.config.example.json` into `forta.config.json`, and set the RPC endpoint (yes, this is
duplicated in the .env file).
2. `yarn run start:prod`
## Alerts
- `OPTIMISM-BALANCE-DANGER-[ACCOUNT_NAME]`
- Fired when the specified account balance is below the configured DANGER threshold
- Severity is always set to "high"
- Type is always set to "info"
- Metadata "balance" field contains amount of wei in account
{
"name": "@eth-optimism/balance-monitor",
"version": "0.0.4",
"description": "[Optimism] Forta Agent that reports whether certain accounts have fallen below some balance",
"main": "dist/index",
"types": "dist/index",
"files": [
"dist/*",
"src/*"
],
"scripts": {
"build": "tsc -p tsconfig.json",
"clean": "rimraf dist/ ./tsconfig.tsbuildinfo",
"start": "yarn run start:dev",
"start:dev": "nodemon --watch src --watch forta.config.json -e js,ts,json --exec 'yarn run build && forta-agent run'",
"start:prod": "forta-agent run",
"tx": "yarn run build && forta-agent run --tx",
"block": "yarn run build && forta-agent run --block",
"range": "yarn run build && forta-agent run --range",
"test": "./scripts/test.sh",
"test:coverage": "echo 'todo: configure test coverage' && yarn test",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"lint": "yarn lint:fix && yarn lint:check"
},
"keywords": [
"optimism",
"ethereum",
"forta",
"monitoring"
],
"homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/balance-monitor#readme",
"license": "MIT",
"author": "Optimism PBC",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism.git"
},
"dependencies": {
"ethers": "^5.7.2",
"node-fetch": "^2.6.1",
"forta-agent": "^0.1.1"
},
"devDependencies": {
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/nodemon": "^1.19.0",
"chai": "^4.3.4",
"nodemon": "^2.0.8",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.4"
},
"chainIds": [
5
]
}
#!/bin/bash
export SEQUENCER_ADDRESS=0xabba
export SEQUENCER_DANGER_THRESHOLD=100 # 100 eth
export PROPOSER_ADDRESS=0xacdc
export PROPOSER_DANGER_THRESHOLD=200 # 200 eth
yarn ts-mocha src/*.spec.ts
import {
Finding,
FindingSeverity,
FindingType,
HandleBlock,
createBlockEvent,
} from 'forta-agent'
import { BigNumber, utils } from 'ethers'
import { expect } from 'chai'
import agent, { accounts } from './agent'
import { describeFinding } from './utils'
describe('minimum balance agent', () => {
let handleBlock: HandleBlock
let mockEthersProvider
const blockEvent = createBlockEvent({
block: { hash: '0xa', number: 1 } as any,
})
// A function which returns a mock provider to give us values based on the case we want
// to test.
const mockEthersProviderByCase = (severity: string) => {
switch (severity) {
case 'safe':
return {
getBalance: async (addr: string): Promise<BigNumber> => {
if (addr === '0xabba') {
return utils.parseEther('101')
}
if (addr === '0xacdc') {
return utils.parseEther('2001')
}
},
} as any
case 'danger':
return {
getBalance: async (addr: string): Promise<BigNumber> => {
if (addr === '0xabba') {
return utils.parseEther('99') // below danger threshold
}
if (addr === '0xacdc') {
return utils.parseEther('2001')
}
},
} as any
default:
break
}
}
before(() => {
handleBlock = agent.provideHandleBlock(mockEthersProvider)
})
describe('handleBlock', () => {
it('returns empty findings if balance is above threshold', async () => {
mockEthersProvider = mockEthersProviderByCase('safe')
handleBlock = agent.provideHandleBlock(mockEthersProvider)
const findings = await handleBlock(blockEvent)
expect(findings).to.deep.equal([])
})
it('returns high severity finding if balance is below danger threshold', async () => {
mockEthersProvider = mockEthersProviderByCase('danger')
handleBlock = agent.provideHandleBlock(mockEthersProvider)
const balance = await mockEthersProvider.getBalance('0xabba')
const findings = await handleBlock(blockEvent)
// Take the second alert in the list, as the first is a warning
expect(findings).to.deep.equal([
Finding.fromObject({
name: 'Minimum Account Balance',
description: describeFinding(
accounts[0].address,
balance,
accounts[0].thresholds.danger
),
alertId: 'OPTIMISM-BALANCE-DANGER-Sequencer',
severity: FindingSeverity.High,
type: FindingType.Info,
metadata: {
balance: balance.toString(),
},
}),
])
})
})
})
import {
BlockEvent,
Finding,
HandleBlock,
FindingSeverity,
FindingType,
} from 'forta-agent'
import { BigNumber, providers, utils } from 'ethers'
import { createAlert, heartBeat, describeFinding } from './utils'
type AccountAlert = {
name: string
address: string
thresholds: {
danger: BigNumber
}
}
export const accounts: AccountAlert[] = [
{
name: 'Sequencer',
address: process.env.SEQUENCER_ADDRESS,
thresholds: {
danger: utils.parseEther(process.env.SEQUENCER_DANGER_THRESHOLD),
},
},
{
name: 'Proposer',
address: process.env.PROPOSER_ADDRESS,
thresholds: {
danger: utils.parseEther(process.env.PROPOSER_DANGER_THRESHOLD),
},
},
]
const provideHandleBlock = (
provider: providers.JsonRpcProvider
): HandleBlock => {
return async (blockEvent: BlockEvent) => {
// report finding if specified account balance falls below threshold
const findings: Finding[] = []
// iterate over accounts with the index
for (const [, account] of accounts.entries()) {
const accountBalance = BigNumber.from(
(
await provider.getBalance(account.address, blockEvent.blockNumber)
).toString()
)
if (accountBalance.lte(account.thresholds.danger)) {
const alertId = `OPTIMISM-BALANCE-DANGER-${account.name}`
const description = describeFinding(
account.address,
accountBalance,
account.thresholds.danger
)
// If an alert is already open with the same alertId, this will have no effect.
// Alerts must be disabled manually in opsgenie. We don't provide a method here
// for closing when the balance is above the threshold again.
if (process.env.OPS_GENIE_KEY !== undefined) {
await createAlert({ alias: alertId, message: description })
}
// Add to the findings array. This will only be meaningful when running on
// public forta nodes.
findings.push(
Finding.fromObject({
name: 'Minimum Account Balance',
description,
alertId,
severity: FindingSeverity.High,
type: FindingType.Info,
metadata: {
balance: accountBalance.toString(),
},
})
)
}
}
// Let ops-genie know that we're still alive.
await heartBeat()
return findings
}
}
const l1Provider = new providers.JsonRpcProvider(process.env.L1_RPC_URL)
export default {
provideHandleBlock,
handleBlock: provideHandleBlock(l1Provider),
}
// import 'ethers'
import { ethers } from 'ethers'
import fetch from 'node-fetch'
// new function to log an account an it's balance
export const describeFinding = (
account: string,
actual: ethers.BigNumber,
threshold: ethers.BigNumber
) => {
return `Balance of account ${account} is (${ethers.utils.formatEther(
actual
)} eth) below threshold (${ethers.utils.formatEther(threshold)} eth)`
}
// Create an alert in ops-genie. The alias will be used an unique identifier for the alert.
// There can only be one open alert per alias. If this is called with an alias which already
// has an alert, it will not be reopened.
export const createAlert = async (alertOpts: {
alias: string
message: string
}) => {
const response = await fetch('https://api.opsgenie.com/v2/alerts', {
method: 'post',
body: JSON.stringify({
message: alertOpts.message,
alias: alertOpts.alias,
responders: [{ id: process.env.OPS_GENIE_TEAM, type: 'team' }],
tags: ['Bedrock-Beta', 'Balance-Low'],
priority: 'P2',
}),
headers: {
'Content-type': 'application/json',
Authorization: `GenieKey ${process.env.OPS_GENIE_KEY}`,
},
})
if (!response.ok) {
console.log(`Error creating alert: ${JSON.stringify(response.body)}`)
}
}
// Send this with every block. If Ops Genie doesn't get this ping for 10 minutes,
// it will trigger a P3 alert.
export const heartBeat = async () => {
const response = await fetch(
`https://api.opsgenie.com/v2/heartbeats/${process.env.OPS_GENIE_HEARTBEAT_NAME}/ping`,
{
method: 'get',
headers: {
Authorization: `GenieKey ${process.env.OPS_GENIE_KEY}`,
},
}
)
if (!response.ok) {
console.log(`Error creating alert: ${JSON.stringify(response.body)}`)
}
}
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": [
"src/**/*"
]
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment