Add Solana market data to wallet tracker

This commit is contained in:
2024-01-28 22:23:08 +01:00
parent 6afd518f58
commit bff72ff5b5
9 changed files with 626 additions and 29 deletions

View File

@@ -36,7 +36,7 @@ Adjust `config.yaml` as you please.
Clone the [keeper-bots-v2](https://github.com/drift-labs/keeper-bots-v2/) repository and build the Docker image. Clone the [keeper-bots-v2](https://github.com/drift-labs/keeper-bots-v2/) repository and build the Docker image.
``` ```
./ctl.sh image build ./ctl.sh keeper build
``` ```
## Run ## Run

View File

@@ -4,9 +4,29 @@ global:
# RPC endpoint to use # RPC endpoint to use
endpoint: endpoint:
# custom websocket endpoint to use (if null will be determined from rpc endpoint)
# Custom websocket endpoint to use (if null will be determined from `endpoint``)
# Note: the default wsEndpoint value simply replaces http(s) with ws(s), so if
# your RPC provider requires a special path (i.e. /ws) for websocket connections
# you must set this.
wsEndpoint: wsEndpoint:
# optional if you want to use helius' global priority fee method AND `endpoint` is not
# already a helius url.
heliusEndpoint:
# `solana` or `helius`. If `helius` `endpoint` must be a helius RPC, or `heliusEndpoint`
# must be set
# solana: uses https://solana.com/docs/rpc/http/getrecentprioritizationfees
# helius: uses https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api
priorityFeeMethod: solana
# max priority fee to use, in micro lamports
# i.e. a fill that uses 500_000 CUs will spend:
# 500_000 * 10_000 * 1e-6 * 1e-9 = 0.000005 SOL on priority fees
# this is on top of the 0.000005 SOL base fee, so 0.000010 SOL total
maxPriorityFeeMicroLamports: 10000
# Private key to use to sign transactions. # Private key to use to sign transactions.
# will load from KEEPER_PRIVATE_KEY env var if null # will load from KEEPER_PRIVATE_KEY env var if null
keeperPrivateKey: keeperPrivateKey:

14
ctl.sh
View File

@@ -5,7 +5,7 @@ API_ENDPOINT=https://api.mainnet-beta.solana.com/
source .env source .env
function image { function keeper {
function build { function build {
mkdir -p .build mkdir -p .build
git clone https://github.com/drift-labs/keeper-bots-v2 -b mainnet-beta .build/keeper-bots-v2 git clone https://github.com/drift-labs/keeper-bots-v2 -b mainnet-beta .build/keeper-bots-v2
@@ -18,6 +18,18 @@ function image {
${@:-} ${@:-}
} }
function tracker {
function build {
pushd wallet-tracker
docker build -t ${DOCKER_IMAGE_WALLET_TRACKER} .
popd
}
function push {
docker push ${DOCKER_IMAGE_WALLET_TRACKER}
}
${@:-}
}
function droplet { function droplet {
function provision { function provision {
terraform init terraform init

View File

@@ -15,6 +15,12 @@ services:
# --------------------------------------------------- # ---------------------------------------------------
# Monitoring # Monitoring
# --------------------------------------------------- # ---------------------------------------------------
wallet-tracker:
image: ${DOCKER_IMAGE_WALLET_TRACKER}
build:
context: wallet-tracker
env_file: .env
restart: unless-stopped
prometheus: prometheus:
image: prom/prometheus image: prom/prometheus
container_name: prometheus container_name: prometheus

View File

@@ -64,7 +64,7 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 5,
"w": 4, "w": 4,
"x": 0, "x": 0,
"y": 1 "y": 1
@@ -136,7 +136,7 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 5,
"w": 4, "w": 4,
"x": 4, "x": 4,
"y": 1 "y": 1
@@ -208,7 +208,7 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 5,
"w": 4, "w": 4,
"x": 8, "x": 8,
"y": 1 "y": 1
@@ -251,6 +251,225 @@
"title": "Unrealized PNL", "title": "Unrealized PNL",
"type": "stat" "type": "stat"
}, },
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "SOL"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 4,
"x": 12,
"y": 1
},
"id": 16,
"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": "sol_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "SOL Balance",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-RdYlGr"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "currencyUSD"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 4,
"x": 16,
"y": 1
},
"id": 21,
"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": "sol_usdc_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "SOL Balance in USDC",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-RdYlGr"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "currencyUSD"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 4,
"x": 20,
"y": 1
},
"id": 22,
"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": "usdc_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "USDC Balance",
"type": "stat"
},
{ {
"datasource": { "datasource": {
"type": "prometheus", "type": "prometheus",
@@ -273,10 +492,10 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 6,
"w": 12, "w": 24,
"x": 12, "x": 0,
"y": 1 "y": 6
}, },
"id": 12, "id": 12,
"options": { "options": {
@@ -356,7 +575,318 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 8 "y": 12
},
"id": 17,
"panels": [],
"title": "Wallet",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"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": 2,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 13
},
"id": 20,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "sol_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "SOL Balance",
"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": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 2,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 8,
"y": 13
},
"id": 18,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "sol_usdc_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "SOL Balance in USDC",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"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": 2,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 16,
"y": 13
},
"id": 19,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "usdc_balance",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "USDC Balance",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 21
}, },
"id": 11, "id": 11,
"panels": [], "panels": [],
@@ -428,7 +958,7 @@
"h": 13, "h": 13,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 9 "y": 22
}, },
"id": 7, "id": 7,
"options": { "options": {
@@ -530,7 +1060,7 @@
"h": 13, "h": 13,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 9 "y": 22
}, },
"id": 8, "id": 8,
"options": { "options": {
@@ -573,7 +1103,7 @@
"h": 1, "h": 1,
"w": 24, "w": 24,
"x": 0, "x": 0,
"y": 22 "y": 35
}, },
"id": 10, "id": 10,
"panels": [], "panels": [],
@@ -644,7 +1174,7 @@
"h": 12, "h": 12,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 23 "y": 36
}, },
"id": 15, "id": 15,
"options": { "options": {
@@ -748,7 +1278,7 @@
"h": 12, "h": 12,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 23 "y": 36
}, },
"id": 1, "id": 1,
"options": { "options": {
@@ -831,7 +1361,7 @@
"h": 12, "h": 12,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 35 "y": 48
}, },
"id": 5, "id": 5,
"options": { "options": {
@@ -910,7 +1440,7 @@
"h": 12, "h": 12,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 35 "y": 48
}, },
"id": 14, "id": 14,
"options": { "options": {
@@ -961,6 +1491,6 @@
"timezone": "", "timezone": "",
"title": "Drift Keeper", "title": "Drift Keeper",
"uid": "f4c3a630-ffd0-41c8-a36a-52e0771b77fb", "uid": "f4c3a630-ffd0-41c8-a36a-52e0771b77fb",
"version": 3, "version": 6,
"weekStart": "" "weekStart": ""
} }

