dwayne/elm-json-rpc/1.0.0

JsonRpc

Send JSON-RPC 2.0 requests over HTTP.

The implementation is guided by the following specifications:

Construct a request

type alias Request result =
{ method : String
, params : Params
, result : Decoder result
}

An RPC call is represented by sending a Request object to a server. This Request record is used to create a Request object.

  • method contains the name of the method to be invoked.
  • params holds the parameter values to be used during the invocation of the method.
  • result is the JSON decoder used to decode the result field of a successful Response object.

For example, the following Request record:

import Json.Decode as JD
import Json.Encode as JE
import JsonRpc


getBalance : JsonRpc.Request String
getBalance =
    { method = "eth_getBalance"
    , params =
        JsonRpc.positionalParams
            [ JE.string "0x0000000000000000000000000000000000000000"
            , JE.string "latest"
            ]
    , result = JD.string
    }

corresponds to the following Request object:

{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [ "0x0000000000000000000000000000000000000000", "latest" ],
    "id": ...
}

And, on a successful response the result field is expected to contain a String.

Note: The id field is set when sending the request.

type Params

A JSON Array or Object that holds the parameter values to be used during invocation of an RPC method.

type alias Param =
Value

A JSON value.

An empty JSON Array.

positionalParams : List Param -> Params

A JSON Array containing the parameter values in the server expected order.

For example, the following:

import Json.Encode as JE
import JsonRpc


JsonRpc.positionalParams
    [ JE.string "0x0000000000000000000000000000000000000000"
    , JE.string "latest"
    ]

represents the JSON Array:

[ "0x0000000000000000000000000000000000000000", "latest" ]
namedParams : List ( String, Param ) -> List ( String, Maybe Param ) -> Params

A JSON Object with member names that match the server expected parameter names.

For example, the following:

import Json.Encode as JE
import JsonRpc


JsonRpc.namedParams
    [ ( "apiKey", JE.string "YOUR API KEY"
    , ( "n", JE.int 5 )
    , ( "min", JE.int 1 )
    , ( "max", JE.int 10 )
    ]
    [ ( "replacement", Just False )
    ]

represents the JSON Object:

{
    "apiKey": "YOUR API KEY",
    "n": 5,
    "min": 1,
    "max": 10,
    "replacement": false
}

Send a request

send : String -> (Result Error result -> msg) -> Request result -> Cmd msg

Sends a Request object over HTTP to a JSON-RPC server with a default request identifier of 1.

import JsonRpc


type Msg
    = GotBalance (Result JsonRpc.Error String)


let
    rpcUrl =
        "https://eth-goerli.public.blastapi.io"
in
JsonRpc.send rpcUrl GotBalance getBalance

The Request object:

{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": [ "0x0000000000000000000000000000000000000000", "latest" ],
    "id": 1
}

will be sent over HTTP to the JSON-RPC server at the endpoint https://eth-goerli.public.blastapi.io.

Send a request with a custom identifier

type Id

An identifier established by you.

intId : Int -> Id

An integer identifier.

stringId : String -> Id

A string identifier. Maybe you'd want to use a UUID as your identifier.

import JsonRpc


JsonRpc.stringId "789e08a1-6156-4046-88b6-5b96a87a2eba"
sendWithId : String -> (Result Error result -> msg) -> Id -> Request result -> Cmd msg

Just like send, but it allows you to set the identifier.

Advanced

type alias HttpOptions =
{ headers : List Header
, timeout : Maybe Float
, tracker : Maybe String
}

The HTTP options you're allowed to modify.

  • headers are the extra HTTP headers you'd like to add. The Content-Type: application/json, Content-Length: ..., and Accept: application/json headers are always automatically added.
  • timeout is the number of milliseconds you are willing to wait before giving up.
  • tracker lets you cancel and track requests.
defaultHttpOptions : HttpOptions

The default HTTP options used by send and sendWithId.

{ headers = []
, timeout = Nothing
, tracker = Nothing
}
HttpOptions
-> String
-> (Result Error result -> msg)
-> Id
-> Request result
-> Cmd msg

Just like sendWithId, but it allows you to customize the HTTP options.

import JsonRpc exposing (defaultHttpOptions)


type Msg
    = GotBalance (Result JsonRpc.Error String)


let
    httpOptions =
        -- wait 1 minute
        { defaultHttpOptions | timeout = Just 60000 }


    rpcUrl =
        "https://eth-goerli.public.blastapi.io"


    id =
        JsonRpc.stringId "abc123"
in
JsonRpc.sendCustom httpOptions rpcUrl GotBalance id getBalance

Error

type Error
= HttpError HttpError
| UnexpectedStatus Metadata String
| DecodeError Error
| MismatchedIds
{ requestId : String
, responseId : String
}
| JsonRpcError
{ kind : Kind
, code : Int
, message : String
, maybeData : Maybe Value
, responseId : String
}

A request can fail in multiple ways.

  • HttpError means there was an error trying to reach the server and the underlying HttpError will tell you why.
  • UnexpectedStatus means the successful HTTP status code returned by the server was not 200. In other words, it was in the 200 to 299 range, maybe it was 202 or 204 but not 200.
  • DecodeError means the Response object returned by the server was malformed.
  • MismatchedIds means the id of the Request object is not the same as the id of the Response object.
  • JsonRpcError means the RPC call encountered an error. The maybeData field may contain detailed error information as defined by the server.
= BadUrl String
| Timeout
| NetworkError
| BadStatus Metadata String

The HTTP transport can fail in multiple ways.

  • BadUrl means you did not provide a valid URL.
  • Timeout means it took too long to get a response.
  • NetworkError means the user turned off their wifi, went in a cave, etc.
  • BadStatus means you got a response back, but the status code indicates failure.
type Kind
= ParseError
| InvalidRequest
| MethodNotFound
| InvalidParams
| InternalError
| ServerError
| ReservedError
| ApplicationError

This data type categorizes the error code of a failed RPC call.

The categories are derived from the specification as given by the table under the Error object section.