commit 113f0133097cd8477682a54b5e5afc2d5bbd6f18 Author: Patrick Balsiger Date: Sat Jan 13 01:31:26 2024 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9657e68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.build/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..72429f1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM public.ecr.aws/bitnami/node:16 +RUN apt-get install git +ENV NODE_ENV=production +RUN npm install -g yarn +RUN npm install -g typescript + +WORKDIR /app + +COPY package.json ./ + +RUN yarn install +COPY . . +RUN yarn build +RUN yarn install --production + +EXPOSE 9464 + +CMD [ "yarn", "start", "--config-file=config.yaml" ] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..32b4452 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Drift Keeper Bot + +> Keeper Bots in the Drift Protocol keep the protocol operational by performing automated actions as autonomous off-chain agents. Keepers are rewarded depending on the duties that they perform. + +More information: +- https://github.com/drift-labs/keeper-bots-v2/ +- https://docs.drift.trade/keeper-bots +- https://docs.drift.trade/tutorial-order-matching-bot + +## Prerequisites + +- Docker, Docker-Compose +- Solana RPC Endpoint +- Solana Private Key for signing transactions +- Jito Private Key for auth to block engine API (optional) + +Drift account is setup according to: https://docs.drift.trade/keeper-bots. +The account can also be set up using the trading app: https://app.drift.trade/. + +## Setup + +Create .env file from example and configure all environment variables. + +``` +cp example.env .env +``` + +## Build + +Clone the keeper-bots repository and build the Docker image. + +``` +./ctl.sh image build +``` + +## Run + +Run the bot. + +``` +docker compose up +``` + +## Metrics + +The bot exposes Prometheus metrics that are automatically scraped. +A Grafana dashboard is exposed on http://localhost:3000 with default username/password: grafana/admin. diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..7197387 --- /dev/null +++ b/config.yaml @@ -0,0 +1,191 @@ +global: + # devnet or mainnet-beta + driftEnv: mainnet-beta + + # RPC endpoint to use + endpoint: + # custom websocket endpoint to use (if null will be determined from rpc endpoint) + wsEndpoint: + + # Private key to use to sign transactions. + # will load from KEEPER_PRIVATE_KEY env var if null + keeperPrivateKey: + + initUser: false # initialize user on startup + testLiveness: false # test liveness, by failing liveness test after 1 min + + # Force deposit this amount of USDC to collateral account, the program will + # end after the deposit transaction is sent + #forceDeposit: 1000 + + websocket: false # use websocket for account loading and events (limited support) + eventSubscriber: false # disables event subscriber (heavy RPC demand), this is primary used for counting fills + runOnce: false # Set true to run once and exit, useful for testing or one off bot runs + debug: false # Enable debug logging + txSenderType: "fast" + + # subaccountIDs to load, if null will load subaccount 0 (default). + # Even if bot specific configs requires subaccountIDs, you should still + # specify it here, since we load the subaccounts before loading individual + # bots. + # subaccounts: + # - 0 + # - 1 + # - 2 + + eventSubscriberPollingInterval: 5000 + bulkAccountLoaderPollingInterval: 5000 + + useJito: false + jitoBlockEngineUrl: + jitoAuthPrivateKey: + + # which subaccounts to load, if null will load subaccount 0 (default) + subaccounts: + +# perpMarketIndexes: +# - 1 + +# Which bots to run, be careful with this, running multiple bots in one instance +# might use more resources than expected. +# Bot specific configs are below +enabledBots: + # Perp order filler bot + - fillerLite + + # Spot order filler bot + #- spotFiller + + # Trigger bot (triggers trigger orders) + #- trigger + # Liquidator bot, liquidates unhealthy positions by taking over the risk (caution, you should manage risk here) + # - liquidator + + # Example maker bot that participates in JIT auction (caution: you will probably lose money) + # - jitMaker + + # Example maker bot that posts floating oracle orders + #- floatingMaker + + # settles PnLs for the insurance fund (may want to run with runOnce: true) + # - ifRevenueSettler + + # settles negative PnLs for users (may want to run with runOnce: true) + # - userPnlSettler + + # - markTwapCrank + + # below are bot configs +botConfigs: + floatingMaker: + botId: "floatingMaker" + dryRun: false + fillerPollingInterval: 6000 + metricsPort: 9464 + revertOnFailure: true + + fillerLite: + botId: "fillerLite" + dryRun: false + fillerPollingInterval: 6000 + metricsPort: 9464 + revertOnFailure: true + + filler: + botId: "filler" + dryRun: false + fillerPollingInterval: 6000 + metricsPort: 9464 + + # will revert a transaction during simulation if a fill fails, this will save on tx fees, + # and be friendlier for use with services like Jito. + # Default is true + revertOnFailure: true + + spotFiller: + botId: "spot-filler" + dryRun: false + fillerPollingInterval: 6000 + metricsPort: 9466 + + liquidator: + botId: "liquidator" + dryRun: false + metricsPort: 9465 + # if true will NOT attempt to sell off any inherited positions + disableAutoDerisking: false + # if true will swap spot assets on jupiter if the price is better + useJupiter: true + # null will handle all markets + perpMarketIndicies: + spotMarketIndicies: + + # this replaces perpMarketIndicies and spotMarketIndicies by allowing you to specify + # which subaccount is responsible for liquidating markets + # Markets are defined on perpMarkets.ts and spotMarkets.ts on the protocol codebase + # Note: you must set global.subaccounts with all the subaccounts you want to load + perpSubAccountConfig: + 0: + # subaccount 0 will watch perp markets 0 and 1 + - 0 + - 1 + spotSubAccountConfig: + 0: + # subaccount 0 will watch all spot markets + + # max slippage (from oracle price) to incur allow when derisking + maxSlippagePct: 0.05 + + # what algo to use for derisking. Options are "market" or "twap" + deriskAlgo: "market" + + # if deriskAlgo == "twap", must supply these as well + # twapDurationSec: 300 # overall duration of to run the twap algo. Aims to derisk the entire position over this duration + + # Minimum deposit amount to try to liquidiate, per spot market, in lamports. + # If null, or a spot market isn't here, it will liquidate any amount + # See perpMarkets.ts on the protocol codebase for the indices + minDepositToLiq: + 1: 10 + 2: 1000 + + # Filter out un-liquidateable accounts that just create log noise + excludedAccounts: + - 9CJLgd5f9nmTp7KRV37RFcQrfEmJn6TU87N7VQAe2Pcq + - Edh39zr8GnQFNYwyvxhPngTJHrr29H3vVup8e8ZD4Hwu + + # max % of collateral to spend when liquidating a user. In percentage terms (0.5 = 50%) + maxPositionTakeoverPctOfCollateral: 0.5 + + # sends a webhook notification (slack, discord, etc.) when a liquidation is attempted (can be noisy due to partial liquidations) + notifyOnLiquidation: true + + trigger: + botId: "trigger" + dryRun: false + metricsPort: 9465 + + markTwapCrank: + botId: "mark-twap-cranker" + dryRun: false + metricsPort: 9465 + crankIntervalToMarketIndicies: + 15000: + - 0 + - 1 + - 2 + 60000: + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 diff --git a/ctl.sh b/ctl.sh new file mode 100755 index 0000000..773e670 --- /dev/null +++ b/ctl.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +LAMPORTS_PER_SOL=1000000000 +API_ENDPOINT=https://api.mainnet-beta.solana.com/ + +source .env + +function balance { + function sol { + local addr=${1:-$WALLET_ADDRESS} + local balance=$(curl $API_ENDPOINT -s -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getBalance", + "params": [ + "'${addr}'" + ] + } + ' | jq .result.value) + echo "$(jq -n $balance/$LAMPORTS_PER_SOL)" + } + + function usdc { + local addr=${1:-$WALLET_ADDRESS} + local balance=$( + curl $API_ENDPOINT -s -X POST -H "Content-Type: application/json" -d ' + { + "jsonrpc": "2.0", "id": 1, + "method": "getTokenAccountsByOwner", + "params": [ + "'${addr}'", + { + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" + },{ + "encoding": "jsonParsed" + }] + }' | jq .result.value[0].account.data.parsed.info.tokenAmount.uiAmount) + echo ${balance} + + } + ${@:-} +} + +function image { + function build { + mkdir -p .build + git clone https://github.com/drift-labs/keeper-bots-v2 -b mainnet-beta .build/keeper-bots-v2 + docker build -f Dockerfile -t ${DOCKER_IMAGE} .build/keeper-bots-v2 + rm -rf .build + } + function push { + docker push ${DOCKER_IMAGE} + } + ${@:-} +} + +${@:-} \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..c6bbae5 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,43 @@ +version: '3' + +services: + + # --------------------------------------------------- + # Bot + # --------------------------------------------------- + keeper: + image: ${DOCKER_IMAGE} + restart: unless-stopped + env_file: .env + volumes: + - ./config.yaml:/app/config.yaml + + # --------------------------------------------------- + # Monitoring + # --------------------------------------------------- + prometheus: + image: prom/prometheus + container_name: prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + #ports: + # - 9090:9090 + restart: unless-stopped + volumes: + - ./prometheus:/etc/prometheus + - prom_data:/prometheus + grafana: + image: grafana/grafana + container_name: grafana + ports: + - 3000:3000 + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=grafana + volumes: + - ./grafana/provisioning:/etc/grafana/provisioning + - ./grafana/dashboards:/var/lib/grafana/dashboards + +volumes: + prom_data: diff --git a/example.env b/example.env new file mode 100644 index 0000000..faaf2ab --- /dev/null +++ b/example.env @@ -0,0 +1,16 @@ +# Build Settings +DOCKER_IMAGE=wirelos/drift-keeper:mainnet-beta + +# Shell Utils +WALLET_ADDRESS=h5XjtA..... + +# Drift Config +ENV=mainnet-beta +ENDPOINT=https://solana-mainnet.rpc.extrnode.com/your-api-key +WS_ENDPOINT=wss://solana-mainnet.rpc.extrnode.com/your-api-key +KEEPER_PRIVATE_KEY="[123,345,...]" + +# Jito Config +# Required if useJito: true is set in config.yaml +JITO_BLOCK_ENGINE_URL=frankfurt.mainnet.block-engine.jito.wtf +JITO_AUTH_PRIVATE_KEY="[123,345,...]" \ No newline at end of file diff --git a/grafana/dashboards/drift-keeper.json b/grafana/dashboards/drift-keeper.json new file mode 100644 index 0000000..b4a6b1f --- /dev/null +++ b/grafana/dashboards/drift-keeper.json @@ -0,0 +1,962 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "panels": [], + "title": "Summary", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyUSD" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "total_collateral", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total Collateral", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyUSD" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "delta(total_collateral[24h])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Profit 24h", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyUSD" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "unrealized_pnl", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Unrealized PNL", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + }, + "fieldMinMax": false + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 12, + "options": { + "calculate": false, + "calculation": { + "xBuckets": { + "mode": "count" + }, + "yBuckets": { + "mode": "count" + } + }, + "cellGap": 1, + "cellValues": { + "unit": "currencyUSD" + }, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Turbo", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": false, + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "hidden", + "reverse": false + } + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "unrealized_pnl", + "format": "heatmap", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Activity", + "type": "heatmap" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 11, + "panels": [], + "title": "Profit & Loss", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 2, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "${__field.labels.authority}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyUSD" + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "total_collateral", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total Collateral", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-RdYlGr" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "scheme", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 2, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "${__field.labels.authority}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyUSD" + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.2.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "unrealized_pnl", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Unrealized PNL", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 10, + "panels": [], + "title": "Order Matching", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "${__field.labels.instance}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 23 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "successful_fills_total", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Successful Fills Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "displayName": "${__field.labels.instance}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 23 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true, + "sortBy": "Last *", + "sortDesc": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "attempted_fills_total", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Attempted Fills Total", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-BlPu" + }, + "custom": { + "fillOpacity": 50, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "displayName": "${__field.labels.instance}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 35 + }, + "id": 6, + "options": { + "bucketOffset": 0, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(sdk_call_duration_histogram_sum{method=\"sendTx\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "SendTX Duration", + "type": "histogram" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-BlPu" + }, + "custom": { + "fillOpacity": 50, + "gradientMode": "hue", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 1 + }, + "displayName": "${__field.labels.instance}", + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 5 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 35 + }, + "id": 5, + "options": { + "bucketOffset": 0, + "combine": false, + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(try_fill_duration_histogram_sum[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Attempted Fill Duration", + "type": "histogram" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-24h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Drift Keeper", + "uid": "f4c3a630-ffd0-41c8-a36a-52e0771b77fb", + "version": 7, + "weekStart": "" +} \ No newline at end of file diff --git a/grafana/provisioning/dashboards/providers.yml b/grafana/provisioning/dashboards/providers.yml new file mode 100644 index 0000000..ab60a0a --- /dev/null +++ b/grafana/provisioning/dashboards/providers.yml @@ -0,0 +1,24 @@ +apiVersion: 1 + +providers: + # an unique provider name. Required + - name: 'a unique provider name' + # Org id. Default to 1 + orgId: 1 + # name of the dashboard folder. + folder: '' + # folder UID. will be automatically generated if not specified + folderUid: '' + # provider type. Default to 'file' + type: file + # disable dashboard deletion + disableDeletion: false + # how often Grafana will scan for changed dashboards + updateIntervalSeconds: 10 + # allow updating provisioned dashboards from the UI + allowUiUpdates: true + options: + # path to dashboard files on disk. Required when using the 'file' type + path: /var/lib/grafana/dashboards + # use folder names from filesystem to create folders in Grafana + foldersFromFilesStructure: true \ No newline at end of file diff --git a/grafana/provisioning/datasources/datasource.yml b/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 0000000..d7b8286 --- /dev/null +++ b/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: +- name: Prometheus + type: prometheus + url: http://prometheus:9090 + isDefault: true + access: proxy + editable: true diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml new file mode 100644 index 0000000..51b601b --- /dev/null +++ b/prometheus/prometheus.yml @@ -0,0 +1,30 @@ +global: + scrape_interval: 15s + scrape_timeout: 10s + evaluation_interval: 15s +alerting: + alertmanagers: + - static_configs: + - targets: [] + scheme: http + timeout: 10s + api_version: v1 +scrape_configs: +- job_name: prometheus + honor_timestamps: true + scrape_interval: 15s + scrape_timeout: 10s + metrics_path: /metrics + scheme: http + static_configs: + - targets: + - localhost:9090 +- job_name: keeper + honor_timestamps: true + scrape_interval: 15s + scrape_timeout: 10s + metrics_path: /metrics + scheme: http + static_configs: + - targets: + - keeper:9464 \ No newline at end of file