View File

@@ -1,5 +1,5 @@
global: global:
scrape_interval: 15s scrape_interval: 60s
scrape_timeout: 10s scrape_timeout: 10s
evaluation_interval: 15s evaluation_interval: 15s
alerting: alerting:
@@ -13,6 +13,12 @@ scrape_configs:
- job_name: node - job_name: node
static_configs: static_configs:
- targets: ['node-exporter:9100'] - targets: ['node-exporter:9100']
- job_name: wallet
scrape_interval: 60s
metrics_path: /metrics
static_configs:
- targets:
- wallet-tracker:3000
- job_name: keeper - job_name: keeper
honor_timestamps: true honor_timestamps: true
scrape_interval: 15s scrape_interval: 15s

View File

@@ -1,21 +1,23 @@
const express = require('express'); const express = require('express');
const { createMetrics } = require('./metrics'); const { createMetrics } = require('./metrics');
const { loadWalletBalance, loadUSDCBalance, extractWalletBalance, extractUSDCBalance } = require('./solana'); const { loadWalletBalance, loadUSDCBalance, loadSolanaMarketData, extractWalletBalance, extractUSDCBalance, extractSOLPrice } = require('./solana');
const WALLET_ADDRESS = process.env.WALLET_ADDRESS; const WALLET_ADDRESS = process.env.WALLET_ADDRESS;
const [registry, usdcBalanceMetric, solBalanceMetric] = createMetrics(); const [registry, usdcBalanceMetric, solBalanceMetric, solUsdcBalanceMetric] = createMetrics();
const app = express(); const app = express();
app.get('/metrics', async (req, res) => { app.get('/metrics', async (req, res) => {
res.setHeader('Content-Type', registry.contentType); res.setHeader('Content-Type', registry.contentType);
let [solBalance, usdcBalance] = await Promise.all([ let [solBalance, usdcBalance, marketData] = await Promise.all([
loadWalletBalance(WALLET_ADDRESS), loadWalletBalance(WALLET_ADDRESS),
loadUSDCBalance(WALLET_ADDRESS)]); loadUSDCBalance(WALLET_ADDRESS),
loadSolanaMarketData()]);
solBalanceMetric.set(extractWalletBalance(solBalance));
usdcBalanceMetric.set(extractUSDCBalance(usdcBalance));
solBalanceMetric.set({ wallet: WALLET_ADDRESS}, extractWalletBalance(solBalance));
usdcBalanceMetric.set({ wallet: WALLET_ADDRESS}, extractUSDCBalance(usdcBalance));
solUsdcBalanceMetric.set({ wallet: WALLET_ADDRESS}, extractWalletBalance(solBalance) * extractSOLPrice(marketData));
res.send(await registry.metrics()); res.send(await registry.metrics());
}); });

