Skip to content

Commit

Permalink
Refactor exceptions for providers and signers, fix more tests
Browse files Browse the repository at this point in the history
- signer procs raise SignerError, provider procs raise ProviderError
- WalletError now inherits from SignerError
- move wallet module under signers
- create jsonrpo moudle under signers
- bump nim-json-rpc for null-handling fixes
- All jsonrpc provider tests passing, still need to fix others
  • Loading branch information
emizzle committed Jan 29, 2024
1 parent c261e96 commit 59a7939
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 169 deletions.
2 changes: 1 addition & 1 deletion ethers.nimble
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ requires "chronicles >= 0.10.3 & < 0.11.0"
requires "chronos#f0a2d4df61302d24baa6c0f1c257f92045c9ee57"
requires "contractabi >= 0.6.0 & < 0.7.0"
requires "questionable >= 0.10.2 & < 0.11.0"
requires "json_rpc"
requires "json_rpc >= 0.3.0 & < 0.4.0"
requires "stint"
requires "stew"
requires "eth"
Expand Down
13 changes: 8 additions & 5 deletions ethers/provider.nim
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ template raiseProviderError(msg: string) =
func toTransaction*(past: PastTransaction): Transaction =
Transaction(
`from`: some past.`from`,
gasPrice: some past.gasPrice,
to: past.to,
data: past.input,
value: past.value,
nonce: some past.nonce,
to: past.to,
`type`: past.`type`,
chainId: past.chainId,
gasPrice: some past.gasPrice,
gasLimit: some past.gas,
chainId: past.chainId
`type`: past.`type`
)

method getBlockNumber*(
Expand Down Expand Up @@ -246,7 +247,9 @@ proc ensureSuccess(
proc confirm*(
tx: TransactionResponse,
confirmations = EthersDefaultConfirmations,
timeout = EthersReceiptTimeoutBlks): Future[TransactionReceipt] {.async: (raises: [CancelledError, ProviderError, EthersError]).} =
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
Expand Down
60 changes: 44 additions & 16 deletions ethers/providers/jsonrpc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ type
JsonRpcProvider* = ref object of Provider
client: Future[RpcClient]
subscriptions: Future[JsonRpcSubscriptions]
JsonRpcSigner* = ref object of Signer
provider: JsonRpcProvider
address: ?Address

JsonRpcProviderError* = object of ProviderError
JsonRpcSubscription* = ref object of Subscription
subscriptions: JsonRpcSubscriptions
id: JsonNode

# Signer
JsonRpcSigner* = ref object of Signer
provider: JsonRpcProvider
address: ?Address
JsonRpcSignerError* = object of SignerError

proc raiseJsonRpcProviderError(
message: string) {.raises: [JsonRpcProviderError].} =

Expand All @@ -50,7 +54,7 @@ template convertError(body) =
except JsonRpcError as error:
raiseJsonRpcProviderError(error.msg)
except CatchableError as error:
raiseJsonRpcProviderError(error.msg)
raise newException(JsonRpcProviderError, error.msg)

# Provider

Expand Down Expand Up @@ -104,23 +108,27 @@ proc callImpl(
args: JsonNode): Future[JsonNode] {.async: (raises: [JsonRpcProviderError]).} =

without response =? (await client.call(call, %args)).catch, error:
echo "[jsonrpc.callImpl] error during call: ", error.msg
raiseJsonRpcProviderError error.msg
echo "[jsonrpc.callImpl] response: ", response.string
without json =? JsonNode.fromJson(response.string), error:
echo "[jsonrpc.callImpl] error after parsing json: ", error.msg
raiseJsonRpcProviderError "Failed to parse response: " & error.msg
json


proc send*(
provider: JsonRpcProvider,
call: string,
arguments: seq[JsonNode] = @[]): Future[JsonNode] {.async.} =
arguments: seq[JsonNode] = @[]): Future[JsonNode]
{.async: (raises: [JsonRpcProviderError]).} =

convertError:
let client = await provider.client
return await client.callImpl(call, %arguments)

proc listAccounts*(provider: JsonRpcProvider): Future[seq[Address]] {.async.} =
proc listAccounts*(provider: JsonRpcProvider): Future[seq[Address]]
{.async: (raises: [JsonRpcProviderError]).} =

convertError:
let client = await provider.client
return await client.eth_accounts()
Expand Down Expand Up @@ -228,7 +236,8 @@ method getChainId*(

method sendTransaction*(
provider: JsonRpcProvider,
rawTransaction: seq[byte]): Future[TransactionResponse] {.async: (raises:[ProviderError]).} =
rawTransaction: seq[byte]): Future[TransactionResponse]
{.async: (raises:[ProviderError]).} =

convertError:
let
Expand Down Expand Up @@ -278,36 +287,55 @@ method close*(

# Signer

method provider*(signer: JsonRpcSigner): Provider =
proc raiseJsonRpcSignerError(
message: string) {.raises: [JsonRpcSignerError].} =

var message = message
if json =? JsonNode.fromJson(message):
if "message" in json:
message = json{"message"}.getStr
raise newException(JsonRpcSignerError, message)

template convertSignerError(body) =
try:
body
except JsonRpcError as error:
raiseJsonRpcSignerError(error.msg)
except CatchableError as error:
raise newException(JsonRpcSignerError, error.msg)

method provider*(signer: JsonRpcSigner): Provider {.gcsafe.} =
signer.provider

method getAddress*(
signer: JsonRpcSigner): Future[Address] {.async: (raises:[ProviderError]).} =
signer: JsonRpcSigner): Future[Address] {.async: (raises:[SignerError]).} =

if address =? signer.address:
return address

convertError:
convertSignerError:
let accounts = await signer.provider.listAccounts()
if accounts.len > 0:
return accounts[0]

raiseJsonRpcProviderError "no address found"
raiseJsonRpcSignerError "no address found"

method signMessage*(
signer: JsonRpcSigner,
message: seq[byte]): Future[seq[byte]] {.async: (raises:[JsonRpcProviderError]).} =
message: seq[byte]): Future[seq[byte]] {.async: (raises:[SignerError]).} =

