nim-ethers/ethers/wallet.nim

114 lines
3.8 KiB
Nim
Raw Permalink Normal View History

2022-07-12 18:23:43 +00:00
import eth/keys
import eth/rlp
import eth/common
import eth/common/transaction as ct
import ./provider
import ./transaction as tx
2022-07-12 18:23:43 +00:00
import ./signer
export keys
2022-07-21 00:00:57 +00:00
var rng {.threadvar.}: ref HmacDrbgContext
proc getRng: ref HmacDrbgContext =
2022-07-14 08:22:54 +00:00
if rng.isNil:
2022-07-21 00:00:57 +00:00
rng = newRng()
2022-07-14 08:22:54 +00:00
rng
2022-07-12 18:23:43 +00:00
type SignableTransaction = common.Transaction
type WalletError* = object of EthersError
type Wallet* = ref object of Signer
privateKey*: PrivateKey
publicKey*: PublicKey
address*: Address
provider*: ?Provider
2022-07-12 18:23:43 +00:00
proc new*(_: type Wallet, pk: string, provider: Provider): Wallet =
2022-07-12 18:23:43 +00:00
result = Wallet()
result.privateKey = PrivateKey.fromHex(pk).value
result.publicKey = result.privateKey.toPublicKey()
result.address = Address.init(result.publicKey.toCanonicalAddress())
result.provider = some provider
proc new*(_: type Wallet, pk: string): Wallet =
result = Wallet()
result.privateKey = PrivateKey.fromHex(pk).value
result.publicKey = result.privateKey.toPublicKey()
result.address = Address.init(result.publicKey.toCanonicalAddress())
proc connect*(wallet: Wallet, provider: Provider) =
2022-07-12 18:23:43 +00:00
wallet.provider = some provider
proc createRandom*(_: type Wallet): Wallet =
result = Wallet()
2022-07-21 00:00:57 +00:00
result.privateKey = PrivateKey.random(getRng()[])
2022-07-12 18:23:43 +00:00
result.publicKey = result.privateKey.toPublicKey()
result.address = Address.init(result.publicKey.toCanonicalAddress())
proc createRandom*(_: type Wallet, provider: Provider): Wallet =
2022-07-12 18:23:43 +00:00
result = Wallet()
2022-07-21 00:00:57 +00:00
result.privateKey = PrivateKey.random(getRng()[])
2022-07-12 18:23:43 +00:00
result.publicKey = result.privateKey.toPublicKey()
result.address = Address.init(result.publicKey.toCanonicalAddress())
result.provider = some provider
2022-07-12 19:13:34 +00:00
method provider*(wallet: Wallet): Provider =
without provider =? wallet.provider:
2022-07-12 19:13:34 +00:00
raise newException(WalletError, "Wallet has no provider")
provider
2022-07-12 19:13:34 +00:00
2022-07-21 00:00:57 +00:00
method getAddress(wallet: Wallet): Future[Address] {.async.} =
return wallet.address
2022-07-12 18:23:43 +00:00
proc signTransaction(tr: var SignableTransaction, pk: PrivateKey) =
let h = tr.txHashNoSignature
let s = sign(pk, SkMessage(h.data))
let r = toRaw(s)
let v = r[64]
tr.R = fromBytesBE(UInt256, r.toOpenArray(0, 31))
2022-07-12 18:23:43 +00:00
tr.S = fromBytesBE(UInt256, r.toOpenArray(32, 63))
case tr.txType:
of TxLegacy:
#tr.V = int64(v) + int64(tr.chainId)*2 + 35 #TODO does not work, not sure why. Sending the tx results in error of too little funds. Maybe something wrong with signature and a wrong sender gets encoded?
tr.V = int64(v) + 27
of TxEip1559:
tr.V = int64(v)
else:
raise newException(WalletError, "Transaction type not supported")
proc signTransaction*(wallet: Wallet, tx: tx.Transaction): Future[seq[byte]] {.async.} =
if sender =? tx.sender and sender != wallet.address:
raise newException(WalletError, "from address mismatch")
without nonce =? tx.nonce and chainId =? tx.chainId and gasLimit =? tx.gasLimit:
raise newException(WalletError, "Transaction is properly populated")
2022-07-12 18:23:43 +00:00
var s: SignableTransaction
if maxFee =? tx.maxFee and maxPriorityFee =? tx.maxPriorityFee:
2022-07-12 18:23:43 +00:00
s.txType = TxEip1559
s.maxFee = GasInt(maxFee.truncate(uint64))
s.maxPriorityFee = GasInt(maxPriorityFee.truncate(uint64))
elif gasPrice =? tx.gasPrice:
2022-07-12 18:23:43 +00:00
s.txType = TxLegacy
s.gasPrice = GasInt(gasPrice.truncate(uint64))
else:
raise newException(WalletError, "Transaction is properly populated")
s.chainId = ChainId(chainId.truncate(uint64))
s.gasLimit = GasInt(gasLimit.truncate(uint64))
s.nonce = nonce.truncate(uint64)
2022-07-12 18:23:43 +00:00
s.to = some EthAddress(tx.to)
s.payload = tx.data
signTransaction(s, wallet.privateKey)
2022-07-21 00:00:57 +00:00
return rlp.encode(s)
2022-07-12 19:21:35 +00:00
method sendTransaction*(wallet: Wallet, tx: tx.Transaction): Future[TransactionResponse] {.async.} =
2022-07-21 00:00:57 +00:00
let rawTX = await signTransaction(wallet, tx)
return await provider(wallet).sendTransaction(rawTX)
2022-07-12 19:21:35 +00:00
#TODO add functionality to sign messages
2022-07-14 08:22:54 +00:00
#TODO add functionality to create wallets from Mnemoniks or Keystores