mirror of
https://github.com/logos-storage/nim-ethers.git
synced 2026-01-14 11:33:09 +00:00
Refactor exceptions for providers and signers, fix more tests
- 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
This commit is contained in:
parent
c261e96274
commit
59a7939e8f
@ -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"
|
||||
|
||||
@ -88,13 +88,14 @@ template raiseProviderError(msg: string) =
|
||||
func toTransaction*(past: PastTransaction): Transaction =
|
||||
Transaction(
|
||||
`from`: some past.`from`,
|
||||
gasPrice: some past.gasPrice,
|
||||
data: past.input,
|
||||
nonce: some past.nonce,
|
||||
to: past.to,
|
||||
`type`: past.`type`,
|
||||
data: past.input,
|
||||
value: past.value,
|
||||
nonce: some past.nonce,
|
||||
chainId: past.chainId,
|
||||
gasPrice: some past.gasPrice,
|
||||
gasLimit: some past.gas,
|
||||
chainId: past.chainId
|
||||
`type`: past.`type`
|
||||
)
|
||||
|
||||
method getBlockNumber*(
|
||||
@ -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
|
||||
|
||||
@ -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].} =
|
||||
|
||||
@ -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
|
||||
|
||||
@ -104,6 +108,7 @@ 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:
|
||||
@ -111,16 +116,19 @@ proc callImpl(
|
||||
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()
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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]).} =
|
||||
|
||||
method getTransactionCount*(signer: Signer,
|
||||
blockTag = BlockTag.latest):
|
||||
Future[UInt256] {.base, async.} =
|
||||
let address = await signer.getAddress()
|
||||
return await signer.provider.getTransactionCount(address, blockTag)
|
||||
convertError:
|
||||
return await signer.provider.getGasPrice()
|
||||
|
||||
method getTransactionCount*(
|
||||
signer: Signer,
|
||||
blockTag = BlockTag.latest): Future[UInt256]
|
||||
{.base, async: (raises:[SignerError]).} =
|
||||
|
||||
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:
|
||||
@ -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():
|
||||
@ -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
|
||||
|
||||
3
ethers/signers/jsonrpc.nim
Normal file
3
ethers/signers/jsonrpc.nim
Normal file
@ -0,0 +1,3 @@
|
||||
import ../providers/jsonrpc
|
||||
|
||||
export provider, getAddress, signMessage, sendTransaction
|
||||
79
ethers/signers/wallet.nim
Normal file
79
ethers/signers/wallet.nim
Normal file
@ -0,0 +1,79 @@
|
||||
import eth/keys
|
||||
import ../basics
|
||||
import ../provider
|
||||
import ../transaction
|
||||
import ../signer
|
||||
import ./wallet/error
|
||||
import ./wallet/signing
|
||||
|
||||
export keys
|
||||
export WalletError
|
||||
export signing
|
||||
|
||||
var rng {.threadvar.}: ref HmacDrbgContext
|
||||
|
||||
proc getRng: ref HmacDrbgContext =
|
||||
if rng.isNil:
|
||||
rng = newRng()
|
||||
rng
|
||||
|
||||
type Wallet* = ref object of Signer
|
||||
privateKey*: PrivateKey
|
||||
publicKey*: PublicKey
|
||||
address*: Address
|
||||
provider*: ?Provider
|
||||
|
||||
proc new*(_: type Wallet, privateKey: PrivateKey): Wallet =
|
||||
let publicKey = privateKey.toPublicKey()
|
||||
let address = Address.init(publicKey.toCanonicalAddress())
|
||||
Wallet(privateKey: privateKey, publicKey: publicKey, address: address)
|
||||
proc new*(_: type Wallet, privateKey: PrivateKey, provider: Provider): Wallet =
|
||||
let wallet = Wallet.new(privateKey)
|
||||
wallet.provider = some provider
|
||||
wallet
|
||||
proc new*(_: type Wallet, privateKey: string): ?!Wallet =
|
||||
let keyResult = PrivateKey.fromHex(privateKey)
|
||||
if keyResult.isErr:
|
||||
return failure newException(WalletError, "invalid key: " & $keyResult.error)
|
||||
success Wallet.new(keyResult.get())
|
||||
proc new*(_: type Wallet, privateKey: string, provider: Provider): ?!Wallet =
|
||||
let wallet = ? Wallet.new(privateKey)
|
||||
wallet.provider = some provider
|
||||
success wallet
|
||||
proc connect*(wallet: Wallet, provider: Provider) =
|
||||
wallet.provider = some provider
|
||||
proc createRandom*(_: type Wallet): Wallet =
|
||||
result = Wallet()
|
||||
result.privateKey = PrivateKey.random(getRng()[])
|
||||
result.publicKey = result.privateKey.toPublicKey()
|
||||
result.address = Address.init(result.publicKey.toCanonicalAddress())
|
||||
proc createRandom*(_: type Wallet, provider: Provider): Wallet =
|
||||
result = Wallet()
|
||||
result.privateKey = PrivateKey.random(getRng()[])
|
||||
result.publicKey = result.privateKey.toPublicKey()
|
||||
result.address = Address.init(result.publicKey.toCanonicalAddress())
|
||||
result.provider = some provider
|
||||
|
||||
method provider*(wallet: Wallet): Provider {.raises: [SignerError].} =
|
||||
without provider =? wallet.provider:
|
||||
raiseWalletError "Wallet has no provider"
|
||||
provider
|
||||
|
||||
method getAddress(wallet: Wallet): Future[Address] {.async: (raises:[ProviderError]).} =
|
||||
return wallet.address
|
||||
|
||||
proc signTransaction*(wallet: Wallet,
|
||||
transaction: Transaction): Future[seq[byte]] {.async: (raises:[WalletError]).} =
|
||||
if sender =? transaction.`from` and sender != wallet.address:
|
||||
raiseWalletError "from address mismatch"
|
||||
|
||||
return wallet.privateKey.sign(transaction)
|
||||
|
||||
method sendTransaction*(
|
||||
wallet: Wallet,
|
||||
transaction: Transaction): Future[TransactionResponse] {.async: (raises:[ProviderError]).} =
|
||||
|
||||
let signed = await signTransaction(wallet, transaction)
|
||||
if nonce =? transaction.nonce:
|
||||
wallet.updateNonce(nonce)
|
||||
return await provider(wallet).sendTransaction(signed)
|
||||
14
ethers/signers/wallet/error.nim
Normal file
14
ethers/signers/wallet/error.nim
Normal file
@ -0,0 +1,14 @@
|
||||
import ../../basics
|
||||
import ../../signer
|
||||
|
||||
type
|
||||
WalletError* = object of SignerError
|
||||
|
||||
func raiseWalletError*(message: string) {.raises: [WalletError].}=
|
||||
raise newException(WalletError, message)
|
||||
|
||||
template convertError(body) =
|
||||
try:
|
||||
body
|
||||
except CatchableError as error:
|
||||
raiseWalletError(error.msg)
|
||||
@ -2,9 +2,9 @@ import pkg/eth/keys
|
||||
import pkg/eth/rlp
|
||||
import pkg/eth/common/transaction as eth
|
||||
import pkg/eth/common/eth_hash
|
||||
import ../basics
|
||||
import ../transaction as ethers
|
||||
import ../provider
|
||||
import ../../basics
|
||||
import ../../transaction as ethers
|
||||
import ../../provider
|
||||
import ./error
|
||||
|
||||
type
|
||||
@ -1,76 +1,3 @@
|
||||
import eth/keys
|
||||
import ./basics
|
||||
import ./provider
|
||||
import ./transaction
|
||||
import ./signer
|
||||
import ./wallet/error
|
||||
import ./wallet/signing
|
||||
import ./signers/wallet
|
||||
|
||||
export keys
|
||||
export WalletError
|
||||
export signing
|
||||
|
||||
var rng {.threadvar.}: ref HmacDrbgContext
|
||||
|
||||
proc getRng: ref HmacDrbgContext =
|
||||
if rng.isNil:
|
||||
rng = newRng()
|
||||
rng
|
||||
|
||||
type Wallet* = ref object of Signer
|
||||
privateKey*: PrivateKey
|
||||
publicKey*: PublicKey
|
||||
address*: Address
|
||||
provider*: ?Provider
|
||||
|
||||
proc new*(_: type Wallet, privateKey: PrivateKey): Wallet =
|
||||
let publicKey = privateKey.toPublicKey()
|
||||
let address = Address.init(publicKey.toCanonicalAddress())
|
||||
Wallet(privateKey: privateKey, publicKey: publicKey, address: address)
|
||||
proc new*(_: type Wallet, privateKey: PrivateKey, provider: Provider): Wallet =
|
||||
let wallet = Wallet.new(privateKey)
|
||||
wallet.provider = some provider
|
||||
wallet
|
||||
proc new*(_: type Wallet, privateKey: string): ?!Wallet =
|
||||
let keyResult = PrivateKey.fromHex(privateKey)
|
||||
if keyResult.isErr:
|
||||
return failure newException(WalletError, "invalid key: " & $keyResult.error)
|
||||
success Wallet.new(keyResult.get())
|
||||
proc new*(_: type Wallet, privateKey: string, provider: Provider): ?!Wallet =
|
||||
let wallet = ? Wallet.new(privateKey)
|
||||
wallet.provider = some provider
|
||||
success wallet
|
||||
proc connect*(wallet: Wallet, provider: Provider) =
|
||||
wallet.provider = some provider
|
||||
proc createRandom*(_: type Wallet): Wallet =
|
||||
result = Wallet()
|
||||
result.privateKey = PrivateKey.random(getRng()[])
|
||||
result.publicKey = result.privateKey.toPublicKey()
|
||||
result.address = Address.init(result.publicKey.toCanonicalAddress())
|
||||
proc createRandom*(_: type Wallet, provider: Provider): Wallet =
|
||||
result = Wallet()
|
||||
result.privateKey = PrivateKey.random(getRng()[])
|
||||
result.publicKey = result.privateKey.toPublicKey()
|
||||
result.address = Address.init(result.publicKey.toCanonicalAddress())
|
||||
result.provider = some provider
|
||||
|
||||
method provider*(wallet: Wallet): Provider {.raises: [WalletError].} =
|
||||
without provider =? wallet.provider:
|
||||
raiseWalletError "Wallet has no provider"
|
||||
provider
|
||||
|
||||
method getAddress(wallet: Wallet): Future[Address] {.async.} =
|
||||
return wallet.address
|
||||
|
||||
proc signTransaction*(wallet: Wallet,
|
||||
transaction: Transaction): Future[seq[byte]] {.async.} =
|
||||
if sender =? transaction.`from` and sender != wallet.address:
|
||||
raiseWalletError "from address mismatch"
|
||||
|
||||
return wallet.privateKey.sign(transaction)
|
||||
|
||||
method sendTransaction*(wallet: Wallet, transaction: Transaction): Future[TransactionResponse] {.async.} =
|
||||
let signed = await signTransaction(wallet, transaction)
|
||||
if nonce =? transaction.nonce:
|
||||
wallet.updateNonce(nonce)
|
||||
return await provider(wallet).sendTransaction(signed)
|
||||
export wallet
|
||||
@ -1,7 +0,0 @@
|
||||
import ../basics
|
||||
|
||||
type
|
||||
WalletError* = object of EthersError
|
||||
|
||||
func raiseWalletError*(message: string) {.raises: [WalletError].}=
|
||||
raise newException(WalletError, message)
|
||||
@ -2,6 +2,8 @@ import std/strutils
|
||||
import std/unittest
|
||||
import pkg/ethers/provider
|
||||
import pkg/ethers/providers/jsonrpc/conversions
|
||||
import pkg/questionable
|
||||
import pkg/questionable/results
|
||||
import pkg/stew/byteutils
|
||||
|
||||
func flatten(s: string): string =
|
||||
@ -111,30 +113,6 @@ suite "JSON Conversions":
|
||||
fail
|
||||
check receipt.blockHash.isNone
|
||||
|
||||
test "newHeads subcription raises exception when deserializing to Log":
|
||||
let json = """{
|
||||
"parentHash":"0xd68d4d0f29307df51e1284fc8a13595ae700ef0f1128830a69e6854381363d42",
|
||||
"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
"miner":"0x0000000000000000000000000000000000000000",
|
||||
"stateRoot":"0x1f6f2d05de35bbfd50213be96ddf960d62b978b472c55d6ac223cd648cbbbbb0",
|
||||
"transactionsRoot":"0xb9bb8a26abe091bb628ab2b6585c5af151aeb3984f4ba47a3c65d438283e069d",
|
||||
"receiptsRoot":"0x33f229b7133e1ba3fb524b8af22d8184ca10b2da5bb170092a219c61ca023c1d",
|
||||
"logsBloom":"0x00000000000000000000000000000000000000000020000000000002000000000000000000000000000000000000000000000000000008080000100200200000000000000000000000000008000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000010000040000000000100000000000800000000000000000000000000000000020000000000020000000000000000000000000000040000008000000000000000000020000000000002000000000000000000000000000000000000000000000000000001000010000000000000000020002000000020000000000000008002000000000000",
|
||||
"difficulty":"0x2",
|
||||
"number":"0x21d",
|
||||
"gasLimit":"0x1c1b59a7",
|
||||
"gasUsed":"0xda41b",
|
||||
"timestamp":"0x6509410e",
|
||||
"extraData":"0xd883010b05846765746888676f312e32302e32856c696e7578000000000000007102a27d75709b90ca9eb23cdaaccf4fc2d571d710f3bc5a7dc874f43af116a93ff832576a53c16f0d0aa1cd9e9a1dc0a60126c4d420f72b0866fc96ba6664f601",
|
||||
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce":"0x0000000000000000",
|
||||
"baseFeePerGas":"0x7",
|
||||
"withdrawalsRoot":null,
|
||||
"hash":"0x64066c7150c660e5357c4b6b02d836c10353dfa8edb32c805fca9367fd29c6e7"
|
||||
}"""
|
||||
expect ValueError:
|
||||
discard Log.fromJson(parseJson(json))
|
||||
|
||||
test "correctly deserializes PastTransaction":
|
||||
let json = %*{
|
||||
"blockHash":"0x595bffbe897e025ea2df3213c4cc52c3f3d69bc04b49011d558f1b0e70038922",
|
||||
@ -159,7 +137,7 @@ suite "JSON Conversions":
|
||||
fail
|
||||
check tx.blockHash == BlockHash(array[32, byte].fromHex("0x595bffbe897e025ea2df3213c4cc52c3f3d69bc04b49011d558f1b0e70038922"))
|
||||
check tx.blockNumber == 0x22e.u256
|
||||
check tx.sender == Address.init("0xe00b677c29ff8d8fe6068530e2bc36158c54dd34").get
|
||||
check tx.`from` == Address.init("0xe00b677c29ff8d8fe6068530e2bc36158c54dd34").get
|
||||
check tx.gas == 0x4d4bb.u256
|
||||
check tx.gasPrice == 0x3b9aca07.u256
|
||||
check tx.hash == TransactionHash(array[32, byte].fromHex("0xa31608907c338d6497b0c6ec81049d845c7d409490ebf78171f35143897ca790"))
|
||||
@ -178,7 +156,7 @@ suite "JSON Conversions":
|
||||
let tx = PastTransaction(
|
||||
blockHash: BlockHash(array[32, byte].fromHex("0x595bffbe897e025ea2df3213c4cc52c3f3d69bc04b49011d558f1b0e70038922")),
|
||||
blockNumber: 0x22e.u256,
|
||||
sender: Address.init("0xe00b677c29ff8d8fe6068530e2bc36158c54dd34").get,
|
||||
`from`: Address.init("0xe00b677c29ff8d8fe6068530e2bc36158c54dd34").get,
|
||||
gas: 0x4d4bb.u256,
|
||||
gasPrice: 0x3b9aca07.u256,
|
||||
hash: TransactionHash(array[32, byte].fromHex("0xa31608907c338d6497b0c6ec81049d845c7d409490ebf78171f35143897ca790")),
|
||||
@ -205,12 +183,12 @@ suite "JSON Conversions":
|
||||
"nonce":"0x3",
|
||||
"to":"0x92f09aa59dccb892a9f5406ddd9c0b98f02ea57e",
|
||||
"transactionIndex":"0x3",
|
||||
"type":"0x0",
|
||||
"chainId":"0xc0de4",
|
||||
"value":"0x0",
|
||||
"v":"0x181bec",
|
||||
"r":"0x57ba18460934526333b80b0fea08737c363f3cd5fbec4a25a8a25e3e8acb362a",
|
||||
"s":"0x33aa50bc8bd719b6b17ad0bf52006bf8943999198f2bf731eb33c118091000f2",
|
||||
"type":"0x0",
|
||||
"chainId":"0xc0de4"
|
||||
"s":"0x33aa50bc8bd719b6b17ad0bf52006bf8943999198f2bf731eb33c118091000f2"
|
||||
}""".flatten
|
||||
check $(%tx) == expected
|
||||
|
||||
@ -239,6 +217,7 @@ suite "JSON Conversions":
|
||||
check %past.toTransaction == %*{
|
||||
"to": !Address.init("0x92f09aa59dccb892a9f5406ddd9c0b98f02ea57e"),
|
||||
"data": hexToSeqByte("0x6368a471d26ff5c7f835c1a8203235e88846ce1a196d6e79df0eaedd1b8ed3deec2ae5c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000012a00000000000000000000000000000000000000000000000000000000000000"),
|
||||
"value": "0x0",
|
||||
"from": !Address.init("0xe00b677c29ff8d8fe6068530e2bc36158c54dd34"),
|
||||
"nonce": 0x3.u256,
|
||||
"chainId": 0xc0de4.u256,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import std/json
|
||||
import pkg/asynctest
|
||||
import pkg/chronos
|
||||
import pkg/ethers
|
||||
@ -97,5 +96,5 @@ for url in ["ws://localhost:8545", "http://localhost:8545"]:
|
||||
discard await provider.getBlock(BlockTag.latest)
|
||||
expect JsonRpcProviderError:
|
||||
discard await provider.subscribe(proc(_: Block) = discard)
|
||||
expect JsonRpcProviderError:
|
||||
expect JsonRpcSignerError:
|
||||
discard await provider.getSigner().sendTransaction(Transaction.example)
|
||||
|
||||
@ -52,7 +52,7 @@ suite "JsonRpcSigner":
|
||||
let signer = provider.getSigner()
|
||||
let transaction = Transaction.example
|
||||
let populated = await signer.populateTransaction(transaction)
|
||||
check !populated.sender == await signer.getAddress()
|
||||
check !populated.`from` == await signer.getAddress()
|
||||
check !populated.gasPrice == await signer.getGasPrice()
|
||||
check !populated.nonce == await signer.getTransactionCount(BlockTag.pending)
|
||||
check !populated.gasLimit == await signer.estimateGas(transaction)
|
||||
|
||||
@ -64,6 +64,7 @@ suite "Web socket subscriptions":
|
||||
client = newRpcWebSocketClient()
|
||||
await client.connect("ws://localhost:8545")
|
||||
subscriptions = JsonRpcSubscriptions.new(client)
|
||||
subscriptions.init()
|
||||
|
||||
teardown:
|
||||
await subscriptions.close()
|
||||
@ -81,6 +82,7 @@ suite "HTTP polling subscriptions":
|
||||
await client.connect("http://localhost:8545")
|
||||
subscriptions = JsonRpcSubscriptions.new(client,
|
||||
pollingInterval = 100.millis)
|
||||
subscriptions.init()
|
||||
|
||||
teardown:
|
||||
await subscriptions.close()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user