Update exceptions

Use {.async: (raises: [...].} where needed
Annotate provider with {.push raises:[].}
Format signatures
This commit is contained in:
Eric 2024-01-24 14:36:53 +11:00
parent ad1fe90492
commit f6f0dbc07b
No known key found for this signature in database
7 changed files with 182 additions and 116 deletions

View File

@ -5,7 +5,7 @@ license = "MIT"
requires "nim >= 1.6.0"
requires "chronicles >= 0.10.3 & < 0.11.0"
requires "chronos >= 3.0.0 & < 4.0.0"
requires "chronos#f0a2d4df61302d24baa6c0f1c257f92045c9ee57"
requires "contractabi >= 0.6.0 & < 0.7.0"
requires "questionable >= 0.10.2 & < 0.11.0"
requires "json_rpc"

View File

@ -97,68 +97,94 @@ func toTransaction*(past: PastTransaction): Transaction =
chainId: past.chainId
)
method getBlockNumber*(provider: Provider): Future[UInt256] {.base, gcsafe.} =
method getBlockNumber*(
provider: Provider): Future[UInt256] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getBlock*(provider: Provider, tag: BlockTag): Future[?Block] {.base, gcsafe.} =
method getBlock*(
provider: Provider,
tag: BlockTag): Future[?Block] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method call*(provider: Provider,
tx: Transaction,
blockTag = BlockTag.latest): Future[seq[byte]] {.base, gcsafe.} =
method call*(
provider: Provider,
tx: Transaction,
blockTag = BlockTag.latest): Future[seq[byte]] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getGasPrice*(provider: Provider): Future[UInt256] {.base, gcsafe.} =
method getGasPrice*(
provider: Provider): Future[UInt256] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getTransactionCount*(provider: Provider,
address: Address,
blockTag = BlockTag.latest):
Future[UInt256] {.base, gcsafe.} =
method getTransactionCount*(
provider: Provider,
address: Address,
blockTag = BlockTag.latest): Future[UInt256] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getTransaction*(provider: Provider,
txHash: TransactionHash):
Future[?PastTransaction] {.base, gcsafe.} =
method getTransaction*(
provider: Provider,
txHash: TransactionHash): Future[?PastTransaction] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getTransactionReceipt*(provider: Provider,
txHash: TransactionHash):
Future[?TransactionReceipt] {.base, gcsafe.} =
method getTransactionReceipt*(
provider: Provider,
txHash: TransactionHash): Future[?TransactionReceipt] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method sendTransaction*(provider: Provider,
rawTransaction: seq[byte]):
Future[TransactionResponse] {.base, gcsafe.} =
method sendTransaction*(
provider: Provider,
rawTransaction: seq[byte]): Future[TransactionResponse] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getLogs*(provider: Provider,
filter: EventFilter): Future[seq[Log]] {.base, gcsafe.} =
method getLogs*(
provider: Provider,
filter: EventFilter): Future[seq[Log]] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method estimateGas*(provider: Provider,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256] {.base, gcsafe.} =
method estimateGas*(
provider: Provider,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method getChainId*(provider: Provider): Future[UInt256] {.base, gcsafe.} =
method getChainId*(
provider: Provider): Future[UInt256] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method subscribe*(provider: Provider,
filter: EventFilter,
callback: LogHandler):
Future[Subscription] {.base, gcsafe.} =
method subscribe*(
provider: Provider,
filter: EventFilter,
callback: LogHandler): Future[Subscription] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method subscribe*(provider: Provider,
callback: BlockHandler):
Future[Subscription] {.base, gcsafe.} =
method subscribe*(
provider: Provider,
callback: BlockHandler): Future[Subscription] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method unsubscribe*(subscription: Subscription) {.base, async.} =
method unsubscribe*(
subscription: Subscription) {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
proc replay*(provider: Provider, tx: Transaction, blockNumber: UInt256) {.async.} =
proc replay*(
provider: Provider,
tx: Transaction,
blockNumber: UInt256) {.async: (raises:[ProviderError]).} =
# Replay transaction at block. Useful for fetching revert reasons, which will
# be present in the raised error message. The replayed block number should
# include the state of the chain in the block previous to the block in which
@ -172,8 +198,7 @@ proc replay*(provider: Provider, tx: Transaction, blockNumber: UInt256) {.async.
method getRevertReason*(
provider: Provider,
hash: TransactionHash,
blockNumber: UInt256
): Future[?string] {.base, async.} =
blockNumber: UInt256): Future[?string] {.base, async: (raises: [ProviderError]).} =
without pastTx =? await provider.getTransaction(hash):
return none string
@ -187,8 +212,7 @@ method getRevertReason*(
method getRevertReason*(
provider: Provider,
receipt: TransactionReceipt
): Future[?string] {.base, async.} =
receipt: TransactionReceipt): Future[?string] {.base, async: (raises: [ProviderError]).} =
if receipt.status != TransactionStatus.Failure:
return none string
@ -200,8 +224,7 @@ method getRevertReason*(
proc ensureSuccess(
provider: Provider,
receipt: TransactionReceipt
) {.async, raises: [ProviderError].} =
receipt: TransactionReceipt) {.async: (raises: [ProviderError]).} =
## If the receipt.status is Failed, the tx is replayed to obtain a revert
## reason, after which a ProviderError with the revert reason is raised.
## If no revert reason was obtained
@ -220,11 +243,10 @@ proc ensureSuccess(
trace "transaction replay completed, no revert reason obtained"
raiseProviderError("Transaction reverted with unknown reason")
proc confirm*(tx: TransactionResponse,
confirmations = EthersDefaultConfirmations,
timeout = EthersReceiptTimeoutBlks):
Future[TransactionReceipt]
{.async, raises: [ProviderError, EthersError].} =
proc confirm*(
tx: TransactionResponse,
confirmations = EthersDefaultConfirmations,
timeout = EthersReceiptTimeoutBlks): Future[TransactionReceipt] {.async: (raises: [CancelledError, ProviderError, EthersError]).} =
## Waits for a transaction to be mined and for the specified number of blocks
## to pass since it was mined (confirmations).
## A timeout, in blocks, can be specified that will raise an error if too many
@ -266,10 +288,10 @@ proc confirm*(tx: TransactionResponse,
await tx.provider.ensureSuccess(receipt)
return receipt
proc confirm*(tx: Future[TransactionResponse],
confirmations: int = EthersDefaultConfirmations,
timeout: int = EthersReceiptTimeoutBlks):
Future[TransactionReceipt] {.async.} =
proc confirm*(
tx: Future[TransactionResponse],
confirmations: int = EthersDefaultConfirmations,
timeout: int = EthersReceiptTimeoutBlks): Future[TransactionReceipt] {.async.} =
## Convenience method that allows wait to be chained to a sendTransaction
## call, eg:
## `await signer.sendTransaction(populated).confirm(3)`
@ -277,5 +299,5 @@ proc confirm*(tx: Future[TransactionResponse],
let txResp = await tx
return await txResp.confirm(confirmations, timeout)
method close*(provider: Provider) {.async, base.} =
method close*(provider: Provider) {.base, async: (raises:[ProviderError]).} =
discard

View File

@ -49,9 +49,7 @@ template convertError(body) =
body
except JsonRpcError as error:
raiseJsonRpcProviderError(error.msg)
# 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:
except CatchableError as error:
raiseJsonRpcProviderError(error.msg)
# Provider
@ -62,9 +60,11 @@ const defaultPollingInterval = 4.seconds
proc jsonHeaders: seq[(string, string)] =
@[("Content-Type", "application/json")]
proc new*(_: type JsonRpcProvider,
url=defaultUrl,
pollingInterval=defaultPollingInterval): JsonRpcProvider =
proc new*(
_: type JsonRpcProvider,
url=defaultUrl,
pollingInterval=defaultPollingInterval): JsonRpcProvider {.raises: [JsonRpcProviderError].} =
var initialized: Future[void]
var client: RpcClient
var subscriptions: JsonRpcSubscriptions
@ -82,19 +82,21 @@ proc new*(_: type JsonRpcProvider,
client = http
subscriptions = JsonRpcSubscriptions.new(http,
pollingInterval = pollingInterval)
subscriptions.init()
proc awaitClient: Future[RpcClient] {.async.} =
proc awaitClient: Future[RpcClient] {.async:(raises:[JsonRpcProviderError]).} =
convertError:
await initialized
return client
proc awaitSubscriptions: Future[JsonRpcSubscriptions] {.async.} =
proc awaitSubscriptions: Future[JsonRpcSubscriptions] {.async:(raises:[JsonRpcProviderError]).} =
convertError:
await initialized
return subscriptions
initialized = initialize()
JsonRpcProvider(client: awaitClient(), subscriptions: awaitSubscriptions())
convertError:
initialized = initialize()
return JsonRpcProvider(client: awaitClient(), subscriptions: awaitSubscriptions())
proc callImpl(
client: RpcClient,
@ -116,7 +118,6 @@ proc send*(
arguments: seq[JsonNode] = @[]): Future[JsonNode] {.async.} =
convertError:
let client = await provider.client
return await client.call(call, %arguments)
return await client.callImpl(call, %arguments)
proc listAccounts*(provider: JsonRpcProvider): Future[seq[Address]] {.async.} =
@ -130,54 +131,66 @@ proc getSigner*(provider: JsonRpcProvider): JsonRpcSigner =
proc getSigner*(provider: JsonRpcProvider, address: Address): JsonRpcSigner =
JsonRpcSigner(provider: provider, address: some address)
method getBlockNumber*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
method getBlockNumber*(
provider: JsonRpcProvider): Future[UInt256] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_blockNumber()
method getBlock*(provider: JsonRpcProvider,
tag: BlockTag): Future[?Block] {.async.} =
method getBlock*(
provider: JsonRpcProvider,
tag: BlockTag): Future[?Block] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_getBlockByNumber(tag, false)
method call*(provider: JsonRpcProvider,
tx: Transaction,
blockTag = BlockTag.latest): Future[seq[byte]] {.async.} =
method call*(
provider: JsonRpcProvider,
tx: Transaction,
blockTag = BlockTag.latest): Future[seq[byte]] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_call(tx, blockTag)
method getGasPrice*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
method getGasPrice*(
provider: JsonRpcProvider): Future[UInt256] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_gasPrice()
method getTransactionCount*(provider: JsonRpcProvider,
address: Address,
blockTag = BlockTag.latest):
Future[UInt256] {.async.} =
method getTransactionCount*(
provider: JsonRpcProvider,
address: Address,
blockTag = BlockTag.latest): Future[UInt256] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_getTransactionCount(address, blockTag)
method getTransaction*(provider: JsonRpcProvider,
txHash: TransactionHash):
Future[?PastTransaction] {.async.} =
method getTransaction*(
provider: JsonRpcProvider,
txHash: TransactionHash): Future[?PastTransaction] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_getTransactionByHash(txHash)
method getTransactionReceipt*(provider: JsonRpcProvider,
txHash: TransactionHash):
Future[?TransactionReceipt] {.async.} =
method getTransactionReceipt*(
provider: JsonRpcProvider,
txHash: TransactionHash): Future[?TransactionReceipt] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_getTransactionReceipt(txHash)
method getLogs*(provider: JsonRpcProvider,
filter: EventFilter):
Future[seq[Log]] {.async.} =
method getLogs*(
provider: JsonRpcProvider,
filter: EventFilter): Future[seq[Log]] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
let logsJson = if filter of Filter:
@ -189,19 +202,23 @@ method getLogs*(provider: JsonRpcProvider,
var logs: seq[Log] = @[]
for logJson in logsJson.getElems:
if log =? Log.fromJson(logJson).catch:
if log =? Log.fromJson(logJson):
logs.add log
return logs
method estimateGas*(provider: JsonRpcProvider,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256] {.async.} =
method estimateGas*(
provider: JsonRpcProvider,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
return await client.eth_estimateGas(transaction, blockTag)
method getChainId*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
method getChainId*(
provider: JsonRpcProvider): Future[UInt256] {.async: (raises:[ProviderError]).} =
convertError:
let client = await provider.client
try:
@ -209,7 +226,10 @@ method getChainId*(provider: JsonRpcProvider): Future[UInt256] {.async.} =
except CatchableError:
return parse(await client.net_version(), UInt256)
method sendTransaction*(provider: JsonRpcProvider, rawTransaction: seq[byte]): Future[TransactionResponse] {.async.} =
method sendTransaction*(
provider: JsonRpcProvider,
rawTransaction: seq[byte]): Future[TransactionResponse] {.async: (raises:[ProviderError]).} =
convertError:
let
client = await provider.client
@ -217,33 +237,42 @@ method sendTransaction*(provider: JsonRpcProvider, rawTransaction: seq[byte]): F
return TransactionResponse(hash: hash, provider: provider)
method subscribe*(provider: JsonRpcProvider,
filter: EventFilter,
onLog: LogHandler):
Future[Subscription] {.async.} =
method subscribe*(
provider: JsonRpcProvider,
filter: EventFilter,
onLog: LogHandler): Future[Subscription] {.async: (raises:[ProviderError]).} =
convertError:
let subscriptions = await provider.subscriptions
let id = await subscriptions.subscribeLogs(filter, onLog)
return JsonRpcSubscription(subscriptions: subscriptions, id: id)
method subscribe*(provider: JsonRpcProvider,
onBlock: BlockHandler):
Future[Subscription] {.async.} =
method subscribe*(
provider: JsonRpcProvider,
onBlock: BlockHandler): Future[Subscription] {.async: (raises:[ProviderError]).} =
convertError:
let subscriptions = await provider.subscriptions
let id = await subscriptions.subscribeBlocks(onBlock)
return JsonRpcSubscription(subscriptions: subscriptions, id: id)
method unsubscribe(subscription: JsonRpcSubscription) {.async.} =
method unsubscribe*(
subscription: JsonRpcSubscription) {.async: (raises:[ProviderError]).} =
convertError:
let subscriptions = subscription.subscriptions
let id = subscription.id
echo "[jsonrpc.unsubscribe] subscription id: ", $id
await subscriptions.unsubscribe(id)
method close*(provider: JsonRpcProvider) {.async.} =
method close*(
provider: JsonRpcProvider) {.async: (raises:[ProviderError]).} =
convertError:
echo "[JsonRpcProvider.close]"
let client = await provider.client
let subscriptions = await provider.subscriptions
echo "[JsonRpcProvider.closing subscriptions]"
await subscriptions.close()
await client.close()
@ -252,25 +281,32 @@ method close*(provider: JsonRpcProvider) {.async.} =
method provider*(signer: JsonRpcSigner): Provider =
signer.provider
method getAddress*(signer: JsonRpcSigner): Future[Address] {.async.} =
method getAddress*(
signer: JsonRpcSigner): Future[Address] {.async: (raises:[ProviderError]).} =
if address =? signer.address:
return address
let accounts = await signer.provider.listAccounts()
if accounts.len > 0:
return accounts[0]
convertError:
let accounts = await signer.provider.listAccounts()
if accounts.len > 0:
return accounts[0]
raiseJsonRpcProviderError "no address found"
method signMessage*(signer: JsonRpcSigner,
message: seq[byte]): Future[seq[byte]] {.async.} =
method signMessage*(
signer: JsonRpcSigner,
message: seq[byte]): Future[seq[byte]] {.async: (raises:[JsonRpcProviderError]).} =
convertError:
let client = await signer.provider.client
let address = await signer.getAddress()
return await client.eth_sign(address, message)
method sendTransaction*(signer: JsonRpcSigner,
transaction: Transaction): Future[TransactionResponse] {.async.} =
method sendTransaction*(
signer: JsonRpcSigner,
transaction: Transaction): Future[TransactionResponse] {.async: (raises:[JsonRpcProviderError]).} =
convertError:
if nonce =? transaction.nonce:
signer.updateNonce(nonce)

View File

@ -2,5 +2,5 @@ template untilCancelled*(body) =
try:
while true:
body
except CancelledError:
raise
except CancelledError as e:
raise e

View File

@ -3,6 +3,8 @@ import ./provider
export basics
{.push raises: [].}
type
Signer* = ref object of RootObj
lastSeenNonce: ?UInt256
@ -19,17 +21,18 @@ template raiseSignerError(message: string, parent: ref ProviderError = nil) =
proc raiseEstimateGasError(
transaction: Transaction,
parent: ref ProviderError = nil
) =
) {.raises: [EstimateGasError] .} =
let e = (ref EstimateGasError)(
msg: "Estimate gas failed",
transaction: transaction,
parent: parent)
raise e
method provider*(signer: Signer): Provider {.base, gcsafe.} =
method provider*(signer: Signer):
Provider {.base, gcsafe, raises: [EthersError].} =
doAssert false, "not implemented"
method getAddress*(signer: Signer): Future[Address] {.base, gcsafe.} =
method getAddress*(signer: Signer): Future[Address] {.base, async: (raises:[ProviderError]).} =
doAssert false, "not implemented"
method signMessage*(signer: Signer,
@ -40,7 +43,8 @@ method sendTransaction*(signer: Signer,
transaction: Transaction): Future[TransactionResponse] {.base, async.} =
doAssert false, "not implemented"
method getGasPrice*(signer: Signer): Future[UInt256] {.base, gcsafe.} =
method getGasPrice*(signer: Signer):
Future[UInt256] {.base, gcsafe, raises: [EthersError].} =
signer.provider.getGasPrice()
method getTransactionCount*(signer: Signer,
@ -59,7 +63,8 @@ method estimateGas*(signer: Signer,
except ProviderError as e:
raiseEstimateGasError transaction, e
method getChainId*(signer: Signer): Future[UInt256] {.base, gcsafe.} =
method getChainId*(signer: Signer):
Future[UInt256] {.base, gcsafe, raises: [EthersError].} =
signer.provider.getChainId()
method getNonce(signer: Signer): Future[UInt256] {.base, gcsafe, async.} =
@ -91,6 +96,7 @@ method populateTransaction*(signer: Signer,
transaction: Transaction):
Future[Transaction] {.base, async.} =
echo "[signer.populatetransaction] signer type: ", typeof signer
if sender =? transaction.sender and sender != await signer.getAddress():
raiseSignerError("from address mismatch")
if chainId =? transaction.chainId and chainId != await signer.getChainId():
@ -119,8 +125,10 @@ method populateTransaction*(signer: Signer,
populated.nonce = some(await signer.getNonce())
try:
populated.gasLimit = some(await signer.estimateGas(populated))
except ProviderError, EstimateGasError:
let e = getCurrentException()
except ProviderError as e:
signer.decreaseNonce()
raise e
except EstimateGasError as e:
signer.decreaseNonce()
raise e

View File

@ -54,7 +54,7 @@ proc createRandom*(_: type Wallet, provider: Provider): Wallet =
result.address = Address.init(result.publicKey.toCanonicalAddress())
result.provider = some provider
method provider*(wallet: Wallet): Provider =
method provider*(wallet: Wallet): Provider {.raises: [WalletError].} =
without provider =? wallet.provider:
raiseWalletError "Wallet has no provider"
provider

View File

@ -3,5 +3,5 @@ import ../basics
type
WalletError* = object of EthersError
func raiseWalletError*(message: string) =
func raiseWalletError*(message: string) {.raises: [WalletError].}=
raise newException(WalletError, message)