Add api endpoint to get total count and volume
This commit is contained in:
parent
01b34a84e8
commit
d8723c056b
|
@ -9,9 +9,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Scorecards struct {
|
type Scorecards struct {
|
||||||
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
|
// Total number of VAAs emitted since the creation of the network (does not include Pyth messages).
|
||||||
TotalTxCount string
|
TotalTxCount string
|
||||||
|
|
||||||
|
// Volume transferred through the token bridge since the creation of the network, in USD.
|
||||||
|
TotalTxVolume string
|
||||||
|
|
||||||
// Number of VAAs emitted in the last 24 hours (does not include Pyth messages).
|
// Number of VAAs emitted in the last 24 hours (does not include Pyth messages).
|
||||||
TxCount24h string
|
TxCount24h string
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
|
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
|
||||||
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
|
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/common/storage/portalanalytic"
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/fly/storage"
|
||||||
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
@ -92,6 +94,7 @@ type Repository struct {
|
||||||
bucketInfiniteRetention string
|
bucketInfiniteRetention string
|
||||||
bucket30DaysRetention string
|
bucket30DaysRetention string
|
||||||
bucket24HoursRetention string
|
bucket24HoursRetention string
|
||||||
|
portalAnalyticRepo *portalanalytic.Repository
|
||||||
db *mongo.Database
|
db *mongo.Database
|
||||||
collections struct {
|
collections struct {
|
||||||
globalTransactions *mongo.Collection
|
globalTransactions *mongo.Collection
|
||||||
|
@ -104,6 +107,7 @@ func NewRepository(
|
||||||
org string,
|
org string,
|
||||||
bucket24HoursRetention, bucket30DaysRetention, bucketInfiniteRetention string,
|
bucket24HoursRetention, bucket30DaysRetention, bucketInfiniteRetention string,
|
||||||
db *mongo.Database,
|
db *mongo.Database,
|
||||||
|
portalAnalyticRepo *portalanalytic.Repository,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
) *Repository {
|
) *Repository {
|
||||||
|
|
||||||
|
@ -113,6 +117,7 @@ func NewRepository(
|
||||||
bucket24HoursRetention: bucket24HoursRetention,
|
bucket24HoursRetention: bucket24HoursRetention,
|
||||||
bucket30DaysRetention: bucket30DaysRetention,
|
bucket30DaysRetention: bucket30DaysRetention,
|
||||||
bucketInfiniteRetention: bucketInfiniteRetention,
|
bucketInfiniteRetention: bucketInfiniteRetention,
|
||||||
|
portalAnalyticRepo: portalAnalyticRepo,
|
||||||
db: db,
|
db: db,
|
||||||
collections: struct{ globalTransactions *mongo.Collection }{globalTransactions: db.Collection("globalTransactions")},
|
collections: struct{ globalTransactions *mongo.Collection }{globalTransactions: db.Collection("globalTransactions")},
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
@ -231,19 +236,19 @@ func (r *Repository) buildFindVolumeQuery(q *ChainActivityQuery) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
|
func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
|
||||||
|
// get historic total count and volume.
|
||||||
|
totalTxCount, totalTxVolume, err := r.getTotalCountAndVolume(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
//TODO the underlying query in this code is not using pre-summarized data.
|
// get 24h transactions
|
||||||
// We should fix that before re-enabling the metric.
|
|
||||||
//totalTxCount, err := r.getTotalTxCount(ctx)
|
|
||||||
//if err != nil {
|
|
||||||
// return nil, fmt.Errorf("failed to query all-time tx count")
|
|
||||||
//}
|
|
||||||
|
|
||||||
txCount24h, err := r.getTxCount24h(ctx)
|
txCount24h, err := r.getTxCount24h(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to query 24h transactions: %w", err)
|
return nil, fmt.Errorf("failed to query 24h transactions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get 24h volume
|
||||||
volume24h, err := r.getVolume24h(ctx)
|
volume24h, err := r.getVolume24h(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to query 24h volume: %w", err)
|
return nil, fmt.Errorf("failed to query 24h volume: %w", err)
|
||||||
|
@ -251,7 +256,8 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
|
||||||
|
|
||||||
// build the result and return
|
// build the result and return
|
||||||
scorecards := Scorecards{
|
scorecards := Scorecards{
|
||||||
//TotalTxCount: totalTxCount,
|
TotalTxCount: fmt.Sprint(totalTxCount),
|
||||||
|
TotalTxVolume: fmt.Sprint(totalTxVolume),
|
||||||
TxCount24h: txCount24h,
|
TxCount24h: txCount24h,
|
||||||
Volume24h: volume24h,
|
Volume24h: volume24h,
|
||||||
}
|
}
|
||||||
|
@ -259,32 +265,26 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
|
||||||
return &scorecards, nil
|
return &scorecards, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) getTotalTxCount(ctx context.Context) (string, error) {
|
// getTotalCountAndVolume get historic total count and volume.
|
||||||
|
func (r *Repository) getTotalCountAndVolume(ctx context.Context) (storage.Uint64, storage.Uint64, error) {
|
||||||
// query 24h transactions
|
var totalTxCount, totalTxVolume storage.Uint64
|
||||||
query := fmt.Sprintf(queryTemplateTotalTxCount, r.bucketInfiniteRetention)
|
var ids = []string{"tx_volume_historic", "tx_volume", "tx_count_historic", "tx_count"}
|
||||||
result, err := r.queryAPI.Query(ctx, query)
|
portalAnalytics, err := r.portalAnalyticRepo.GetPortalAnalyticByIds(ctx, ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("failed to query total transaction count", zap.Error(err))
|
return 0, 0, err
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
if result.Err() != nil {
|
for _, portalAnalytic := range portalAnalytics {
|
||||||
r.logger.Error("total transaction count query result has errors", zap.Error(err))
|
if portalAnalytic.ID == "tx_count_historic" || portalAnalytic.ID == "tx_count" {
|
||||||
return "", result.Err()
|
totalTxCount = totalTxCount + portalAnalytic.Value
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if !result.Next() {
|
if portalAnalytic.ID == "tx_volume_historic" || portalAnalytic.ID == "tx_volume" {
|
||||||
return "", errors.New("expected at least one record in total transaction count query result")
|
totalTxVolume = totalTxVolume + portalAnalytic.Value
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// deserialize the row returned
|
|
||||||
row := struct {
|
|
||||||
Value uint64 `mapstructure:"_value"`
|
|
||||||
}{}
|
|
||||||
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to decode total transaction count query response: %w", err)
|
|
||||||
}
|
}
|
||||||
|
return totalTxCount, totalTxVolume, nil
|
||||||
return fmt.Sprint(row.Value), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) getTxCount24h(ctx context.Context) (string, error) {
|
func (r *Repository) getTxCount24h(ctx context.Context) (string, error) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import (
|
||||||
wormscanCache "github.com/wormhole-foundation/wormhole-explorer/common/client/cache"
|
wormscanCache "github.com/wormhole-foundation/wormhole-explorer/common/client/cache"
|
||||||
wormscanNotionalCache "github.com/wormhole-foundation/wormhole-explorer/common/client/cache/notional"
|
wormscanNotionalCache "github.com/wormhole-foundation/wormhole-explorer/common/client/cache/notional"
|
||||||
xlogger "github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
xlogger "github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
||||||
|
"github.com/wormhole-foundation/wormhole-explorer/common/storage/portalanalytic"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,6 +113,7 @@ func main() {
|
||||||
governorRepo := governor.NewRepository(db, rootLogger)
|
governorRepo := governor.NewRepository(db, rootLogger)
|
||||||
infrastructureRepo := infrastructure.NewRepository(db, rootLogger)
|
infrastructureRepo := infrastructure.NewRepository(db, rootLogger)
|
||||||
heartbeatsRepo := heartbeats.NewRepository(db, rootLogger)
|
heartbeatsRepo := heartbeats.NewRepository(db, rootLogger)
|
||||||
|
portalAnalytcRepo := portalanalytic.NewPortalAnalytic(db, rootLogger)
|
||||||
transactionsRepo := transactions.NewRepository(
|
transactionsRepo := transactions.NewRepository(
|
||||||
influxCli,
|
influxCli,
|
||||||
cfg.Influx.Organization,
|
cfg.Influx.Organization,
|
||||||
|
@ -119,6 +121,7 @@ func main() {
|
||||||
cfg.Influx.Bucket30Days,
|
cfg.Influx.Bucket30Days,
|
||||||
cfg.Influx.BucketInfinite,
|
cfg.Influx.BucketInfinite,
|
||||||
db,
|
db,
|
||||||
|
portalAnalytcRepo,
|
||||||
rootLogger,
|
rootLogger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ func (c *Controller) GetScorecards(ctx *fiber.Ctx) error {
|
||||||
// Convert indicators to the response model
|
// Convert indicators to the response model
|
||||||
response := ScorecardsResponse{
|
response := ScorecardsResponse{
|
||||||
TotalTxCount: scorecards.TotalTxCount,
|
TotalTxCount: scorecards.TotalTxCount,
|
||||||
|
TotalTxVolume: scorecards.TotalTxVolume,
|
||||||
TxCount24h: scorecards.TxCount24h,
|
TxCount24h: scorecards.TxCount24h,
|
||||||
Volume24h: scorecards.Volume24h,
|
Volume24h: scorecards.Volume24h,
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,11 @@ type ScorecardsResponse struct {
|
||||||
// Number of VAAs emitted in the last 24 hours (includes Pyth messages).
|
// Number of VAAs emitted in the last 24 hours (includes Pyth messages).
|
||||||
//Messages24h string `json:"24h_messages"`
|
//Messages24h string `json:"24h_messages"`
|
||||||
|
|
||||||
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
|
// Total number of VAAs emitted since the creation of the network (does not include Pyth messages).
|
||||||
TotalTxCount string `json:"total_tx_count,omitempty"`
|
TotalTxCount string `json:"total_tx_count"`
|
||||||
|
|
||||||
//TotalVolume string `json:"total_volume"`
|
// Volume transferred through the token bridge since the creation of the network, in USD.
|
||||||
|
TotalTxVolume string `json:"total_volume"`
|
||||||
|
|
||||||
//TVL string `json:"tvl"`
|
//TVL string `json:"tvl"`
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package portalanalytic
|
package portalanalytic
|
||||||
|
|
||||||
import "github.com/wormhole-foundation/wormhole-explorer/common/storage"
|
import "github.com/wormhole-foundation/wormhole-explorer/fly/storage"
|
||||||
|
|
||||||
// PortalAnalyticDoc is a portal analytic document.
|
// PortalAnalyticDoc is a portal analytic document.
|
||||||
type PortalAnalyticdDoc struct {
|
type PortalAnalyticdDoc struct {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package portalanalytic
|
package portalanalytic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -23,6 +25,6 @@ func NewPortalAnalytic(db *mongo.Database, log *zap.Logger) *Repository {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPortalAnalytic get portal analytic.
|
// GetPortalAnalytic get portal analytic.
|
||||||
func (r *Repository) GetPortalAnalyticByIds(ids []string) ([]*PortalAnalyticdDoc, error) {
|
func (r *Repository) GetPortalAnalyticByIds(ctx context.Context, ids []string) ([]*PortalAnalyticdDoc, error) {
|
||||||
return []*PortalAnalyticdDoc{}, nil
|
return []*PortalAnalyticdDoc{}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson/bsontype"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Uint64 uint64
|
|
||||||
|
|
||||||
func (u Uint64) MarshalBSONValue() (bsontype.Type, []byte, error) {
|
|
||||||
ui64Str := strconv.FormatUint(uint64(u), 10)
|
|
||||||
d128, err := primitive.ParseDecimal128(ui64Str)
|
|
||||||
return bsontype.Decimal128, bsoncore.AppendDecimal128(nil, d128), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Uint64) UnmarshalBSONValue(t bsontype.Type, b []byte) error {
|
|
||||||
d128, _, ok := bsoncore.ReadDecimal128(b)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("Uint64 UnmarshalBSONValue error")
|
|
||||||
}
|
|
||||||
|
|
||||||
ui64, err := strconv.ParseUint(d128.String(), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*u = Uint64(ui64)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -65,6 +65,12 @@ func Run(db *mongo.Database) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create vaaIdTxHash collection.
|
||||||
|
err = db.CreateCollection(context.TODO(), "portalAnalytic")
|
||||||
|
if err != nil && isNotAlreadyExistsError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// create index in vaas collection by vaa key (emitterchain, emitterAddr, sequence)
|
// create index in vaas collection by vaa key (emitterchain, emitterAddr, sequence)
|
||||||
indexVaaByKey := mongo.IndexModel{
|
indexVaaByKey := mongo.IndexModel{
|
||||||
Keys: bson.D{
|
Keys: bson.D{
|
||||||
|
|
Loading…
Reference in New Issue