package storage import ( "context" "errors" "time" "github.com/wormhole-foundation/wormhole-explorer/common/client/alert" "github.com/wormhole-foundation/wormhole-explorer/common/repository" cwAlert "github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/alert" "github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/metrics" 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" ) // repository errors var ErrDocNotFound = errors.New("NOT FOUND") // Repository definitions. type Repository struct { db *mongo.Database log *zap.Logger metrics metrics.Metrics alerts alert.AlertClient collections struct { watcherBlock *mongo.Collection globalTransactions *mongo.Collection } } // NewRepository create a new respository instance. func NewRepository(db *mongo.Database, metrics metrics.Metrics, alerts alert.AlertClient, log *zap.Logger) *Repository { return &Repository{db, log, metrics, alerts, struct { watcherBlock *mongo.Collection globalTransactions *mongo.Collection }{ watcherBlock: db.Collection("watcherBlock"), globalTransactions: db.Collection("globalTransactions"), }} } func (s *Repository) UpsertGlobalTransaction(ctx context.Context, chainID sdk.ChainID, globalTx TransactionUpdate) error { update := bson.M{ "$set": globalTx, "$setOnInsert": repository.IndexedAt(time.Now()), "$inc": bson.D{{Key: "revision", Value: 1}}, } _, err := s.collections.globalTransactions.UpdateByID(ctx, globalTx.ID, update, options.Update().SetUpsert(true)) if err != nil { s.log.Error("Error inserting global transaction", zap.Error(err)) // send alert when exists an error saving ptth vaa. alertContext := alert.AlertContext{ Details: globalTx.ToMap(), Error: err, } s.alerts.CreateAndSend(ctx, cwAlert.ErrorSaveDestinationTx, alertContext) return err } s.metrics.IncDestinationTrxSaved(chainID) return err } func (s *Repository) GetGlobalTransactionByID(ctx context.Context, id string) (TransactionUpdate, error) { var tx TransactionUpdate err := s.collections.globalTransactions.FindOne(ctx, bson.M{"_id": id}).Decode(&tx) if err != nil { if err == mongo.ErrNoDocuments { return tx, ErrDocNotFound } return tx, err } return tx, nil } func (s *Repository) UpdateWatcherBlock(ctx context.Context, chainID sdk.ChainID, watcherBlock WatcherBlock) error { update := bson.M{ "$set": watcherBlock, "$setOnInsert": repository.IndexedAt(time.Now()), } s.metrics.SetCurrentBlock(chainID, uint64(watcherBlock.BlockNumber)) _, err := s.collections.watcherBlock.UpdateByID(ctx, watcherBlock.ID, update, options.Update().SetUpsert(true)) if err != nil { s.log.Error("Error inserting watcher block", zap.Error(err)) return err } return err } func (s *Repository) GetCurrentBlock(ctx context.Context, blockchain string, defaultBlock int64) (int64, error) { var block WatcherBlock err := s.collections.watcherBlock.FindOne(ctx, bson.M{"_id": blockchain}).Decode(&block) if err != nil { if err == mongo.ErrNoDocuments { return defaultBlock, nil } return 0, err } return block.BlockNumber, nil }