diff --git a/ethers/providers/jsonrpc.nim b/ethers/providers/jsonrpc.nim index 5b5dd9e..1f8f468 100644 --- a/ethers/providers/jsonrpc.nim +++ b/ethers/providers/jsonrpc.nim @@ -151,7 +151,7 @@ method getChainId*(provider: JsonRpcProvider): Future[UInt256] {.async.} = except CatchableError: return parse(await client.net_version(), UInt256) -method sendRawTransaction*(provider: JsonRpcProvider, rawTransaction: string): Future[TransactionResponse] {.async.} = +method sendRawTransaction*(provider: JsonRpcProvider, rawTransaction: seq[byte]): Future[TransactionResponse] {.async.} = convertError: let client = await provider.client diff --git a/ethers/providers/jsonrpc/signatures.nim b/ethers/providers/jsonrpc/signatures.nim index 223bb7a..8738615 100644 --- a/ethers/providers/jsonrpc/signatures.nim +++ b/ethers/providers/jsonrpc/signatures.nim @@ -8,7 +8,7 @@ proc eth_getTransactionCount(address: Address, blockTag: BlockTag): UInt256 proc eth_estimateGas(transaction: Transaction): UInt256 proc eth_chainId(): UInt256 proc eth_sendTransaction(transaction: Transaction): TransactionHash -proc eth_sendRawTransaction(data: string): TransactionHash +proc eth_sendRawTransaction(data: seq[byte]): TransactionHash proc eth_getTransactionReceipt(hash: TransactionHash): ?TransactionReceipt proc eth_sign(account: Address, message: seq[byte]): seq[byte] proc eth_subscribe(name: string, filter: Filter): JsonNode diff --git a/ethers/wallet.nim b/ethers/wallet.nim index 37f7d1b..f8b48fd 100644 --- a/ethers/wallet.nim +++ b/ethers/wallet.nim @@ -9,7 +9,12 @@ import ./signer export keys -let rng = newRng() +var rng {.threadvar.}: ref HmacDrbgContext + +proc getRng: ref HmacDrbgContext = + if rng.isnil: + rng = newRng() + return rng type SignableTransaction = common.Transaction @@ -35,12 +40,12 @@ proc connect*(wallet: Wallet, provider: JsonRpcProvider) = wallet.provider = some provider proc createRandom*(_: type Wallet): Wallet = result = Wallet() - result.privateKey = PrivateKey.random(rng[]) + result.privateKey = PrivateKey.random(getRng()[]) result.publicKey = result.privateKey.toPublicKey() result.address = Address.init(result.publicKey.toCanonicalAddress()) proc createRandom*(_: type Wallet, provider: JsonRpcProvider): Wallet = result = Wallet() - result.privateKey = PrivateKey.random(rng[]) + result.privateKey = PrivateKey.random(getRng()[]) result.publicKey = result.privateKey.toPublicKey() result.address = Address.init(result.publicKey.toCanonicalAddress()) result.provider = some provider @@ -51,19 +56,8 @@ method provider*(wallet: Wallet): Provider = else: raise newException(WalletError, "Wallet has no provider") -#TODO add other methods defined in signer. Currently you have to specify nonce, gaslimit, gasPrice. - -method populateTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[transaction.Transaction] {.async.} = - var populated = tx - if tx.nonce.isNone: - populated.nonce = some(await wallet.getTransactionCount(BlockTag.pending)) - if tx.chainId.isNone: - populated.chainId = some(await wallet.getChainId()) - if tx.gasLimit.isNone: - populated.gasLimit = some(await wallet.estimateGas(populated)) - if tx.gasPrice.isNone and (tx.maxFee.isNone and tx.maxPriorityFee.isNone): - populated.gasPrice = some(await wallet.getGasPrice()) - return populated +method getAddress(wallet: Wallet): Future[Address] {.async.} = + return wallet.address func isPopulated(tx: transaction.Transaction) = if tx.nonce.isNone or @@ -91,29 +85,7 @@ proc signTransaction(tr: var SignableTransaction, pk: PrivateKey) = else: raise newException(WalletError, "Transaction type not supported") -method sendTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[TransactionResponse] {.async.} = - if tx.sender.isSome: - doAssert tx.sender.get == wallet.address, "from Address mismatch" - isPopulated(tx) - var s: SignableTransaction - if tx.maxFee.isSome and tx.maxPriorityFee.isSome: - s.txType = TxEip1559 - s.maxFee = GasInt(tx.maxFee.get.truncate(uint64)) - s.maxPriorityFee = GasInt(tx.maxPriorityFee.get.truncate(uint64)) - else: - s.txType = TxLegacy - s.gasPrice = GasInt(tx.gasPrice.get.truncate(uint64)) - s.chainId = ChainId(tx.chainId.get.truncate(uint64)) - s.gasLimit = GasInt(tx.gasLimit.get.truncate(uint64)) - s.nonce = tx.nonce.get.truncate(uint64) - s.to = some EthAddress(tx.to) - s.payload = tx.data - signTransaction(s, wallet.privateKey) - - let rawTX = "0x" & rlp.encode(s).toHex - return await wallet.provider.get.sendRawTransaction(rawTX) - -proc signTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[string] {.async.} = +proc signTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[seq[byte]] {.async.} = if tx.sender.isSome: doAssert tx.sender.get == wallet.address, "from Address mismatch" isPopulated(tx) @@ -132,8 +104,11 @@ proc signTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[strin s.payload = tx.data signTransaction(s, wallet.privateKey) - return "0x" & rlp.encode(s).toHex + return rlp.encode(s) +method sendTransaction*(wallet: Wallet, tx: transaction.Transaction): Future[TransactionResponse] {.async.} = + let rawTX = await signTransaction(wallet, tx) + return await wallet.provider.get.sendRawTransaction(rawTX) #TODO add functionality to sign messages diff --git a/testmodule/testWallet.nim b/testmodule/testWallet.nim index 7165133..d1d3da8 100644 --- a/testmodule/testWallet.nim +++ b/testmodule/testWallet.nim @@ -13,6 +13,16 @@ suite "Wallet": #TODO take close look at current signing tests. I am not 100% sure they are correct and work #TODO add setup/teardown if required. Currently doing all nonces manually + var provider: JsonRpcProvider + var snapshot: JsonNode + + setup: + provider = JsonRpcProvider.new() + snapshot = await provider.send("evm_snapshot") + + #teardown: + # discard await provider.send("evm_revert", @[snapshot]) + test "Can create Wallet with private key": discard Wallet.new(pk1) @@ -24,7 +34,6 @@ suite "Wallet": discard Wallet.new(pk1, provider) test "Can connect Wallet to provider": - let provider = JsonRpcProvider.new() let wallet = Wallet.new(pk1) wallet.connect(provider) @@ -32,7 +41,6 @@ suite "Wallet": discard Wallet.createRandom() test "Can create Random Wallet with provider": - let provider = JsonRpcProvider.new() discard Wallet.createRandom(provider) test "Multiple Random Wallets are different": @@ -55,7 +63,7 @@ suite "Wallet": gasLimit: some 21_000.u256, ) let signedTx = await wallet.signTransaction(tx) - check signedTx == "0xf86380843b9aca0082520894328809bc894f92807417d2dad6b7c998c1afdac680801ba04ae9b24cba72103bb30a1e91c016796fc2bf2d46d2b75ca80211fae0337c3c03a05e3c81ce9944a07f18b65142a1847c5b72f993c8e7c28d5d4360ff36a2fed049" + check signedTx == @[248.byte, 99, 128, 132, 59, 154, 202, 0, 130, 82, 8, 148, 50, 136, 9, 188, 137, 79, 146, 128, 116, 23, 210, 218, 214, 183, 201, 152, 193, 175, 218, 198, 128, 128, 27, 160, 74, 233, 178, 76, 186, 114, 16, 59, 179, 10, 30, 145, 192, 22, 121, 111, 194, 191, 45, 70, 210, 183, 92, 168, 2, 17, 250, 224, 51, 124, 60, 3, 160, 94, 60, 129, 206, 153, 68, 160, 127, 24, 182, 81, 66, 161, 132, 124, 91, 114, 249, 147, 200, 231, 194, 141, 93, 67, 96, 255, 54, 162, 254, 208, 73] test "Can sign manually created contract call": let wallet = Wallet.new(pk1) @@ -68,7 +76,7 @@ suite "Wallet": gasLimit: some 21_000.u256, ) let signedTx = await wallet.signTransaction(tx) - check signedTx == "0xf86780843b9aca0082520894328809bc894f92807417d2dad6b7c998c1afdac6808418160ddd1ca029261fc74ffbbb5ce3d0a3b6eac9726f05d8a849e0f1535722e057bdd83b9659a044f857852bd8b7bb8c0c0a5a61c2c56fce42edacab73f42301b509edb7600ff1" + check signedTx == @[248.byte, 103, 128, 132, 59, 154, 202, 0, 130, 82, 8, 148, 50, 136, 9, 188, 137, 79, 146, 128, 116, 23, 210, 218, 214, 183, 201, 152, 193, 175, 218, 198, 128, 132, 24, 22, 13, 221, 28, 160, 41, 38, 31, 199, 79, 251, 187, 92, 227, 208, 163, 182, 234, 201, 114, 111, 5, 216, 168, 73, 224, 241, 83, 87, 34, 224, 87, 189, 216, 59, 150, 89, 160, 68, 248, 87, 133, 43, 216, 183, 187, 140, 12, 10, 90, 97, 194, 197, 111, 206, 66, 237, 172, 171, 115, 244, 35, 1, 181, 9, 237, 183, 96, 15, 241] test "Can sign manually created tx with EIP1559": let wallet = Wallet.new(pk1) @@ -81,10 +89,9 @@ suite "Wallet": gasLimit: some 21_000.u256 ) let signedTx = await wallet.signTransaction(tx) - check signedTx == "0x02f86c827a6980843b9aca00847735940082520894328809bc894f92807417d2dad6b7c998c1afdac68080c001a0162929fc5b4cb286ed4cd630d172d1dd747dad4ffbeb413b037f21168f4fe366a062b931c1fc55028ae1fdf5342564300cae251791d785a0efd31c088405a651e7" + check signedTx == @[2.byte, 248, 108, 130, 122, 105, 128, 132, 59, 154, 202, 0, 132, 119, 53, 148, 0, 130, 82, 8, 148, 50, 136, 9, 188, 137, 79, 146, 128, 116, 23, 210, 218, 214, 183, 201, 152, 193, 175, 218, 198, 128, 128, 192, 1, 160, 22, 41, 41, 252, 91, 76, 178, 134, 237, 76, 214, 48, 209, 114, 209, 221, 116, 125, 173, 79, 251, 235, 65, 59, 3, 127, 33, 22, 143, 79, 227, 102, 160, 98, 185, 49, 193, 252, 85, 2, 138, 225, 253, 245, 52, 37, 100, 48, 12, 174, 37, 23, 145, 215, 133, 160, 239, 211, 28, 8, 132, 5, 166, 81, 231] test "Can send rawTransaction": - let provider = JsonRpcProvider.new() let wallet = Wallet.new(pk_with_funds) let tx = Transaction( to: wallet.address, @@ -99,7 +106,6 @@ suite "Wallet": test "Can call state-changing function automatically": #TODO add actual token contract, not random address. Should work regardless - let provider = JsonRpcProvider.new() let wallet = Wallet.new(pk_with_funds, provider) let overrides = TransactionOverrides( nonce: some 1.u256, @@ -110,7 +116,6 @@ suite "Wallet": test "Can call state-changing function automatically EIP1559": #TODO add actual token contract, not random address. Should work regardless - let provider = JsonRpcProvider.new() let wallet = Wallet.new(pk_with_funds, provider) let overrides = TransactionOverrides( nonce: some 2.u256,