Add total tx count and total tx volume by portal bridge for scorecards (#314)
Co-authored-by: walker-16 <agpazos85@gmail.com>
This commit is contained in:
parent
c25ebcb6fc
commit
c2b94f6448
|
@ -0,0 +1,17 @@
|
||||||
|
import "date"
|
||||||
|
|
||||||
|
option task = {
|
||||||
|
name: "total tx count by portal bridge",
|
||||||
|
every: 24h,
|
||||||
|
}
|
||||||
|
|
||||||
|
stop = date.truncate(t: now(), unit: 24h)
|
||||||
|
|
||||||
|
from(bucket: "wormscan")
|
||||||
|
|> range(start: 1970-01-01T00:00:00Z, stop: stop)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> count()
|
||||||
|
|> map(fn: (r) => ({ _time: r._stop, _value: r._value, _measurement: "total_tx_count", _field: "value" }))
|
||||||
|
|> to(bucket: "wormscan-30days")
|
|
@ -0,0 +1,17 @@
|
||||||
|
import "date"
|
||||||
|
|
||||||
|
option task = {
|
||||||
|
name: "total tx volume by portal bridge",
|
||||||
|
every: 24h,
|
||||||
|
}
|
||||||
|
|
||||||
|
stop = date.truncate(t: now(), unit: 24h)
|
||||||
|
|
||||||
|
from(bucket: "wormscan")
|
||||||
|
|> range(start: 1970-01-01T00:00:00Z, stop: stop)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
|> map(fn: (r) => ({ _time: r._stop, _value: r._value, _measurement: "total_tx_volume", _field: "value" }))
|
||||||
|
|> to(bucket: "wormscan-30days")
|
|
@ -12,6 +12,9 @@ type Scorecards struct {
|
||||||
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
|
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
|
||||||
TotalTxCount string
|
TotalTxCount string
|
||||||
|
|
||||||
|
//Volume transferred 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
|
||||||
|
|
||||||
|
@ -118,8 +121,8 @@ type TransactionCountResult struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainActivityResult struct {
|
type ChainActivityResult struct {
|
||||||
ChainSourceID string `mapstructure:"chain_source_id"`
|
ChainSourceID string `mapstructure:"emitter_chain"`
|
||||||
ChainDestinationID string `mapstructure:"chain_destination_id"`
|
ChainDestinationID string `mapstructure:"destination_chain"`
|
||||||
Volume uint64 `mapstructure:"volume"`
|
Volume uint64 `mapstructure:"volume"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,3 +64,45 @@ func createRangeQuery(t time.Time, timeSpan string) (string, string) {
|
||||||
|
|
||||||
return startLastVaa.Format(format), startAggregatesVaa.Format(format)
|
return startLastVaa.Format(format), startAggregatesVaa.Format(format)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const queryTemplateTotalTrxCount = `
|
||||||
|
current = from(bucket: "%s")
|
||||||
|
|> range(start: %s)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> count()
|
||||||
|
last = from(bucket: "%s")
|
||||||
|
|> range(start: -1mo)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "total_tx_count")
|
||||||
|
|> last()
|
||||||
|
union(tables: [current, last])
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
`
|
||||||
|
|
||||||
|
func buildTotalTrxCountQuery(bucketForever, bucket30Days string, t time.Time) string {
|
||||||
|
start := t.Truncate(time.Hour * 24).Format(time.RFC3339Nano)
|
||||||
|
return fmt.Sprintf(queryTemplateTotalTrxCount, bucketForever, start, bucket30Days)
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryTemplateTotalTrxVolume = `
|
||||||
|
current = from(bucket: "%s")
|
||||||
|
|> range(start: %s)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
last = from(bucket: "%s")
|
||||||
|
|> range(start: -1mo)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "total_tx_volume")
|
||||||
|
|> last()
|
||||||
|
union(tables: [current, last])
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
`
|
||||||
|
|
||||||
|
func buildTotalTrxVolumeQuery(bucketForever, bucket30Days string, t time.Time) string {
|
||||||
|
start := t.Truncate(time.Hour * 24).Format(time.RFC3339Nano)
|
||||||
|
return fmt.Sprintf(queryTemplateTotalTrxVolume, bucketForever, start, bucket30Days)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package transactions
|
package transactions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -62,8 +61,6 @@ union(tables: [aggregatesVaaCount, lastVaaCount])
|
||||||
//2023-05-04T18:39:10.985Z
|
//2023-05-04T18:39:10.985Z
|
||||||
tm := time.Date(2023, 5, 4, 18, 39, 10, 985, time.UTC)
|
tm := time.Date(2023, 5, 4, 18, 39, 10, 985, time.UTC)
|
||||||
actual := buildLastTrxQuery("wormscan-1month", tm, &TransactionCountQuery{TimeSpan: "1d", SampleRate: "1h"})
|
actual := buildLastTrxQuery("wormscan-1month", tm, &TransactionCountQuery{TimeSpan: "1d", SampleRate: "1h"})
|
||||||
fmt.Println(actual)
|
|
||||||
fmt.Println(actual)
|
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +83,51 @@ union(tables: [aggregatesVaaCount, lastVaaCount])
|
||||||
//2023-05-04T18:39:10.985Z
|
//2023-05-04T18:39:10.985Z
|
||||||
tm := time.Date(2023, 5, 4, 18, 39, 10, 985, time.UTC)
|
tm := time.Date(2023, 5, 4, 18, 39, 10, 985, time.UTC)
|
||||||
actual := buildLastTrxQuery("wormscan-1month", tm, &TransactionCountQuery{TimeSpan: "1w", SampleRate: "1d"})
|
actual := buildLastTrxQuery("wormscan-1month", tm, &TransactionCountQuery{TimeSpan: "1w", SampleRate: "1d"})
|
||||||
fmt.Println(actual)
|
assert.Equal(t, expected, actual)
|
||||||
fmt.Println(actual)
|
}
|
||||||
|
|
||||||
|
func TestQueries_buildTotalTrxCountQuery(t *testing.T) {
|
||||||
|
|
||||||
|
expected := `
|
||||||
|
current = from(bucket: "bucket-forever")
|
||||||
|
|> range(start: 2023-05-12T00:00:00Z)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> count()
|
||||||
|
last = from(bucket: "bucket-30days")
|
||||||
|
|> range(start: -1mo)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "total_tx_count")
|
||||||
|
|> last()
|
||||||
|
union(tables: [current, last])
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
`
|
||||||
|
//2023-05-04T18:39:10.985Z
|
||||||
|
tm := time.Date(2023, 5, 12, 16, 53, 10, 985, time.UTC)
|
||||||
|
actual := buildTotalTrxCountQuery("bucket-forever", "bucket-30days", tm)
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQueries_buildTotalTrxVolumeQuery(t *testing.T) {
|
||||||
|
|
||||||
|
expected := `
|
||||||
|
current = from(bucket: "bucket-forever")
|
||||||
|
|> range(start: 2023-05-10T00:00:00Z)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "vaa_volume")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "volume")
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
last = from(bucket: "bucket-30days")
|
||||||
|
|> range(start: -1mo)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "total_tx_volume")
|
||||||
|
|> last()
|
||||||
|
union(tables: [current, last])
|
||||||
|
|> group()
|
||||||
|
|> sum()
|
||||||
|
`
|
||||||
|
//2023-05-04T18:39:10.985Z
|
||||||
|
tm := time.Date(2023, 5, 10, 16, 53, 10, 985, time.UTC)
|
||||||
|
actual := buildTotalTrxVolumeQuery("bucket-forever", "bucket-30days", tm)
|
||||||
assert.Equal(t, expected, actual)
|
assert.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,6 @@ from(bucket: "%s")
|
||||||
|> %s(column: "volume")
|
|> %s(column: "volume")
|
||||||
`
|
`
|
||||||
|
|
||||||
const queryTemplateTotalTxCount = `
|
|
||||||
from(bucket: "%s")
|
|
||||||
|> range(start: 2018-01-01T00:00:00Z)
|
|
||||||
|> filter(fn: (r) => r._field == "total_vaa_count")
|
|
||||||
|> last()
|
|
||||||
`
|
|
||||||
|
|
||||||
const queryTemplateTxCount24h = `
|
const queryTemplateTxCount24h = `
|
||||||
from(bucket: "%s")
|
from(bucket: "%s")
|
||||||
|> range(start: -24h)
|
|> range(start: -24h)
|
||||||
|
@ -169,7 +162,7 @@ func (r *Repository) GetTopAssets(ctx context.Context, timeSpan *TopStatisticsTi
|
||||||
EmitterChain string `mapstructure:"emitter_chain"`
|
EmitterChain string `mapstructure:"emitter_chain"`
|
||||||
TokenChain string `mapstructure:"token_chain"`
|
TokenChain string `mapstructure:"token_chain"`
|
||||||
TokenAddress string `mapstructure:"token_address"`
|
TokenAddress string `mapstructure:"token_address"`
|
||||||
Volume int64 `mapstructure:"_value"`
|
Volume uint64 `mapstructure:"_value"`
|
||||||
}
|
}
|
||||||
var rows []Row
|
var rows []Row
|
||||||
for result.Next() {
|
for result.Next() {
|
||||||
|
@ -265,7 +258,7 @@ func (r *Repository) GetTopChainPairs(ctx context.Context, timeSpan *TopStatisti
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertToDecimal converts an integer amount to a decimal string, with 8 decimals of precision.
|
// convertToDecimal converts an integer amount to a decimal string, with 8 decimals of precision.
|
||||||
func convertToDecimal(amount int64) string {
|
func convertToDecimal(amount uint64) string {
|
||||||
|
|
||||||
// If the amount is less than 1, just use a format mask.
|
// If the amount is less than 1, just use a format mask.
|
||||||
if amount < 1_0000_0000 {
|
if amount < 1_0000_0000 {
|
||||||
|
@ -318,12 +311,15 @@ 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) {
|
||||||
|
|
||||||
//TODO the underlying query in this code is not using pre-summarized data.
|
totalTxCount, err := r.getTotalTxCount(ctx)
|
||||||
// We should fix that before re-enabling the metric.
|
if err != nil {
|
||||||
//totalTxCount, err := r.getTotalTxCount(ctx)
|
return nil, fmt.Errorf("failed to query total tx count by portal bridge")
|
||||||
//if err != nil {
|
}
|
||||||
// return nil, fmt.Errorf("failed to query all-time tx count")
|
|
||||||
//}
|
totalTxVolume, err := r.getTotalTxVolume(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to query tx volume by portal bridge")
|
||||||
|
}
|
||||||
|
|
||||||
txCount24h, err := r.getTxCount24h(ctx)
|
txCount24h, err := r.getTxCount24h(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -337,9 +333,10 @@ 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: totalTxCount,
|
||||||
TxCount24h: txCount24h,
|
TotalTxVolume: totalTxVolume,
|
||||||
Volume24h: volume24h,
|
TxCount24h: txCount24h,
|
||||||
|
Volume24h: volume24h,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &scorecards, nil
|
return &scorecards, nil
|
||||||
|
@ -347,30 +344,50 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
|
||||||
|
|
||||||
func (r *Repository) getTotalTxCount(ctx context.Context) (string, error) {
|
func (r *Repository) getTotalTxCount(ctx context.Context) (string, error) {
|
||||||
|
|
||||||
// query 24h transactions
|
query := buildTotalTrxCountQuery(r.bucketInfiniteRetention, r.bucket30DaysRetention, time.Now())
|
||||||
query := fmt.Sprintf(queryTemplateTotalTxCount, r.bucketInfiniteRetention)
|
|
||||||
result, err := r.queryAPI.Query(ctx, query)
|
result, err := r.queryAPI.Query(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("failed to query total transaction count", zap.Error(err))
|
r.logger.Error("failed to query total tx count by portal bridge", zap.Error(err))
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if result.Err() != nil {
|
if result.Err() != nil {
|
||||||
r.logger.Error("total transaction count query result has errors", zap.Error(err))
|
r.logger.Error("failed to query total tx count by portal bridge result has errors", zap.Error(err))
|
||||||
return "", result.Err()
|
return "", result.Err()
|
||||||
}
|
}
|
||||||
if !result.Next() {
|
if !result.Next() {
|
||||||
return "", errors.New("expected at least one record in total transaction count query result")
|
return "", errors.New("expected at least one record in query total tx count by portal bridge result")
|
||||||
}
|
}
|
||||||
|
|
||||||
// deserialize the row returned
|
|
||||||
row := struct {
|
row := struct {
|
||||||
Value uint64 `mapstructure:"_value"`
|
Value uint64 `mapstructure:"_value"`
|
||||||
}{}
|
}{}
|
||||||
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
||||||
return "", fmt.Errorf("failed to decode total transaction count query response: %w", err)
|
return "", fmt.Errorf("failed to decode total tx count by portal bridge query response: %w", err)
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("%d", row.Value), nil
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprint(row.Value), nil
|
func (r *Repository) getTotalTxVolume(ctx context.Context) (string, error) {
|
||||||
|
|
||||||
|
query := buildTotalTrxVolumeQuery(r.bucketInfiniteRetention, r.bucket30DaysRetention, time.Now())
|
||||||
|
result, err := r.queryAPI.Query(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("failed to query total tx volume by portal bridge", zap.Error(err))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if result.Err() != nil {
|
||||||
|
r.logger.Error("failed to query tx volume by portal bridge result has errors", zap.Error(err))
|
||||||
|
return "", result.Err()
|
||||||
|
}
|
||||||
|
if !result.Next() {
|
||||||
|
return "", errors.New("expected at least one record in query tx volume by portal bridge result")
|
||||||
|
}
|
||||||
|
row := struct {
|
||||||
|
Value uint64 `mapstructure:"_value"`
|
||||||
|
}{}
|
||||||
|
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to decode tx volume by portal bridge query response: %w", err)
|
||||||
|
}
|
||||||
|
return convertToDecimal(row.Value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) getTxCount24h(ctx context.Context) (string, error) {
|
func (r *Repository) getTxCount24h(ctx context.Context) (string, error) {
|
||||||
|
@ -420,7 +437,7 @@ func (r *Repository) getVolume24h(ctx context.Context) (string, error) {
|
||||||
|
|
||||||
// deserialize the row returned
|
// deserialize the row returned
|
||||||
row := struct {
|
row := struct {
|
||||||
Value int64 `mapstructure:"_value"`
|
Value uint64 `mapstructure:"_value"`
|
||||||
}{}
|
}{}
|
||||||
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
if err := mapstructure.Decode(result.Record().Values(), &row); err != nil {
|
||||||
return "", fmt.Errorf("failed to decode 24h volume count query response: %w", err)
|
return "", fmt.Errorf("failed to decode 24h volume count query response: %w", err)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import "testing"
|
||||||
func Test_convertToDecimal(t *testing.T) {
|
func Test_convertToDecimal(t *testing.T) {
|
||||||
|
|
||||||
tcs := []struct {
|
tcs := []struct {
|
||||||
input int64
|
input uint64
|
||||||
output string
|
output string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
TotalVolume: scorecards.TotalTxVolume,
|
||||||
TxCount24h: scorecards.TxCount24h,
|
TxCount24h: scorecards.TxCount24h,
|
||||||
Volume24h: scorecards.Volume24h,
|
Volume24h: scorecards.Volume24h,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ type ScorecardsResponse struct {
|
||||||
//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)
|
// 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"`
|
TotalVolume string `json:"total_volume"`
|
||||||
|
|
||||||
//TVL string `json:"tvl"`
|
//TVL string `json:"tvl"`
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue