Skip to content

Commit

Permalink
WIP: Custom RPC client (#356)
Browse files Browse the repository at this point in the history
* Move Rpc.res to npm package

* Downgrade rescript-schema

* Vendore Express bindings

* Vendor rescript-rest and use it for RPC client

* Fix chains test
  • Loading branch information
DZakh authored Nov 28, 2024
1 parent 84f9fdc commit f723e33
Show file tree
Hide file tree
Showing 30 changed files with 1,099 additions and 282 deletions.
2 changes: 1 addition & 1 deletion codegenerator/cli/npm/envio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"dependencies": {
"@envio-dev/hypersync-client": "0.6.2",
"rescript": "11.1.3",
"rescript-schema": "8.2.0",
"rescript-schema": "8.1.0",
"viem": "2.21.0"
}
}
2 changes: 1 addition & 1 deletion codegenerator/cli/npm/envio/package.json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dependencies": {
"@envio-dev/hypersync-client": "0.6.2",
"rescript": "11.1.3",
"rescript-schema": "8.2.0",
"rescript-schema": "8.1.0",
"viem": "2.21.0"
},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion codegenerator/cli/npm/envio/src/Enum.res
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type enum<'a> = {
let make = (~name, ~variants) => {
name,
variants,
schema: S.enum(variants),
schema: Utils.Schema.enum(variants),
default: switch variants->Belt.Array.get(0) {
| Some(v) => v
| None => Js.Exn.raiseError("No variants defined for enum " ++ name)
Expand Down
2 changes: 2 additions & 0 deletions codegenerator/cli/npm/envio/src/Utils.res
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ let unwrapResultExn = res =>
external queueMicrotask: (unit => unit) => unit = "queueMicrotask"

module Schema = {
let enum = items => S.union(items->Belt.Array.mapU(S.literal))

let getNonOptionalFieldNames = schema => {
let acc = []
switch schema->S.classify {
Expand Down
29 changes: 29 additions & 0 deletions codegenerator/cli/npm/envio/src/bindings/Express.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Inspired by https://github.com/bloodyowl/rescript-express

type app

// "default" & seem to conflict a bit right now
// https://github.com/rescript-lang/rescript-compiler/issues/5004
@module external makeCjs: unit => app = "express"
@module("express") external make: unit => app = "default"

type req
type res

type handler = (req, res) => unit
type middleware = (req, res, unit => unit) => unit

@module("express") external jsonMiddleware: unit => middleware = "json"

@send external use: (app, middleware) => unit = "use"

@send external get: (app, string, handler) => unit = "get"

type server

@send external listen: (app, int) => server = "listen"

// res methods
@send external sendStatus: (res, int) => res = "sendStatus"
@send external set: (res, string, string) => unit = "set"
@send external endWithData: (res, 'a) => res = "end"
2 changes: 1 addition & 1 deletion codegenerator/cli/npm/envio/src/bindings/Postgres.res
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type sslOptions =
| @as("prefer") Prefer
| @as("verify-full") VerifyFull

let sslOptionsSchema: S.schema<sslOptions> = S.enum([
let sslOptionsSchema: S.schema<sslOptions> = Utils.Schema.enum([
Bool(true),
Bool(false),
Require,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,34 @@
module Query = {
type method =
| @as("eth_getLogs") EthGetLogs
| @as("eth_getBlockByNumber") EthGetBlockByNumber
| @as("eth_blockNumber") EthBlockNumber
let methodSchema = S.union([
S.literal(EthGetLogs),
S.literal(EthGetBlockByNumber),
S.literal(EthBlockNumber),
])
type jsonRpcVersion = | @as("2.0") TwoPointZero
let jsonRpcVersionSchema = S.literal(TwoPointZero)
//use json params to be ablet to mix query types in an array for batch queries
type t = {method: method, params: array<Js.Json.t>, mutable id: int, jsonrpc: jsonRpcVersion}
let schema: S.schema<t> = S.object(s => {
method: s.field("method", methodSchema),
params: s.field("params", S.array(S.json(~validate=false))),
id: s.field("id", S.int),
jsonrpc: s.field("jsonrpc", jsonRpcVersionSchema),
let makeRpcRoute = (method: string, paramsSchema, resultSchema) => {
let idSchema = S.literal(1)
let versionSchema = S.literal("2.0")
Rest.route(() => {
method: Post,
path: "",
variables: s => {
let _ = s.field("method", S.literal(method))
let _ = s.field("id", idSchema)
let _ = s.field("jsonrpc", versionSchema)
s.field("params", paramsSchema)
},
responses: [
s => {
let _ = s.field("jsonrpc", versionSchema)
let _ = s.field("id", idSchema)
s.field("result", resultSchema)
},
],
})

type response<'a> = {
jsonrpc: jsonRpcVersion,
id: int,
result: 'a,
}

let makeResponseSchema = (resultSchema: S.schema<'a>) =>
S.object((s): response<'a> => {
jsonrpc: s.field("jsonrpc", jsonRpcVersionSchema),
id: s.field("id", S.int),
result: s.field("result", resultSchema),
})

let make = (~method, ~params, ~id=1) => {method, params, id, jsonrpc: TwoPointZero}
}

module BatchQuery = {
type t = array<Query.t>
}

type hex = string

let makeHexSchema = fromStr =>
S.string->S.transform(s => {
parser: str =>
switch str->fromStr {
| Some(v) => v
| None => s.fail("The string is not valid hex")
},
serializer: bigint => bigint->Viem.toHex->Utils.magic,
serializer: value => value->Viem.toHex->Utils.magic,
})

let hexBigintSchema: S.schema<bigint> = makeHexSchema(BigInt.fromString)
Expand Down Expand Up @@ -134,17 +114,7 @@ module GetLogs = {
removed: s.field("removed", S.bool),
})

let responseSchema = Query.makeResponseSchema(S.array(logSchema))

let make = (~fromBlock, ~toBlock, ~address, ~topics) => {
let params = {
fromBlock,
toBlock,
address,
topics,
}->S.serializeOrRaiseWith(paramsSchema)
Query.make(~method=EthGetLogs, ~params=[params])
}
let route = makeRpcRoute("eth_getLogs", S.tuple1(paramsSchema), S.array(logSchema))
}

module GetBlockByNumber = {
Expand Down Expand Up @@ -194,20 +164,18 @@ module GetBlockByNumber = {
uncles: s.field("uncles", S.null(S.array(S.string))),
})

let responseSchema: S.t<Query.response<option<block>>> = Query.makeResponseSchema(
let route = makeRpcRoute(
"eth_getBlockByNumber",
S.tuple(s =>
{
"blockNumber": s.item(0, hexIntSchema),
"includeTransactions": s.item(1, S.bool),
}
),
S.null(blockSchema),
)

let make = (~blockNumber, ~includeTransactions=false) => {
let blockNumber = blockNumber->Viem.toHex->Utils.magic
let transactionDetailFlag = includeTransactions->S.serializeOrRaiseWith(S.bool)
Query.make(~method=EthGetBlockByNumber, ~params=[blockNumber, transactionDetailFlag])
}
}

module GetBlockHeight = {
type response = int
let responseSchema = Query.makeResponseSchema(hexIntSchema)

let make = () => Query.make(~method=EthBlockNumber, ~params=[])
let route = makeRpcRoute("eth_blockNumber", S.tuple(_ => ()), hexIntSchema)
}
Loading

0 comments on commit f723e33

Please sign in to comment.