convertError:
convertSignerError:
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: (raises:[JsonRpcProviderError]).} =
transaction: Transaction): Future[TransactionResponse]
{.async: (raises:[SignerError]).} =

convertError:
convertSignerError:
echo "[jsonrpc.sendTransaction]"
if nonce =? transaction.nonce:
signer.updateNonce(nonce)
let
Expand Down
22 changes: 22 additions & 0 deletions ethers/providers/jsonrpc/conversions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ func fromJson*(_: type UInt256, json: JsonNode): ?!UInt256 =

# 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.`from`:
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

# proc writeValue*(
# writer: var JsonWriter[JrpcConv],
# value: Transaction
Expand Down Expand Up @@ -172,6 +191,9 @@ func fromJson*(_: type BlockTag, json: JsonNode): ?!BlockTag =

# TransactionStatus | TransactionType

func `%`*(e: TransactionStatus | TransactionType): JsonNode =
% ("0x" & e.int8.toHex(1))

proc fromJson*[E: TransactionStatus | TransactionType](
T: type E,
json: JsonNode
Expand Down
91 changes: 61 additions & 30 deletions ethers/signer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ type
Signer* = ref object of RootObj
lastSeenNonce: ?UInt256
populateLock: AsyncLock

type
SignerError* = object of EthersError
SignerError* = object of ProviderError
EstimateGasError* = object of SignerError
transaction*: Transaction

Expand All @@ -28,46 +26,78 @@ proc raiseEstimateGasError(
parent: parent)
raise e

method provider*(signer: Signer):
Provider {.base, gcsafe, raises: [EthersError].} =
template convertError(body) =
try:
body
except EthersError as error:
raiseSignerError(error.msg)
except CatchableError as error:
raiseSignerError(error.msg)

method provider*(
signer: Signer): Provider {.base, gcsafe, raises: [SignerError].} =
doAssert false, "not implemented"

method getAddress*(signer: Signer): Future[Address] {.base, async: (raises:[ProviderError]).} =
method getAddress*(
signer: Signer): Future[Address] {.base, async: (raises:[SignerError]).} =

doAssert false, "not implemented"

method signMessage*(signer: Signer,
message: seq[byte]): Future[seq[byte]] {.base, async.} =
method signMessage*(
signer: Signer,
message: seq[byte]): Future[seq[byte]] {.base, async: (raises:[SignerError]).} =

doAssert false, "not implemented"

method sendTransaction*(signer: Signer,
transaction: Transaction): Future[TransactionResponse] {.base, async.} =
method sendTransaction*(
signer: Signer,
transaction: Transaction): Future[TransactionResponse]
{.base, async: (raises:[SignerError]).} =

doAssert false, "not implemented"

method getGasPrice*(signer: Signer):
Future[UInt256] {.base, gcsafe, raises: [EthersError].} =
signer.provider.getGasPrice()
method getGasPrice*(
signer: Signer): Future[UInt256] {.base, gcsafe, async: (raises: [SignerError]).} =

convertError:
return await signer.provider.getGasPrice()

method getTransactionCount*(
signer: Signer,
blockTag = BlockTag.latest): Future[UInt256]
{.base, async: (raises:[SignerError]).} =

method getTransactionCount*(signer: Signer,
blockTag = BlockTag.latest):
Future[UInt256] {.base, async.} =
let address = await signer.getAddress()
return await signer.provider.getTransactionCount(address, blockTag)
convertError:
let address = await signer.getAddress()
return await signer.provider.getTransactionCount(address, blockTag)

method estimateGas*(
signer: Signer,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256]
{.base, async: (raises:[SignerError]).} =

method estimateGas*(signer: Signer,
transaction: Transaction,
blockTag = BlockTag.latest): Future[UInt256] {.base, async.} =
var transaction = transaction
transaction.`from` = some(await signer.getAddress)
var address: Address

convertError:
address = await signer.getAddress

transaction.`from` = some(address)
try:
return await signer.provider.estimateGas(transaction)
except ProviderError as e:
raiseEstimateGasError transaction, e

method getChainId*(signer: Signer):
Future[UInt256] {.base, gcsafe, raises: [EthersError].} =
signer.provider.getChainId()
method getChainId*(
signer: Signer): Future[UInt256] {.base, async: (raises: [SignerError]).} =

convertError:
return await signer.provider.getChainId()

method getNonce(
signer: Signer): Future[UInt256] {.base, async: (raises: [SignerError]).} =

method getNonce(signer: Signer): Future[UInt256] {.base, gcsafe, async.} =
var nonce = await signer.getTransactionCount(BlockTag.pending)

if lastSeen =? signer.lastSeenNonce and lastSeen >= nonce:
Expand All @@ -92,9 +122,10 @@ method decreaseNonce*(signer: Signer) {.base, gcsafe.} =
if lastSeen =? signer.lastSeenNonce and lastSeen > 0:
signer.lastSeenNonce = some lastSeen - 1

method populateTransaction*(signer: Signer,
transaction: Transaction):
Future[Transaction] {.base, async.} =
method populateTransaction*(
signer: Signer,
transaction: Transaction): Future[Transaction]
{.base, async: (raises: [CancelledError, AsyncLockError, SignerError]).} =

echo "[signer.populatetransaction] signer type: ", typeof signer
if sender =? transaction.`from` and sender != await signer.getAddress():
Expand Down Expand Up @@ -127,7 +158,7 @@ method populateTransaction*(signer: Signer,
populated.gasLimit = some(await signer.estimateGas(populated))
except ProviderError as e:
signer.decreaseNonce()
raise e
raiseSignerError(e.msg)
except EstimateGasError as e:
signer.decreaseNonce()
raise e
Expand Down
3 changes: 3 additions & 0 deletions ethers/signers/jsonrpc.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ../providers/jsonrpc

export provider, getAddress, signMessage, sendTransaction
Loading

0 comments on commit 59a7939

Please sign in to comment.