nim-ethers/ethers/providers/jsonrpc/conversions.nim

123 lines
3.4 KiB
Nim

import std/strformat
import std/strutils
import pkg/chronicles except fromJson, `%`, `%*`, toJson
import pkg/json_rpc/jsonmarshal except toJson
import pkg/questionable/results
import pkg/serde
import pkg/stew/byteutils
import ../../basics
import ../../transaction
import ../../blocktag
import ../../provider
export jsonmarshal except toJson
export serde
export chronicles except fromJson, `%`, `%*`, toJson
{.push raises: [].}
proc getOrRaise*[T, E](self: ?!T, exc: typedesc[E]): T {.raises: [E].} =
let val = self.valueOr:
raise newException(E, self.error.msg)
val
template mapFailure*[T, V, E](
exp: Result[T, V],
exc: typedesc[E],
): Result[T, ref CatchableError] =
## Convert `Result[T, E]` to `Result[E, ref CatchableError]`
##
exp.mapErr(proc (e: V): ref CatchableError = (ref exc)(msg: e.msg))
# Address
func `%`*(address: Address): JsonNode =
%($address)
func fromJson(_: type Address, json: JsonNode): ?!Address =
expectJsonKind(Address, JString, json)
without address =? Address.init(json.getStr), error:
return failure newException(SerializationError,
"Failed to convert '" & $json & "' to Address: " & error.msg)
success address
# UInt256
func `%`*(integer: UInt256): JsonNode =
%("0x" & toHex(integer))
# Transaction
# TODO: add option that ignores none Option[T]
# TODO: add name option (gasLimit => gas, sender => from)
func `%`*(transaction: Transaction): JsonNode =
result = %*{
"to": transaction.to,
"data": %transaction.data,
"value": %transaction.value
}
if sender =? transaction.sender:
result["from"] = %sender
if nonce =? transaction.nonce:
result["nonce"] = %nonce
if chainId =? transaction.chainId:
result["chainId"] = %chainId
if gasPrice =? transaction.gasPrice:
result["gasPrice"] = %gasPrice
if gasLimit =? transaction.gasLimit:
result["gas"] = %gasLimit
# BlockTag
func `%`*(tag: BlockTag): JsonNode =
% $tag
func fromJson*(_: type BlockTag, json: JsonNode): ?!BlockTag =
expectJsonKind(BlockTag, JString, json)
let jsonVal = json.getStr
if jsonVal.len >= 2 and jsonVal[0..1].toLowerAscii == "0x":
without blkNum =? UInt256.fromHex(jsonVal).catch, error:
return BlockTag.failure error.msg
return success BlockTag.init(blkNum)
case jsonVal:
of "earliest": return success BlockTag.earliest
of "latest": return success BlockTag.latest
of "pending": return success BlockTag.pending
else: return failure newException(SerializationError,
"Failed to convert '" & $json &
"' to BlockTag: must be one of 'earliest', 'latest', 'pending'")
# TransactionStatus | TransactionType
type TransactionEnums = TransactionStatus | TransactionType
func `%`*(e: TransactionEnums): JsonNode =
% ("0x" & e.int8.toHex(1))
proc fromJson*(
T: type TransactionEnums,
json: JsonNode
): ?!T =
expectJsonKind(string, JString, json)
let integer = ? fromHex[int](json.str).catch.mapFailure(SerializationError)
success T(integer)
## Generic conversions to use nim-json instead of nim-json-serialization for
## json rpc serialization purposes
## writeValue => `%`
## readValue => fromJson
proc writeValue*[T: not JsonNode](
writer: var JsonWriter[JrpcConv],
value: T) {.raises:[IOError].} =
writer.writeValue(%value)
proc readValue*[T: not JsonNode](
r: var JsonReader[JrpcConv],
result: var T) {.raises: [SerializationError, IOError].} =
var json = r.readValue(JsonNode)
result = T.fromJson(json).getOrRaise(SerializationError)