From 00ac8e0650eaa4b5958173fe450d8e5386543be4 Mon Sep 17 00:00:00 2001 From: pablomendezroyo <41727368+pablomendezroyo@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:43:54 +0100 Subject: [PATCH] Implement relays checker (#53) * Implement relays checker * Implement api * remove first execution complete * run go * fix get relays * fix initialization * remove new line --- abi/MEVBoostRelaysAllowList.json | 182 +++ cmd/main.go | 12 +- internal/adapters/api/api_adapter.go | 66 +- .../bindings/mevboost_relays_allowlist.go | 1402 +++++++++++++++++ .../relaysAllowed/relaysAllowed_adapter.go | 54 + .../adapters/relaysUsed/relaysUsed_adapter.go | 64 + .../mevboost_relays_allowlist_events.go | 8 + .../application/ports/relaysallowed_port.go | 10 + internal/application/ports/relaysused_port.go | 7 + .../application/services/relaysChecker.go | 147 ++ internal/config/config_loader.go | 24 +- 11 files changed, 1963 insertions(+), 13 deletions(-) create mode 100644 abi/MEVBoostRelaysAllowList.json create mode 100644 internal/adapters/relaysAllowed/bindings/mevboost_relays_allowlist.go create mode 100644 internal/adapters/relaysAllowed/relaysAllowed_adapter.go create mode 100644 internal/adapters/relaysUsed/relaysUsed_adapter.go create mode 100644 internal/application/domain/mevboost_relays_allowlist_events.go create mode 100644 internal/application/ports/relaysallowed_port.go create mode 100644 internal/application/ports/relaysused_port.go create mode 100644 internal/application/services/relaysChecker.go diff --git a/abi/MEVBoostRelaysAllowList.json b/abi/MEVBoostRelaysAllowList.json new file mode 100644 index 0000000..eab467c --- /dev/null +++ b/abi/MEVBoostRelaysAllowList.json @@ -0,0 +1,182 @@ +[ + { + "name": "RelayAdded", + "inputs": [ + { "name": "uri_hash", "type": "string", "indexed": true }, + { + "name": "relay", + "type": "tuple", + "components": [ + { "name": "uri", "type": "string" }, + { "name": "operator", "type": "string" }, + { "name": "is_mandatory", "type": "bool" }, + { "name": "description", "type": "string" } + ], + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "RelayRemoved", + "inputs": [ + { "name": "uri_hash", "type": "string", "indexed": true }, + { "name": "uri", "type": "string", "indexed": false } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "AllowedListUpdated", + "inputs": [ + { "name": "allowed_list_version", "type": "uint256", "indexed": true } + ], + "anonymous": false, + "type": "event" + }, + { + "name": "OwnerChanged", + "inputs": [{ "name": "new_owner", "type": "address", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "ManagerChanged", + "inputs": [{ "name": "new_manager", "type": "address", "indexed": true }], + "anonymous": false, + "type": "event" + }, + { + "name": "ERC20Recovered", + "inputs": [ + { "name": "token", "type": "address", "indexed": true }, + { "name": "amount", "type": "uint256", "indexed": false }, + { "name": "recipient", "type": "address", "indexed": true } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "nonpayable", + "type": "constructor", + "inputs": [{ "name": "owner", "type": "address" }], + "outputs": [] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_relays_amount", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_owner", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_manager", + "inputs": [], + "outputs": [{ "name": "", "type": "address" }] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_relays", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "tuple[]", + "components": [ + { "name": "uri", "type": "string" }, + { "name": "operator", "type": "string" }, + { "name": "is_mandatory", "type": "bool" }, + { "name": "description", "type": "string" } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_relay_by_uri", + "inputs": [{ "name": "relay_uri", "type": "string" }], + "outputs": [ + { + "name": "", + "type": "tuple", + "components": [ + { "name": "uri", "type": "string" }, + { "name": "operator", "type": "string" }, + { "name": "is_mandatory", "type": "bool" }, + { "name": "description", "type": "string" } + ] + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_allowed_list_version", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256" }] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "add_relay", + "inputs": [ + { "name": "uri", "type": "string" }, + { "name": "operator", "type": "string" }, + { "name": "is_mandatory", "type": "bool" }, + { "name": "description", "type": "string" } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "remove_relay", + "inputs": [{ "name": "uri", "type": "string" }], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "change_owner", + "inputs": [{ "name": "owner", "type": "address" }], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "set_manager", + "inputs": [{ "name": "manager", "type": "address" }], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "dismiss_manager", + "inputs": [], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "recover_erc20", + "inputs": [ + { "name": "token", "type": "address" }, + { "name": "amount", "type": "uint256" }, + { "name": "recipient", "type": "address" } + ], + "outputs": [] + }, + { "stateMutability": "nonpayable", "type": "fallback" } +] diff --git a/cmd/main.go b/cmd/main.go index d883d57..aa80058 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,6 +18,8 @@ import ( "lido-events/internal/adapters/ipfs" "lido-events/internal/adapters/notifier" proxyapi "lido-events/internal/adapters/proxyApi" + relaysallowed "lido-events/internal/adapters/relaysAllowed" + relaysused "lido-events/internal/adapters/relaysUsed" "lido-events/internal/adapters/storage" "lido-events/internal/adapters/vebo" "lido-events/internal/application/services" @@ -46,8 +48,13 @@ func main() { if err != nil { logger.WarnWithPrefix(logPrefix, "Telegram notifier not initialized: %v", err) } + relaysUsedAdapter := relaysused.NewRelaysUsedAdapter(networkConfig.DappmanagerUrl, networkConfig.MevBoostDnpName) + relaysAllowedAdapter, err := relaysallowed.NewRelaysAllowedAdapter(networkConfig.WsURL, networkConfig.MEVBoostRelaysAllowListAddres, networkConfig.DappmanagerUrl, networkConfig.MevBoostDnpName) + if err != nil { + logger.Fatal("Failed to initialize relaysAllowedAdapter: %v", err) + } - apiAdapter := api.NewAPIAdapter(storageAdapter, networkConfig.CORS) + apiAdapter := api.NewAPIAdapter(ctx, storageAdapter, relaysUsedAdapter, relaysAllowedAdapter, networkConfig.CORS) proxyApiAdapter := proxyapi.NewProxyAPIAdapter(networkConfig.CORS, networkConfig.LidoKeysApiUrl) // Initialize API services @@ -91,8 +98,11 @@ func main() { validatorExitRequestScannerService := services.NewValidatorExitRequestEventScanner(storageAdapter, notifierAdapter, veboAdapter, executionAdapter, beaconchainAdapter, networkConfig.VeboBlockDeployment) validatorEjectorService := services.NewValidatorEjectorService(storageAdapter, notifierAdapter, exitValidatorAdapter, beaconchainAdapter) pendingHashesLoaderService := services.NewPendingHashesLoader(storageAdapter, ipfsAdapter) + relaysCheckerService := services.NewRelayCronService(relaysAllowedAdapter, relaysUsedAdapter, notifierAdapter) // Start domain services + go relaysCheckerService.StartRelayMonitoringCron(ctx, 5*time.Minute, &wg) + distributionLogUpdatedExecutionComplete := make(chan struct{}) go distributionLogUpdatedScannerService.ScanDistributionLogUpdatedEventsCron(ctx, 384*time.Second, &wg, distributionLogUpdatedExecutionComplete) go pendingHashesLoaderService.LoadPendingHashesCron(ctx, 3*time.Hour, &wg, distributionLogUpdatedExecutionComplete) diff --git a/internal/adapters/api/api_adapter.go b/internal/adapters/api/api_adapter.go index 812978a..6c57a72 100644 --- a/internal/adapters/api/api_adapter.go +++ b/internal/adapters/api/api_adapter.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "math/big" "net/http" @@ -15,9 +16,13 @@ import ( // APIHandler holds the necessary dependencies for API endpoints type APIHandler struct { - StoragePort ports.StoragePort - Router *mux.Router - adapterPrefix string + ctx context.Context + StoragePort ports.StoragePort + NotifierPort ports.NotifierPort + RelaysUsedPort ports.RelaysUsedPort + RelaysAllowedPort ports.RelaysAllowedPort + Router *mux.Router + adapterPrefix string } // Ensure APIHandler implements the ports.API interface @@ -29,11 +34,14 @@ func (h *APIHandler) GetRouter() http.Handler { } // NewAPIAdapter initializes the APIHandler and sets up routes with CORS enabled -func NewAPIAdapter(storagePort ports.StoragePort, allowedOrigins []string) *APIHandler { +func NewAPIAdapter(ctx context.Context, storagePort ports.StoragePort, relaysUsedPort ports.RelaysUsedPort, relaysAllowedPort ports.RelaysAllowedPort, allowedOrigins []string) *APIHandler { h := &APIHandler{ - StoragePort: storagePort, - Router: mux.NewRouter(), - adapterPrefix: "API", + ctx: ctx, + StoragePort: storagePort, + RelaysUsedPort: relaysUsedPort, + RelaysAllowedPort: relaysAllowedPort, + Router: mux.NewRouter(), + adapterPrefix: "API", } // Set up API routes @@ -63,6 +71,8 @@ func (h *APIHandler) SetupRoutes() { h.Router.HandleFunc("/api/v0/events_indexer/operatorId", h.DeleteOperator).Methods("DELETE", "OPTIONS") h.Router.HandleFunc("/api/v0/events_indexer/operator_performance", h.GetOperatorPerformance).Methods("GET", "OPTIONS") h.Router.HandleFunc("/api/v0/events_indexer/exit_requests", h.GetExitRequests).Methods("GET", "OPTIONS") + h.Router.HandleFunc("/api/v0/events_indexer/relays_allowed", h.GetRelaysAllowed).Methods("GET", "OPTIONS") + h.Router.HandleFunc("/api/v0/events_indexer/relays_used", h.GetRelaysUsed).Methods("GET", "OPTIONS") // Add a generic OPTIONS handler to ensure preflight requests are handled h.Router.Methods("OPTIONS").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -70,6 +80,48 @@ func (h *APIHandler) SetupRoutes() { }) } +// GetRelaysAllowed retrieves the list of allowed relays +func (h *APIHandler) GetRelaysAllowed(w http.ResponseWriter, r *http.Request) { + logger.DebugWithPrefix("API", "GetRelaysAllowed request received") + relays, err := h.RelaysAllowedPort.GetRelaysAllowList(h.ctx) + if err != nil { + logger.ErrorWithPrefix("API", "Error fetching allowed relays: %v", err) + writeErrorResponse(w, "Error fetching allowed relays", http.StatusInternalServerError) + return + } + + jsonResponse, err := json.Marshal(relays) + if err != nil { + logger.ErrorWithPrefix("API", "Error generating JSON response in GetRelaysAllowed: %v", err) + writeErrorResponse(w, "Error generating JSON response", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Write(jsonResponse) +} + +// GetRelaysUsed retrieves the list of used relays +func (h *APIHandler) GetRelaysUsed(w http.ResponseWriter, r *http.Request) { + logger.DebugWithPrefix("API", "GetRelaysUsed request received") + relays, err := h.RelaysUsedPort.GetRelaysUsed(h.ctx) + if err != nil { + logger.ErrorWithPrefix("API", "Error fetching used relays: %v", err) + writeErrorResponse(w, "Error fetching used relays", http.StatusInternalServerError) + return + } + + jsonResponse, err := json.Marshal(relays) + if err != nil { + logger.ErrorWithPrefix("API", "Error generating JSON response in GetRelaysUsed: %v", err) + writeErrorResponse(w, "Error generating JSON response", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Write(jsonResponse) +} + // GetTelegramConfig retrieves the Telegram configuration func (h *APIHandler) GetTelegramConfig(w http.ResponseWriter, r *http.Request) { logger.DebugWithPrefix("API", "GetTelegramConfig request received") diff --git a/internal/adapters/relaysAllowed/bindings/mevboost_relays_allowlist.go b/internal/adapters/relaysAllowed/bindings/mevboost_relays_allowlist.go new file mode 100644 index 0000000..685c160 --- /dev/null +++ b/internal/adapters/relaysAllowed/bindings/mevboost_relays_allowlist.go @@ -0,0 +1,1402 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + "lido-events/internal/application/domain" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// Struct0 is an auto generated low-level Go binding around an user-defined struct. +// type Struct0 struct { +// Uri string +// Operator string +// IsMandatory bool +// Description string +// } + +// BindingsMetaData contains all meta data concerning the Bindings contract. +var BindingsMetaData = &bind.MetaData{ + ABI: "[{\"name\":\"RelayAdded\",\"inputs\":[{\"name\":\"uri_hash\",\"type\":\"string\",\"indexed\":true},{\"name\":\"relay\",\"type\":\"tuple\",\"components\":[{\"name\":\"uri\",\"type\":\"string\"},{\"name\":\"operator\",\"type\":\"string\"},{\"name\":\"is_mandatory\",\"type\":\"bool\"},{\"name\":\"description\",\"type\":\"string\"}],\"indexed\":false}],\"anonymous\":false,\"type\":\"event\"},{\"name\":\"RelayRemoved\",\"inputs\":[{\"name\":\"uri_hash\",\"type\":\"string\",\"indexed\":true},{\"name\":\"uri\",\"type\":\"string\",\"indexed\":false}],\"anonymous\":false,\"type\":\"event\"},{\"name\":\"AllowedListUpdated\",\"inputs\":[{\"name\":\"allowed_list_version\",\"type\":\"uint256\",\"indexed\":true}],\"anonymous\":false,\"type\":\"event\"},{\"name\":\"OwnerChanged\",\"inputs\":[{\"name\":\"new_owner\",\"type\":\"address\",\"indexed\":true}],\"anonymous\":false,\"type\":\"event\"},{\"name\":\"ManagerChanged\",\"inputs\":[{\"name\":\"new_manager\",\"type\":\"address\",\"indexed\":true}],\"anonymous\":false,\"type\":\"event\"},{\"name\":\"ERC20Recovered\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"indexed\":true},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true}],\"anonymous\":false,\"type\":\"event\"},{\"stateMutability\":\"nonpayable\",\"type\":\"constructor\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"outputs\":[]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_relays_amount\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\"}]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_manager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\"}]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_relays\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"components\":[{\"name\":\"uri\",\"type\":\"string\"},{\"name\":\"operator\",\"type\":\"string\"},{\"name\":\"is_mandatory\",\"type\":\"bool\"},{\"name\":\"description\",\"type\":\"string\"}]}]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_relay_by_uri\",\"inputs\":[{\"name\":\"relay_uri\",\"type\":\"string\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"components\":[{\"name\":\"uri\",\"type\":\"string\"},{\"name\":\"operator\",\"type\":\"string\"},{\"name\":\"is_mandatory\",\"type\":\"bool\"},{\"name\":\"description\",\"type\":\"string\"}]}]},{\"stateMutability\":\"view\",\"type\":\"function\",\"name\":\"get_allowed_list_version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"add_relay\",\"inputs\":[{\"name\":\"uri\",\"type\":\"string\"},{\"name\":\"operator\",\"type\":\"string\"},{\"name\":\"is_mandatory\",\"type\":\"bool\"},{\"name\":\"description\",\"type\":\"string\"}],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"remove_relay\",\"inputs\":[{\"name\":\"uri\",\"type\":\"string\"}],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"change_owner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\"}],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"set_manager\",\"inputs\":[{\"name\":\"manager\",\"type\":\"address\"}],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"dismiss_manager\",\"inputs\":[],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"function\",\"name\":\"recover_erc20\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"recipient\",\"type\":\"address\"}],\"outputs\":[]},{\"stateMutability\":\"nonpayable\",\"type\":\"fallback\"}]", +} + +// BindingsABI is the input ABI used to generate the binding from. +// Deprecated: Use BindingsMetaData.ABI instead. +var BindingsABI = BindingsMetaData.ABI + +// Bindings is an auto generated Go binding around an Ethereum contract. +type Bindings struct { + BindingsCaller // Read-only binding to the contract + BindingsTransactor // Write-only binding to the contract + BindingsFilterer // Log filterer for contract events +} + +// BindingsCaller is an auto generated read-only Go binding around an Ethereum contract. +type BindingsCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BindingsTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BindingsTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BindingsFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BindingsFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BindingsSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BindingsSession struct { + Contract *Bindings // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BindingsCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BindingsCallerSession struct { + Contract *BindingsCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BindingsTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BindingsTransactorSession struct { + Contract *BindingsTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BindingsRaw is an auto generated low-level Go binding around an Ethereum contract. +type BindingsRaw struct { + Contract *Bindings // Generic contract binding to access the raw methods on +} + +// BindingsCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BindingsCallerRaw struct { + Contract *BindingsCaller // Generic read-only contract binding to access the raw methods on +} + +// BindingsTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BindingsTransactorRaw struct { + Contract *BindingsTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBindings creates a new instance of Bindings, bound to a specific deployed contract. +func NewBindings(address common.Address, backend bind.ContractBackend) (*Bindings, error) { + contract, err := bindBindings(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Bindings{BindingsCaller: BindingsCaller{contract: contract}, BindingsTransactor: BindingsTransactor{contract: contract}, BindingsFilterer: BindingsFilterer{contract: contract}}, nil +} + +// NewBindingsCaller creates a new read-only instance of Bindings, bound to a specific deployed contract. +func NewBindingsCaller(address common.Address, caller bind.ContractCaller) (*BindingsCaller, error) { + contract, err := bindBindings(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BindingsCaller{contract: contract}, nil +} + +// NewBindingsTransactor creates a new write-only instance of Bindings, bound to a specific deployed contract. +func NewBindingsTransactor(address common.Address, transactor bind.ContractTransactor) (*BindingsTransactor, error) { + contract, err := bindBindings(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BindingsTransactor{contract: contract}, nil +} + +// NewBindingsFilterer creates a new log filterer instance of Bindings, bound to a specific deployed contract. +func NewBindingsFilterer(address common.Address, filterer bind.ContractFilterer) (*BindingsFilterer, error) { + contract, err := bindBindings(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BindingsFilterer{contract: contract}, nil +} + +// bindBindings binds a generic wrapper to an already deployed contract. +func bindBindings(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := BindingsMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Bindings *BindingsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bindings.Contract.BindingsCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Bindings *BindingsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bindings.Contract.BindingsTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bindings *BindingsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bindings.Contract.BindingsTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Bindings *BindingsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Bindings.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Bindings *BindingsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bindings.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Bindings *BindingsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Bindings.Contract.contract.Transact(opts, method, params...) +} + +// GetAllowedListVersion is a free data retrieval call binding the contract method 0x76650ad3. +// +// Solidity: function get_allowed_list_version() view returns(uint256) +func (_Bindings *BindingsCaller) GetAllowedListVersion(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_allowed_list_version") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetAllowedListVersion is a free data retrieval call binding the contract method 0x76650ad3. +// +// Solidity: function get_allowed_list_version() view returns(uint256) +func (_Bindings *BindingsSession) GetAllowedListVersion() (*big.Int, error) { + return _Bindings.Contract.GetAllowedListVersion(&_Bindings.CallOpts) +} + +// GetAllowedListVersion is a free data retrieval call binding the contract method 0x76650ad3. +// +// Solidity: function get_allowed_list_version() view returns(uint256) +func (_Bindings *BindingsCallerSession) GetAllowedListVersion() (*big.Int, error) { + return _Bindings.Contract.GetAllowedListVersion(&_Bindings.CallOpts) +} + +// GetManager is a free data retrieval call binding the contract method 0x9e4a0fc4. +// +// Solidity: function get_manager() view returns(address) +func (_Bindings *BindingsCaller) GetManager(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_manager") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetManager is a free data retrieval call binding the contract method 0x9e4a0fc4. +// +// Solidity: function get_manager() view returns(address) +func (_Bindings *BindingsSession) GetManager() (common.Address, error) { + return _Bindings.Contract.GetManager(&_Bindings.CallOpts) +} + +// GetManager is a free data retrieval call binding the contract method 0x9e4a0fc4. +// +// Solidity: function get_manager() view returns(address) +func (_Bindings *BindingsCallerSession) GetManager() (common.Address, error) { + return _Bindings.Contract.GetManager(&_Bindings.CallOpts) +} + +// GetOwner is a free data retrieval call binding the contract method 0x0ac298dc. +// +// Solidity: function get_owner() view returns(address) +func (_Bindings *BindingsCaller) GetOwner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetOwner is a free data retrieval call binding the contract method 0x0ac298dc. +// +// Solidity: function get_owner() view returns(address) +func (_Bindings *BindingsSession) GetOwner() (common.Address, error) { + return _Bindings.Contract.GetOwner(&_Bindings.CallOpts) +} + +// GetOwner is a free data retrieval call binding the contract method 0x0ac298dc. +// +// Solidity: function get_owner() view returns(address) +func (_Bindings *BindingsCallerSession) GetOwner() (common.Address, error) { + return _Bindings.Contract.GetOwner(&_Bindings.CallOpts) +} + +// GetRelayByUri is a free data retrieval call binding the contract method 0xf5f33c7b. +// +// Solidity: function get_relay_by_uri(string relay_uri) view returns((string,string,bool,string)) +func (_Bindings *BindingsCaller) GetRelayByUri(opts *bind.CallOpts, relay_uri string) (domain.RelayAllowed, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_relay_by_uri", relay_uri) + + if err != nil { + return *new(domain.RelayAllowed), err + } + + out0 := *abi.ConvertType(out[0], new(domain.RelayAllowed)).(*domain.RelayAllowed) + + return out0, err + +} + +// GetRelayByUri is a free data retrieval call binding the contract method 0xf5f33c7b. +// +// Solidity: function get_relay_by_uri(string relay_uri) view returns((string,string,bool,string)) +func (_Bindings *BindingsSession) GetRelayByUri(relay_uri string) (domain.RelayAllowed, error) { + return _Bindings.Contract.GetRelayByUri(&_Bindings.CallOpts, relay_uri) +} + +// GetRelayByUri is a free data retrieval call binding the contract method 0xf5f33c7b. +// +// Solidity: function get_relay_by_uri(string relay_uri) view returns((string,string,bool,string)) +func (_Bindings *BindingsCallerSession) GetRelayByUri(relay_uri string) (domain.RelayAllowed, error) { + return _Bindings.Contract.GetRelayByUri(&_Bindings.CallOpts, relay_uri) +} + +// GetRelays is a free data retrieval call binding the contract method 0x04e469ea. +// +// Solidity: function get_relays() view returns((string,string,bool,string)[]) +func (_Bindings *BindingsCaller) GetRelays(opts *bind.CallOpts) ([]domain.RelayAllowed, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_relays") + + if err != nil { + return *new([]domain.RelayAllowed), err + } + + out0 := *abi.ConvertType(out[0], new([]domain.RelayAllowed)).(*[]domain.RelayAllowed) + + return out0, err + +} + +// GetRelays is a free data retrieval call binding the contract method 0x04e469ea. +// +// Solidity: function get_relays() view returns((string,string,bool,string)[]) +func (_Bindings *BindingsSession) GetRelays() ([]domain.RelayAllowed, error) { + return _Bindings.Contract.GetRelays(&_Bindings.CallOpts) +} + +// GetRelays is a free data retrieval call binding the contract method 0x04e469ea. +// +// Solidity: function get_relays() view returns((string,string,bool,string)[]) +func (_Bindings *BindingsCallerSession) GetRelays() ([]domain.RelayAllowed, error) { + return _Bindings.Contract.GetRelays(&_Bindings.CallOpts) +} + +// GetRelaysAmount is a free data retrieval call binding the contract method 0x312c3165. +// +// Solidity: function get_relays_amount() view returns(uint256) +func (_Bindings *BindingsCaller) GetRelaysAmount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Bindings.contract.Call(opts, &out, "get_relays_amount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetRelaysAmount is a free data retrieval call binding the contract method 0x312c3165. +// +// Solidity: function get_relays_amount() view returns(uint256) +func (_Bindings *BindingsSession) GetRelaysAmount() (*big.Int, error) { + return _Bindings.Contract.GetRelaysAmount(&_Bindings.CallOpts) +} + +// GetRelaysAmount is a free data retrieval call binding the contract method 0x312c3165. +// +// Solidity: function get_relays_amount() view returns(uint256) +func (_Bindings *BindingsCallerSession) GetRelaysAmount() (*big.Int, error) { + return _Bindings.Contract.GetRelaysAmount(&_Bindings.CallOpts) +} + +// AddRelay is a paid mutator transaction binding the contract method 0x2e21ecef. +// +// Solidity: function add_relay(string uri, string operator, bool is_mandatory, string description) returns() +func (_Bindings *BindingsTransactor) AddRelay(opts *bind.TransactOpts, uri string, operator string, is_mandatory bool, description string) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "add_relay", uri, operator, is_mandatory, description) +} + +// AddRelay is a paid mutator transaction binding the contract method 0x2e21ecef. +// +// Solidity: function add_relay(string uri, string operator, bool is_mandatory, string description) returns() +func (_Bindings *BindingsSession) AddRelay(uri string, operator string, is_mandatory bool, description string) (*types.Transaction, error) { + return _Bindings.Contract.AddRelay(&_Bindings.TransactOpts, uri, operator, is_mandatory, description) +} + +// AddRelay is a paid mutator transaction binding the contract method 0x2e21ecef. +// +// Solidity: function add_relay(string uri, string operator, bool is_mandatory, string description) returns() +func (_Bindings *BindingsTransactorSession) AddRelay(uri string, operator string, is_mandatory bool, description string) (*types.Transaction, error) { + return _Bindings.Contract.AddRelay(&_Bindings.TransactOpts, uri, operator, is_mandatory, description) +} + +// ChangeOwner is a paid mutator transaction binding the contract method 0x253c8bd4. +// +// Solidity: function change_owner(address owner) returns() +func (_Bindings *BindingsTransactor) ChangeOwner(opts *bind.TransactOpts, owner common.Address) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "change_owner", owner) +} + +// ChangeOwner is a paid mutator transaction binding the contract method 0x253c8bd4. +// +// Solidity: function change_owner(address owner) returns() +func (_Bindings *BindingsSession) ChangeOwner(owner common.Address) (*types.Transaction, error) { + return _Bindings.Contract.ChangeOwner(&_Bindings.TransactOpts, owner) +} + +// ChangeOwner is a paid mutator transaction binding the contract method 0x253c8bd4. +// +// Solidity: function change_owner(address owner) returns() +func (_Bindings *BindingsTransactorSession) ChangeOwner(owner common.Address) (*types.Transaction, error) { + return _Bindings.Contract.ChangeOwner(&_Bindings.TransactOpts, owner) +} + +// DismissManager is a paid mutator transaction binding the contract method 0x417a02b4. +// +// Solidity: function dismiss_manager() returns() +func (_Bindings *BindingsTransactor) DismissManager(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "dismiss_manager") +} + +// DismissManager is a paid mutator transaction binding the contract method 0x417a02b4. +// +// Solidity: function dismiss_manager() returns() +func (_Bindings *BindingsSession) DismissManager() (*types.Transaction, error) { + return _Bindings.Contract.DismissManager(&_Bindings.TransactOpts) +} + +// DismissManager is a paid mutator transaction binding the contract method 0x417a02b4. +// +// Solidity: function dismiss_manager() returns() +func (_Bindings *BindingsTransactorSession) DismissManager() (*types.Transaction, error) { + return _Bindings.Contract.DismissManager(&_Bindings.TransactOpts) +} + +// RecoverErc20 is a paid mutator transaction binding the contract method 0xedd885b4. +// +// Solidity: function recover_erc20(address token, uint256 amount, address recipient) returns() +func (_Bindings *BindingsTransactor) RecoverErc20(opts *bind.TransactOpts, token common.Address, amount *big.Int, recipient common.Address) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "recover_erc20", token, amount, recipient) +} + +// RecoverErc20 is a paid mutator transaction binding the contract method 0xedd885b4. +// +// Solidity: function recover_erc20(address token, uint256 amount, address recipient) returns() +func (_Bindings *BindingsSession) RecoverErc20(token common.Address, amount *big.Int, recipient common.Address) (*types.Transaction, error) { + return _Bindings.Contract.RecoverErc20(&_Bindings.TransactOpts, token, amount, recipient) +} + +// RecoverErc20 is a paid mutator transaction binding the contract method 0xedd885b4. +// +// Solidity: function recover_erc20(address token, uint256 amount, address recipient) returns() +func (_Bindings *BindingsTransactorSession) RecoverErc20(token common.Address, amount *big.Int, recipient common.Address) (*types.Transaction, error) { + return _Bindings.Contract.RecoverErc20(&_Bindings.TransactOpts, token, amount, recipient) +} + +// RemoveRelay is a paid mutator transaction binding the contract method 0xf5a70a80. +// +// Solidity: function remove_relay(string uri) returns() +func (_Bindings *BindingsTransactor) RemoveRelay(opts *bind.TransactOpts, uri string) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "remove_relay", uri) +} + +// RemoveRelay is a paid mutator transaction binding the contract method 0xf5a70a80. +// +// Solidity: function remove_relay(string uri) returns() +func (_Bindings *BindingsSession) RemoveRelay(uri string) (*types.Transaction, error) { + return _Bindings.Contract.RemoveRelay(&_Bindings.TransactOpts, uri) +} + +// RemoveRelay is a paid mutator transaction binding the contract method 0xf5a70a80. +// +// Solidity: function remove_relay(string uri) returns() +func (_Bindings *BindingsTransactorSession) RemoveRelay(uri string) (*types.Transaction, error) { + return _Bindings.Contract.RemoveRelay(&_Bindings.TransactOpts, uri) +} + +// SetManager is a paid mutator transaction binding the contract method 0x9aece83e. +// +// Solidity: function set_manager(address manager) returns() +func (_Bindings *BindingsTransactor) SetManager(opts *bind.TransactOpts, manager common.Address) (*types.Transaction, error) { + return _Bindings.contract.Transact(opts, "set_manager", manager) +} + +// SetManager is a paid mutator transaction binding the contract method 0x9aece83e. +// +// Solidity: function set_manager(address manager) returns() +func (_Bindings *BindingsSession) SetManager(manager common.Address) (*types.Transaction, error) { + return _Bindings.Contract.SetManager(&_Bindings.TransactOpts, manager) +} + +// SetManager is a paid mutator transaction binding the contract method 0x9aece83e. +// +// Solidity: function set_manager(address manager) returns() +func (_Bindings *BindingsTransactorSession) SetManager(manager common.Address) (*types.Transaction, error) { + return _Bindings.Contract.SetManager(&_Bindings.TransactOpts, manager) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_Bindings *BindingsTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _Bindings.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_Bindings *BindingsSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _Bindings.Contract.Fallback(&_Bindings.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() returns() +func (_Bindings *BindingsTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _Bindings.Contract.Fallback(&_Bindings.TransactOpts, calldata) +} + +// BindingsAllowedListUpdatedIterator is returned from FilterAllowedListUpdated and is used to iterate over the raw logs and unpacked data for AllowedListUpdated events raised by the Bindings contract. +type BindingsAllowedListUpdatedIterator struct { + Event *BindingsAllowedListUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsAllowedListUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsAllowedListUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsAllowedListUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsAllowedListUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsAllowedListUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsAllowedListUpdated represents a AllowedListUpdated event raised by the Bindings contract. +type BindingsAllowedListUpdated struct { + AllowedListVersion *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAllowedListUpdated is a free log retrieval operation binding the contract event 0x49f5627aa055ec3fcd474f99c8b7799b798c04af7b9f215305512c867e5a1839. +// +// Solidity: event AllowedListUpdated(uint256 indexed allowed_list_version) +func (_Bindings *BindingsFilterer) FilterAllowedListUpdated(opts *bind.FilterOpts, allowed_list_version []*big.Int) (*BindingsAllowedListUpdatedIterator, error) { + + var allowed_list_versionRule []interface{} + for _, allowed_list_versionItem := range allowed_list_version { + allowed_list_versionRule = append(allowed_list_versionRule, allowed_list_versionItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "AllowedListUpdated", allowed_list_versionRule) + if err != nil { + return nil, err + } + return &BindingsAllowedListUpdatedIterator{contract: _Bindings.contract, event: "AllowedListUpdated", logs: logs, sub: sub}, nil +} + +// WatchAllowedListUpdated is a free log subscription operation binding the contract event 0x49f5627aa055ec3fcd474f99c8b7799b798c04af7b9f215305512c867e5a1839. +// +// Solidity: event AllowedListUpdated(uint256 indexed allowed_list_version) +func (_Bindings *BindingsFilterer) WatchAllowedListUpdated(opts *bind.WatchOpts, sink chan<- *BindingsAllowedListUpdated, allowed_list_version []*big.Int) (event.Subscription, error) { + + var allowed_list_versionRule []interface{} + for _, allowed_list_versionItem := range allowed_list_version { + allowed_list_versionRule = append(allowed_list_versionRule, allowed_list_versionItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "AllowedListUpdated", allowed_list_versionRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsAllowedListUpdated) + if err := _Bindings.contract.UnpackLog(event, "AllowedListUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAllowedListUpdated is a log parse operation binding the contract event 0x49f5627aa055ec3fcd474f99c8b7799b798c04af7b9f215305512c867e5a1839. +// +// Solidity: event AllowedListUpdated(uint256 indexed allowed_list_version) +func (_Bindings *BindingsFilterer) ParseAllowedListUpdated(log types.Log) (*BindingsAllowedListUpdated, error) { + event := new(BindingsAllowedListUpdated) + if err := _Bindings.contract.UnpackLog(event, "AllowedListUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BindingsERC20RecoveredIterator is returned from FilterERC20Recovered and is used to iterate over the raw logs and unpacked data for ERC20Recovered events raised by the Bindings contract. +type BindingsERC20RecoveredIterator struct { + Event *BindingsERC20Recovered // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsERC20RecoveredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsERC20Recovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsERC20Recovered) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsERC20RecoveredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsERC20RecoveredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsERC20Recovered represents a ERC20Recovered event raised by the Bindings contract. +type BindingsERC20Recovered struct { + Token common.Address + Amount *big.Int + Recipient common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterERC20Recovered is a free log retrieval operation binding the contract event 0x8619312ed4eff1cf9f0116e6db2f49d9570a86f0350d1c5ad1bd0f7b0cf9e132. +// +// Solidity: event ERC20Recovered(address indexed token, uint256 amount, address indexed recipient) +func (_Bindings *BindingsFilterer) FilterERC20Recovered(opts *bind.FilterOpts, token []common.Address, recipient []common.Address) (*BindingsERC20RecoveredIterator, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "ERC20Recovered", tokenRule, recipientRule) + if err != nil { + return nil, err + } + return &BindingsERC20RecoveredIterator{contract: _Bindings.contract, event: "ERC20Recovered", logs: logs, sub: sub}, nil +} + +// WatchERC20Recovered is a free log subscription operation binding the contract event 0x8619312ed4eff1cf9f0116e6db2f49d9570a86f0350d1c5ad1bd0f7b0cf9e132. +// +// Solidity: event ERC20Recovered(address indexed token, uint256 amount, address indexed recipient) +func (_Bindings *BindingsFilterer) WatchERC20Recovered(opts *bind.WatchOpts, sink chan<- *BindingsERC20Recovered, token []common.Address, recipient []common.Address) (event.Subscription, error) { + + var tokenRule []interface{} + for _, tokenItem := range token { + tokenRule = append(tokenRule, tokenItem) + } + + var recipientRule []interface{} + for _, recipientItem := range recipient { + recipientRule = append(recipientRule, recipientItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "ERC20Recovered", tokenRule, recipientRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsERC20Recovered) + if err := _Bindings.contract.UnpackLog(event, "ERC20Recovered", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseERC20Recovered is a log parse operation binding the contract event 0x8619312ed4eff1cf9f0116e6db2f49d9570a86f0350d1c5ad1bd0f7b0cf9e132. +// +// Solidity: event ERC20Recovered(address indexed token, uint256 amount, address indexed recipient) +func (_Bindings *BindingsFilterer) ParseERC20Recovered(log types.Log) (*BindingsERC20Recovered, error) { + event := new(BindingsERC20Recovered) + if err := _Bindings.contract.UnpackLog(event, "ERC20Recovered", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BindingsManagerChangedIterator is returned from FilterManagerChanged and is used to iterate over the raw logs and unpacked data for ManagerChanged events raised by the Bindings contract. +type BindingsManagerChangedIterator struct { + Event *BindingsManagerChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsManagerChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsManagerChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsManagerChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsManagerChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsManagerChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsManagerChanged represents a ManagerChanged event raised by the Bindings contract. +type BindingsManagerChanged struct { + NewManager common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterManagerChanged is a free log retrieval operation binding the contract event 0x198db6e425fb8aafd1823c6ca50be2d51e5764571a5ae0f0f21c6812e45def0b. +// +// Solidity: event ManagerChanged(address indexed new_manager) +func (_Bindings *BindingsFilterer) FilterManagerChanged(opts *bind.FilterOpts, new_manager []common.Address) (*BindingsManagerChangedIterator, error) { + + var new_managerRule []interface{} + for _, new_managerItem := range new_manager { + new_managerRule = append(new_managerRule, new_managerItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "ManagerChanged", new_managerRule) + if err != nil { + return nil, err + } + return &BindingsManagerChangedIterator{contract: _Bindings.contract, event: "ManagerChanged", logs: logs, sub: sub}, nil +} + +// WatchManagerChanged is a free log subscription operation binding the contract event 0x198db6e425fb8aafd1823c6ca50be2d51e5764571a5ae0f0f21c6812e45def0b. +// +// Solidity: event ManagerChanged(address indexed new_manager) +func (_Bindings *BindingsFilterer) WatchManagerChanged(opts *bind.WatchOpts, sink chan<- *BindingsManagerChanged, new_manager []common.Address) (event.Subscription, error) { + + var new_managerRule []interface{} + for _, new_managerItem := range new_manager { + new_managerRule = append(new_managerRule, new_managerItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "ManagerChanged", new_managerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsManagerChanged) + if err := _Bindings.contract.UnpackLog(event, "ManagerChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseManagerChanged is a log parse operation binding the contract event 0x198db6e425fb8aafd1823c6ca50be2d51e5764571a5ae0f0f21c6812e45def0b. +// +// Solidity: event ManagerChanged(address indexed new_manager) +func (_Bindings *BindingsFilterer) ParseManagerChanged(log types.Log) (*BindingsManagerChanged, error) { + event := new(BindingsManagerChanged) + if err := _Bindings.contract.UnpackLog(event, "ManagerChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BindingsOwnerChangedIterator is returned from FilterOwnerChanged and is used to iterate over the raw logs and unpacked data for OwnerChanged events raised by the Bindings contract. +type BindingsOwnerChangedIterator struct { + Event *BindingsOwnerChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsOwnerChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsOwnerChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsOwnerChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsOwnerChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsOwnerChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsOwnerChanged represents a OwnerChanged event raised by the Bindings contract. +type BindingsOwnerChanged struct { + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnerChanged is a free log retrieval operation binding the contract event 0xa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf36. +// +// Solidity: event OwnerChanged(address indexed new_owner) +func (_Bindings *BindingsFilterer) FilterOwnerChanged(opts *bind.FilterOpts, new_owner []common.Address) (*BindingsOwnerChangedIterator, error) { + + var new_ownerRule []interface{} + for _, new_ownerItem := range new_owner { + new_ownerRule = append(new_ownerRule, new_ownerItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "OwnerChanged", new_ownerRule) + if err != nil { + return nil, err + } + return &BindingsOwnerChangedIterator{contract: _Bindings.contract, event: "OwnerChanged", logs: logs, sub: sub}, nil +} + +// WatchOwnerChanged is a free log subscription operation binding the contract event 0xa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf36. +// +// Solidity: event OwnerChanged(address indexed new_owner) +func (_Bindings *BindingsFilterer) WatchOwnerChanged(opts *bind.WatchOpts, sink chan<- *BindingsOwnerChanged, new_owner []common.Address) (event.Subscription, error) { + + var new_ownerRule []interface{} + for _, new_ownerItem := range new_owner { + new_ownerRule = append(new_ownerRule, new_ownerItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "OwnerChanged", new_ownerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsOwnerChanged) + if err := _Bindings.contract.UnpackLog(event, "OwnerChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnerChanged is a log parse operation binding the contract event 0xa2ea9883a321a3e97b8266c2b078bfeec6d50c711ed71f874a90d500ae2eaf36. +// +// Solidity: event OwnerChanged(address indexed new_owner) +func (_Bindings *BindingsFilterer) ParseOwnerChanged(log types.Log) (*BindingsOwnerChanged, error) { + event := new(BindingsOwnerChanged) + if err := _Bindings.contract.UnpackLog(event, "OwnerChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BindingsRelayAddedIterator is returned from FilterRelayAdded and is used to iterate over the raw logs and unpacked data for RelayAdded events raised by the Bindings contract. +type BindingsRelayAddedIterator struct { + Event *BindingsRelayAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsRelayAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsRelayAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsRelayAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsRelayAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsRelayAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsRelayAdded represents a RelayAdded event raised by the Bindings contract. +type BindingsRelayAdded struct { + UriHash common.Hash + Relay domain.RelayAllowed + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayAdded is a free log retrieval operation binding the contract event 0xeee5faa84d45af657ab405cdbf2c6a8a6d466e83fa694a358fee5ff84431d0bf. +// +// Solidity: event RelayAdded(string indexed uri_hash, (string,string,bool,string) relay) +func (_Bindings *BindingsFilterer) FilterRelayAdded(opts *bind.FilterOpts, uri_hash []string) (*BindingsRelayAddedIterator, error) { + + var uri_hashRule []interface{} + for _, uri_hashItem := range uri_hash { + uri_hashRule = append(uri_hashRule, uri_hashItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "RelayAdded", uri_hashRule) + if err != nil { + return nil, err + } + return &BindingsRelayAddedIterator{contract: _Bindings.contract, event: "RelayAdded", logs: logs, sub: sub}, nil +} + +// WatchRelayAdded is a free log subscription operation binding the contract event 0xeee5faa84d45af657ab405cdbf2c6a8a6d466e83fa694a358fee5ff84431d0bf. +// +// Solidity: event RelayAdded(string indexed uri_hash, (string,string,bool,string) relay) +func (_Bindings *BindingsFilterer) WatchRelayAdded(opts *bind.WatchOpts, sink chan<- *BindingsRelayAdded, uri_hash []string) (event.Subscription, error) { + + var uri_hashRule []interface{} + for _, uri_hashItem := range uri_hash { + uri_hashRule = append(uri_hashRule, uri_hashItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "RelayAdded", uri_hashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsRelayAdded) + if err := _Bindings.contract.UnpackLog(event, "RelayAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRelayAdded is a log parse operation binding the contract event 0xeee5faa84d45af657ab405cdbf2c6a8a6d466e83fa694a358fee5ff84431d0bf. +// +// Solidity: event RelayAdded(string indexed uri_hash, (string,string,bool,string) relay) +func (_Bindings *BindingsFilterer) ParseRelayAdded(log types.Log) (*BindingsRelayAdded, error) { + event := new(BindingsRelayAdded) + if err := _Bindings.contract.UnpackLog(event, "RelayAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BindingsRelayRemovedIterator is returned from FilterRelayRemoved and is used to iterate over the raw logs and unpacked data for RelayRemoved events raised by the Bindings contract. +type BindingsRelayRemovedIterator struct { + Event *BindingsRelayRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BindingsRelayRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BindingsRelayRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BindingsRelayRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BindingsRelayRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BindingsRelayRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BindingsRelayRemoved represents a RelayRemoved event raised by the Bindings contract. +type BindingsRelayRemoved struct { + UriHash common.Hash + Uri string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterRelayRemoved is a free log retrieval operation binding the contract event 0xef756634af7ee7210f786ec0f91930afa63fda84d9ff6493ae681c332055dadb. +// +// Solidity: event RelayRemoved(string indexed uri_hash, string uri) +func (_Bindings *BindingsFilterer) FilterRelayRemoved(opts *bind.FilterOpts, uri_hash []string) (*BindingsRelayRemovedIterator, error) { + + var uri_hashRule []interface{} + for _, uri_hashItem := range uri_hash { + uri_hashRule = append(uri_hashRule, uri_hashItem) + } + + logs, sub, err := _Bindings.contract.FilterLogs(opts, "RelayRemoved", uri_hashRule) + if err != nil { + return nil, err + } + return &BindingsRelayRemovedIterator{contract: _Bindings.contract, event: "RelayRemoved", logs: logs, sub: sub}, nil +} + +// WatchRelayRemoved is a free log subscription operation binding the contract event 0xef756634af7ee7210f786ec0f91930afa63fda84d9ff6493ae681c332055dadb. +// +// Solidity: event RelayRemoved(string indexed uri_hash, string uri) +func (_Bindings *BindingsFilterer) WatchRelayRemoved(opts *bind.WatchOpts, sink chan<- *BindingsRelayRemoved, uri_hash []string) (event.Subscription, error) { + + var uri_hashRule []interface{} + for _, uri_hashItem := range uri_hash { + uri_hashRule = append(uri_hashRule, uri_hashItem) + } + + logs, sub, err := _Bindings.contract.WatchLogs(opts, "RelayRemoved", uri_hashRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BindingsRelayRemoved) + if err := _Bindings.contract.UnpackLog(event, "RelayRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseRelayRemoved is a log parse operation binding the contract event 0xef756634af7ee7210f786ec0f91930afa63fda84d9ff6493ae681c332055dadb. +// +// Solidity: event RelayRemoved(string indexed uri_hash, string uri) +func (_Bindings *BindingsFilterer) ParseRelayRemoved(log types.Log) (*BindingsRelayRemoved, error) { + event := new(BindingsRelayRemoved) + if err := _Bindings.contract.UnpackLog(event, "RelayRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/internal/adapters/relaysAllowed/relaysAllowed_adapter.go b/internal/adapters/relaysAllowed/relaysAllowed_adapter.go new file mode 100644 index 0000000..8709b31 --- /dev/null +++ b/internal/adapters/relaysAllowed/relaysAllowed_adapter.go @@ -0,0 +1,54 @@ +package relaysallowed + +import ( + "context" + "fmt" + + "lido-events/internal/adapters/relaysAllowed/bindings" + "lido-events/internal/application/domain" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) + +type RelaysAllowedAdapter struct { + Client *ethclient.Client + MevBoostRelaysAllowListAddress common.Address + DappmanagerUrl string + MevBoostDnpName string +} + +func NewRelaysAllowedAdapter( + wsURL string, + mevBoostRelaysAllowListAddress common.Address, + dappmanagerUrl string, + mevBoostDnpName string, +) (*RelaysAllowedAdapter, error) { + client, err := ethclient.Dial(wsURL) + if err != nil { + return nil, fmt.Errorf("failed to connect to Ethereum client at %s: %w", wsURL, err) + } + + return &RelaysAllowedAdapter{ + client, + mevBoostRelaysAllowListAddress, + dappmanagerUrl, + mevBoostDnpName, + }, nil +} + +// getRelaysAllowList fetches relays allow list from the SC +func (mev *RelaysAllowedAdapter) GetRelaysAllowList(ctx context.Context) ([]domain.RelayAllowed, error) { + relaysAllowListContract, err := bindings.NewBindings(mev.MevBoostRelaysAllowListAddress, mev.Client) + if err != nil { + return nil, fmt.Errorf("failed to create MevBoostRelaysAllowList contract instance: %w", err) + } + + relaysAllowList, err := relaysAllowListContract.GetRelays(&bind.CallOpts{Context: ctx}) + if err != nil { + return nil, fmt.Errorf("failed to get relays allow list: %w", err) + } + + return relaysAllowList, nil +} diff --git a/internal/adapters/relaysUsed/relaysUsed_adapter.go b/internal/adapters/relaysUsed/relaysUsed_adapter.go new file mode 100644 index 0000000..96bb588 --- /dev/null +++ b/internal/adapters/relaysUsed/relaysUsed_adapter.go @@ -0,0 +1,64 @@ +package relaysused + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type RelaysUsedAdapter struct { + dappmanagerUrl string + mevBootDnpName string +} + +// NewRelaysUsedAdapter creates a new instance of DappmanagerAdapter with provided dappmanagerUrl and mevBoostDnpName +func NewRelaysUsedAdapter(dappmanagerUrl, mevBoostDnpName string) *RelaysUsedAdapter { + return &RelaysUsedAdapter{ + dappmanagerUrl, + mevBoostDnpName, + } +} + +// GetRelaysUsed fetches env RELAYS from MEV BOOST pkg +func (da *RelaysUsedAdapter) GetRelaysUsed(ctx context.Context) ([]string, error) { + url := fmt.Sprintf("%s/env/%s?envName=RELAYS", da.dappmanagerUrl, da.mevBootDnpName) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("failed to create GET request for URL %s: %w", url, err) + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request to Dappmanager at %s: %w", url, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %d", resp.StatusCode) + } + + var responseString string + if err := json.NewDecoder(resp.Body).Decode(&responseString); err != nil { + return nil, fmt.Errorf("failed to decode response body: %w", err) + } + + // Split the comma-separated string into a slice of strings + relays := []string{} + if responseString != "" { + relays = splitAndTrim(responseString, ",") + } + + return relays, nil +} + +// Helper function to split and trim strings +func splitAndTrim(input string, sep string) []string { + parts := strings.Split(input, sep) + for i := range parts { + parts[i] = strings.TrimSpace(parts[i]) + } + return parts +} diff --git a/internal/application/domain/mevboost_relays_allowlist_events.go b/internal/application/domain/mevboost_relays_allowlist_events.go new file mode 100644 index 0000000..09b85cf --- /dev/null +++ b/internal/application/domain/mevboost_relays_allowlist_events.go @@ -0,0 +1,8 @@ +package domain + +type RelayAllowed struct { + Uri string + Operator string + IsMandatory bool + Description string +} diff --git a/internal/application/ports/relaysallowed_port.go b/internal/application/ports/relaysallowed_port.go new file mode 100644 index 0000000..9dc03ce --- /dev/null +++ b/internal/application/ports/relaysallowed_port.go @@ -0,0 +1,10 @@ +package ports + +import ( + "context" + "lido-events/internal/application/domain" +) + +type RelaysAllowedPort interface { + GetRelaysAllowList(ctx context.Context) ([]domain.RelayAllowed, error) +} diff --git a/internal/application/ports/relaysused_port.go b/internal/application/ports/relaysused_port.go new file mode 100644 index 0000000..fb36a52 --- /dev/null +++ b/internal/application/ports/relaysused_port.go @@ -0,0 +1,7 @@ +package ports + +import "context" + +type RelaysUsedPort interface { + GetRelaysUsed(ctx context.Context) ([]string, error) +} diff --git a/internal/application/services/relaysChecker.go b/internal/application/services/relaysChecker.go new file mode 100644 index 0000000..f0b1936 --- /dev/null +++ b/internal/application/services/relaysChecker.go @@ -0,0 +1,147 @@ +package services + +import ( + "context" + "fmt" + "sync" + "time" + + "lido-events/internal/application/domain" + "lido-events/internal/application/ports" + "lido-events/internal/logger" +) + +type RelayCronService struct { + relaysAllowedPort ports.RelaysAllowedPort + relaysUsedPort ports.RelaysUsedPort + notifierPort ports.NotifierPort + servicePrefix string +} + +func NewRelayCronService( + relaysAllowedPort ports.RelaysAllowedPort, + relaysUsedPort ports.RelaysUsedPort, + notifierPort ports.NotifierPort, +) *RelayCronService { + return &RelayCronService{ + relaysAllowedPort: relaysAllowedPort, + relaysUsedPort: relaysUsedPort, + notifierPort: notifierPort, + servicePrefix: "RelaysChecker", + } +} + +func (rcs *RelayCronService) StartRelayMonitoringCron(ctx context.Context, interval time.Duration, wg *sync.WaitGroup) { + defer wg.Done() + wg.Add(1) + + // Execute immediately on startup + if err := rcs.monitorRelays(ctx); err != nil { + logger.ErrorWithPrefix(rcs.servicePrefix, "Error monitoring relays: %v", err) + } + + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := rcs.monitorRelays(ctx); err != nil { + logger.ErrorWithPrefix(rcs.servicePrefix, "Error monitoring relays: %v", err) + } + case <-ctx.Done(): + logger.InfoWithPrefix(rcs.servicePrefix, "Stopping periodic cron for monitoring relays") + return + } + } +} + +func (rcs *RelayCronService) monitorRelays(ctx context.Context) error { + // Fetch allowed relays + allowedRelays, err := rcs.relaysAllowedPort.GetRelaysAllowList(ctx) + if err != nil { + return fmt.Errorf("failed to fetch allowed relays: %w", err) + } + + // Fetch used relays + usedRelays, err := rcs.relaysUsedPort.GetRelaysUsed(ctx) + if err != nil { + return fmt.Errorf("failed to fetch used relays: %w", err) + } + + // Analyze relays + blacklistedRelays, mandatoryRelaysUsed := rcs.analyzeRelays(allowedRelays, usedRelays) + + // Send notifications if issues are found + if len(blacklistedRelays) > 0 { + message := rcs.buildBlacklistNotification(blacklistedRelays) + if err := rcs.notifierPort.SendNotification(message); err != nil { + return fmt.Errorf("failed to send blacklist notification: %w", err) + } + } + + // Change: Notify if no mandatory relays are being used + if mandatoryRelaysUsed == 0 { + message := rcs.buildMissingMandatoryNotification(allowedRelays) + if err := rcs.notifierPort.SendNotification(message); err != nil { + return fmt.Errorf("failed to send missing mandatory relays notification: %w", err) + } + } + + return nil +} + +func (rcs *RelayCronService) analyzeRelays(allowedRelays []domain.RelayAllowed, usedRelays []string) ([]string, int) { + allowedMap := make(map[string]domain.RelayAllowed) + for _, relay := range allowedRelays { + allowedMap[relay.Uri] = relay + } + + var blacklistedRelays []string + mandatoryRelaysUsed := 0 + + // Check for blacklisted relays + for _, used := range usedRelays { + if _, exists := allowedMap[used]; !exists { + blacklistedRelays = append(blacklistedRelays, used) + } + } + + // Check for mandatory relays in use + for _, allowed := range allowedRelays { + if allowed.IsMandatory { + for _, used := range usedRelays { + if allowed.Uri == used { + mandatoryRelaysUsed++ + break + } + } + } + } + + return blacklistedRelays, mandatoryRelaysUsed +} + +func (rcs *RelayCronService) buildMissingMandatoryNotification(allowedRelays []domain.RelayAllowed) string { + message := "⚠️ Mandatory Relay Alert\n\nNo mandatory relays are currently in use. At least one mandatory relay must be used. Edit your relays in the stakers UI\n" + message += "\nMandatory relays:\n" + for _, relay := range allowedRelays { + if relay.IsMandatory { + message += fmt.Sprintf("- `%s` (Operator: %s, Description: %s)\n", relay.Uri, relay.Operator, relay.Description) + } + } + message += "\nPlease ensure at least one mandatory relay is in use. Allowed are:" + for _, relay := range allowedRelays { + message += fmt.Sprintf("\n- `%s`", relay.Uri) + } + return message +} + +func (rcs *RelayCronService) buildBlacklistNotification(blacklistedRelays []string) string { + message := "🚨 Relay Blacklist Warning\n\nThe following relays are being used but are not allowed:\n" + for _, relay := range blacklistedRelays { + message += fmt.Sprintf("- `%s`\n", relay) + } + message += "\nPlease review and address these issues promptly." + return message +} diff --git a/internal/config/config_loader.go b/internal/config/config_loader.go index 4f4cbae..e0ab285 100644 --- a/internal/config/config_loader.go +++ b/internal/config/config_loader.go @@ -11,6 +11,8 @@ import ( ) type Config struct { + MevBoostDnpName string + DappmanagerUrl string SignerUrl string IpfsUrl string WsURL string @@ -24,11 +26,12 @@ type Config struct { CORS []string // Individual contract addresses - CSAccountingAddress common.Address - CSFeeDistributorAddress common.Address - CSFeeDistributorImplAddress common.Address - VEBOAddress common.Address - CSModuleAddress common.Address + CSAccountingAddress common.Address + CSFeeDistributorAddress common.Address + CSFeeDistributorImplAddress common.Address + VEBOAddress common.Address + CSModuleAddress common.Address + MEVBoostRelaysAllowListAddres common.Address // Block number of the deployment of the VEBO contract and the CSFeeDistributor contract VeboBlockDeployment uint64 @@ -60,6 +63,11 @@ func parseCORS(envCORS string, defaultCORS []string) []string { } func LoadNetworkConfig() (Config, error) { + dappmanagerUrl := os.Getenv("DAPPMANAGER_URL") + if dappmanagerUrl == "" { + dappmanagerUrl = "http://my.dappnode" + } + apiPortStr := os.Getenv("API_PORT") apiPort := uint64(8080) if apiPortStr != "" { @@ -119,6 +127,8 @@ func LoadNetworkConfig() (Config, error) { beaconchainURL = "http://beacon-chain.holesky.dncore.dappnode:3500" } config = Config{ + MevBoostDnpName: "mev-boost-holesky.dnp.dappnode.eth", + DappmanagerUrl: dappmanagerUrl, SignerUrl: "http://signer.holesky.dncore.dappnode:9000", IpfsUrl: ipfsUrl, WsURL: wsURL, @@ -133,6 +143,7 @@ func LoadNetworkConfig() (Config, error) { CSFeeDistributorAddress: common.HexToAddress("0xD7ba648C8F72669C6aE649648B516ec03D07c8ED"), CSFeeDistributorImplAddress: common.HexToAddress("0xe1863C61d2AF2899f06223152ebaaf993C29aEa7"), VEBOAddress: common.HexToAddress("0xffDDF7025410412deaa05E3E1cE68FE53208afcb"), + MEVBoostRelaysAllowListAddres: common.HexToAddress("0x2d86C5855581194a386941806E38cA119E50aEA3"), VeboBlockDeployment: uint64(30701), CsFeeDistributorBlockDeployment: uint64(1774650), CSModuleAddress: common.HexToAddress("0x4562c3e63c2e586cD1651B958C22F88135aCAd4f"), @@ -151,6 +162,8 @@ func LoadNetworkConfig() (Config, error) { beaconchainURL = "http://beacon-chain.mainnet.dncore.dappnode:3500" } config = Config{ + MevBoostDnpName: "mev-boost.dnp.dappnode.eth", + DappmanagerUrl: dappmanagerUrl, SignerUrl: "http://signer.mainnet.dncore.dappnode:9000", IpfsUrl: ipfsUrl, WsURL: wsURL, @@ -165,6 +178,7 @@ func LoadNetworkConfig() (Config, error) { CSFeeDistributorAddress: common.HexToAddress("0xD99CC66fEC647E68294C6477B40fC7E0F6F618D0"), CSFeeDistributorImplAddress: common.HexToAddress("0x17Fc610ecbbAc3f99751b3B2aAc1bA2b22E444f0"), VEBOAddress: common.HexToAddress("0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e"), + MEVBoostRelaysAllowListAddres: common.HexToAddress("0xF95f069F9AD107938F6ba802a3da87892298610E"), VeboBlockDeployment: uint64(17172556), CsFeeDistributorBlockDeployment: uint64(20935463), CSModuleAddress: common.HexToAddress("0xdA7dE2ECdDfccC6c3AF10108Db212ACBBf9EA83F"),