Add total_message field in the scorecard endpoint (#1079)

* Add total_message field in the scorecard endpoint

* update scorecard endpoint documentation

* Add support to total_message in testnet
This commit is contained in:
walker-16 2024-02-05 10:42:55 -03:00 committed by GitHub
parent 49c594c5c7
commit 73a31dbbc8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 873 additions and 10 deletions

View File

@ -881,6 +881,104 @@ const docTemplate = `{
}
}
},
"/api/v1/operations": {
"get": {
"description": "Find all operations.",
"tags": [
"wormholescan"
],
"operationId": "get-operations",
"parameters": [
{
"type": "string",
"description": "address of the emitter",
"name": "address",
"in": "query"
},
{
"type": "string",
"description": "hash of the transaction",
"name": "txHash",
"in": "query"
},
{
"type": "integer",
"description": "page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "pageSize",
"name": "pageSize",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/operations.OperationResponse"
}
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/api/v1/operations/{chain_id}/{emitter}/{seq}": {
"get": {
"description": "Find operations by ID (chainID/emitter/sequence).",
"tags": [
"wormholescan"
],
"operationId": "get-operation-by-id",
"parameters": [
{
"type": "integer",
"description": "id of the blockchain",
"name": "chain_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "address of the emitter",
"name": "emitter",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "sequence of the VAA",
"name": "seq",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/operations.OperationResponse"
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/api/v1/ready": {
"get": {
"description": "Ready check",
@ -2419,15 +2517,208 @@ const docTemplate = `{
}
}
},
"operations.Content": {
"type": "object",
"properties": {
"payload": {
"type": "object",
"additionalProperties": {}
},
"standarizedProperties": {
"$ref": "#/definitions/operations.StandardizedProperties"
}
}
},
"operations.Data": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"value": {
"type": "object",
"additionalProperties": {}
}
}
},
"operations.EmitterAddress": {
"type": "object",
"properties": {
"hex": {
"type": "string"
},
"native": {
"type": "string"
}
}
},
"operations.OperationResponse": {
"type": "object",
"properties": {
"content": {
"$ref": "#/definitions/operations.Content"
},
"data": {
"type": "object",
"additionalProperties": {}
},
"emitterAddress": {
"$ref": "#/definitions/operations.EmitterAddress"
},
"emitterChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"id": {
"type": "string"
},
"sequence": {
"type": "string"
},
"sourceChain": {
"$ref": "#/definitions/operations.SourceChain"
},
"targetChain": {
"$ref": "#/definitions/operations.TargetChain"
},
"vaa": {
"$ref": "#/definitions/operations.Vaa"
}
}
},
"operations.SourceChain": {
"type": "object",
"properties": {
"attribute": {
"$ref": "#/definitions/operations.Data"
},
"chainId": {
"$ref": "#/definitions/vaa.ChainID"
},
"from": {
"type": "string"
},
"status": {
"type": "string"
},
"timestamp": {
"type": "string"
},
"transaction": {
"$ref": "#/definitions/operations.Transaction"
}
}
},
"operations.StandardizedProperties": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"appIds": {
"type": "array",
"items": {
"type": "string"
}
},
"fee": {
"type": "string"
},
"feeAddress": {
"type": "string"
},
"feeChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"fromAddress": {
"type": "string"
},
"fromChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"toAddress": {
"type": "string"
},
"toChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"tokenAddress": {
"type": "string"
},
"tokenChain": {
"$ref": "#/definitions/vaa.ChainID"
}
}
},
"operations.TargetChain": {
"type": "object",
"properties": {
"chainId": {
"$ref": "#/definitions/vaa.ChainID"
},
"from": {
"type": "string"
},
"status": {
"type": "string"
},
"timestamp": {
"type": "string"
},
"to": {
"type": "string"
},
"transaction": {
"$ref": "#/definitions/operations.Transaction"
}
}
},
"operations.Transaction": {
"type": "object",
"properties": {
"secondTxHash": {
"type": "string"
},
"txHash": {
"type": "string"
}
}
},
"operations.Vaa": {
"type": "object",
"properties": {
"guardianSetIndex": {
"type": "integer"
},
"raw": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"parser.ParseVaaWithStandarizedPropertiesdResponse": {
"type": "object",
"properties": {
"parsedPayload": {},
"parsedPayload": {
"$ref": "#/definitions/parser.ParsedPayload"
},
"standardizedProperties": {
"$ref": "#/definitions/parser.StandardizedProperties"
}
}
},
"parser.ParsedPayload": {
"type": "object",
"properties": {
"tokenAddress": {
"type": "string"
},
"tokenChain": {
"type": "integer"
}
}
},
"parser.StandardizedProperties": {
"type": "object",
"properties": {
@ -3033,6 +3324,10 @@ const docTemplate = `{
"description": "Volume transferred through the token bridge in the last 24 hours, in USD.",
"type": "string"
},
"total_messages": {
"description": "Number of VAAs emitted since the creation of the network (includes Pyth messages).",
"type": "string"
},
"total_tx_count": {
"description": "Number of VAAs emitted since the creation of the network (does not include Pyth messages)",
"type": "string"

View File

@ -874,6 +874,104 @@
}
}
},
"/api/v1/operations": {
"get": {
"description": "Find all operations.",
"tags": [
"wormholescan"
],
"operationId": "get-operations",
"parameters": [
{
"type": "string",
"description": "address of the emitter",
"name": "address",
"in": "query"
},
{
"type": "string",
"description": "hash of the transaction",
"name": "txHash",
"in": "query"
},
{
"type": "integer",
"description": "page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "pageSize",
"name": "pageSize",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/operations.OperationResponse"
}
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/api/v1/operations/{chain_id}/{emitter}/{seq}": {
"get": {
"description": "Find operations by ID (chainID/emitter/sequence).",
"tags": [
"wormholescan"
],
"operationId": "get-operation-by-id",
"parameters": [
{
"type": "integer",
"description": "id of the blockchain",
"name": "chain_id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "address of the emitter",
"name": "emitter",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "sequence of the VAA",
"name": "seq",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/operations.OperationResponse"
}
},
"400": {
"description": "Bad Request"
},
"500": {
"description": "Internal Server Error"
}
}
}
},
"/api/v1/ready": {
"get": {
"description": "Ready check",
@ -2412,15 +2510,208 @@
}
}
},
"operations.Content": {
"type": "object",
"properties": {
"payload": {
"type": "object",
"additionalProperties": {}
},
"standarizedProperties": {
"$ref": "#/definitions/operations.StandardizedProperties"
}
}
},
"operations.Data": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"value": {
"type": "object",
"additionalProperties": {}
}
}
},
"operations.EmitterAddress": {
"type": "object",
"properties": {
"hex": {
"type": "string"
},
"native": {
"type": "string"
}
}
},
"operations.OperationResponse": {
"type": "object",
"properties": {
"content": {
"$ref": "#/definitions/operations.Content"
},
"data": {
"type": "object",
"additionalProperties": {}
},
"emitterAddress": {
"$ref": "#/definitions/operations.EmitterAddress"
},
"emitterChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"id": {
"type": "string"
},
"sequence": {
"type": "string"
},
"sourceChain": {
"$ref": "#/definitions/operations.SourceChain"
},
"targetChain": {
"$ref": "#/definitions/operations.TargetChain"
},
"vaa": {
"$ref": "#/definitions/operations.Vaa"
}
}
},
"operations.SourceChain": {
"type": "object",
"properties": {
"attribute": {
"$ref": "#/definitions/operations.Data"
},
"chainId": {
"$ref": "#/definitions/vaa.ChainID"
},
"from": {
"type": "string"
},
"status": {
"type": "string"
},
"timestamp": {
"type": "string"
},
"transaction": {
"$ref": "#/definitions/operations.Transaction"
}
}
},
"operations.StandardizedProperties": {
"type": "object",
"properties": {
"amount": {
"type": "string"
},
"appIds": {
"type": "array",
"items": {
"type": "string"
}
},
"fee": {
"type": "string"
},
"feeAddress": {
"type": "string"
},
"feeChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"fromAddress": {
"type": "string"
},
"fromChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"toAddress": {
"type": "string"
},
"toChain": {
"$ref": "#/definitions/vaa.ChainID"
},
"tokenAddress": {
"type": "string"
},
"tokenChain": {
"$ref": "#/definitions/vaa.ChainID"
}
}
},
"operations.TargetChain": {
"type": "object",
"properties": {
"chainId": {
"$ref": "#/definitions/vaa.ChainID"
},
"from": {
"type": "string"
},
"status": {
"type": "string"
},
"timestamp": {
"type": "string"
},
"to": {
"type": "string"
},
"transaction": {
"$ref": "#/definitions/operations.Transaction"
}
}
},
"operations.Transaction": {
"type": "object",
"properties": {
"secondTxHash": {
"type": "string"
},
"txHash": {
"type": "string"
}
}
},
"operations.Vaa": {
"type": "object",
"properties": {
"guardianSetIndex": {
"type": "integer"
},
"raw": {
"type": "array",
"items": {
"type": "integer"
}
}
}
},
"parser.ParseVaaWithStandarizedPropertiesdResponse": {
"type": "object",
"properties": {
"parsedPayload": {},
"parsedPayload": {
"$ref": "#/definitions/parser.ParsedPayload"
},
"standardizedProperties": {
"$ref": "#/definitions/parser.StandardizedProperties"
}
}
},
"parser.ParsedPayload": {
"type": "object",
"properties": {
"tokenAddress": {
"type": "string"
},
"tokenChain": {
"type": "integer"
}
}
},
"parser.StandardizedProperties": {
"type": "object",
"properties": {
@ -3026,6 +3317,10 @@
"description": "Volume transferred through the token bridge in the last 24 hours, in USD.",
"type": "string"
},
"total_messages": {
"description": "Number of VAAs emitted since the creation of the network (includes Pyth messages).",
"type": "string"
},
"total_tx_count": {
"description": "Number of VAAs emitted since the creation of the network (does not include Pyth messages)",
"type": "string"

View File

@ -363,12 +363,138 @@ definitions:
updatedAt:
type: string
type: object
operations.Content:
properties:
payload:
additionalProperties: {}
type: object
standarizedProperties:
$ref: '#/definitions/operations.StandardizedProperties'
type: object
operations.Data:
properties:
type:
type: string
value:
additionalProperties: {}
type: object
type: object
operations.EmitterAddress:
properties:
hex:
type: string
native:
type: string
type: object
operations.OperationResponse:
properties:
content:
$ref: '#/definitions/operations.Content'
data:
additionalProperties: {}
type: object
emitterAddress:
$ref: '#/definitions/operations.EmitterAddress'
emitterChain:
$ref: '#/definitions/vaa.ChainID'
id:
type: string
sequence:
type: string
sourceChain:
$ref: '#/definitions/operations.SourceChain'
targetChain:
$ref: '#/definitions/operations.TargetChain'
vaa:
$ref: '#/definitions/operations.Vaa'
type: object
operations.SourceChain:
properties:
attribute:
$ref: '#/definitions/operations.Data'
chainId:
$ref: '#/definitions/vaa.ChainID'
from:
type: string
status:
type: string
timestamp:
type: string
transaction:
$ref: '#/definitions/operations.Transaction'
type: object
operations.StandardizedProperties:
properties:
amount:
type: string
appIds:
items:
type: string
type: array
fee:
type: string
feeAddress:
type: string
feeChain:
$ref: '#/definitions/vaa.ChainID'
fromAddress:
type: string
fromChain:
$ref: '#/definitions/vaa.ChainID'
toAddress:
type: string
toChain:
$ref: '#/definitions/vaa.ChainID'
tokenAddress:
type: string
tokenChain:
$ref: '#/definitions/vaa.ChainID'
type: object
operations.TargetChain:
properties:
chainId:
$ref: '#/definitions/vaa.ChainID'
from:
type: string
status:
type: string
timestamp:
type: string
to:
type: string
transaction:
$ref: '#/definitions/operations.Transaction'
type: object
operations.Transaction:
properties:
secondTxHash:
type: string
txHash:
type: string
type: object
operations.Vaa:
properties:
guardianSetIndex:
type: integer
raw:
items:
type: integer
type: array
type: object
parser.ParseVaaWithStandarizedPropertiesdResponse:
properties:
parsedPayload: {}
parsedPayload:
$ref: '#/definitions/parser.ParsedPayload'
standardizedProperties:
$ref: '#/definitions/parser.StandardizedProperties'
type: object
parser.ParsedPayload:
properties:
tokenAddress:
type: string
tokenChain:
type: integer
type: object
parser.StandardizedProperties:
properties:
amount:
@ -764,6 +890,10 @@ definitions:
description: Volume transferred through the token bridge in the last 24 hours,
in USD.
type: string
total_messages:
description: Number of VAAs emitted since the creation of the network (includes
Pyth messages).
type: string
total_tx_count:
description: Number of VAAs emitted since the creation of the network (does
not include Pyth messages)
@ -1579,6 +1709,71 @@ paths:
description: Internal Server Error
tags:
- wormholescan
/api/v1/operations:
get:
description: Find all operations.
operationId: get-operations
parameters:
- description: address of the emitter
in: query
name: address
type: string
- description: hash of the transaction
in: query
name: txHash
type: string
- description: page number
in: query
name: page
type: integer
- description: pageSize
in: query
name: pageSize
type: integer
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/operations.OperationResponse'
type: array
"400":
description: Bad Request
"500":
description: Internal Server Error
tags:
- wormholescan
/api/v1/operations/{chain_id}/{emitter}/{seq}:
get:
description: Find operations by ID (chainID/emitter/sequence).
operationId: get-operation-by-id
parameters:
- description: id of the blockchain
in: path
name: chain_id
required: true
type: integer
- description: address of the emitter
in: path
name: emitter
required: true
type: string
- description: sequence of the VAA
in: path
name: seq
required: true
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/operations.OperationResponse'
"400":
description: Bad Request
"500":
description: Internal Server Error
tags:
- wormholescan
/api/v1/ready:
get:
description: Ready check

View File

@ -14,6 +14,9 @@ type Scorecards struct {
// Number of VAAs emitted in the last 24 hours (includes Pyth messages).
Messages24h string
// Number of VAAs emitted since the creation of the network (includes Pyth messages).
TotalMessages string
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
TotalTxCount string

View File

@ -13,6 +13,7 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/handlers/common"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/config"
errs "github.com/wormhole-foundation/wormhole-explorer/api/internal/errors"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
"github.com/wormhole-foundation/wormhole-explorer/api/internal/tvl"
@ -20,6 +21,7 @@ import (
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap"
)
@ -126,12 +128,14 @@ from(bucket: "%s")
type repositoryCollections struct {
vaas *mongo.Collection
vaasPythnet *mongo.Collection
parsedVaa *mongo.Collection
globalTransactions *mongo.Collection
}
type Repository struct {
tvl *tvl.Tvl
p2pNetwork string
influxCli influxdb2.Client
queryAPI api.QueryAPI
bucketInfiniteRetention string
@ -145,6 +149,7 @@ type Repository struct {
func NewRepository(
tvl *tvl.Tvl,
p2pNetwork string,
client influxdb2.Client,
org string,
bucket24HoursRetention, bucket30DaysRetention, bucketInfiniteRetention string,
@ -154,6 +159,7 @@ func NewRepository(
r := Repository{
tvl: tvl,
p2pNetwork: p2pNetwork,
influxCli: client,
queryAPI: client.QueryAPI(org),
bucket24HoursRetention: bucket24HoursRetention,
@ -162,6 +168,7 @@ func NewRepository(
db: db,
collections: repositoryCollections{
vaas: db.Collection("vaas"),
vaasPythnet: db.Collection("vaasPythnet"),
parsedVaa: db.Collection("parsedVaa"),
globalTransactions: db.Collection("globalTransactions"),
},
@ -407,7 +414,7 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
// We use a `sync.WaitGroup` to block until all goroutines are done.
var wg sync.WaitGroup
var messages24h, tvl, totalTxCount, totalTxVolume, txCount24h, volume24h string
var messages24h, tvl, totalTxCount, totalTxVolume, txCount24h, volume24h, totalPythMessage string
wg.Add(1)
go func() {
@ -437,6 +444,17 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
if err != nil {
r.logger.Error("failed to tx count", zap.Error(err))
}
}()
wg.Add(1)
go func() {
defer wg.Done()
var err error
totalPythMessage, err = r.getTotalPythMessage(ctx)
if err != nil {
r.logger.Error("failed to get total pyth message", zap.Error(err))
}
}()
wg.Add(1)
@ -475,9 +493,11 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
// context timeouts are properly handled in each goroutine.
wg.Wait()
totalMessage := calculateTotalMessage(r.p2pNetwork, totalTxCount, totalPythMessage)
// Build the result and return
scorecards := Scorecards{
Messages24h: messages24h,
TotalMessages: totalMessage,
TotalTxCount: totalTxCount,
TotalTxVolume: totalTxVolume,
Tvl: tvl,
@ -487,6 +507,30 @@ func (r *Repository) GetScorecards(ctx context.Context) (*Scorecards, error) {
return &scorecards, nil
}
// calculateTotalMessage calculate the total message from the total tx count and the total pyth message
func calculateTotalMessage(p2pNetwork string, totalTxCount, totalPythMessage string) string {
var totalPythMessagelegacyEmitter uint64 = 0
if p2pNetwork == config.P2pMainNet {
// totalPythMessagelegacyEmitter contain the last sequence for the legacy pyth emitter address
// last vaa ==> 26/f8cd23c2ab91237730770bbea08d61005cdda0984348f3f6eecb559638c0bba0/965463498
totalPythMessagelegacyEmitter = 965463498
} else if p2pNetwork == config.P2pTestNet {
// totalPythMessagelegacyEmitter contain the last sequence for the legacy pyth emitter address testnet
// 26/a27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6/6566583
totalPythMessagelegacyEmitter = 6566583
}
uTotalTxCount, err := strconv.ParseUint(totalTxCount, 10, 64)
if err != nil {
uTotalTxCount = 0
}
uTotalPyth, err := strconv.ParseUint(totalPythMessage, 10, 64)
if err != nil {
uTotalPyth = 0
}
totalMessage := totalPythMessagelegacyEmitter + uTotalTxCount + uTotalPyth
return strconv.FormatUint(totalMessage, 10)
}
func (r *Repository) getTotalTxCount(ctx context.Context) (string, error) {
query := buildTotalTrxCountQuery(r.bucketInfiniteRetention, r.bucket30DaysRetention, time.Now())
@ -655,6 +699,32 @@ func (r *Repository) GetTransactionCount(ctx context.Context, q *TransactionCoun
return response, nil
}
// getTotalPythMessage returns the last sequence for the pyth emitter address
func (r *Repository) getTotalPythMessage(ctx context.Context) (string, error) {
if r.p2pNetwork != config.P2pMainNet {
return "0", nil
}
pythEmitterAddr := "e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71"
var vaaPyth struct {
ID string `bson:"_id"`
Sequence string `bson:"sequence"`
}
filter := bson.M{"emitterAddr": pythEmitterAddr}
options := options.FindOne().SetSort(bson.D{{Key: "timestamp", Value: -1}})
err := r.collections.vaasPythnet.FindOne(ctx, filter, options).Decode(&vaaPyth)
if err != nil {
if err == mongo.ErrNoDocuments {
r.logger.Warn("no pyth message found")
return "0", nil
}
r.logger.Error("failed to get pyth message", zap.String("emitterAddr", pythEmitterAddr), zap.Error(err))
return "", err
}
return vaaPyth.Sequence, nil
}
func (r *Repository) FindGlobalTransactionByID(ctx context.Context, q *GlobalTransactionQuery) (*GlobalTransactionDoc, error) {
// Look up the global transaction

View File

@ -146,6 +146,7 @@ func main() {
heartbeatsRepo := heartbeats.NewRepository(db.Database, rootLogger)
transactionsRepo := transactions.NewRepository(
tvl,
cfg.P2pNetwork,
influxCli,
cfg.Influx.Organization,
cfg.Influx.Bucket24Hours,

View File

@ -81,12 +81,13 @@ func (c *Controller) GetScorecards(ctx *fiber.Ctx) error {
// Convert indicators to the response model
response := ScorecardsResponse{
Messages24h: scorecards.Messages24h,
TotalTxCount: scorecards.TotalTxCount,
TotalVolume: scorecards.TotalTxVolume,
Tvl: scorecards.Tvl,
TxCount24h: scorecards.TxCount24h,
Volume24h: scorecards.Volume24h,
TotalMessages: scorecards.TotalMessages,
Messages24h: scorecards.Messages24h,
TotalTxCount: scorecards.TotalTxCount,
TotalVolume: scorecards.TotalTxVolume,
Tvl: scorecards.Tvl,
TxCount24h: scorecards.TxCount24h,
Volume24h: scorecards.Volume24h,
}
return ctx.JSON(response)

View File

@ -29,6 +29,9 @@ type ScorecardsResponse struct {
// Number of VAAs emitted in the last 24 hours (includes Pyth messages).
Messages24h string `json:"24h_messages"`
// Number of VAAs emitted since the creation of the network (includes Pyth messages).
TotalMessages string `json:"total_messages"`
// Number of VAAs emitted since the creation of the network (does not include Pyth messages)
TotalTxCount string `json:"total_tx_count"`