Skip to content

Commit

Permalink
Merge pull request #41 from bonedaddy/dashboard
Browse files Browse the repository at this point in the history
Dashboard
  • Loading branch information
bonedaddy authored Jan 18, 2021
2 parents e3e2cc7 + 8eac518 commit 83fcd17
Show file tree
Hide file tree
Showing 20 changed files with 1,064 additions and 44 deletions.
4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ files
config.yml
./gondx
./indexed.db
.scripts
.scripts
grafana
prom_data
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ release/
*.db

db_backups/

prom_data/
102 changes: 101 additions & 1 deletion cmd/gondx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"time"

"github.com/bonedaddy/go-indexed/bclient"
"github.com/bonedaddy/go-indexed/dashboard"
"github.com/bonedaddy/go-indexed/db"
"github.com/bonedaddy/go-indexed/discord"
"github.com/bonedaddy/go-indexed/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -82,6 +84,59 @@ func main() {
return err
}
app.Commands = cli.Commands{
&cli.Command{
Name: "prometheus",
Usage: "serves the prometheus metric endpoint",
Action: func(c *cli.Context) error {
ctx, cancel := context.WithCancel(c.Context)
defer cancel()
cfg, err := discord.LoadConfig(c.String("config"))
if err != nil {
return err
}
if cfg.InfuraAPIKey != "" {
bc, err = bclient.NewInfuraClient(cfg.InfuraAPIKey, cfg.InfuraWSEnabled)
} else {
bc, err = bclient.NewClient(cfg.ETHRPCEndpoint)
}
if err != nil {
return err
}
defer bc.Close()
database, err := db.New(&db.Opts{
Type: cfg.Database.Type,
Host: cfg.Database.Host,
Port: cfg.Database.Port,
User: cfg.Database.User,
Password: cfg.Database.Pass,
DBName: cfg.Database.DBName,
SSLModeDisable: cfg.Database.SSLModeDisable,
})
if err != nil {
return err
}
defer database.Close()
if err := database.AutoMigrate(); err != nil {
return err
}
// serve prometheus metrics
go dashboard.ServePrometheusMetrics(ctx, c.String("listen.address"))
// update metrics information
go dashboard.UpdateMetrics(ctx, database, bc)
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc
cancel()
return nil
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "listen.address",
Usage: "the address we will expose the metrics listener on",
Value: "0.0.0.0:9123",
},
},
},
&cli.Command{
Name: "discord",
Usage: "discord bot management",
Expand Down Expand Up @@ -275,7 +330,7 @@ func main() {
}

func dbPriceUpdateLoop(ctx context.Context, bc *bclient.Client, db *db.Database) {
ticker := time.NewTicker(time.Second * 30)
ticker := time.NewTicker(time.Second * 60) // update every 1m
defer ticker.Stop()
// this will tick less frequently as its an extremely expensive RPC to calculate
// a single price update likely requires 100 -> 150 RPC calls
Expand Down Expand Up @@ -384,6 +439,51 @@ func dbPriceUpdateLoop(ctx context.Context, bc *bclient.Client, db *db.Database)
log.Println("failed to update ndx dai price: ", err)
}
}
// update defi5 total supply
ip, err := bc.DEFI5()
if err != nil {
log.Println("failed to get defi5 contract: ", err)
} else {
supply, err := ip.TotalSupply(nil)
if err != nil {
log.Println("failed to get total supply: ", err)
} else {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
if err := db.RecordTotalSupply("defi5", supplyF); err != nil {
log.Println("failed to update defi5 total supply")
}
}
}
// update cc10 total supply
ip, err = bc.CC10()
if err != nil {
log.Println("failed to get cc10 contract: ", err)
} else {
supply, err := ip.TotalSupply(nil)
if err != nil {
log.Println("failed to get total supply: ", err)
} else {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
if err := db.RecordTotalSupply("cc10", supplyF); err != nil {
log.Println("failed to update cc10 total supply")
}
}
}
// update ndx total supply
erc, err := bc.NDX()
if err != nil {
log.Println("failed to get ndx contract: ", err)
} else {
supply, err := erc.TotalSupply(nil)
if err != nil {
log.Println("failed to get total supply: ", err)
} else {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
if err := db.RecordTotalSupply("ndx", supplyF); err != nil {
log.Println("failed to update ndx total supply")
}
}
}
}

}
Expand Down
134 changes: 134 additions & 0 deletions dashboard/dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package dashboard

import (
"context"
"log"
"net/http"
"time"

"github.com/bonedaddy/go-indexed/bclient"
"github.com/bonedaddy/go-indexed/db"
"github.com/bonedaddy/go-indexed/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
// CC10
cc10TVL = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "cc10_tvl",
})
cc10TotalSupply = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "cc10_total_supply",
})
cc10DaiPrice = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "cc10_dai_price",
})
// DEFI5
defi5TVL = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "defi5_tvl",
})
defi5TotalSupply = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "defi5_total_supply",
})
defi5DaiPrice = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "pool",
Name: "defi5_dai_price",
})
// NDX
ndxTotalSupply = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "governance",
Name: "ndx_total_supply",
})
ndxDaiPrice = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "indexed",
Subsystem: "governance",
Name: "ndx_dai_price",
})
)

func init() {
prometheus.MustRegister(cc10TVL, cc10TotalSupply, cc10DaiPrice, defi5TVL, defi5TotalSupply, defi5DaiPrice, ndxTotalSupply, ndxDaiPrice)
}

// ServePrometheusMetrics starts a http server to expose prometheus metrics
func ServePrometheusMetrics(ctx context.Context, listenAddr string) error {
handler := http.NewServeMux()
handler.Handle("/metrics", promhttp.Handler())
srv := &http.Server{
Handler: handler,
Addr: listenAddr,
}
go func() {
<-ctx.Done()
srv.Close()
}()
return srv.ListenAndServe()
}

// UpdateMetrics is used to update the prometheus metrics
func UpdateMetrics(ctx context.Context, database *db.Database, bc *bclient.Client) {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// set price information
if price, err := database.LastPrice("ndx"); err == nil {
ndxDaiPrice.Set(price)
}
if price, err := database.LastPrice("defi5"); err == nil {
defi5DaiPrice.Set(price)
}
if price, err := database.LastPrice("cc10"); err == nil {
cc10DaiPrice.Set(price)
}
// set tvl information
if tvl, err := database.LastValueLocked("cc10"); err == nil {
cc10TVL.Set(tvl)
}
if tvl, err := database.LastValueLocked("defi5"); err == nil {
defi5TVL.Set(tvl)
}
// set total supply information
if erc, err := bc.NDX(); err == nil {
if supply, err := erc.TotalSupply(nil); err == nil {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
ndxTotalSupply.Set(supplyF)
} else {
log.Println("failed to get total supply: ", err)
}
}
if erc, err := bc.DEFI5(); err == nil {
if supply, err := erc.TotalSupply(nil); err == nil {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
defi5TotalSupply.Set(supplyF)
} else {
log.Println("failed to get total supply: ", err)
}
}
if erc, err := bc.CC10(); err == nil {
if supply, err := erc.TotalSupply(nil); err == nil {
supplyF, _ := utils.ToDecimal(supply, 18).Float64()
cc10TotalSupply.Set(supplyF)
} else {
log.Println("failed to get total supply: ", err)
}
}
}
}
}
17 changes: 17 additions & 0 deletions dashboard/dashboard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dashboard

import (
"context"
"net/http"
"testing"
"time"
)

func TestServePrometheusMetrics(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
err := ServePrometheusMetrics(ctx, ":12345")
if err != nil && err != http.ErrServerClosed {
t.Fatal(err)
}
}
2 changes: 2 additions & 0 deletions dashboard/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package dashboard provides utilities for rendering tracked information through grafana
package dashboard
4 changes: 2 additions & 2 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func New(opts *Opts) (*Database, error) {
if err != nil {
return nil, err
}
db, err := gorm.Open(dialector, &gorm.Config{})
db, err := gorm.Open(dialector, &gorm.Config{PrepareStmt: true})
if err != nil {
return nil, err
}
Expand All @@ -67,7 +67,7 @@ func New(opts *Opts) (*Database, error) {
// AutoMigrate is used to automatically migrate datbase tables
func (d *Database) AutoMigrate() error {
var tables []interface{}
tables = append(tables, &Price{}, &TotalValueLocked{})
tables = append(tables, &Price{}, &TotalValueLocked{}, &TotalSupply{})
for _, table := range tables {
if err := d.db.AutoMigrate(table); err != nil {
return err
Expand Down
4 changes: 1 addition & 3 deletions db/price.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import (
"errors"
"math"
"time"

"gorm.io/gorm"
)

// Price is a given price entry for an asset
type Price struct {
gorm.Model
Model
Type Asset `gorm:"varchar(255)"`
USDPrice float64
}
Expand Down
23 changes: 23 additions & 0 deletions db/total_supply.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package db

// TotalSupply is used to track the total supply of a given asset
type TotalSupply struct {
Model
Type string
Supply float64
}

// RecordTotalSupply is used to record the supply for an asset
func (d *Database) RecordTotalSupply(asset string, supply float64) error {
return d.db.Create(&TotalSupply{Type: asset, Supply: supply}).Error
}

// LastTotalSupply returns the last recorded total supply for an asset
func (d *Database) LastTotalSupply(asset string) (float64, error) {
var ts TotalSupply
if err := d.db.Model(&TotalSupply{}).Where(
"type = ?", asset).Last(&ts).Error; err != nil {
return 0, err
}
return ts.Supply, nil
}
Loading

0 comments on commit 83fcd17

Please sign in to comment.