Skip to content

Commit d88e461

Browse files
AuHauemizzle
andauthored
feat: subscriptions get passed result questionable (#91)
Co-authored-by: Eric <[email protected]>
1 parent 04c00e2 commit d88e461

File tree

12 files changed

+194
-79
lines changed

12 files changed

+194
-79
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
name: CI
22

3-
on: [push, pull_request, workflow_dispatch]
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
49

510
jobs:
611
test:
@@ -12,6 +17,8 @@ jobs:
1217
steps:
1318
- name: Checkout
1419
uses: actions/checkout@v4
20+
with:
21+
ref: ${{ github.event.pull_request.head.sha }}
1522

1623
- name: Install Nim
1724
uses: iffy/install-nim@v4

Readme.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,22 @@ You can now subscribe to Transfer events by calling `subscribe` on the contract
131131
instance.
132132

133133
```nim
134-
proc handleTransfer(transfer: Transfer) =
135-
echo "received transfer: ", transfer
134+
proc handleTransfer(transferResult: ?!Transfer) =
135+
if transferResult.isOk:
136+
echo "received transfer: ", transferResult.value
137+
else:
138+
echo "error during transfer: ", transferResult.error.msg
136139
137140
let subscription = await token.subscribe(Transfer, handleTransfer)
138141
```
139142

140143
When a Transfer event is emitted, the `handleTransfer` proc that you just
141-
defined will be called.
144+
defined will be called with a [Result](https://github.com/arnetheduck/nim-results) type
145+
which contains the event value.
146+
147+
In case there is some underlying error in the event subscription, the handler will
148+
be called as well, but the Result will contain error instead, so do proper error
149+
management in your handlers.
142150

143151
When you're no longer interested in these events, you can unsubscribe:
144152

ethers/contract.nim

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import std/macros
33
import std/sequtils
44
import pkg/chronicles
55
import pkg/chronos
6+
import pkg/questionable
67
import pkg/contractabi
78
import ./basics
89
import ./provider
@@ -35,11 +36,10 @@ type
3536
gasLimit*: ?UInt256
3637
CallOverrides* = ref object of TransactionOverrides
3738
blockTag*: ?BlockTag
38-
ContractError* = object of EthersError
3939
Confirmable* = object
4040
response*: ?TransactionResponse
4141
convert*: ConvertCustomErrors
42-
EventHandler*[E: Event] = proc(event: E) {.gcsafe, raises:[].}
42+
EventHandler*[E: Event] = proc(event: ?!E) {.gcsafe, raises:[].}
4343

4444
func new*(ContractType: type Contract,
4545
address: Address,
@@ -292,9 +292,13 @@ proc subscribe*[E: Event](contract: Contract,
292292
let topic = topic($E, E.fieldTypes).toArray
293293
let filter = EventFilter(address: contract.address, topics: @[topic])
294294
295-
proc logHandler(log: Log) {.raises: [].} =
295+
proc logHandler(logResult: ?!Log) {.raises: [].} =
296+
without log =? logResult, error:
297+
handler(failure(E, error))
298+
return
299+
296300
if event =? E.decode(log.data, log.topics):
297-
handler(event)
301+
handler(success(event))
298302
299303
contract.provider.subscribe(filter, logHandler)
300304

ethers/errors.nim

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
import ./basics
22

3-
type SolidityError* = object of EthersError
3+
type
4+
SolidityError* = object of EthersError
5+
ContractError* = object of EthersError
6+
SignerError* = object of EthersError
7+
SubscriptionError* = object of EthersError
8+
ProviderError* = object of EthersError
9+
data*: ?seq[byte]
410

511
{.push raises:[].}
612

13+
proc toErr*[E1: ref CatchableError, E2: EthersError](
14+
e1: E1,
15+
_: type E2,
16+
msg: string = e1.msg): ref E2 =
17+
18+
return newException(E2, msg, e1)
19+
720
template errors*(types) {.pragma.}

ethers/provider.nim

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pkg/chronicles
22
import pkg/serde
3+
import pkg/questionable
34
import ./basics
45
import ./transaction
56
import ./blocktag
@@ -8,13 +9,12 @@ import ./errors
89
export basics
910
export transaction
1011
export blocktag
12+
export errors
1113

1214
{.push raises: [].}
1315

1416
type
1517
Provider* = ref object of RootObj
16-
ProviderError* = object of EthersError
17-
data*: ?seq[byte]
1818
EstimateGasError* = object of ProviderError
1919
transaction*: Transaction
2020
Subscription* = ref object of RootObj
@@ -56,8 +56,8 @@ type
5656
effectiveGasPrice*: ?UInt256
5757
status*: TransactionStatus
5858
transactionType* {.serialize("type"), deserialize("type").}: TransactionType
59-
LogHandler* = proc(log: Log) {.gcsafe, raises:[].}
60-
BlockHandler* = proc(blck: Block) {.gcsafe, raises:[].}
59+
LogHandler* = proc(log: ?!Log) {.gcsafe, raises:[].}
60+
BlockHandler* = proc(blck: ?!Block) {.gcsafe, raises:[].}
6161
Topic* = array[32, byte]
6262
Block* {.serialize.} = object
6363
number*: ?UInt256
@@ -227,7 +227,7 @@ proc confirm*(
227227
tx: TransactionResponse,
228228
confirmations = EthersDefaultConfirmations,
229229
timeout = EthersReceiptTimeoutBlks): Future[TransactionReceipt]
230-
{.async: (raises: [CancelledError, ProviderError, EthersError]).} =
230+
{.async: (raises: [CancelledError, ProviderError, SubscriptionError, EthersError]).} =
231231

232232
## Waits for a transaction to be mined and for the specified number of blocks
233233
## to pass since it was mined (confirmations). The number of confirmations
@@ -238,6 +238,12 @@ proc confirm*(
238238
assert confirmations > 0
239239

240240
var blockNumber: UInt256
241+
242+
## We need initialized succesfull Result, because the first iteration of the `while` loop
243+
## bellow is triggered "manually" by calling `await updateBlockNumber` and not by block
244+
## subscription. If left uninitialized then the Result is in error state and error is raised.
245+
## This result is not used for block value, but for block subscription errors.
246+
var blockSubscriptionResult: ?!Block = success(Block(number: UInt256.none, timestamp: 0.u256, hash: BlockHash.none))
241247
let blockEvent = newAsyncEvent()
242248

243249
proc updateBlockNumber {.async: (raises: []).} =
@@ -250,7 +256,13 @@ proc confirm*(
250256
# there's nothing we can do here
251257
discard
252258

253-
proc onBlock(_: Block) =
259+
proc onBlock(blckResult: ?!Block) =
260+
blockSubscriptionResult = blckResult
261+
262+
if blckResult.isErr:
263+
blockEvent.fire()
264+
return
265+
254266
# ignore block parameter; hardhat may call this with pending blocks
255267
asyncSpawn updateBlockNumber()
256268

@@ -264,6 +276,16 @@ proc confirm*(
264276
await blockEvent.wait()
265277
blockEvent.clear()
266278

279+
if blockSubscriptionResult.isErr:
280+
let error = blockSubscriptionResult.error()
281+
282+
if error of SubscriptionError:
283+
raise (ref SubscriptionError)(error)
284+
elif error of CancelledError:
285+
raise (ref CancelledError)(error)
286+
else:
287+
raise error.toErr(ProviderError)
288+
267289
if blockNumber >= finish:
268290
await subscription.unsubscribe()
269291
raise newException(EthersError, "tx not mined before timeout")

ethers/providers/jsonrpc/errors.nim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import std/strutils
22
import pkg/stew/byteutils
33
import ../../basics
4+
import ../../errors
45
import ../../provider
56
import ./conversions
67

8+
export errors
9+
710
{.push raises:[].}
811

912
type JsonRpcProviderError* = object of ProviderError

0 commit comments

Comments
 (0)