synapsecns/sanguine

View on GitHub
ethergo/parser/rpc/rpc.go

Summary

Maintainability
A
0 mins
Test Coverage
package rpc

import (
    "fmt"

    "github.com/goccy/go-json"
    "github.com/invopop/jsonschema"
)

// Requests is a list or rpc requests.
type Requests []Request

// Method returns the method of the rpc request.
func (r Requests) Method() string {
    reqLength := len(r)
    if reqLength == 0 {
        return "none"
    }

    if reqLength > 1 {
        return "batch"
    }

    return r[0].Method
}

// ByID will get an rpc requet by the id.
func (r Requests) ByID(id int) *Request {
    for _, req := range r {
        if req.ID == id {
            return &req
        }
    }
    return nil
}

// Request is a raw rpc request format.
type Request struct {
    ID      int               `json:"id"`
    Method  string            `json:"method"`
    JSONRPC string            `json:"jsonrpc,omitempty"`
    Params  []json.RawMessage `json:"params"`
}

func init() {
    schema := jsonschema.Reflect(&Request{})
    rawSchema, err := schema.MarshalJSON()
    if err != nil {
        panic(fmt.Errorf("could not reflect rpc schema: %w", err))
    }
    rpcReqSchema = string(rawSchema)
}

// rpcReqSchema contains the raw rpc request schema.
var rpcReqSchema string

// IsBatch determines if a request is batch. This method's implementation is borrowed from
// rpc/json.go in ethereum-go where it is unexported.
func IsBatch(body []byte) bool {
    for _, c := range body {
        // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt)
        if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d {
            continue
        }
        return c == '['
    }
    return false
}

// ParseRPCPayload parses a raw rpc request body and returns a list of rpc requests.
func ParseRPCPayload(body []byte) (_ Requests, err error) {
    if IsBatch(body) {
        var rpcPayload []Request
        err = json.Unmarshal(body, &rpcPayload)
        if err != nil {
            return nil, fmt.Errorf("could not parse batch json payload: %w, must conform to: %s", err, rpcReqSchema)
        }
        return rpcPayload, nil
    }
    rpcRequest := Request{}
    err = json.Unmarshal(body, &rpcRequest)
    if err != nil {
        return nil, fmt.Errorf("could not parse json payload: %w, must conform to: %s", err, rpcReqSchema)
    }
    return []Request{rpcRequest}, nil
}

// IsNullResponse checks if the result field in the JSON-RPC response is null
func IsNullResponse(body []byte) bool {
    var response rpcResponse
    err := json.Unmarshal(body, &response)
    if err != nil {
        return false
    }
    return string(response.Result) == "null"
}

type rpcResponse struct {
    JSONRPC string          `json:"jsonrpc"`
    ID      int             `json:"id"`
    Result  json.RawMessage `json:"result"`
}