diff --git a/.changeset/honest-ants-own.md b/.changeset/honest-ants-own.md
new file mode 100644
index 0000000000000..2ccadabdefe58
--- /dev/null
+++ b/.changeset/honest-ants-own.md
@@ -0,0 +1,5 @@
+---
+'@eth-optimism/contracts-periphery': patch
+---
+
+Tweaks Drippie contract for client-side ease
diff --git a/.changeset/slow-numbers-knock.md b/.changeset/slow-numbers-knock.md
new file mode 100644
index 0000000000000..29eda7d4c1927
--- /dev/null
+++ b/.changeset/slow-numbers-knock.md
@@ -0,0 +1,5 @@
+---
+'@eth-optimism/drippie-mon': minor
+---
+
+Release drippie-mon
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 96c3b3c887592..b490599304998 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -91,6 +91,7 @@ jobs:
- packages/contracts-periphery/node_modules
- packages/core-utils/node_modules
- packages/data-transport-layer/node_modules
+ - packages/drippie-mon/node_modules
- packages/fault-detector/node_modules
- packages/message-relayer/node_modules
- packages/replica-healthcheck/node_modules
@@ -538,6 +539,11 @@ workflows:
package_name: fault-detector
requires:
- yarn-monorepo
+ - js-lint-test:
+ name: drippie-mon-tests
+ package_name: drippie-mon
+ requires:
+ - yarn-monorepo
- js-lint-test:
name: message-relayer-tests
package_name: message-relayer
@@ -628,6 +634,14 @@ workflows:
target: fault-detector
context:
- optimism
+ - docker-publish:
+ name: drippie-mon-release
+ docker_file: ops/docker/Dockerfile.packages
+ docker_tags: ethereumoptimism/drippie-mon:nightly
+ docker_context: .
+ target: drippie-mon
+ context:
+ - optimism
- docker-publish:
name: message-relayer-release
docker_file: ops/docker/Dockerfile.packages
diff --git a/.github/labeler.yml b/.github/labeler.yml
index e6a363ac6bb56..0682fd05e3d31 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -7,6 +7,7 @@
- 'packages/contracts/**/*'
- 'packages/contracts-periphery/**/*'
- 'packages/data-transport-layer/**/*'
+ - 'packages/drippie-mon/**/*'
- 'packages/message-relayer/**/*'
- 'packages/fault-detector/**/*'
- 'patches/**/*'
diff --git a/.github/workflows/publish-canary.yml b/.github/workflows/publish-canary.yml
index f96719111f312..7e666e0c076a7 100644
--- a/.github/workflows/publish-canary.yml
+++ b/.github/workflows/publish-canary.yml
@@ -18,6 +18,7 @@ jobs:
l2geth: ${{ steps.packages.outputs.l2geth }}
message-relayer: ${{ steps.packages.outputs.message-relayer }}
fault-detector: ${{ steps.packages.outputs.fault-detector }}
+ drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
data-transport-layer: ${{ steps.packages.outputs.data-transport-layer }}
contracts: ${{ steps.packages.outputs.contracts }}
gas-oracle: ${{ steps.packages.outputs.gas-oracle }}
@@ -229,6 +230,33 @@ jobs:
push: true
tags: ethereumoptimism/fault-detector:${{ needs.canary-publish.outputs.canary-docker-tag }}
+ drippie-mon:
+ name: Publish Drippie Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
+ needs: canary-publish
+ if: needs.canary-publish.outputs.drippie-mon != ''
+ 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: relayer
+ push: true
+ tags: ethereumoptimism/drippie-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}
+
data-transport-layer:
name: Publish Data Transport Layer Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 86b62226a4780..ac23cc7ecf25c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -14,6 +14,7 @@ jobs:
l2geth: ${{ steps.packages.outputs.l2geth }}
message-relayer: ${{ steps.packages.outputs.message-relayer }}
fault-detector: ${{ steps.packages.outputs.fault-detector }}
+ drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
data-transport-layer: ${{ steps.packages.outputs.data-transport-layer }}
contracts: ${{ steps.packages.outputs.contracts }}
gas-oracle: ${{ steps.packages.outputs.gas-oracle }}
@@ -372,6 +373,33 @@ jobs:
push: true
tags: ethereumoptimism/fault-detector:${{ needs.release.outputs.fault-detector }},ethereumoptimism/fault-detector:latest
+ drippie-mon:
+ name: Publish Drippie Monitor Version ${{ needs.release.outputs.drippie-mon }}
+ needs: release
+ if: needs.release.outputs.drippie-mon != ''
+ 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: drippie-mon
+ push: true
+ tags: ethereumoptimism/drippie-mon:${{ needs.release.outputs.drippie-mon }},ethereumoptimism/drippie-mon:latest
+
data-transport-layer:
name: Publish Data Transport Layer Version ${{ needs.release.outputs.data-transport-layer }}
needs: release
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 403c9aedc74c6..0d53657588b21 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -9,6 +9,7 @@
{"directory": "packages/contracts", "changeProcessCWD": true },
{"directory": "packages/contracts-periphery", "changeProcessCWD": true },
{"directory": "packages/data-transport-layer", "changeProcessCWD": true },
+ {"directory": "packages/drippie-mon", "changeProcessCWD": true },
{"directory": "packages/batch-submitter", "changeProcessCWD": true },
{"directory": "packages/message-relayer", "changeProcessCWD": true },
{"directory": "packages/fault-detector", "changeProcessCWD": true },
diff --git a/README.md b/README.md
index 4ef11f2438581..7d13ea6717b45 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,8 @@ root
│ ├── contracts-periphery: Peripheral contracts for Optimism
│ ├── core-utils: Low-level utilities that make building Optimism easier
│ ├── data-transport-layer: Service for indexing Optimism-related L1 data
-│ ├── fault-detector:
+│ ├── drippie-mon: Service for monitoring Drippie instances
+│ ├── fault-detector: Service for detecting Sequencer faults
│ ├── integration-tests-bedrock (BEDROCK upgrade): Bedrock integration tests.
│ ├── message-relayer: Tool for automatically relaying L1<>L2 messages in development
│ ├── replica-healthcheck: Service for monitoring the health of a replica node
diff --git a/packages/contracts-periphery/contracts/universal/drippie/Drippie.sol b/packages/contracts-periphery/contracts/universal/drippie/Drippie.sol
index 0a334d1e2afb1..bc30d8131264d 100644
--- a/packages/contracts-periphery/contracts/universal/drippie/Drippie.sol
+++ b/packages/contracts-periphery/contracts/universal/drippie/Drippie.sol
@@ -52,22 +52,39 @@ contract Drippie is AssetReceiver {
DripStatus status;
DripConfig config;
uint256 last;
+ uint256 count;
}
/**
* Emitted when a new drip is created.
*/
- event DripCreated(string indexed name, DripConfig config);
+ event DripCreated(
+ // Emit name twice because indexed version is hashed.
+ string indexed nameref,
+ string name,
+ DripConfig config
+ );
/**
* Emitted when a drip status is updated.
*/
- event DripStatusUpdated(string indexed name, DripStatus status);
+ event DripStatusUpdated(
+ // Emit name twice because indexed version is hashed.
+ string indexed nameref,
+ string name,
+ DripStatus status
+ );
/**
* Emitted when a drip is executed.
*/
- event DripExecuted(string indexed name, address indexed executor, uint256 timestamp);
+ event DripExecuted(
+ // Emit name twice because indexed version is hashed.
+ string indexed nameref,
+ string name,
+ address executor,
+ uint256 timestamp
+ );
/**
* Maps from drip names to drip states.
@@ -109,7 +126,7 @@ contract Drippie is AssetReceiver {
}
// Tell the world!
- emit DripCreated(_name, _config);
+ emit DripCreated(_name, _name, _config);
}
/**
@@ -163,20 +180,16 @@ contract Drippie is AssetReceiver {
// If we made it here then we can safely update the status.
drips[_name].status = _status;
- emit DripStatusUpdated(_name, drips[_name].status);
+ emit DripStatusUpdated(_name, _name, drips[_name].status);
}
/**
- * Triggers a drip. This function is deliberately left as a public function because the
- * assumption being made here is that setting the drip to ACTIVE is an affirmative signal that
- * the drip should be executable according to the drip parameters, drip check, and drip
- * interval. Note that drip parameters are read entirely from the state and are not supplied as
- * user input, so there should not be any way for a non-authorized user to influence the
- * behavior of the drip.
+ * Checks if a given drip is executable.
*
- * @param _name Name of the drip to trigger.
+ * @param _name Drip to check.
+ * @return True if the drip is executable, false otherwise.
*/
- function drip(string memory _name) external {
+ function executable(string memory _name) public view returns (bool) {
DripState storage state = drips[_name];
// Only allow active drips to be executed, an obvious security measure.
@@ -201,6 +214,29 @@ contract Drippie is AssetReceiver {
"Drippie: dripcheck failed so drip is not yet ready to be triggered"
);
+ // Alright, we're good to execute.
+ return true;
+ }
+
+ /**
+ * Triggers a drip. This function is deliberately left as a public function because the
+ * assumption being made here is that setting the drip to ACTIVE is an affirmative signal that
+ * the drip should be executable according to the drip parameters, drip check, and drip
+ * interval. Note that drip parameters are read entirely from the state and are not supplied as
+ * user input, so there should not be any way for a non-authorized user to influence the
+ * behavior of the drip.
+ *
+ * @param _name Name of the drip to trigger.
+ */
+ function drip(string memory _name) external {
+ DripState storage state = drips[_name];
+
+ // Make sure the drip can be executed.
+ require(
+ executable(_name) == true,
+ "Drippie: drip cannot be executed at this time, try again later"
+ );
+
// Update the last execution time for this drip before the call. Note that it's entirely
// possible for a drip to be executed multiple times per block or even multiple times
// within the same transaction (via re-entrancy) if the drip interval is set to zero. Users
@@ -240,6 +276,7 @@ contract Drippie is AssetReceiver {
);
}
- emit DripExecuted(_name, msg.sender, block.timestamp);
+ state.count++;
+ emit DripExecuted(_name, _name, msg.sender, block.timestamp);
}
}
diff --git a/packages/contracts-periphery/hardhat.config.ts b/packages/contracts-periphery/hardhat.config.ts
index a348517928a8d..51964bfcc9341 100644
--- a/packages/contracts-periphery/hardhat.config.ts
+++ b/packages/contracts-periphery/hardhat.config.ts
@@ -27,7 +27,7 @@ const config: HardhatUserConfig = {
},
},
},
- opkovan: {
+ 'optimism-kovan': {
chainId: 69,
url: 'https://kovan.optimism.io',
verify: {
@@ -97,7 +97,10 @@ const config: HardhatUserConfig = {
},
},
namedAccounts: {
- deployer: `ledger://${getenv('LEDGER_ADDRESS')}`,
+ deployer: {
+ default: `ledger://${getenv('LEDGER_ADDRESS')}`,
+ hardhat: 0,
+ },
},
}
diff --git a/packages/drippie-mon/.depcheckrc b/packages/drippie-mon/.depcheckrc
new file mode 100644
index 0000000000000..3a691b778839b
--- /dev/null
+++ b/packages/drippie-mon/.depcheckrc
@@ -0,0 +1,13 @@
+ignores: [
+ "@babel/eslint-parser",
+ "@typescript-eslint/parser",
+ "eslint-plugin-import",
+ "eslint-plugin-unicorn",
+ "eslint-plugin-jsdoc",
+ "eslint-plugin-prefer-arrow",
+ "eslint-plugin-react",
+ "@typescript-eslint/eslint-plugin",
+ "eslint-config-prettier",
+ "eslint-plugin-prettier",
+ "chai"
+]
diff --git a/packages/drippie-mon/.env.example b/packages/drippie-mon/.env.example
new file mode 100644
index 0000000000000..2db348516674e
--- /dev/null
+++ b/packages/drippie-mon/.env.example
@@ -0,0 +1,5 @@
+# RPC pointing to network where Drippie is deployed
+DRIPPIE_MON__RPC=
+
+# Address of the Drippie contract
+DRIPPIE_MON__DRIPPIE_ADDRESS=
diff --git a/packages/drippie-mon/.eslintrc.js b/packages/drippie-mon/.eslintrc.js
new file mode 100644
index 0000000000000..bfd2057be80bd
--- /dev/null
+++ b/packages/drippie-mon/.eslintrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ extends: '../../.eslintrc.js',
+}
diff --git a/packages/drippie-mon/.lintstagedrc.yml b/packages/drippie-mon/.lintstagedrc.yml
new file mode 100644
index 0000000000000..a3035a2299b26
--- /dev/null
+++ b/packages/drippie-mon/.lintstagedrc.yml
@@ -0,0 +1,2 @@
+"*.{ts,js}":
+ - eslint
diff --git a/packages/drippie-mon/.prettierrc.js b/packages/drippie-mon/.prettierrc.js
new file mode 100644
index 0000000000000..6b3fa8e2ce237
--- /dev/null
+++ b/packages/drippie-mon/.prettierrc.js
@@ -0,0 +1,3 @@
+module.exports = {
+ ...require('../../.prettierrc.js'),
+};
\ No newline at end of file
diff --git a/packages/drippie-mon/LICENSE b/packages/drippie-mon/LICENSE
new file mode 100644
index 0000000000000..6a7da5218bb25
--- /dev/null
+++ b/packages/drippie-mon/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright 2020-2021 Optimism
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/drippie-mon/README.md b/packages/drippie-mon/README.md
new file mode 100644
index 0000000000000..629a5b70f98aa
--- /dev/null
+++ b/packages/drippie-mon/README.md
@@ -0,0 +1,22 @@
+# @eth-optimism/drippie-mon
+
+`drippie-mon` is a simple service for monitoring Drippie contracts.
+
+## Installation
+
+Clone, install, and build the Optimism monorepo:
+
+```
+git clone https://github.com/ethereum-optimism/optimism.git
+yarn install
+yarn build
+```
+
+## Running the service
+
+Copy `.env.example` into a new file named `.env`, then set the environment variables listed there.
+Once your environment variables have been set, run via:
+
+```
+yarn start
+```
diff --git a/packages/drippie-mon/package.json b/packages/drippie-mon/package.json
new file mode 100644
index 0000000000000..70f329ca8b61e
--- /dev/null
+++ b/packages/drippie-mon/package.json
@@ -0,0 +1,45 @@
+{
+ "private": true,
+ "name": "@eth-optimism/drippie-mon",
+ "version": "0.1.0",
+ "description": "[Optimism] Service for monitoring Drippie instances",
+ "main": "dist/index",
+ "types": "dist/index",
+ "files": [
+ "dist/*"
+ ],
+ "scripts": {
+ "start": "ts-node ./src/service.ts",
+ "test:coverage": "echo 'No tests defined.'",
+ "build": "tsc -p ./tsconfig.json",
+ "clean": "rimraf dist/ ./tsconfig.tsbuildinfo",
+ "lint": "yarn lint:fix && yarn lint:check",
+ "pre-commit": "lint-staged",
+ "lint:fix": "yarn lint:check --fix",
+ "lint:check": "eslint . --max-warnings=0"
+ },
+ "keywords": [
+ "optimism",
+ "ethereum",
+ "drippie",
+ "monitoring"
+ ],
+ "homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/drippie-mon#readme",
+ "license": "MIT",
+ "author": "Optimism PBC",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ethereum-optimism/optimism.git"
+ },
+ "dependencies": {
+ "@eth-optimism/common-ts": "0.2.9",
+ "@eth-optimism/contracts-periphery": "0.1.1",
+ "@eth-optimism/core-utils": "0.8.6",
+ "@eth-optimism/sdk": "1.1.7",
+ "ethers": "^5.6.8"
+ },
+ "devDependencies": {
+ "@ethersproject/abstract-provider": "^5.6.1",
+ "ts-node": "^10.0.0"
+ }
+}
diff --git a/packages/drippie-mon/src/index.ts b/packages/drippie-mon/src/index.ts
new file mode 100644
index 0000000000000..caf7fffa10172
--- /dev/null
+++ b/packages/drippie-mon/src/index.ts
@@ -0,0 +1 @@
+export * from './service'
diff --git a/packages/drippie-mon/src/service.ts b/packages/drippie-mon/src/service.ts
new file mode 100644
index 0000000000000..4958483a84efa
--- /dev/null
+++ b/packages/drippie-mon/src/service.ts
@@ -0,0 +1,201 @@
+import {
+ BaseServiceV2,
+ Gauge,
+ Counter,
+ validators,
+} from '@eth-optimism/common-ts'
+import { Provider } from '@ethersproject/abstract-provider'
+import { ethers } from 'ethers'
+import * as DrippieArtifact from '@eth-optimism/contracts-periphery/artifacts/contracts/universal/drippie/Drippie.sol/Drippie.json'
+
+type DrippieMonOptions = {
+ rpc: Provider
+ drippieAddress: string
+}
+
+type DrippieMonMetrics = {
+ metadata: Gauge
+ isExecutable: Gauge
+ executedDripCount: Gauge
+ unexpectedRpcErrors: Counter
+}
+
+type DrippieMonState = {
+ drippie: ethers.Contract
+}
+
+export class DrippieMonService extends BaseServiceV2<
+ DrippieMonOptions,
+ DrippieMonMetrics,
+ DrippieMonState
+> {
+ constructor(options?: Partial) {
+ super({
+ name: 'drippie-mon',
+ loop: true,
+ loopIntervalMs: 60_000,
+ options,
+ optionsSpec: {
+ rpc: {
+ validator: validators.provider,
+ desc: 'Provider for network where Drippie is deployed',
+ },
+ drippieAddress: {
+ validator: validators.str,
+ desc: 'Address of Drippie contract',
+ },
+ },
+ metricsSpec: {
+ metadata: {
+ type: Gauge,
+ desc: 'Drippie Monitor metadata',
+ labels: ['version', 'address'],
+ },
+ isExecutable: {
+ type: Gauge,
+ desc: 'Whether or not the drip is currently executable',
+ labels: ['name'],
+ },
+ executedDripCount: {
+ type: Gauge,
+ desc: 'Number of times a drip has been executed',
+ labels: ['name'],
+ },
+ unexpectedRpcErrors: {
+ type: Counter,
+ desc: 'Number of unexpected RPC errors',
+ labels: ['section', 'name'],
+ },
+ },
+ })
+ }
+
+ protected async init(): Promise {
+ this.state.drippie = new ethers.Contract(
+ this.options.drippieAddress,
+ DrippieArtifact.abi,
+ this.options.rpc
+ )
+
+ this.metrics.metadata.set(
+ {
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ version: require('../package.json').version,
+ address: this.options.drippieAddress,
+ },
+ 1
+ )
+ }
+
+ protected async main(): Promise {
+ let dripCreatedEvents: ethers.Event[]
+ try {
+ dripCreatedEvents = await this.state.drippie.queryFilter(
+ this.state.drippie.filters.DripCreated()
+ )
+ } catch (err) {
+ this.logger.info(`got unexpected RPC error`, {
+ section: 'creations',
+ name: 'NULL',
+ err,
+ })
+
+ this.metrics.unexpectedRpcErrors.inc({
+ section: 'creations',
+ name: 'NULL',
+ })
+
+ return
+ }
+
+ // Not the most efficient thing in the world. Will end up making one request for every drip
+ // created. We don't expect there to be many drips, so this is fine for now. We can also cache
+ // and skip any archived drips to cut down on a few requests. Worth keeping an eye on this to
+ // see if it's a bottleneck.
+ for (const event of dripCreatedEvents) {
+ const name = event.args.name
+
+ let drip: any
+ try {
+ drip = await this.state.drippie.drips(name)
+ } catch (err) {
+ this.logger.info(`got unexpected RPC error`, {
+ section: 'drips',
+ name,
+ err,
+ })
+
+ this.metrics.unexpectedRpcErrors.inc({
+ section: 'drips',
+ name,
+ })
+
+ continue
+ }
+
+ this.logger.info(`getting drip executable status`, {
+ name,
+ count: drip.count.toNumber(),
+ })
+
+ this.metrics.executedDripCount.set(
+ {
+ name,
+ },
+ drip.count.toNumber()
+ )
+
+ let executable: boolean
+ try {
+ // To avoid making unnecessary RPC requests, filter out any drips that we don't expect to
+ // be executable right now. Only active drips (status = 1) and drips that are due to be
+ // executed are expected to be executable (but might not be based on the dripcheck).
+ if (
+ drip.status === 1 &&
+ drip.last.toNumber() + drip.config.interval.toNumber() <
+ Date.now() / 1000
+ ) {
+ executable = await this.state.drippie.executable(name)
+ } else {
+ executable = false
+ }
+ } catch (err) {
+ // All reverts include the string "Drippie:", so we can check for that.
+ if (err.message.includes('Drippie:')) {
+ // Not executable yet.
+ executable = false
+ } else {
+ this.logger.info(`got unexpected RPC error`, {
+ section: 'executable',
+ name,
+ err,
+ })
+
+ this.metrics.unexpectedRpcErrors.inc({
+ section: 'executable',
+ name,
+ })
+
+ continue
+ }
+ }
+
+ this.logger.info(`got drip executable status`, {
+ name,
+ executable,
+ })
+
+ this.metrics.isExecutable.set(
+ {
+ name,
+ },
+ executable ? 1 : 0
+ )
+ }
+ }
+}
+
+if (require.main === module) {
+ const service = new DrippieMonService()
+ service.run()
+}
diff --git a/packages/drippie-mon/tsconfig.json b/packages/drippie-mon/tsconfig.json
new file mode 100644
index 0000000000000..5cb4fda3c5469
--- /dev/null
+++ b/packages/drippie-mon/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "./src",
+ "outDir": "./dist"
+ },
+ "include": [
+ "src/**/*"
+ ]
+}