mirror of
https://github.com/status-im/nim-ethers.git
synced 2025-01-27 23:56:06 +00:00
765379a662
* fix nonce issues by locking populate and send transaction Concurrent asynchronous population of transactions cause issues with nonces not being in sync with the transaction count for an account on chain. This was being mitigated by tracking a "last seen" nonce and locking inside of `populateTransaction` so that the nonce could be populated in a concurrent fashion. However, if there was an async cancellation before the transaction was sent, then the nonce would become out of sync. One solution was to decrease the nonce if a cancellation occurred. The other solution, in this commit, is simply to lock the populate and sendTransaction calls together, so that there will not be concurrent nonce discrepancies. This removes the need for "lastSeenNonce" and is overall more simple. * remove lastSeenNonce Internal nonce tracking is no longer needed since populate/sendTransaction is now locked. Even if cancelled midway, the nonce will get a refreshed value from the number of transactions from chain. * chronos v4 exception tracking * Add tests
90 lines
2.7 KiB
Nim
90 lines
2.7 KiB
Nim
import eth/keys
|
|
import ../basics
|
|
import ../provider
|
|
import ../transaction
|
|
import ../signer
|
|
import ./wallet/error
|
|
import ./wallet/signing
|
|
|
|
export keys
|
|
export WalletError
|
|
export signing
|
|
|
|
{.push raises: [].}
|
|
|
|
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 {.gcsafe, raises: [SignerError].} =
|
|
without provider =? wallet.provider:
|
|
raiseWalletError "Wallet has no provider"
|
|
provider
|
|
|
|
method getAddress*(
|
|
wallet: Wallet): Future[Address]
|
|
{.async: (raises:[ProviderError, SignerError]).} =
|
|
|
|
return wallet.address
|
|
|
|
proc signTransaction*(wallet: Wallet,
|
|
transaction: Transaction): Future[seq[byte]] {.async: (raises:[WalletError]).} =
|
|
if sender =? transaction.sender and sender != wallet.address:
|
|
raiseWalletError "from address mismatch"
|
|
|
|
return wallet.privateKey.sign(transaction)
|
|
|
|
method sendTransaction*(
|
|
wallet: Wallet,
|
|
transaction: Transaction): Future[TransactionResponse]
|
|
{.async: (raises:[SignerError, ProviderError]).} =
|
|
|
|
let signed = await signTransaction(wallet, transaction)
|
|
return await provider(wallet).sendTransaction(signed)
|