View File

@@ -6,16 +6,24 @@ const createMetrics = () => {
const solBalanceMetric = new client.Gauge({ const solBalanceMetric = new client.Gauge({
name: "sol_balance", name: "sol_balance",
help: "SOL Balance", help: "SOL Balance",
labelNames: ['wallet']
}); });
const usdcBalanceMetric = new client.Gauge({ const usdcBalanceMetric = new client.Gauge({
name: "usdc_balance", name: "usdc_balance",
help: "USDC Balance", help: "USDC Balance",
labelNames: ['wallet']
});
const solUsdcBalanceMetric = new client.Gauge({
name: "sol_usdc_balance",
help: "SOL Balance in USDC",
labelNames: ['wallet']
}); });
registry.registerMetric(usdcBalanceMetric); registry.registerMetric(usdcBalanceMetric);
registry.registerMetric(solBalanceMetric); registry.registerMetric(solBalanceMetric);
registry.registerMetric(solUsdcBalanceMetric);
return [registry, usdcBalanceMetric, solBalanceMetric]; return [registry, usdcBalanceMetric, solBalanceMetric, solUsdcBalanceMetric];
}; };
module.exports = { module.exports = {

View File

@@ -39,6 +39,12 @@ async function loadUSDCBalance(walletAddress) {
return json; return json;
} }
async function loadSolanaMarketData() {
const response = await fetch("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=solana");
const json = await response.json();
return json;
}
// extract wallet balance // extract wallet balance
function extractWalletBalance(walletBalance) { function extractWalletBalance(walletBalance) {
return walletBalance.result.value / LAMPORTS_PER_SOL; return walletBalance.result.value / LAMPORTS_PER_SOL;
@@ -49,10 +55,17 @@ function extractUSDCBalance(usdcBalance) {
return usdcBalance.result.value[0].account.data.parsed.info.tokenAmount.uiAmount; return usdcBalance.result.value[0].account.data.parsed.info.tokenAmount.uiAmount;
} }
// extract SOL price
function extractSOLPrice(marketData) {
return marketData[0].current_price;
}
// export functions // export functions
module.exports = { module.exports = {
loadWalletBalance, loadWalletBalance,
loadUSDCBalance, loadUSDCBalance,
loadSolanaMarketData,
extractWalletBalance, extractWalletBalance,
extractUSDCBalance extractUSDCBalance,
extractSOLPrice
}; };