mirror of
https://github.com/logos-storage/nim-ethers.git
synced 2026-02-26 16:43:12 +00:00
Add json de/serialization lib from codex to handle conversions
json-rpc now requires nim-json-serialization to convert types to/from json. Use the nim-json-serialization signatures to call the json serialization lib from nim-codex (should be moved to its own lib)
This commit is contained in:
parent
fd16d71ea5
commit
daa88cc8bf
@ -3,6 +3,7 @@ import pkg/stew/byteutils
|
||||
import ./basics
|
||||
import ./transaction
|
||||
import ./blocktag
|
||||
import ./providers/jsonrpc/json
|
||||
|
||||
export basics
|
||||
export transaction
|
||||
@ -15,19 +16,19 @@ type
|
||||
ProviderError* = object of EthersError
|
||||
Subscription* = ref object of RootObj
|
||||
EventFilter* = ref object of RootObj
|
||||
address*: Address
|
||||
topics*: seq[Topic]
|
||||
address* {.serialize.}: Address
|
||||
topics* {.serialize.}: seq[Topic]
|
||||
Filter* = ref object of EventFilter
|
||||
fromBlock*: BlockTag
|
||||
toBlock*: BlockTag
|
||||
fromBlock* {.serialize.}: BlockTag
|
||||
toBlock* {.serialize.}: BlockTag
|
||||
FilterByBlockHash* = ref object of EventFilter
|
||||
blockHash*: BlockHash
|
||||
blockHash* {.serialize.}: BlockHash
|
||||
Log* = object
|
||||
blockNumber*: UInt256
|
||||
data*: seq[byte]
|
||||
logIndex*: UInt256
|
||||
removed*: bool
|
||||
topics*: seq[Topic]
|
||||
blockNumber* {.serialize.}: UInt256
|
||||
data* {.serialize.}: seq[byte]
|
||||
logIndex* {.serialize.}: UInt256
|
||||
removed* {.serialize.}: bool
|
||||
topics* {.serialize.}: seq[Topic]
|
||||
TransactionHash* = array[32, byte]
|
||||
BlockHash* = array[32, byte]
|
||||
TransactionStatus* = enum
|
||||
@ -35,45 +36,45 @@ type
|
||||
Success = 1,
|
||||
Invalid = 2
|
||||
TransactionResponse* = object
|
||||
provider*: Provider
|
||||
hash*: TransactionHash
|
||||
provider* {.serialize.}: Provider
|
||||
hash* {.serialize.}: TransactionHash
|
||||
TransactionReceipt* = object
|
||||
sender*: ?Address
|
||||
to*: ?Address
|
||||
contractAddress*: ?Address
|
||||
transactionIndex*: UInt256
|
||||
gasUsed*: UInt256
|
||||
logsBloom*: seq[byte]
|
||||
blockHash*: ?BlockHash
|
||||
transactionHash*: TransactionHash
|
||||
logs*: seq[Log]
|
||||
blockNumber*: ?UInt256
|
||||
cumulativeGasUsed*: UInt256
|
||||
effectiveGasPrice*: ?UInt256
|
||||
sender* {.serialize.}: ?Address
|
||||
to* {.serialize.}: ?Address
|
||||
contractAddress* {.serialize.}: ?Address
|
||||
transactionIndex* {.serialize.}: UInt256
|
||||
gasUsed* {.serialize.}: UInt256
|
||||
logsBloom* {.serialize.}: seq[byte]
|
||||
blockHash* {.serialize.}: ?BlockHash
|
||||
transactionHash* {.serialize.}: TransactionHash
|
||||
logs* {.serialize.}: seq[Log]
|
||||
blockNumber* {.serialize.}: ?UInt256
|
||||
cumulativeGasUsed* {.serialize.}: UInt256
|
||||
effectiveGasPrice* {.serialize.}: ?UInt256
|
||||
status*: TransactionStatus
|
||||
transactionType*: TransactionType
|
||||
`type`* {.serialize.}: TransactionType
|
||||
LogHandler* = proc(log: Log) {.gcsafe, raises:[].}
|
||||
BlockHandler* = proc(blck: Block) {.gcsafe, raises:[].}
|
||||
Topic* = array[32, byte]
|
||||
Block* = object
|
||||
number*: ?UInt256
|
||||
timestamp*: UInt256
|
||||
hash*: ?BlockHash
|
||||
number* {.serialize.}: ?UInt256
|
||||
timestamp* {.serialize.}: UInt256
|
||||
hash* {.serialize.}: ?BlockHash
|
||||
PastTransaction* = object
|
||||
blockHash*: BlockHash
|
||||
blockNumber*: UInt256
|
||||
sender*: Address
|
||||
gas*: UInt256
|
||||
gasPrice*: UInt256
|
||||
hash*: TransactionHash
|
||||
input*: seq[byte]
|
||||
nonce*: UInt256
|
||||
to*: Address
|
||||
transactionIndex*: UInt256
|
||||
transactionType*: ?TransactionType
|
||||
chainId*: ?UInt256
|
||||
value*: UInt256
|
||||
v*, r*, s*: UInt256
|
||||
blockHash* {.serialize.}: BlockHash
|
||||
blockNumber* {.serialize.}: UInt256
|
||||
sender* {.serialize.}: Address
|
||||
gas* {.serialize.}: UInt256
|
||||
gasPrice* {.serialize.}: UInt256
|
||||
hash* {.serialize.}: TransactionHash
|
||||
input* {.serialize.}: seq[byte]
|
||||
nonce* {.serialize.}: UInt256
|
||||
to* {.serialize.}: Address
|
||||
transactionIndex* {.serialize.}: UInt256
|
||||
`type`* {.serialize.}: ?TransactionType
|
||||
chainId* {.serialize.}: ?UInt256
|
||||
value* {.serialize.}: UInt256
|
||||
v* {.serialize.}, r* {.serialize.}, s* {.serialize.}: UInt256
|
||||
|
||||
const EthersDefaultConfirmations* {.intdefine.} = 12
|
||||
const EthersReceiptTimeoutBlks* {.intdefine.} = 50 # in blocks
|
||||
@ -91,7 +92,7 @@ func toTransaction*(past: PastTransaction): Transaction =
|
||||
data: past.input,
|
||||
nonce: some past.nonce,
|
||||
to: past.to,
|
||||
transactionType: past.transactionType,
|
||||
`type`: past.`type`,
|
||||
gasLimit: some past.gas,
|
||||
chainId: past.chainId
|
||||
)
|
||||
|
||||
@ -1,71 +1,148 @@
|
||||
import std/json
|
||||
# import std/json
|
||||
import std/strformat
|
||||
import std/strutils
|
||||
import std/sugar
|
||||
# import pkg/eth/common/eth_types_json_serialization
|
||||
import pkg/chronicles except fromJson, `%`, `%*`, toJson
|
||||
import pkg/json_rpc/jsonmarshal
|
||||
import pkg/questionable/results
|
||||
import pkg/stew/byteutils
|
||||
import ../../basics
|
||||
import ../../transaction
|
||||
import ../../blocktag
|
||||
import ../../provider
|
||||
import ./json
|
||||
|
||||
export jsonmarshal
|
||||
export json
|
||||
export chronicles except fromJson, `%`, `%*`, toJson
|
||||
|
||||
# {.push raises: [].}
|
||||
|
||||
type JsonSerializationError = object of EthersError
|
||||
|
||||
func toException*(v: ref CatchableError): ref SerializationError = (ref SerializationError)(msg: v.msg)
|
||||
|
||||
template raiseSerializationError(message: string) =
|
||||
raise newException(JsonSerializationError, message)
|
||||
|
||||
proc getOrRaise*[T, E](self: ?!T, exc: typedesc[E]): T {.raises: [E].} =
|
||||
let val = self.valueOr:
|
||||
raise newException(E, self.error.msg)
|
||||
val
|
||||
|
||||
proc expectFields(json: JsonNode, expectedFields: varargs[string]) =
|
||||
for fieldName in expectedFields:
|
||||
if not json.hasKey(fieldName):
|
||||
raiseSerializationError(fmt"'{fieldName}' field not found in ${json}")
|
||||
raiseSerializationError("'" & fieldName & "' field not found in " & $json)
|
||||
|
||||
# func fromJson*(T: type, json: JsonNode): T
|
||||
# func fromJson*[T](json: JsonNode, result: var Option[T])
|
||||
# func fromJson*[T](json: JsonNode, result: var seq[T])
|
||||
# func fromJson*(json: JsonNode, result var T): T
|
||||
|
||||
|
||||
func fromJson*(T: type, json: JsonNode, name = ""): T =
|
||||
fromJson(json, name, result)
|
||||
|
||||
# byte sequence
|
||||
|
||||
func `%`*(bytes: seq[byte]): JsonNode =
|
||||
%("0x" & bytes.toHex)
|
||||
# func `%`*(bytes: seq[byte]): JsonNode =
|
||||
# %("0x" & bytes.toHex)
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var seq[byte]) =
|
||||
result = hexToSeqByte(json.getStr())
|
||||
# func fromJson*(json: JsonNode, result: var seq[byte]) =
|
||||
# result = hexToSeqByte(json.getStr())
|
||||
|
||||
# byte arrays
|
||||
|
||||
func `%`*[N](bytes: array[N, byte]): JsonNode =
|
||||
%("0x" & bytes.toHex)
|
||||
# func `%`*[N](bytes: array[N, byte]): JsonNode =
|
||||
# %("0x" & bytes.toHex)
|
||||
|
||||
func fromJson*[N](json: JsonNode, name: string, result: var array[N, byte]) =
|
||||
hexToByteArray(json.getStr(), result)
|
||||
# func fromJson*[N](json: JsonNode, result: var array[N, byte]) =
|
||||
# hexToByteArray(json.getStr(), result)
|
||||
|
||||
# func fromJson*[N](json: JsonNode, result: var seq[array[N, byte]]) =
|
||||
# for elem in json.getElems:
|
||||
# var byteArr: array[N, byte]
|
||||
# fromJson(elem, byteArr)
|
||||
# result.add byteArr
|
||||
|
||||
# Address
|
||||
|
||||
func `%`*(address: Address): JsonNode =
|
||||
%($address)
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var Address) =
|
||||
if address =? Address.init(json.getStr()):
|
||||
result = address
|
||||
else:
|
||||
raise newException(ValueError, "\"" & name & "\"is not an Address")
|
||||
# func fromJson(jsonVal: string, result: var Address) =
|
||||
# without address =? Address.init(jsonVal):
|
||||
# raiseSerializationError "Failed to convert '" & jsonVal & "' to Address"
|
||||
# result = address
|
||||
|
||||
# func fromJson*(json: JsonNode, result: var Address) =
|
||||
# let val = json.getStr
|
||||
# fromJson(val, result)
|
||||
|
||||
proc readValue*(r: var JsonReader[JrpcConv], result: var Address)
|
||||
{.raises: [SerializationError, IOError].} =
|
||||
var val = r.readValue(string)
|
||||
without address =? Address.init(val):
|
||||
raiseSerializationError "Failed to convert '" & val & "' to Address"
|
||||
result = address
|
||||
|
||||
proc writeValue*(
|
||||
writer: var JsonWriter[JrpcConv],
|
||||
value: Address
|
||||
) {.raises:[IOError].} =
|
||||
writer.writeValue(%value)
|
||||
|
||||
# Filter
|
||||
func `%`*(filter: Filter): JsonNode =
|
||||
%*{
|
||||
"fromBlock": filter.fromBlock,
|
||||
"toBlock": filter.toBlock
|
||||
}
|
||||
|
||||
proc writeValue*(
|
||||
writer: var JsonWriter[JrpcConv],
|
||||
value: Filter
|
||||
) {.raises:[IOError].} =
|
||||
writer.writeValue(%value)
|
||||
|
||||
# EventFilter
|
||||
func `%`*(filter: EventFilter): JsonNode =
|
||||
%*{
|
||||
"address": filter.address,
|
||||
"topics": filter.topics
|
||||
}
|
||||
proc writeValue*(
|
||||
writer: var JsonWriter[JrpcConv],
|
||||
value: EventFilter
|
||||
) {.raises:[IOError].} =
|
||||
writer.writeValue(%value)
|
||||
|
||||
# UInt256
|
||||
|
||||
func `%`*(integer: UInt256): JsonNode =
|
||||
%("0x" & toHex(integer))
|
||||
# func `%`*(integer: UInt256): JsonNode =
|
||||
# %("0x" & toHex(integer))
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var UInt256) =
|
||||
result = UInt256.fromHex(json.getStr())
|
||||
# func fromJson*(json: JsonNode, result: var UInt256) =
|
||||
# result = UInt256.fromHex(json.getStr())
|
||||
|
||||
# TransactionType
|
||||
proc writeValue*(
|
||||
w: var JsonWriter, value: StUint) {.inline, raises: [IOError].} =
|
||||
w.writeValue $value
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var TransactionType) =
|
||||
let val = fromHex[int](json.getStr)
|
||||
result = TransactionType(val)
|
||||
proc readValue*(
|
||||
r: var JsonReader, value: var StUint
|
||||
) {.inline, raises: [IOError, SerializationError].} =
|
||||
let json = r.readValue(JsonNode)
|
||||
value = typeof(value).fromJson(json).getOrRaise(SerializationError)
|
||||
|
||||
func `%`*(txType: TransactionType): JsonNode =
|
||||
%("0x" & txType.int.toHex(1))
|
||||
# # TransactionType
|
||||
|
||||
# func fromJson*(json: JsonNode, result: var TransactionType) =
|
||||
# let val = fromHex[int](json.getStr)
|
||||
# result = TransactionType(val)
|
||||
|
||||
# func `%`*(txType: TransactionType): JsonNode =
|
||||
# %("0x" & txType.int.toHex(1))
|
||||
|
||||
# Transaction
|
||||
|
||||
@ -82,107 +159,193 @@ func `%`*(transaction: Transaction): JsonNode =
|
||||
if gasLimit =? transaction.gasLimit:
|
||||
result["gas"] = %gasLimit
|
||||
|
||||
proc writeValue*(
|
||||
writer: var JsonWriter[JrpcConv],
|
||||
value: Transaction
|
||||
) {.raises:[IOError].} =
|
||||
writer.writeValue(%value)
|
||||
|
||||
# Block
|
||||
# func fromJson*(json: JsonNode, result: var Block) {.raises: [JsonSerializationError].} =
|
||||
# var number: ?UInt256
|
||||
# var timestamp: UInt256
|
||||
# var hash: ?BlockHash
|
||||
# expectFields json, "number", "timestamp", "hash"
|
||||
# fromJson(json{"number"}, number)
|
||||
# fromJson(json{"timestamp"}, timestamp)
|
||||
# fromJson(json{"hash"}, hash)
|
||||
# result = Block(number: number, timestamp: timestamp, hash: hash)
|
||||
|
||||
proc readValue*(r: var JsonReader[JrpcConv], result: var Option[Block])
|
||||
{.raises: [SerializationError, IOError].} =
|
||||
var json = r.readValue(JsonNode)
|
||||
if json.isNil or json.kind == JNull:
|
||||
result = none Block
|
||||
|
||||
# result = some Block()
|
||||
# result.number = Json.decode(result{"number"}, Option[UInt256])
|
||||
result = Option[Block].fromJson(json).getOrRaise(SerializationError)
|
||||
# without val =? Option[Block].fromJson(json) #.mapErr(e => newException(SerializationError, e.msg))
|
||||
# without blk =? Option[Block].fromJson(json), error:
|
||||
# warn "failed to deserialize into Option[Block]", json
|
||||
# result = none Block
|
||||
# result = blk
|
||||
# if json.isNil or json.kind == JNull:
|
||||
# result = none Block
|
||||
# var res: Block
|
||||
# fromJson(Block, json, res)
|
||||
# result = some res
|
||||
|
||||
# BlockTag
|
||||
|
||||
func `%`*(blockTag: BlockTag): JsonNode =
|
||||
%($blockTag)
|
||||
# func `%`*(blockTag: BlockTag): JsonNode =
|
||||
# %($blockTag)
|
||||
|
||||
proc writeValue*(
|
||||
writer: var JsonWriter[JrpcConv],
|
||||
value: BlockTag
|
||||
) {.raises:[IOError].} =
|
||||
writer.writeValue($value)
|
||||
|
||||
proc readValue*(r: var JsonReader[JrpcConv],
|
||||
result: var BlockTag) {.raises:[SerializationError, IOError].} =
|
||||
var json = r.readValue(JsonNode)
|
||||
result = BlockTag.fromJson(json).getOrRaise(SerializationError)
|
||||
|
||||
# Log
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var Log) =
|
||||
if not (json.hasKey("data") and json.hasKey("topics")):
|
||||
raise newException(ValueError, "'data' and/or 'topics' fields not found")
|
||||
# func fromJson*(json: JsonNode, result: var Log) =
|
||||
# expectFields json, "data", "topics"
|
||||
|
||||
var data: seq[byte]
|
||||
var topics: seq[Topic]
|
||||
fromJson(json["data"], "data", data)
|
||||
fromJson(json["topics"], "topics", topics)
|
||||
result = Log(data: data, topics: topics)
|
||||
# var data: seq[byte]
|
||||
# var topics: seq[Topic]
|
||||
# fromJson(json["data"], data)
|
||||
# fromJson(json["topics"], topics)
|
||||
# result = Log(data: data, topics: topics)
|
||||
|
||||
# TransactionStatus
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var TransactionStatus) =
|
||||
let val = fromHex[int](json.getStr)
|
||||
result = TransactionStatus(val)
|
||||
# func fromJson*(json: JsonNode, result: var TransactionStatus) =
|
||||
# let val = fromHex[int](json.getStr)
|
||||
# result = TransactionStatus(val)
|
||||
|
||||
func `%`*(status: TransactionStatus): JsonNode =
|
||||
%("0x" & status.int.toHex(1))
|
||||
# func `%`*(status: TransactionStatus): JsonNode =
|
||||
# %("0x" & status.int.toHex(1))
|
||||
|
||||
# PastTransaction
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var PastTransaction) =
|
||||
# Deserializes a past transaction, eg eth_getTransactionByHash.
|
||||
# Spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash
|
||||
json.expectFields "blockHash", "blockNumber", "from", "gas", "gasPrice",
|
||||
"hash", "input", "nonce", "to", "transactionIndex", "value",
|
||||
"v", "r", "s"
|
||||
proc readValue*(r: var JsonReader[JrpcConv], result: var Option[PastTransaction])
|
||||
{.raises: [SerializationError, IOError].} =
|
||||
var json = r.readValue(JsonNode)
|
||||
result = Option[PastTransaction].fromJson(json).getOrRaise(SerializationError)
|
||||
|
||||
result = PastTransaction(
|
||||
blockHash: BlockHash.fromJson(json["blockHash"], "blockHash"),
|
||||
blockNumber: UInt256.fromJson(json["blockNumber"], "blockNumber"),
|
||||
sender: Address.fromJson(json["from"], "from"),
|
||||
gas: UInt256.fromJson(json["gas"], "gas"),
|
||||
gasPrice: UInt256.fromJson(json["gasPrice"], "gasPrice"),
|
||||
hash: TransactionHash.fromJson(json["hash"], "hash"),
|
||||
input: seq[byte].fromJson(json["input"], "input"),
|
||||
nonce: UInt256.fromJson(json["nonce"], "nonce"),
|
||||
to: Address.fromJson(json["to"], "to"),
|
||||
transactionIndex: UInt256.fromJson(json["transactionIndex"], "transactionIndex"),
|
||||
value: UInt256.fromJson(json["value"], "value"),
|
||||
v: UInt256.fromJson(json["v"], "v"),
|
||||
r: UInt256.fromJson(json["r"], "r"),
|
||||
s: UInt256.fromJson(json["s"], "s"),
|
||||
)
|
||||
if json.hasKey("type"):
|
||||
result.transactionType = fromJson(?TransactionType, json["type"], "type")
|
||||
if json.hasKey("chainId"):
|
||||
result.chainId = fromJson(?UInt256, json["chainId"], "chainId")
|
||||
# func fromJson*(json: JsonNode, result: var PastTransaction) =
|
||||
# # Deserializes a past transaction, eg eth_getTransactionByHash.
|
||||
# # Spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionbyhash
|
||||
# json.expectFields "blockHash", "blockNumber", "from", "gas", "gasPrice",
|
||||
# "hash", "input", "nonce", "to", "transactionIndex", "value",
|
||||
# "v", "r", "s"
|
||||
|
||||
func `%`*(tx: PastTransaction): JsonNode =
|
||||
let json = %*{
|
||||
"blockHash": tx.blockHash,
|
||||
"blockNumber": tx.blockNumber,
|
||||
"from": tx.sender,
|
||||
"gas": tx.gas,
|
||||
"gasPrice": tx.gasPrice,
|
||||
"hash": tx.hash,
|
||||
"input": tx.input,
|
||||
"nonce": tx.nonce,
|
||||
"to": tx.to,
|
||||
"transactionIndex": tx.transactionIndex,
|
||||
"value": tx.value,
|
||||
"v": tx.v,
|
||||
"r": tx.r,
|
||||
"s": tx.s
|
||||
}
|
||||
if txType =? tx.transactionType:
|
||||
json["type"] = %txType
|
||||
if chainId =? tx.chainId:
|
||||
json["chainId"] = %chainId
|
||||
return json
|
||||
# result = PastTransaction(
|
||||
# blockHash: BlockHash.fromJson(json["blockHash"]), #, "blockHash"),
|
||||
# blockNumber: UInt256.fromJson(json["blockNumber"]), #, "blockNumber"),
|
||||
# sender: Address.fromJson(json["from"]), #, "from"),
|
||||
# gas: UInt256.fromJson(json["gas"]), #, "gas"),
|
||||
# gasPrice: UInt256.fromJson(json["gasPrice"]), #, "gasPrice"),
|
||||
# hash: TransactionHash.fromJson(json["hash"]), #, "hash"),
|
||||
# input: seq[byte].fromJson(json["input"]), #, "input"),
|
||||
# nonce: UInt256.fromJson(json["nonce"]), #, "nonce"),
|
||||
# to: Address.fromJson(json["to"]), #, "to"),
|
||||
# transactionIndex: UInt256.fromJson(json["transactionIndex"]), #, "transactionIndex"),
|
||||
# value: UInt256.fromJson(json["value"]), #, "value"),
|
||||
# v: UInt256.fromJson(json["v"]), #, "v"),
|
||||
# r: UInt256.fromJson(json["r"]), #, "r"),
|
||||
# s: UInt256.fromJson(json["s"]) #, "s"),
|
||||
# )
|
||||
# if json.hasKey("type"):
|
||||
# result.transactionType = fromJson(?TransactionType, json["type"], "type")
|
||||
# if json.hasKey("chainId"):
|
||||
# result.chainId = fromJson(?UInt256, json["chainId"], "chainId")
|
||||
|
||||
# func `%`*(tx: PastTransaction): JsonNode =
|
||||
# let json = %*{
|
||||
# "blockHash": tx.blockHash,
|
||||
# "blockNumber": tx.blockNumber,
|
||||
# "from": tx.sender,
|
||||
# "gas": tx.gas,
|
||||
# "gasPrice": tx.gasPrice,
|
||||
# "hash": tx.hash,
|
||||
# "input": tx.input,
|
||||
# "nonce": tx.nonce,
|
||||
# "to": tx.to,
|
||||
# "transactionIndex": tx.transactionIndex,
|
||||
# "value": tx.value,
|
||||
# "v": tx.v,
|
||||
# "r": tx.r,
|
||||
# "s": tx.s
|
||||
# }
|
||||
# if txType =? tx.transactionType:
|
||||
# json["type"] = %txType
|
||||
# if chainId =? tx.chainId:
|
||||
# json["chainId"] = %chainId
|
||||
# return json
|
||||
|
||||
# TransactionReceipt
|
||||
|
||||
func fromJson*(json: JsonNode, name: string, result: var TransactionReceipt) =
|
||||
# Deserializes a transaction receipt, eg eth_getTransactionReceipt.
|
||||
# Spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
|
||||
json.expectFields "transactionHash", "transactionIndex", "cumulativeGasUsed",
|
||||
"effectiveGasPrice", "gasUsed", "logs", "logsBloom", "type",
|
||||
"status"
|
||||
proc readValue*(r: var JsonReader[JrpcConv], result: var Option[TransactionReceipt])
|
||||
{.raises: [SerializationError, IOError].} =
|
||||
var json = r.readValue(JsonNode)
|
||||
result = Option[TransactionReceipt].fromJson(json).getOrRaise(SerializationError)
|
||||
|
||||
result = TransactionReceipt(
|
||||
transactionHash: fromJson(TransactionHash, json["transactionHash"], "transactionHash"),
|
||||
transactionIndex: UInt256.fromJson(json["transactionIndex"], "transactionIndex"),
|
||||
blockHash: fromJson(?BlockHash, json["blockHash"], "blockHash"),
|
||||
blockNumber: fromJson(?UInt256, json["blockNumber"], "blockNumber"),
|
||||
sender: fromJson(?Address, json["from"], "from"),
|
||||
to: fromJson(?Address, json["to"], "to"),
|
||||
cumulativeGasUsed: UInt256.fromJson(json["cumulativeGasUsed"], "cumulativeGasUsed"),
|
||||
effectiveGasPrice: fromJson(?UInt256, json["effectiveGasPrice"], "effectiveGasPrice"),
|
||||
gasUsed: UInt256.fromJson(json["gasUsed"], "gasUsed"),
|
||||
contractAddress: fromJson(?Address, json["contractAddress"], "contractAddress"),
|
||||
logs: seq[Log].fromJson(json["logs"], "logs"),
|
||||
logsBloom: seq[byte].fromJson(json["logsBloom"], "logsBloom"),
|
||||
transactionType: TransactionType.fromJson(json["type"], "type"),
|
||||
status: TransactionStatus.fromJson(json["status"], "status")
|
||||
)
|
||||
# func fromJson*(json: JsonNode, result: var TransactionReceipt) =
|
||||
# # Deserializes a transaction receipt, eg eth_getTransactionReceipt.
|
||||
# # Spec: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gettransactionreceipt
|
||||
# json.expectFields "transactionHash", "transactionIndex", "cumulativeGasUsed",
|
||||
# "effectiveGasPrice", "gasUsed", "logs", "logsBloom", "type",
|
||||
# "status"
|
||||
|
||||
# result = TransactionReceipt(
|
||||
# transactionHash: fromJson(TransactionHash, json["transactionHash"], "transactionHash"),
|
||||
# transactionIndex: UInt256.fromJson(json["transactionIndex"], "transactionIndex"),
|
||||
# blockHash: fromJson(?BlockHash, json["blockHash"], "blockHash"),
|
||||
# blockNumber: fromJson(?UInt256, json["blockNumber"], "blockNumber"),
|
||||
# sender: fromJson(?Address, json["from"], "from"),
|
||||
# to: fromJson(?Address, json["to"], "to"),
|
||||
# cumulativeGasUsed: UInt256.fromJson(json["cumulativeGasUsed"], "cumulativeGasUsed"),
|
||||
# effectiveGasPrice: fromJson(?UInt256, json["effectiveGasPrice"], "effectiveGasPrice"),
|
||||
# gasUsed: UInt256.fromJson(json["gasUsed"], "gasUsed"),
|
||||
# contractAddress: fromJson(?Address, json["contractAddress"], "contractAddress"),
|
||||
# logs: seq[Log].fromJson(json["logs"], "logs"),
|
||||
# logsBloom: seq[byte].fromJson(json["logsBloom"], "logsBloom"),
|
||||
# transactionType: TransactionType.fromJson(json["type"], "type"),
|
||||
# status: TransactionStatus.fromJson(json["status"], "status")
|
||||
# )
|
||||
|
||||
# func fromJson*[T](json: JsonNode, result: var Option[T]) =
|
||||
# if json.isNil or json.kind == JNull:
|
||||
# result = none T
|
||||
# return
|
||||
|
||||
# var val: T
|
||||
# fromJson(json, val)
|
||||
# result = some val
|
||||
# # if val =? T.fromJson(json):
|
||||
# # result = some val
|
||||
|
||||
# # # result = none T
|
||||
|
||||
# func fromJson*[T](json: JsonNode, name = "", result: var seq[T]) =
|
||||
# if json.kind != JArray:
|
||||
# raiseSerializationError(fmt"Expected JArray to convert to seq, but got {json.kind}")
|
||||
|
||||
# for elem in json.elems:
|
||||
# var v: T
|
||||
# fromJson(elem, name, v)
|
||||
# result.add(v)
|
||||
|
||||
# func fromJson*(T: type, json: JsonNode, name = ""): T =
|
||||
# fromJson(json, name, result)
|
||||
|
||||
|
||||
# func fromJson*(T: type, json: JsonNode, name = ""): T =
|
||||
# fromJson(json, name, result)
|
||||
403
ethers/providers/jsonrpc/json.nim
Normal file
403
ethers/providers/jsonrpc/json.nim
Normal file
@ -0,0 +1,403 @@
|
||||
|
||||
import std/json except `%`, `%*`
|
||||
import std/macros
|
||||
import std/options
|
||||
import std/strutils
|
||||
# import std/strformat
|
||||
import std/tables
|
||||
import std/typetraits
|
||||
import pkg/chronicles
|
||||
import pkg/contractabi
|
||||
import pkg/stew/byteutils
|
||||
import pkg/stint
|
||||
import pkg/questionable/results
|
||||
|
||||
import ../../basics
|
||||
|
||||
export json except `%`, `%*`
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
logScope:
|
||||
topics = "json serialization"
|
||||
|
||||
type
|
||||
SerializationError = object of EthersError
|
||||
UnexpectedKindError = object of SerializationError
|
||||
|
||||
template serialize* {.pragma.}
|
||||
|
||||
proc mapErrTo[T, E1: CatchableError, E2: CatchableError](r: Result[T, E1], _: type E2): ?!T =
|
||||
r.mapErr(proc (e: E1): E2 = E2(msg: e.msg))
|
||||
|
||||
proc newUnexpectedKindError(
|
||||
expectedType: type,
|
||||
expectedKinds: string,
|
||||
json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
let kind = if json.isNil: "nil"
|
||||
else: $json.kind
|
||||
newException(UnexpectedKindError,
|
||||
"deserialization to " & $expectedType & " failed: expected " &
|
||||
expectedKinds & "but got " & $kind)
|
||||
|
||||
proc newUnexpectedKindError(
|
||||
expectedType: type,
|
||||
expectedKinds: set[JsonNodeKind],
|
||||
json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
newUnexpectedKindError(expectedType, $expectedKinds, json)
|
||||
|
||||
proc newUnexpectedKindError(
|
||||
expectedType: type,
|
||||
expectedKind: JsonNodeKind,
|
||||
json: JsonNode
|
||||
): ref UnexpectedKindError =
|
||||
newUnexpectedKindError(expectedType, {expectedKind}, json)
|
||||
|
||||
template expectJsonKind(
|
||||
expectedType: type,
|
||||
expectedKinds: set[JsonNodeKind],
|
||||
json: JsonNode
|
||||
) =
|
||||
if json.isNil or json.kind notin expectedKinds:
|
||||
return failure(newUnexpectedKindError(expectedType, expectedKinds, json))
|
||||
|
||||
template expectJsonKind(
|
||||
expectedType: type,
|
||||
expectedKind: JsonNodeKind,
|
||||
json: JsonNode
|
||||
) =
|
||||
expectJsonKind(expectedType, {expectedKind}, json)
|
||||
|
||||
proc fromJson*(
|
||||
T: type enum,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
expectJsonKind(string, JString, json)
|
||||
catch parseEnum[T](json.str)
|
||||
|
||||
proc fromJson*(
|
||||
_: type string,
|
||||
json: JsonNode
|
||||
): ?!string =
|
||||
if json.isNil:
|
||||
let err = newException(ValueError, "'json' expected, but was nil")
|
||||
return failure(err)
|
||||
elif json.kind == JNull:
|
||||
return success("null")
|
||||
elif json.isNil or json.kind != JString:
|
||||
return failure(newUnexpectedKindError(string, JString, json))
|
||||
catch json.getStr
|
||||
|
||||
proc fromJson*(
|
||||
_: type bool,
|
||||
json: JsonNode
|
||||
): ?!bool =
|
||||
expectJsonKind(bool, JBool, json)
|
||||
catch json.getBool
|
||||
|
||||
proc fromJson*(
|
||||
_: type int,
|
||||
json: JsonNode
|
||||
): ?!int =
|
||||
expectJsonKind(int, JInt, json)
|
||||
catch json.getInt
|
||||
|
||||
proc fromJson*[T: SomeInteger](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
when T is uint|uint64 or (not defined(js) and int.sizeof == 4):
|
||||
expectJsonKind(T, {JInt, JString}, json)
|
||||
case json.kind
|
||||
of JString:
|
||||
let x = parseBiggestUInt(json.str)
|
||||
return success cast[T](x)
|
||||
else:
|
||||
return success T(json.num)
|
||||
else:
|
||||
expectJsonKind(T, {JInt}, json)
|
||||
return success cast[T](json.num)
|
||||
|
||||
proc fromJson*[T: SomeFloat](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
expectJsonKind(T, {JInt, JFloat, JString}, json)
|
||||
if json.kind == JString:
|
||||
case json.str
|
||||
of "nan":
|
||||
let b = NaN
|
||||
return success T(b)
|
||||
# dst = NaN # would fail some tests because range conversions would cause CT error
|
||||
# in some cases; but this is not a hot-spot inside this branch and backend can optimize this.
|
||||
of "inf":
|
||||
let b = Inf
|
||||
return success T(b)
|
||||
of "-inf":
|
||||
let b = -Inf
|
||||
return success T(b)
|
||||
else:
|
||||
let err = newUnexpectedKindError(T, "'nan|inf|-inf'", json)
|
||||
return failure(err)
|
||||
else:
|
||||
if json.kind == JFloat:
|
||||
return success T(json.fnum)
|
||||
else:
|
||||
return success T(json.num)
|
||||
|
||||
proc fromJson*(
|
||||
_: type seq[byte],
|
||||
json: JsonNode
|
||||
): ?!seq[byte] =
|
||||
expectJsonKind(seq[byte], JString, json)
|
||||
hexToSeqByte(json.getStr).catch
|
||||
|
||||
proc fromJson*[N: static[int], T: array[N, byte]](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
expectJsonKind(T, JString, json)
|
||||
T.fromHex(json.getStr).catch
|
||||
|
||||
proc fromJson*[T: distinct](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
success T(? T.distinctBase.fromJson(json))
|
||||
|
||||
proc fromJson*[N: static[int], T: StUint[N]](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
expectJsonKind(T, JString, json)
|
||||
let jsonStr = json.getStr
|
||||
let prefix = jsonStr[0..1].toLowerAscii
|
||||
case prefix:
|
||||
of "0x": catch parse(jsonStr, T, 16)
|
||||
of "0o": catch parse(jsonStr, T, 8)
|
||||
of "0b": catch parse(jsonStr, T, 2)
|
||||
else: catch parse(jsonStr, T)
|
||||
|
||||
proc fromJson*[T](
|
||||
_: type Option[T],
|
||||
json: JsonNode
|
||||
): ?! Option[T] =
|
||||
if json.isNil or json.kind == JNull:
|
||||
return success(none T)
|
||||
without val =? T.fromJson(json), error:
|
||||
return failure(error)
|
||||
success(val.some)
|
||||
|
||||
proc fromJson*[T](
|
||||
_: type seq[T],
|
||||
json: JsonNode
|
||||
): ?! seq[T] =
|
||||
expectJsonKind(seq[T], JArray, json)
|
||||
var arr: seq[T] = @[]
|
||||
for elem in json.elems:
|
||||
arr.add(? T.fromJson(elem))
|
||||
success arr
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
): ?!T =
|
||||
|
||||
when T is JsonNode:
|
||||
return success T(json)
|
||||
|
||||
expectJsonKind(T, JObject, json)
|
||||
var res = when type(T) is ref: T.new() else: T.default
|
||||
|
||||
# Leave this in, it's good for debugging:
|
||||
# trace "deserializing object", to = $T, json
|
||||
for name, value in fieldPairs(when type(T) is ref: res[] else: res):
|
||||
|
||||
if jsonVal =? json{name}.catch and not jsonVal.isNil:
|
||||
without parsed =? type(value).fromJson(jsonVal), e:
|
||||
error "error deserializing field",
|
||||
field = $T & "." & name,
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
success(res)
|
||||
|
||||
proc parse*(json: string): ?!JsonNode =
|
||||
try:
|
||||
echo "[json.parse] json: ", json
|
||||
return parseJson(json).catch
|
||||
except Exception as e:
|
||||
echo "[json.parse] exception: ", e.msg
|
||||
return err newException(CatchableError, e.msg)
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
bytes: seq[byte]
|
||||
): ?!T =
|
||||
let json = ? parse(string.fromBytes(bytes))
|
||||
T.fromJson(json)
|
||||
|
||||
|
||||
# import std/streams
|
||||
# import std/parsejson
|
||||
|
||||
# type StringStreamFixed = ref object of StringStream
|
||||
# closeImplFixed: proc (s: StringStreamFixed)
|
||||
# {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.}
|
||||
|
||||
# proc closeImpl*: proc (s: StringStreamFixed)
|
||||
# {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.} = discard
|
||||
|
||||
# proc ssCloseFixed(s: StringStreamFixed) =
|
||||
# # var s = StringStream(s)
|
||||
# s.data = ""
|
||||
|
||||
# proc close*(s: StringStreamFixed) {.raises: [IOError, OSError].} =
|
||||
# ## Closes the stream `s`.
|
||||
# ##
|
||||
# ## See also:
|
||||
# ## * `flush proc <#flush,Stream>`_
|
||||
# # runnableExamples:
|
||||
# # var strm = newStringStream("The first line\nthe second line\nthe third line")
|
||||
# # ## do something...
|
||||
# # strm.close()
|
||||
# if not isNil(s.closeImplFixed): s.closeImplFixed(s)
|
||||
|
||||
# proc newStringStreamFixed*(s: sink string = ""): owned StringStreamFixed {.raises:[].} =
|
||||
# var ss = StringStreamFixed(newStringStream(s))
|
||||
# ss.closeImplFixed = ssCloseFixed
|
||||
# ss
|
||||
|
||||
|
||||
# proc parseJson*(buffer: string; rawIntegers = false, rawFloats = false): JsonNode {.raises: [IOError, OSError, JsonParsingError, ValueError].} =
|
||||
# ## Parses JSON from `buffer`.
|
||||
# ## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||
# ## If `rawIntegers` is true, integer literals will not be converted to a `JInt`
|
||||
# ## field but kept as raw numbers via `JString`.
|
||||
# ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat`
|
||||
# ## field but kept as raw numbers via `JString`.
|
||||
# result = parseJson(newStringStreamFixed(buffer), "input", rawIntegers, rawFloats)
|
||||
|
||||
|
||||
proc fromJson*(
|
||||
_: type JsonNode,
|
||||
json: string
|
||||
): ?!JsonNode =
|
||||
echo "[JsonNode.fromJson] json: ", json
|
||||
return parse(json)
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
json: string
|
||||
): ?!T =
|
||||
let json = ? parse(json)
|
||||
T.fromJson(json)
|
||||
|
||||
func `%`*(s: string): JsonNode = newJString(s)
|
||||
|
||||
func `%`*(n: uint): JsonNode =
|
||||
if n > cast[uint](int.high):
|
||||
newJString($n)
|
||||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: int): JsonNode = newJInt(n)
|
||||
|
||||
func `%`*(n: BiggestUInt): JsonNode =
|
||||
if n > cast[BiggestUInt](BiggestInt.high):
|
||||
newJString($n)
|
||||
else:
|
||||
newJInt(BiggestInt(n))
|
||||
|
||||
func `%`*(n: BiggestInt): JsonNode = newJInt(n)
|
||||
|
||||
func `%`*(n: float): JsonNode =
|
||||
if n != n: newJString("nan")
|
||||
elif n == Inf: newJString("inf")
|
||||
elif n == -Inf: newJString("-inf")
|
||||
else: newJFloat(n)
|
||||
|
||||
func `%`*(b: bool): JsonNode = newJBool(b)
|
||||
|
||||
func `%`*(keyVals: openArray[tuple[key: string, val: JsonNode]]): JsonNode =
|
||||
if keyVals.len == 0: return newJArray()
|
||||
let jObj = newJObject()
|
||||
for key, val in items(keyVals): jObj.fields[key] = val
|
||||
jObj
|
||||
|
||||
template `%`*(j: JsonNode): JsonNode = j
|
||||
|
||||
func `%`*[T](table: Table[string, T]|OrderedTable[string, T]): JsonNode =
|
||||
let jObj = newJObject()
|
||||
for k, v in table: jObj[k] = ? %v
|
||||
jObj
|
||||
|
||||
func `%`*[T](opt: Option[T]): JsonNode =
|
||||
if opt.isSome: %(opt.get) else: newJNull()
|
||||
|
||||
func `%`*[T: object](obj: T): JsonNode =
|
||||
let jsonObj = newJObject()
|
||||
for name, value in obj.fieldPairs:
|
||||
when value.hasCustomPragma(serialize):
|
||||
jsonObj[name] = %value
|
||||
jsonObj
|
||||
|
||||
func `%`*[T: ref object](obj: T): JsonNode =
|
||||
let jsonObj = newJObject()
|
||||
for name, value in obj[].fieldPairs:
|
||||
when value.hasCustomPragma(serialize):
|
||||
jsonObj[name] = %(value)
|
||||
jsonObj
|
||||
|
||||
proc `%`*(o: enum): JsonNode = % $o
|
||||
|
||||
func `%`*(stint: StInt|StUint): JsonNode = %stint.toString
|
||||
|
||||
func `%`*(cstr: cstring): JsonNode = % $cstr
|
||||
|
||||
func `%`*(arr: openArray[byte]): JsonNode = % arr.to0xHex
|
||||
|
||||
func `%`*[T](elements: openArray[T]): JsonNode =
|
||||
let jObj = newJArray()
|
||||
for elem in elements: jObj.add(%elem)
|
||||
jObj
|
||||
|
||||
func `%`*[T: distinct](id: T): JsonNode =
|
||||
type baseType = T.distinctBase
|
||||
% baseType(id)
|
||||
|
||||
func toJson*[T](item: T): string = $(%item)
|
||||
|
||||
proc toJsnImpl(x: NimNode): NimNode =
|
||||
case x.kind
|
||||
of nnkBracket: # array
|
||||
if x.len == 0: return newCall(bindSym"newJArray")
|
||||
result = newNimNode(nnkBracket)
|
||||
for i in 0 ..< x.len:
|
||||
result.add(toJsnImpl(x[i]))
|
||||
result = newCall(bindSym("%", brOpen), result)
|
||||
of nnkTableConstr: # object
|
||||
if x.len == 0: return newCall(bindSym"newJObject")
|
||||
result = newNimNode(nnkTableConstr)
|
||||
for i in 0 ..< x.len:
|
||||
x[i].expectKind nnkExprColonExpr
|
||||
result.add newTree(nnkExprColonExpr, x[i][0], toJsnImpl(x[i][1]))
|
||||
result = newCall(bindSym("%", brOpen), result)
|
||||
of nnkCurly: # empty object
|
||||
x.expectLen(0)
|
||||
result = newCall(bindSym"newJObject")
|
||||
of nnkNilLit:
|
||||
result = newCall(bindSym"newJNull")
|
||||
of nnkPar:
|
||||
if x.len == 1: result = toJsnImpl(x[0])
|
||||
else: result = newCall(bindSym("%", brOpen), x)
|
||||
else:
|
||||
result = newCall(bindSym("%", brOpen), x)
|
||||
|
||||
macro `%*`*(x: untyped): JsonNode =
|
||||
## Convert an expression to a JsonNode directly, without having to specify
|
||||
## `%` for every element.
|
||||
result = toJsnImpl(x)
|
||||
Loading…
x
Reference in New Issue
Block a user