package config import ( "encoding/json" "errors" "fmt" "os" "strconv" "strings" "github.com/joho/godotenv" "github.com/kelseyhightower/envconfig" sdk "github.com/wormhole-foundation/wormhole/sdk/vaa" ) type BackfillingStrategy string const ( // StrategyReprocessAll will reprocess documents in the `globalTransactions` // collection that don't have the `sourceTx` field set, or that have the // `sourceTx.status` field set to "internalError". BackfillerStrategyReprocessFailed BackfillingStrategy = "reprocess_failed" // BackfillerStrategyTimeRange will reprocess all VAAs that have a timestamp between the specified range. BackfillerStrategyTimeRange BackfillingStrategy = "time_range" ) type BackfillerSettings struct { LogLevel string `split_words:"true" default:"INFO"` NumWorkers uint `split_words:"true" required:"true"` BulkSize uint `split_words:"true" required:"true"` P2pNetwork string `split_words:"true" required:"true"` RpcProviderPath string `split_words:"true" required:"false"` // Strategy determines which VAAs will be affected by the backfiller. Strategy struct { Name BackfillingStrategy `split_words:"true" required:"true"` TimestampAfter string `split_words:"true" required:"false"` TimestampBefore string `split_words:"true" required:"false"` } MongodbSettings *RpcProviderSettings `required:"false"` *TestnetRpcProviderSettings `required:"false"` *RpcProviderSettingsJson `required:"false"` } type ServiceSettings struct { // MonitoringPort defines the TCP port for the /health and /ready endpoints. MonitoringPort string `split_words:"true" default:"8000"` Environment string `split_words:"true" required:"true"` LogLevel string `split_words:"true" default:"INFO"` PprofEnabled bool `split_words:"true" default:"false"` MetricsEnabled bool `split_words:"true" default:"false"` P2pNetwork string `split_words:"true" required:"true"` RpcProviderPath string `split_words:"true" required:"false"` ConsumerWorkersSize int `split_words:"true" default:"10"` AwsSettings MongodbSettings *RpcProviderSettings `required:"false"` *TestnetRpcProviderSettings `required:"false"` *RpcProviderSettingsJson `required:"false"` } type RpcProviderSettingsJson struct { RpcProviders []ChainRpcProviderSettings `json:"rpcProviders"` } type ChainRpcProviderSettings struct { ChainId uint16 `json:"chainId"` Chain string `json:"chain"` RpcSettings []RpcSettings `json:"rpcs"` } type RpcSettings struct { Url string `json:"url"` RequestPerMinute uint16 `json:"requestPerMinute"` Priority uint8 `json:"priority"` } type AwsSettings struct { AwsEndpoint string `split_words:"true" required:"false"` AwsAccessKeyID string `split_words:"true" required:"false"` AwsSecretAccessKey string `split_words:"true" required:"false"` AwsRegion string `split_words:"true" required:"true"` PipelineSqsUrl string `split_words:"true" required:"true"` NotificationsSqsUrl string `split_words:"true" required:"true"` } type MongodbSettings struct { MongodbUri string `split_words:"true" required:"true"` MongodbDatabase string `split_words:"true" required:"true"` } type RpcProviderSettings struct { AcalaBaseUrl string `split_words:"true" required:"false"` AcalaRequestsPerMinute uint16 `split_words:"true" required:"false"` AcalaFallbackUrls string `split_words:"true" required:"false"` AcalaFallbackRequestsPerMinute string `split_words:"true" required:"false"` AlgorandBaseUrl string `split_words:"true" required:"false"` AlgorandRequestsPerMinute uint16 `split_words:"true" required:"false"` AlgorandFallbackUrls string `split_words:"true" required:"false"` AlgorandFallbackRequestsPerMinute string `split_words:"true" required:"false"` AptosBaseUrl string `split_words:"true" required:"false"` AptosRequestsPerMinute uint16 `split_words:"true" required:"false"` AptosFallbackUrls string `split_words:"true" required:"false"` AptosFallbackRequestsPerMinute string `split_words:"true" required:"false"` ArbitrumBaseUrl string `split_words:"true" required:"false"` ArbitrumRequestsPerMinute uint16 `split_words:"true" required:"false"` ArbitrumFallbackUrls string `split_words:"true" required:"false"` ArbitrumFallbackRequestsPerMinute string `split_words:"true" required:"false"` AvalancheBaseUrl string `split_words:"true" required:"false"` AvalancheRequestsPerMinute uint16 `split_words:"true" required:"false"` AvalancheFallbackUrls string `split_words:"true" required:"false"` AvalancheFallbackRequestsPerMinute string `split_words:"true" required:"false"` BaseBaseUrl string `split_words:"true" required:"false"` BaseRequestsPerMinute uint16 `split_words:"true" required:"false"` BaseFallbackUrls string `split_words:"true" required:"false"` BaseFallbackRequestsPerMinute string `split_words:"true" required:"false"` BscBaseUrl string `split_words:"true" required:"false"` BscRequestsPerMinute uint16 `split_words:"true" required:"false"` BscFallbackUrls string `split_words:"true" required:"false"` BscFallbackRequestsPerMinute string `split_words:"true" required:"false"` CeloBaseUrl string `split_words:"true" required:"false"` CeloRequestsPerMinute uint16 `split_words:"true" required:"false"` CeloFallbackUrls string `split_words:"true" required:"false"` CeloFallbackRequestsPerMinute string `split_words:"true" required:"false"` EthereumBaseUrl string `split_words:"true" required:"false"` EthereumRequestsPerMinute uint16 `split_words:"true" required:"false"` EthereumFallbackUrls string `split_words:"true" required:"false"` EthereumFallbackRequestsPerMinute string `split_words:"true" required:"false"` EvmosBaseUrl string `split_words:"true" required:"false"` EvmosRequestsPerMinute uint16 `split_words:"true" required:"false"` EvmosFallbackUrls string `split_words:"true" required:"false"` EvmosFallbackRequestsPerMinute string `split_words:"true" required:"false"` FantomBaseUrl string `split_words:"true" required:"false"` FantomRequestsPerMinute uint16 `split_words:"true" required:"false"` FantomFallbackUrls string `split_words:"true" required:"false"` FantomFallbackRequestsPerMinute string `split_words:"true" required:"false"` InjectiveBaseUrl string `split_words:"true" required:"false"` InjectiveRequestsPerMinute uint16 `split_words:"true" required:"false"` InjectiveFallbackUrls string `split_words:"true" required:"false"` InjectiveFallbackRequestsPerMinute string `split_words:"true" required:"false"` KaruraBaseUrl string `split_words:"true" required:"false"` KaruraRequestsPerMinute uint16 `split_words:"true" required:"false"` KaruraFallbackUrls string `split_words:"true" required:"false"` KaruraFallbackRequestsPerMinute string `split_words:"true" required:"false"` KlaytnBaseUrl string `split_words:"true" required:"false"` KlaytnRequestsPerMinute uint16 `split_words:"true" required:"false"` KlaytnFallbackUrls string `split_words:"true" required:"false"` KlaytnFallbackRequestsPerMinute string `split_words:"true" required:"false"` KujiraBaseUrl string `split_words:"true" required:"false"` KujiraRequestsPerMinute uint16 `split_words:"true" required:"false"` KujiraFallbackUrls string `split_words:"true" required:"false"` KujiraFallbackRequestsPerMinute string `split_words:"true" required:"false"` MoonbeamBaseUrl string `split_words:"true" required:"false"` MoonbeamRequestsPerMinute uint16 `split_words:"true" required:"false"` MoonbeamFallbackUrls string `split_words:"true" required:"false"` MoonbeamFallbackRequestsPerMinute string `split_words:"true" required:"false"` OasisBaseUrl string `split_words:"true" required:"false"` OasisRequestsPerMinute uint16 `split_words:"true" required:"false"` OasisFallbackUrls string `split_words:"true" required:"false"` OasisFallbackRequestsPerMinute string `split_words:"true" required:"false"` OptimismBaseUrl string `split_words:"true" required:"false"` OptimismRequestsPerMinute uint16 `split_words:"true" required:"false"` OptimismFallbackUrls string `split_words:"true" required:"false"` OptimismFallbackRequestsPerMinute string `split_words:"true" required:"false"` OsmosisBaseUrl string `split_words:"true" required:"false"` OsmosisRequestsPerMinute uint16 `split_words:"true" required:"false"` OsmosisFallbackUrls string `split_words:"true" required:"false"` OsmosisFallbackRequestsPerMinute string `split_words:"true" required:"false"` PolygonBaseUrl string `split_words:"true" required:"false"` PolygonRequestsPerMinute uint16 `split_words:"true" required:"false"` PolygonFallbackUrls string `split_words:"true" required:"false"` PolygonFallbackRequestsPerMinute string `split_words:"true" required:"false"` SeiBaseUrl string `split_words:"true" required:"false"` SeiRequestsPerMinute uint16 `split_words:"true" required:"false"` SeiFallbackUrls string `split_words:"true" required:"false"` SeiFallbackRequestsPerMinute string `split_words:"true" required:"false"` SolanaBaseUrl string `split_words:"true" required:"false"` SolanaRequestsPerMinute uint16 `split_words:"true" required:"false"` SolanaFallbackUrls string `split_words:"true" required:"false"` SolanaFallbackRequestsPerMinute string `split_words:"true" required:"false"` SuiBaseUrl string `split_words:"true" required:"false"` SuiRequestsPerMinute uint16 `split_words:"true" required:"false"` SuiFallbackUrls string `split_words:"true" required:"false"` SuiFallbackRequestsPerMinute string `split_words:"true" required:"false"` TerraBaseUrl string `split_words:"true" required:"false"` TerraRequestsPerMinute uint16 `split_words:"true" required:"false"` TerraFallbackUrls string `split_words:"true" required:"false"` TerraFallbackRequestsPerMinute string `split_words:"true" required:"false"` Terra2BaseUrl string `split_words:"true" required:"false"` Terra2RequestsPerMinute uint16 `split_words:"true" required:"false"` Terra2FallbackUrls string `split_words:"true" required:"false"` Terra2FallbackRequestsPerMinute string `split_words:"true" required:"false"` XplaBaseUrl string `split_words:"true" required:"false"` XplaRequestsPerMinute uint16 `split_words:"true" required:"false"` XplaFallbackUrls string `split_words:"true" required:"false"` XplaFallbackRequestsPerMinute string `split_words:"true" required:"false"` WormchainBaseUrl string `split_words:"true" required:"false"` WormchainRequestsPerMinute uint16 `split_words:"true" required:"false"` WormchainFallbackUrls string `split_words:"true" required:"false"` WormchainFallbackRequestsPerMinute string `split_words:"true" required:"false"` } type TestnetRpcProviderSettings struct { ArbitrumSepoliaBaseUrl string `split_words:"true" required:"false"` ArbitrumSepoliaRequestsPerMinute uint16 `split_words:"true" required:"false"` ArbitrumSepoliaFallbackUrls string `split_words:"true" required:"false"` ArbitrumSepoliaFallbackRequestsPerMinute string `split_words:"true" required:"false"` BaseSepoliaBaseUrl string `split_words:"true" required:"false"` BaseSepoliaRequestsPerMinute uint16 `split_words:"true" required:"false"` BaseSepoliaFallbackUrls string `split_words:"true" required:"false"` BaseSepoliaFallbackRequestsPerMinute string `split_words:"true" required:"false"` EthereumSepoliaBaseUrl string `split_words:"true" required:"false"` EthereumSepoliaRequestsPerMinute uint16 `split_words:"true" required:"false"` EthereumSepoliaFallbackUrls string `split_words:"true" required:"false"` EthereumSepoliaFallbackRequestsPerMinute string `split_words:"true" required:"false"` OptimismSepoliaBaseUrl string `split_words:"true" required:"false"` OptimismSepoliaRequestsPerMinute uint16 `split_words:"true" required:"false"` OptimismSepoliaFallbackUrls string `split_words:"true" required:"false"` OptimismSepoliaFallbackRequestsPerMinute string `split_words:"true" required:"false"` } func NewBackfillerSettings() (*BackfillerSettings, error) { _ = godotenv.Load() var settings BackfillerSettings err := envconfig.Process("", &settings) if err != nil { return nil, fmt.Errorf("failed to read config from environment: %w", err) } if settings.RpcProviderPath != "" { rpcJsonFile, err := os.ReadFile(settings.RpcProviderPath) if err != nil { return nil, fmt.Errorf("failed to read rpc provider settings from file: %w", err) } var rpcProviderSettingsJson RpcProviderSettingsJson err = json.Unmarshal(rpcJsonFile, &rpcProviderSettingsJson) if err != nil { return nil, fmt.Errorf("failed to unmarshal rpc provider settings from file: %w", err) } settings.RpcProviderSettingsJson = &rpcProviderSettingsJson } else { rpcProviderSettings, err := LoadFromEnv[RpcProviderSettings]() if err != nil { return nil, err } settings.RpcProviderSettings = rpcProviderSettings } return &settings, nil } // MapRpcProviderToRpcConfig converts the RpcProviderSettings to a map of RpcConfig func (s *BackfillerSettings) MapRpcProviderToRpcConfig() (map[sdk.ChainID][]RpcConfig, error) { if s.RpcProviderSettingsJson != nil { return s.RpcProviderSettingsJson.ToMap() } return s.RpcProviderSettings.ToMap() } func New() (*ServiceSettings, error) { _ = godotenv.Load() var settings ServiceSettings err := envconfig.Process("", &settings) if err != nil { return nil, fmt.Errorf("failed to read config from environment: %w", err) } if settings.RpcProviderPath != "" { rpcJsonFile, err := os.ReadFile(settings.RpcProviderPath) if err != nil { return nil, fmt.Errorf("failed to read rpc provider settings from file: %w", err) } var rpcProviderSettingsJson RpcProviderSettingsJson err = json.Unmarshal(rpcJsonFile, &rpcProviderSettingsJson) if err != nil { return nil, fmt.Errorf("failed to unmarshal rpc provider settings from file: %w", err) } settings.RpcProviderSettingsJson = &rpcProviderSettingsJson settings.RpcProviderSettings = nil } else { rpcProviderSettings, err := LoadFromEnv[RpcProviderSettings]() if err != nil { return nil, err } settings.RpcProviderSettings = rpcProviderSettings settings.RpcProviderSettingsJson = nil } return &settings, nil } func LoadFromEnv[T any]() (*T, error) { _ = godotenv.Load() var settings T err := envconfig.Process("", &settings) if err != nil { return nil, fmt.Errorf("failed to read config from environment: %w", err) } return &settings, nil } // RpcConfig defines the configuration for a single RPC provider type RpcConfig struct { Url string Priority uint8 RequestsPerMinute uint16 } // MapRpcProviderToRpcConfig converts the RpcProviderSettings to a map of RpcConfig func (s *ServiceSettings) MapRpcProviderToRpcConfig() (map[sdk.ChainID][]RpcConfig, error) { if s.RpcProviderSettingsJson != nil { return s.RpcProviderSettingsJson.ToMap() } return s.RpcProviderSettings.ToMap() } // ToMap converts the RpcProviderSettingsJson to a map of RpcConfig func (r RpcProviderSettingsJson) ToMap() (map[sdk.ChainID][]RpcConfig, error) { rpcs := make(map[sdk.ChainID][]RpcConfig) for _, rpcProvider := range r.RpcProviders { chainID := sdk.ChainID(rpcProvider.ChainId) var rpcConfigs []RpcConfig for _, rpcSetting := range rpcProvider.RpcSettings { rpcConfigs = append(rpcConfigs, RpcConfig{ Url: rpcSetting.Url, Priority: rpcSetting.Priority, RequestsPerMinute: rpcSetting.RequestPerMinute, }) } rpcs[chainID] = rpcConfigs } return rpcs, nil } // ToMap converts the RpcProviderSettings to a map of RpcConfig func (r RpcProviderSettings) ToMap() (map[sdk.ChainID][]RpcConfig, error) { rpcs := make(map[sdk.ChainID][]RpcConfig) // add acala rpcs acalaRpcConfigs, err := addRpcConfig( r.AcalaBaseUrl, r.AcalaRequestsPerMinute, r.AcalaFallbackUrls, r.AcalaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDAcala] = acalaRpcConfigs // add algorand rpcs algorandRpcConfigs, err := addRpcConfig( r.AlgorandBaseUrl, r.AlgorandRequestsPerMinute, r.AlgorandFallbackUrls, r.AlgorandFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDAlgorand] = algorandRpcConfigs // add aptos rpcs aptosRpcConfigs, err := addRpcConfig( r.AptosBaseUrl, r.AptosRequestsPerMinute, r.AptosFallbackUrls, r.AptosFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDAptos] = aptosRpcConfigs // add arbitrum rpcs arbitrumRpcConfigs, err := addRpcConfig( r.ArbitrumBaseUrl, r.ArbitrumRequestsPerMinute, r.ArbitrumFallbackUrls, r.ArbitrumFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDArbitrum] = arbitrumRpcConfigs // add avalanche rpcs avalancheRpcConfigs, err := addRpcConfig( r.AvalancheBaseUrl, r.AvalancheRequestsPerMinute, r.AvalancheFallbackUrls, r.AvalancheFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDAvalanche] = avalancheRpcConfigs // add base rpcs baseRpcConfigs, err := addRpcConfig( r.BaseBaseUrl, r.BaseRequestsPerMinute, r.BaseFallbackUrls, r.BaseFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDBase] = baseRpcConfigs // add bsc rpcs bscRpcConfigs, err := addRpcConfig( r.BscBaseUrl, r.BscRequestsPerMinute, r.BscFallbackUrls, r.BscFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDBSC] = bscRpcConfigs // add celo rpcs celoRpcConfigs, err := addRpcConfig( r.CeloBaseUrl, r.CeloRequestsPerMinute, r.CeloFallbackUrls, r.CeloFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDCelo] = celoRpcConfigs // add ethereum rpcs ethereumRpcConfigs, err := addRpcConfig( r.EthereumBaseUrl, r.EthereumRequestsPerMinute, r.EthereumFallbackUrls, r.EthereumFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDEthereum] = ethereumRpcConfigs // add evmos rpcs evmosRpcConfigs, err := addRpcConfig( r.EvmosBaseUrl, r.EvmosRequestsPerMinute, r.EvmosFallbackUrls, r.EvmosFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDEvmos] = evmosRpcConfigs // add fantom rpcs fantomRpcConfigs, err := addRpcConfig( r.FantomBaseUrl, r.FantomRequestsPerMinute, r.FantomFallbackUrls, r.FantomFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDFantom] = fantomRpcConfigs // add injective rpcs injectiveRpcConfigs, err := addRpcConfig( r.InjectiveBaseUrl, r.InjectiveRequestsPerMinute, r.InjectiveFallbackUrls, r.InjectiveFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDInjective] = injectiveRpcConfigs // add karura rpcs karuraRpcConfigs, err := addRpcConfig( r.KaruraBaseUrl, r.KaruraRequestsPerMinute, r.KaruraFallbackUrls, r.KaruraFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDKarura] = karuraRpcConfigs // add klaytn rpcs klaytnRpcConfigs, err := addRpcConfig( r.KlaytnBaseUrl, r.KlaytnRequestsPerMinute, r.KlaytnFallbackUrls, r.KlaytnFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDKlaytn] = klaytnRpcConfigs // add kujira rpcs kujiraRpcConfigs, err := addRpcConfig( r.KujiraBaseUrl, r.KujiraRequestsPerMinute, r.KujiraFallbackUrls, r.KujiraFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDKujira] = kujiraRpcConfigs // add moonbeam rpcs moonbeamRpcConfigs, err := addRpcConfig( r.MoonbeamBaseUrl, r.MoonbeamRequestsPerMinute, r.MoonbeamFallbackUrls, r.MoonbeamFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDMoonbeam] = moonbeamRpcConfigs // add oasis rpcs oasisRpcConfigs, err := addRpcConfig( r.OasisBaseUrl, r.OasisRequestsPerMinute, r.OasisFallbackUrls, r.OasisFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDOasis] = oasisRpcConfigs // add optimism rpcs optimismRpcConfigs, err := addRpcConfig( r.OptimismBaseUrl, r.OptimismRequestsPerMinute, r.OptimismFallbackUrls, r.OptimismFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDOptimism] = optimismRpcConfigs // add osmosis rpcs osmosisRpcConfigs, err := addRpcConfig( r.OsmosisBaseUrl, r.OsmosisRequestsPerMinute, r.OsmosisFallbackUrls, r.OsmosisFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDOsmosis] = osmosisRpcConfigs // add polygon rpcs polygonRpcConfigs, err := addRpcConfig( r.PolygonBaseUrl, r.PolygonRequestsPerMinute, r.PolygonFallbackUrls, r.PolygonFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDPolygon] = polygonRpcConfigs // add sei rpcs seiRpcConfigs, err := addRpcConfig( r.SeiBaseUrl, r.SeiRequestsPerMinute, r.SeiFallbackUrls, r.SeiFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDSei] = seiRpcConfigs // add solana rpcs solanaRpcConfigs, err := addRpcConfig( r.SolanaBaseUrl, r.SolanaRequestsPerMinute, r.SolanaFallbackUrls, r.SolanaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDSolana] = solanaRpcConfigs // add sui rpcs suiRpcConfigs, err := addRpcConfig( r.SuiBaseUrl, r.SuiRequestsPerMinute, r.SuiFallbackUrls, r.SuiFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDSui] = suiRpcConfigs // add terra rpcs terraRpcConfigs, err := addRpcConfig( r.TerraBaseUrl, r.TerraRequestsPerMinute, r.TerraFallbackUrls, r.TerraFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDTerra] = terraRpcConfigs // add terra2 rpcs terra2RpcConfigs, err := addRpcConfig( r.Terra2BaseUrl, r.Terra2RequestsPerMinute, r.Terra2FallbackUrls, r.Terra2FallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDTerra2] = terra2RpcConfigs // add xpla rpcs xplaRpcConfigs, err := addRpcConfig( r.XplaBaseUrl, r.XplaRequestsPerMinute, r.XplaFallbackUrls, r.XplaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDXpla] = xplaRpcConfigs // add wormchain rpcs wormchainRpcConfigs, err := addRpcConfig( r.WormchainBaseUrl, r.WormchainRequestsPerMinute, r.WormchainFallbackUrls, r.WormchainFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDWormchain] = wormchainRpcConfigs return rpcs, nil } // ToMap converts the TestnetRpcProviderSettings to a map of RpcConfig func (r TestnetRpcProviderSettings) ToMap() (map[sdk.ChainID][]RpcConfig, error) { rpcs := make(map[sdk.ChainID][]RpcConfig) // add arbitrum sepolia rpcs arbitrumSepoliaRpcConfigs, err := addRpcConfig( r.ArbitrumSepoliaBaseUrl, r.ArbitrumSepoliaRequestsPerMinute, r.ArbitrumSepoliaFallbackUrls, r.ArbitrumSepoliaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDArbitrumSepolia] = arbitrumSepoliaRpcConfigs // add base sepolia rpcs baseSepoliaRpcConfigs, err := addRpcConfig( r.BaseSepoliaBaseUrl, r.BaseSepoliaRequestsPerMinute, r.BaseSepoliaFallbackUrls, r.BaseSepoliaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDBaseSepolia] = baseSepoliaRpcConfigs // add ethereum sepolia rpcs ethereumSepoliaRpcConfigs, err := addRpcConfig( r.EthereumSepoliaBaseUrl, r.EthereumSepoliaRequestsPerMinute, r.EthereumSepoliaFallbackUrls, r.EthereumSepoliaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDSepolia] = ethereumSepoliaRpcConfigs // add optimism sepolia rpcs optimismSepoliaRpcConfigs, err := addRpcConfig( r.OptimismSepoliaBaseUrl, r.OptimismSepoliaRequestsPerMinute, r.OptimismSepoliaFallbackUrls, r.OptimismSepoliaFallbackRequestsPerMinute) if err != nil { return nil, err } rpcs[sdk.ChainIDOptimismSepolia] = optimismSepoliaRpcConfigs return rpcs, nil } // addRpcConfig convert chain rpc settings to RpcConfig func addRpcConfig(baseURl string, requestPerMinute uint16, fallbackUrls string, fallbackRequestPerMinute string) ([]RpcConfig, error) { // check if the primary rpc url and rate limit are empty if baseURl == "" { return []RpcConfig{}, errors.New("primary rpc url is empty") } if requestPerMinute == 0 { return []RpcConfig{}, errors.New("primary rpc rate limit is 0") } var rpcConfigs []RpcConfig // add primary rpc rpcConfigs = append(rpcConfigs, RpcConfig{ Url: baseURl, Priority: 1, RequestsPerMinute: requestPerMinute, }) // add fallback rpc if fallbackUrls == "" { return rpcConfigs, nil } sfallbackUrls := strings.Split(fallbackUrls, ",") sFallbackRequestPerMinute := strings.Split(fallbackRequestPerMinute, ",") // check if the number of fallback urls and fallback rate limits are matched if len(sfallbackUrls) != len(sFallbackRequestPerMinute) { return rpcConfigs, errors.New("fallback urls and fallback rate limits are not matched") } // add fallback rpcs for i, v := range sFallbackRequestPerMinute { uRateLimiter, err := strconv.ParseUint(v, 10, 64) if err != nil { return rpcConfigs, err } rpcConfigs = append(rpcConfigs, RpcConfig{ Url: sfallbackUrls[i], Priority: 2, RequestsPerMinute: uint16(uRateLimiter), }) } return rpcConfigs, nil }