package proxy import ( "encoding/json" "fmt" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/rpc" "github.com/hashicorp/go-multierror" "github.com/hedzr/cmdr/tool" "github.com/synapsecns/sanguine/ethergo/client" ethergoRPC "github.com/synapsecns/sanguine/ethergo/parser/rpc" "golang.org/x/exp/slices" "math/big" ) func isConfirmable(r ethergoRPC.Request) (bool, error) { // TODO: should we error on default? // TODO: look at RPCMethod.Comparable for lower, necessary? //nolint: exhaustive switch client.RPCMethod(r.Method) { case client.BlockByNumberMethod, client.PendingTransactionCountMethod: return isBlockNumConfirmable(r.Params[0]), nil case client.BlockNumberMethod, client.SyncProgressMethod, client.GasPriceMethod, client.MaxPriorityMethod, client.EstimateGasMethod: return false, nil case client.GetBalanceMethod, client.GetCodeMethod, client.TransactionCountMethod, client.CallMethod: return isBlockNumConfirmable(r.Params[1]), nil case client.StorageAtMethod: return isBlockNumConfirmable(r.Params[2]), nil case client.GetLogsMethod: return isFilterArgConfirmable(r.Params[0]) // not confirmable because tx could be pending. We might want to handle w/ omnicast though // left separate for comment case client.SendRawTransactionMethod: return false, nil } return true, nil } func areConfirmable(r ethergoRPC.Requests) (_ bool, errs error) { unconfirmable := false for i, request := range r { canConfirm, err := isConfirmable(request) if err != nil { errs = multierror.Append(errs, fmt.Errorf("request at index %d: %s is not parsable", i, spew.Sprint(request))) } if !canConfirm { unconfirmable = true } } if errs != nil { //nolint:wrapcheck return false, errs } return !unconfirmable, nil } func isBlockNumConfirmable(arg json.RawMessage) bool { // nonConfirmableBlockNumArgs is a list of non numerical block args var nonConfirmableBlockNumArgs = []string{"latest", "pending"} return !slices.Contains(nonConfirmableBlockNumArgs, tool.StripQuotes(string(arg))) } // isFilterArgConfirmable checks if filter.filterCriteria is confirmable. func isFilterArgConfirmable(arg json.RawMessage) (bool, error) { // cast latest block number to a big int for comparison latestBlockNumber := new(big.Int).SetInt64(rpc.LatestBlockNumber.Int64()) filterCriteria := filters.FilterCriteria{} err := filterCriteria.UnmarshalJSON(arg) if err != nil { return false, fmt.Errorf("could not unmarshall filter: %w", err) } // Block filter requested, construct a single-shot filter if filterCriteria.BlockHash != nil { return true, nil } usesLatest := filterCriteria.FromBlock.Cmp(latestBlockNumber) == 0 || filterCriteria.ToBlock.Cmp(latestBlockNumber) == 0 return !usesLatest, nil }