2023-05-31 06:29:47 -07:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2023-06-30 07:25:09 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/client/alert"
|
2023-08-07 12:05:08 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/dbutil"
|
2023-05-31 06:29:47 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/domain"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/health"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/common/logger"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/builder"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/config"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/http/infrastructure"
|
2023-06-30 07:25:09 -07:00
|
|
|
cwAlert "github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/alert"
|
2023-05-31 06:29:47 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/ankr"
|
2023-06-30 07:25:09 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/metrics"
|
2023-05-31 06:29:47 -07:00
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/processor"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/storage"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/watcher"
|
|
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
|
|
"go.uber.org/ratelimit"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
type exitCode int
|
|
|
|
|
|
|
|
func handleExit() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
if e, ok := r.(exitCode); ok {
|
|
|
|
os.Exit(int(e))
|
|
|
|
}
|
|
|
|
panic(r) // not an Exit, bubble up
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type watchersConfig struct {
|
2023-08-10 08:12:16 -07:00
|
|
|
ankr []config.WatcherBlockchainAddresses
|
2023-05-31 06:29:47 -07:00
|
|
|
aptos *config.WatcherBlockchain
|
2023-07-26 12:26:07 -07:00
|
|
|
arbitrum *config.WatcherBlockchainAddresses
|
2023-08-10 11:51:12 -07:00
|
|
|
avalanche *config.WatcherBlockchainAddresses
|
2023-08-10 07:41:52 -07:00
|
|
|
base *config.WatcherBlockchainAddresses
|
2023-08-10 08:12:16 -07:00
|
|
|
ethereum *config.WatcherBlockchainAddresses
|
|
|
|
celo *config.WatcherBlockchainAddresses
|
|
|
|
moonbeam *config.WatcherBlockchainAddresses
|
|
|
|
oasis *config.WatcherBlockchainAddresses
|
|
|
|
optimism *config.WatcherBlockchainAddresses
|
|
|
|
solana *config.WatcherBlockchain
|
|
|
|
terra *config.WatcherBlockchain
|
2023-05-31 06:29:47 -07:00
|
|
|
rateLimit rateLimitConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
type rateLimitConfig struct {
|
2023-08-10 11:51:12 -07:00
|
|
|
ankr int
|
|
|
|
aptos int
|
|
|
|
arbitrum int
|
|
|
|
avalanche int
|
|
|
|
base int
|
|
|
|
celo int
|
|
|
|
ethereum int
|
|
|
|
moonbeam int
|
|
|
|
oasis int
|
|
|
|
optimism int
|
|
|
|
solana int
|
|
|
|
terra int
|
2023-05-31 06:29:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func Run() {
|
|
|
|
|
|
|
|
defer handleExit()
|
|
|
|
rootCtx, rootCtxCancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
config, err := config.New(rootCtx)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error creating config", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logger := logger.New("wormhole-explorer-contract-watcher", logger.WithLevel(config.LogLevel))
|
|
|
|
|
|
|
|
logger.Info("Starting wormhole-explorer-contract-watcher ...")
|
|
|
|
|
|
|
|
//setup DB connection
|
2023-08-07 12:05:08 -07:00
|
|
|
db, err := dbutil.Connect(rootCtx, logger, config.MongoURI, config.MongoDatabase)
|
2023-05-31 06:29:47 -07:00
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("failed to connect MongoDB", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
// get health check functions.
|
|
|
|
healthChecks, err := newHealthChecks(rootCtx, db.Database)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("failed to create health checks", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2023-06-30 07:25:09 -07:00
|
|
|
// create metrics client
|
|
|
|
metrics := metrics.NewPrometheusMetrics(config.Environment)
|
|
|
|
|
|
|
|
// create alert client
|
|
|
|
alerts := newAlertClient(config, logger)
|
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
// create repositories
|
2023-06-30 07:25:09 -07:00
|
|
|
repo := storage.NewRepository(db.Database, metrics, alerts, logger)
|
2023-05-31 06:29:47 -07:00
|
|
|
|
|
|
|
// create watchers
|
2023-06-30 07:25:09 -07:00
|
|
|
watchers := newWatchers(config, repo, metrics, logger)
|
2023-05-31 06:29:47 -07:00
|
|
|
|
|
|
|
//create processor
|
|
|
|
processor := processor.NewProcessor(watchers, logger)
|
|
|
|
processor.Start(rootCtx)
|
|
|
|
|
|
|
|
// create and start server.
|
|
|
|
server := infrastructure.NewServer(logger, config.Port, config.PprofEnabled, healthChecks...)
|
|
|
|
server.Start()
|
|
|
|
|
|
|
|
logger.Info("Started wormhole-explorer-contract-watcher")
|
|
|
|
|
|
|
|
// Waiting for signal
|
|
|
|
sigterm := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
select {
|
|
|
|
case <-rootCtx.Done():
|
|
|
|
logger.Warn("Terminating with root context cancelled.")
|
|
|
|
case signal := <-sigterm:
|
|
|
|
logger.Info("Terminating with signal.", zap.String("signal", signal.String()))
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Info("root context cancelled, exiting...")
|
|
|
|
rootCtxCancel()
|
|
|
|
|
|
|
|
logger.Info("Closing processor ...")
|
|
|
|
processor.Close()
|
2023-08-07 12:05:08 -07:00
|
|
|
|
|
|
|
logger.Info("closing MongoDB connection...")
|
|
|
|
db.DisconnectWithTimeout(10 * time.Second)
|
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
logger.Info("Closing Http server ...")
|
|
|
|
server.Stop()
|
2023-08-07 12:05:08 -07:00
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
logger.Info("Finished wormhole-explorer-contract-watcher")
|
|
|
|
}
|
|
|
|
|
|
|
|
func newHealthChecks(ctx context.Context, db *mongo.Database) ([]health.Check, error) {
|
|
|
|
return []health.Check{health.Mongo(db)}, nil
|
|
|
|
}
|
|
|
|
|
2023-06-30 07:25:09 -07:00
|
|
|
func newWatchers(config *config.ServiceConfiguration, repo *storage.Repository, metrics metrics.Metrics, logger *zap.Logger) []watcher.ContractWatcher {
|
2023-05-31 06:29:47 -07:00
|
|
|
var watchers *watchersConfig
|
|
|
|
switch config.P2pNetwork {
|
|
|
|
case domain.P2pMainNet:
|
2023-07-24 06:45:19 -07:00
|
|
|
watchers = newWatchersForMainnet(config)
|
2023-05-31 06:29:47 -07:00
|
|
|
case domain.P2pTestNet:
|
2023-07-24 06:45:19 -07:00
|
|
|
watchers = newWatchersForTestnet(config)
|
2023-05-31 06:29:47 -07:00
|
|
|
default:
|
|
|
|
watchers = &watchersConfig{}
|
|
|
|
}
|
|
|
|
|
|
|
|
result := make([]watcher.ContractWatcher, 0)
|
|
|
|
|
|
|
|
// add evm watchers
|
2023-07-24 06:45:19 -07:00
|
|
|
evmLimiter := ratelimit.New(watchers.rateLimit.ankr, ratelimit.Per(time.Second))
|
2023-06-30 07:25:09 -07:00
|
|
|
ankrClient := ankr.NewAnkrSDK(config.AnkrUrl, evmLimiter, metrics)
|
2023-08-10 08:12:16 -07:00
|
|
|
for _, w := range watchers.ankr {
|
2023-06-08 13:53:51 -07:00
|
|
|
params := watcher.EVMParams{ChainID: w.ChainID, Blockchain: w.Name, SizeBlocks: w.SizeBlocks,
|
|
|
|
WaitSeconds: w.WaitSeconds, InitialBlock: w.InitialBlock, MethodsByAddress: w.MethodsByAddress}
|
2023-06-30 07:25:09 -07:00
|
|
|
result = append(result, watcher.NewEVMWatcher(ankrClient, repo, params, metrics, logger))
|
2023-05-31 06:29:47 -07:00
|
|
|
}
|
|
|
|
|
2023-08-10 08:12:16 -07:00
|
|
|
// add ethereum watcher
|
|
|
|
if watchers.ethereum != nil {
|
|
|
|
ethereumWatcher := builder.CreateEvmWatcher(watchers.rateLimit.ethereum, config.EthereumUrl, *watchers.ethereum, logger, repo, metrics)
|
|
|
|
result = append(result, ethereumWatcher)
|
|
|
|
}
|
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
// add solana watcher
|
|
|
|
if watchers.solana != nil {
|
2023-08-10 08:12:16 -07:00
|
|
|
solanaWatcher := builder.CreateSolanaWatcher(watchers.rateLimit.solana, config.SolanaUrl, *watchers.solana, logger, repo, metrics)
|
|
|
|
result = append(result, solanaWatcher)
|
2023-05-31 06:29:47 -07:00
|
|
|
}
|
|
|
|
|
2023-08-10 11:51:12 -07:00
|
|
|
// add avalanche watcher
|
|
|
|
if watchers.avalanche != nil {
|
|
|
|
avalancheWatcher := builder.CreateEvmWatcher(watchers.rateLimit.avalanche, config.AvalancheUrl, *watchers.avalanche, logger, repo, metrics)
|
|
|
|
result = append(result, avalancheWatcher)
|
|
|
|
}
|
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
// add terra watcher
|
|
|
|
if watchers.terra != nil {
|
2023-06-30 07:25:09 -07:00
|
|
|
terraWatcher := builder.CreateTerraWatcher(watchers.rateLimit.terra, config.TerraUrl, *watchers.terra, logger, repo, metrics)
|
2023-05-31 06:29:47 -07:00
|
|
|
result = append(result, terraWatcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add aptos watcher
|
|
|
|
if watchers.aptos != nil {
|
2023-06-30 07:25:09 -07:00
|
|
|
aptosWatcher := builder.CreateAptosWatcher(watchers.rateLimit.aptos, config.AptosUrl, *watchers.aptos, logger, repo, metrics)
|
2023-05-31 06:29:47 -07:00
|
|
|
result = append(result, aptosWatcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add oasis watcher
|
|
|
|
if watchers.oasis != nil {
|
2023-08-10 07:41:52 -07:00
|
|
|
oasisWatcher := builder.CreateEvmWatcher(watchers.rateLimit.oasis, config.OasisUrl, *watchers.oasis, logger, repo, metrics)
|
2023-05-31 06:29:47 -07:00
|
|
|
result = append(result, oasisWatcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add moonbeam watcher
|
|
|
|
if watchers.moonbeam != nil {
|
2023-08-10 07:41:52 -07:00
|
|
|
moonbeamWatcher := builder.CreateEvmWatcher(watchers.rateLimit.moonbeam, config.MoonbeamUrl, *watchers.moonbeam, logger, repo, metrics)
|
2023-05-31 06:29:47 -07:00
|
|
|
result = append(result, moonbeamWatcher)
|
|
|
|
}
|
|
|
|
|
2023-07-26 12:26:07 -07:00
|
|
|
// add celo watcher
|
2023-06-09 13:57:22 -07:00
|
|
|
if watchers.celo != nil {
|
2023-08-10 07:41:52 -07:00
|
|
|
celoWatcher := builder.CreateEvmWatcher(watchers.rateLimit.celo, config.CeloUrl, *watchers.celo, logger, repo, metrics)
|
2023-06-09 13:57:22 -07:00
|
|
|
result = append(result, celoWatcher)
|
|
|
|
}
|
2023-07-26 12:26:07 -07:00
|
|
|
|
|
|
|
// add optimism watcher
|
|
|
|
if watchers.optimism != nil {
|
2023-08-10 07:41:52 -07:00
|
|
|
optimismWatcher := builder.CreateEvmWatcher(watchers.rateLimit.optimism, config.OptimismUrl, *watchers.optimism, logger, repo, metrics)
|
2023-07-26 12:26:07 -07:00
|
|
|
result = append(result, optimismWatcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add arbitrum watcher
|
|
|
|
if watchers.arbitrum != nil {
|
2023-08-10 07:41:52 -07:00
|
|
|
arbitrumWatcher := builder.CreateEvmWatcher(watchers.rateLimit.arbitrum, config.ArbitrumUrl, *watchers.arbitrum, logger, repo, metrics)
|
2023-07-26 12:26:07 -07:00
|
|
|
result = append(result, arbitrumWatcher)
|
|
|
|
}
|
|
|
|
|
2023-08-10 07:41:52 -07:00
|
|
|
// add base watcher
|
|
|
|
if watchers.base != nil {
|
|
|
|
baseWatcher := builder.CreateEvmWatcher(watchers.rateLimit.base, config.BaseUrl, *watchers.base, logger, repo, metrics)
|
|
|
|
result = append(result, baseWatcher)
|
|
|
|
}
|
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-07-24 06:45:19 -07:00
|
|
|
func newWatchersForMainnet(cfg *config.ServiceConfiguration) *watchersConfig {
|
2023-05-31 06:29:47 -07:00
|
|
|
return &watchersConfig{
|
2023-08-10 08:12:16 -07:00
|
|
|
ankr: []config.WatcherBlockchainAddresses{
|
2023-05-31 06:29:47 -07:00
|
|
|
config.POLYGON_MAINNET,
|
|
|
|
config.BSC_MAINNET,
|
|
|
|
config.FANTOM_MAINNET,
|
|
|
|
},
|
2023-08-10 11:51:12 -07:00
|
|
|
aptos: &config.APTOS_MAINNET,
|
2023-08-10 13:11:31 -07:00
|
|
|
arbitrum: &config.ARBITRUM_MAINNET,
|
|
|
|
avalanche: &config.AVALANCHE_MAINNET,
|
2023-08-10 11:51:12 -07:00
|
|
|
base: &config.BASE_MAINNET,
|
|
|
|
celo: &config.CELO_MAINNET,
|
|
|
|
ethereum: &config.ETHEREUM_MAINNET,
|
|
|
|
moonbeam: &config.MOONBEAM_MAINNET,
|
|
|
|
oasis: &config.OASIS_MAINNET,
|
2023-08-10 13:11:31 -07:00
|
|
|
optimism: &config.OPTIMISM_MAINNET,
|
2023-08-10 11:51:12 -07:00
|
|
|
solana: &config.SOLANA_MAINNET,
|
|
|
|
terra: &config.TERRA_MAINNET,
|
2023-08-10 13:11:31 -07:00
|
|
|
|
2023-05-31 06:29:47 -07:00
|
|
|
rateLimit: rateLimitConfig{
|
2023-08-10 11:51:12 -07:00
|
|
|
ankr: cfg.AnkrRequestsPerSecond,
|
|
|
|
avalanche: cfg.AvalancheRequestsPerSecond,
|
|
|
|
aptos: cfg.AptosRequestsPerSecond,
|
|
|
|
arbitrum: cfg.ArbitrumRequestsPerSecond,
|
|
|
|
base: cfg.BaseRequestsPerSecond,
|
|
|
|
celo: cfg.CeloRequestsPerSecond,
|
|
|
|
ethereum: cfg.EthereumRequestsPerSecond,
|
|
|
|
moonbeam: cfg.MoonbeamRequestsPerSecond,
|
|
|
|
oasis: cfg.OasisRequestsPerSecond,
|
|
|
|
optimism: cfg.OptimismRequestsPerSecond,
|
|
|
|
solana: cfg.SolanaRequestsPerSecond,
|
|
|
|
terra: cfg.TerraRequestsPerSecond,
|
2023-05-31 06:29:47 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-24 06:45:19 -07:00
|
|
|
func newWatchersForTestnet(cfg *config.ServiceConfiguration) *watchersConfig {
|
2023-05-31 06:29:47 -07:00
|
|
|
return &watchersConfig{
|
2023-08-10 08:12:16 -07:00
|
|
|
ankr: []config.WatcherBlockchainAddresses{
|
2023-05-31 06:29:47 -07:00
|
|
|
config.POLYGON_TESTNET,
|
|
|
|
config.BSC_TESTNET,
|
|
|
|
config.FANTOM_TESTNET,
|
|
|
|
},
|
2023-08-10 11:51:12 -07:00
|
|
|
aptos: &config.APTOS_TESTNET,
|
2023-08-10 13:11:31 -07:00
|
|
|
arbitrum: &config.ARBITRUM_TESTNET,
|
|
|
|
avalanche: &config.AVALANCHE_TESTNET,
|
2023-08-10 11:51:12 -07:00
|
|
|
celo: &config.CELO_TESTNET,
|
|
|
|
base: &config.BASE_TESTNET,
|
|
|
|
ethereum: &config.ETHEREUM_TESTNET,
|
|
|
|
moonbeam: &config.MOONBEAM_TESTNET,
|
|
|
|
oasis: &config.OASIS_TESTNET,
|
2023-08-10 13:11:31 -07:00
|
|
|
optimism: &config.OPTIMISM_TESTNET,
|
2023-08-10 11:51:12 -07:00
|
|
|
solana: &config.SOLANA_TESTNET,
|
2023-05-31 06:29:47 -07:00
|
|
|
rateLimit: rateLimitConfig{
|
2023-08-10 11:51:12 -07:00
|
|
|
ankr: cfg.AnkrRequestsPerSecond,
|
|
|
|
avalanche: cfg.AvalancheRequestsPerSecond,
|
|
|
|
aptos: cfg.AptosRequestsPerSecond,
|
|
|
|
arbitrum: cfg.ArbitrumRequestsPerSecond,
|
|
|
|
base: cfg.BaseRequestsPerSecond,
|
|
|
|
celo: cfg.CeloRequestsPerSecond,
|
|
|
|
ethereum: cfg.EthereumRequestsPerSecond,
|
|
|
|
moonbeam: cfg.MoonbeamRequestsPerSecond,
|
|
|
|
oasis: cfg.OasisRequestsPerSecond,
|
|
|
|
optimism: cfg.OptimismRequestsPerSecond,
|
|
|
|
solana: cfg.SolanaRequestsPerSecond,
|
|
|
|
terra: cfg.TerraRequestsPerSecond,
|
2023-05-31 06:29:47 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2023-06-30 07:25:09 -07:00
|
|
|
|
|
|
|
func newAlertClient(config *config.ServiceConfiguration, logger *zap.Logger) alert.AlertClient {
|
|
|
|
|
|
|
|
if !config.AlertEnabled {
|
|
|
|
return alert.NewDummyClient()
|
|
|
|
}
|
|
|
|
alertConfig := alert.AlertConfig{
|
2023-07-06 07:20:18 -07:00
|
|
|
Environment: config.Environment,
|
|
|
|
ApiKey: config.AlertApiKey,
|
|
|
|
Enabled: config.AlertEnabled,
|
2023-06-30 07:25:09 -07:00
|
|
|
}
|
|
|
|
client, err := alert.NewAlertService(alertConfig, cwAlert.LoadAlerts)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatal("Error creating alert client", zap.Error(err))
|
|
|
|
}
|
|
|
|
return client
|
|
|
|
}
|