nim-ethers/ethers/providers/jsonrpc.nim

249 lines
8.0 KiB
Nim
Raw Normal View History

import std/json
import std/tables
2022-01-18 11:10:20 +00:00
import std/uri
import pkg/json_rpc/rpcclient
2022-06-29 13:13:25 +00:00
import pkg/json_rpc/errors
2022-01-18 11:10:20 +00:00
import ../basics
import ../provider
2022-01-20 13:39:37 +00:00
import ../signer
import ./jsonrpc/rpccalls
import ./jsonrpc/conversions
import ./jsonrpc/subscriptions
2022-01-18 11:10:20 +00:00
export json
2022-01-18 11:10:20 +00:00
export basics
export provider
push: {.upraises: [].}
2022-01-20 13:39:37 +00:00
type
JsonRpcProvider* = ref object of Provider
client: Future[RpcClient]
subscriptions: Future[JsonRpcSubscriptions]
2022-01-20 13:39:37 +00:00
JsonRpcSigner* = ref object of Signer
provider: JsonRpcProvider
address: ?Address
JsonRpcProviderError* = object of ProviderError
JsonRpcSubscription* = ref object of Subscription
subscriptions: JsonRpcSubscriptions
id: JsonNode
2022-01-20 13:39:37 +00:00
proc raiseProviderError(message: string) {.upraises: [JsonRpcProviderError].} =
var message = message
try:
message = parseJson(message){"message"}.getStr
except Exception:
discard
2022-01-20 13:39:37 +00:00
raise newException(JsonRpcProviderError, message)
2022-06-29 13:13:25 +00:00
template convertError(body) =
try:
body
except JsonRpcError as error:
raiseProviderError(error.msg)
2022-09-15 10:38:52 +00:00
# Catch all ValueErrors for now, at least until JsonRpcError is actually
# raised. PR created: https://github.com/status-im/nim-json-rpc/pull/151
except ValueError as error:
raiseProviderError(error.msg)
2022-06-29 13:13:25 +00:00
2022-01-20 13:39:37 +00:00
# Provider
2022-01-18 11:10:20 +00:00
const defaultUrl = "http://localhost:8545"
const defaultPollingInterval = 4.seconds
2022-01-18 11:10:20 +00:00
2023-06-20 11:32:14 +00:00
proc jsonHeaders: seq[(string, string)] =
@[("Content-Type", "application/json")]
proc new*(_: type JsonRpcProvider,
url=defaultUrl,
pollingInterval=defaultPollingInterval): JsonRpcProvider =
var initialized: Future[void]
var client: RpcClient
var subscriptions: JsonRpcSubscriptions
proc initialize {.async.} =
case parseUri(url).scheme
of "ws", "wss":
let websocket = newRpcWebSocketClient(getHeaders = jsonHeaders)
await websocket.connect(url)
client = websocket
subscriptions = JsonRpcSubscriptions.new(websocket)
else:
let http = newRpcHttpClient(getHeaders = jsonHeaders)
await http.connect(url)
client = http
subscriptions = JsonRpcSubscriptions.new(http,
pollingInterval = pollingInterval)
proc awaitClient: Future[RpcClient] {.async.} =
convertError:
await initialized
return client
proc awaitSubscriptions: Future[JsonRpcSubscriptions] {.async.} =
convertError:
await initialized
return subscriptions
initialized = initialize()
JsonRpcProvider(client: awaitClient(), subscriptions: awaitSubscriptions())
2022-01-18 11:10:20 +00:00
2022-01-18 13:24:46 +00:00
proc send*(provider: JsonRpcProvider,
call: string,
2022-01-25 14:05:54 +00:00
arguments: seq[JsonNode] = @[]): Future[JsonNode] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.call(call, %arguments)
2022-01-18 13:24:46 +00:00
2022-01-18 11:10:20 +00:00
proc listAccounts*(provider: JsonRpcProvider): Future[seq[Address]] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_accounts()
2022-01-18 13:26:41 +00:00
2022-01-20 13:39:37 +00:00
proc getSigner*(provider: JsonRpcProvider): JsonRpcSigner =
JsonRpcSigner(provider: provider)
proc getSigner*(provider: JsonRpcProvider, address: Address): JsonRpcSigner =
JsonRpcSigner(provider: provider, address: some address)
2022-01-18 13:26:41 +00:00
method getBlockNumber*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_blockNumber()
2022-01-20 11:56:18 +00:00
method getBlock*(provider: JsonRpcProvider,
tag: BlockTag): Future[?Block] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_getBlockByNumber(tag, false)
2022-01-20 11:56:18 +00:00
method call*(provider: JsonRpcProvider,
tx: Transaction,
blockTag = BlockTag.latest): Future[seq[byte]] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_call(tx, blockTag)
2022-01-20 13:39:37 +00:00
2022-01-24 11:12:52 +00:00
method getGasPrice*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_gasPrice()
2022-01-24 11:12:52 +00:00
2022-01-24 11:14:31 +00:00
method getTransactionCount*(provider: JsonRpcProvider,
address: Address,
blockTag = BlockTag.latest):
Future[UInt256] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_getTransactionCount(address, blockTag)
method getTransactionReceipt*(provider: JsonRpcProvider,
txHash: TransactionHash):
Future[?TransactionReceipt] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_getTransactionReceipt(txHash)
2022-01-24 11:14:31 +00:00
method getLogs*(provider: JsonRpcProvider,
filter: EventFilter):
Future[seq[Log]] {.async.} =
convertError:
let client = await provider.client
let logsJson = if filter of Filter:
await client.eth_getLogs(Filter(filter))
elif filter of FilterByBlockHash:
await client.eth_getLogs(FilterByBlockHash(filter))
else:
await client.eth_getLogs(filter)
var logs: seq[Log] = @[]
for logJson in logsJson.getElems:
if log =? Log.fromJson(logJson).catch:
logs.add log
return logs
2022-01-24 13:40:47 +00:00
method estimateGas*(provider: JsonRpcProvider,
transaction: Transaction): Future[UInt256] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
return await client.eth_estimateGas(transaction)
2022-01-24 13:40:47 +00:00
2022-01-24 16:29:25 +00:00
method getChainId*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await provider.client
try:
return await client.eth_chainId()
except CatchableError:
return parse(await client.net_version(), UInt256)
2022-01-24 16:29:25 +00:00
method sendTransaction*(provider: JsonRpcProvider, rawTransaction: seq[byte]): Future[TransactionResponse] {.async.} =
2022-07-12 18:24:52 +00:00
convertError:
let
client = await provider.client
hash = await client.eth_sendRawTransaction(rawTransaction)
2022-07-14 08:22:54 +00:00
2022-07-12 18:24:52 +00:00
return TransactionResponse(hash: hash, provider: provider)
method subscribe*(provider: JsonRpcProvider,
filter: EventFilter,
onLog: LogHandler):
Future[Subscription] {.async.} =
convertError:
let subscriptions = await provider.subscriptions
let id = await subscriptions.subscribeLogs(filter, onLog)
return JsonRpcSubscription(subscriptions: subscriptions, id: id)
2022-05-16 12:51:39 +00:00
method subscribe*(provider: JsonRpcProvider,
onBlock: BlockHandler):
2022-05-16 12:51:39 +00:00
Future[Subscription] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let subscriptions = await provider.subscriptions
let id = await subscriptions.subscribeBlocks(onBlock)
return JsonRpcSubscription(subscriptions: subscriptions, id: id)
method unsubscribe(subscription: JsonRpcSubscription) {.async.} =
convertError:
let subscriptions = subscription.subscriptions
let id = subscription.id
await subscriptions.unsubscribe(id)
method close*(provider: JsonRpcProvider) {.async.} =
convertError:
let client = await provider.client
let subscriptions = await provider.subscriptions
await subscriptions.close()
await client.close()
2022-01-20 13:39:37 +00:00
# Signer
2022-01-24 11:12:52 +00:00
method provider*(signer: JsonRpcSigner): Provider =
signer.provider
2022-01-20 13:39:37 +00:00
method getAddress*(signer: JsonRpcSigner): Future[Address] {.async.} =
if address =? signer.address:
return address
let accounts = await signer.provider.listAccounts()
if accounts.len > 0:
return accounts[0]
raiseProviderError "no address found"
2022-01-25 16:17:43 +00:00
2022-01-26 10:21:28 +00:00
method signMessage*(signer: JsonRpcSigner,
message: seq[byte]): Future[seq[byte]] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
let client = await signer.provider.client
let address = await signer.getAddress()
return await client.eth_sign(address, message)
2022-01-26 10:21:28 +00:00
2022-01-25 16:17:43 +00:00
method sendTransaction*(signer: JsonRpcSigner,
transaction: Transaction): Future[TransactionResponse] {.async.} =
2022-06-29 13:13:25 +00:00
convertError:
signer.updateNonce(transaction.nonce)
2022-06-29 13:13:25 +00:00
let
client = await signer.provider.client
hash = await client.eth_sendTransaction(transaction)
2022-06-29 13:13:25 +00:00
return TransactionResponse(hash: hash, provider: signer.provider)