package metrics import ( "context" "errors" "fmt" "time" "github.com/influxdata/influxdb-client-go/v2/api/write" "github.com/shopspring/decimal" "github.com/wormhole-foundation/wormhole-explorer/analytics/cmd/token" "github.com/wormhole-foundation/wormhole-explorer/analytics/internal/metrics" "github.com/wormhole-foundation/wormhole-explorer/analytics/metric" "github.com/wormhole-foundation/wormhole-explorer/analytics/prices" "github.com/wormhole-foundation/wormhole-explorer/common/domain" sdk "github.com/wormhole-foundation/wormhole/sdk/vaa" ) type VaaConverter struct { MissingTokens map[sdk.Address]sdk.ChainID MissingTokensCounter map[sdk.Address]int PriceCache *prices.CoinPricesCache Metrics metrics.Metrics GetTransferredTokenByVaa token.GetTransferredTokenByVaa TokenProvider *domain.TokenProvider } func NewVaaConverter(priceCache *prices.CoinPricesCache, GetTransferredTokenByVaa token.GetTransferredTokenByVaa, tokenProvider *domain.TokenProvider) *VaaConverter { return &VaaConverter{ MissingTokens: make(map[sdk.Address]sdk.ChainID), MissingTokensCounter: make(map[sdk.Address]int), PriceCache: priceCache, Metrics: metrics.NewNoopMetrics(), GetTransferredTokenByVaa: GetTransferredTokenByVaa, TokenProvider: tokenProvider, } } func (c *VaaConverter) Convert(ctx context.Context, vaaBytes []byte) (string, error) { // Parse the VAA and payload vaa, err := sdk.Unmarshal(vaaBytes) if err != nil { return "", fmt.Errorf("error unmarshaling vaa: %v", err) } transferredToken, err := c.GetTransferredTokenByVaa(ctx, vaa) if err != nil { return "", fmt.Errorf("error decoding payload: %v", err) } // Look up token metadata tokenMetadata, ok := c.TokenProvider.GetTokenByAddress(transferredToken.TokenChain, transferredToken.TokenAddress.String()) if !ok { // if not found, add to missing tokens c.MissingTokens[transferredToken.TokenAddress] = transferredToken.TokenChain c.MissingTokensCounter[transferredToken.TokenAddress] = c.MissingTokensCounter[transferredToken.TokenAddress] + 1 return "", fmt.Errorf("unknown token: %s %s", transferredToken.TokenChain.String(), transferredToken.TokenAddress.String()) } // Generate a data point for the VAA volume metric var point *write.Point { p := metric.MakePointForVaaVolumeParams{ Vaa: vaa, TokenPriceFunc: func(_ string, timestamp time.Time) (decimal.Decimal, error) { // fetch the historic price from cache price, err := c.PriceCache.GetPriceByTime(tokenMetadata.CoingeckoID, timestamp) if err != nil { return decimal.NewFromInt(0), err } return price, nil }, Metrics: c.Metrics, TransferredToken: transferredToken, TokenProvider: c.TokenProvider, } var err error point, err = metric.MakePointForVaaVolume(&p) if err != nil { return "", fmt.Errorf("failed to create data point for VAA volume metric: %v", err) } if point == nil { // Some VAAs don't generate any data points for this metric (e.g.: PythNet, non-token-bridge VAAs) return "", errors.New("can't generate point for VAA volume metric") } } // Convert the data point to line protocol result := convertPointToLineProtocol(point) return result, nil }