diff --git a/examples/local_signer.rs b/examples/local_signer.rs index 2d1d2d5c..f0d96ba4 100644 --- a/examples/local_signer.rs +++ b/examples/local_signer.rs @@ -4,16 +4,21 @@ use std::str::FromStr; #[tokio::main] async fn main() -> Result<(), failure::Error> { + // connect to the network let provider = HttpProvider::try_from("http://localhost:8545")?; + + // create a wallet and connect it to the provider let client = MainnetWallet::from_str( "d8ebe1e50cfea1f9961908d9df28e64bb163fee9ee48320361b2eb0a54974269", )? .connect(&provider); + // get the account's nonce let nonce = provider .get_transaction_count(client.signer.address, None) .await?; + // craft the transaction let tx = UnsignedTransaction { to: Some("986eE0C8B91A58e490Ee59718Cca41056Cf55f24".parse().unwrap()), gas: 21000.into(), @@ -23,8 +28,10 @@ async fn main() -> Result<(), failure::Error> { nonce, }; + // send it! let tx = client.send_transaction(tx).await?; + // get the mined tx let tx = client.get_transaction(tx.hash).await?; println!("{}", serde_json::to_string(&tx)?); diff --git a/examples/sign.rs b/examples/sign.rs new file mode 100644 index 00000000..69ec04b6 --- /dev/null +++ b/examples/sign.rs @@ -0,0 +1,17 @@ +use ethers::{MainnetWallet as Wallet, Signer}; + +fn main() { + let message = "Some data"; + let wallet = Wallet::new(&mut rand::thread_rng()); + + // sign a message + let signature = wallet.sign_message(message); + println!("Produced signature {}", signature); + + // recover the address that signed it + let recovered = signature.recover(message).unwrap(); + + assert_eq!(recovered, wallet.address); + + println!("Verified signature produced by {:?}!", wallet.address); +} diff --git a/examples/transfer_eth.rs b/examples/transfer_eth.rs index 4a9b0e7e..45cab594 100644 --- a/examples/transfer_eth.rs +++ b/examples/transfer_eth.rs @@ -6,20 +6,23 @@ use std::convert::TryFrom; #[tokio::main] async fn main() -> Result<(), failure::Error> { + // connect to the network let provider = HttpProvider::try_from("http://localhost:8545")?; let from = "4916064D2E9C1b2ccC466EEc3d30B2b08F1C130D".parse()?; - let tx_hash = provider - .send_transaction(TransactionRequest { - from, - to: Some("9A7e5d4bcA656182e66e33340d776D1542143006".parse()?), - value: Some(1000u64.into()), - gas: None, - gas_price: None, - data: None, - nonce: None, - }) - .await?; + // craft the tx + let tx = TransactionRequest { + from, + to: Some("9A7e5d4bcA656182e66e33340d776D1542143006".parse()?), + value: Some(1000u64.into()), + gas: None, + gas_price: None, + data: None, + nonce: None, + }; + + // broadcast it via the eth_sendTransaction API + let tx_hash = provider.send_transaction(tx).await?; let tx = provider.get_transaction(tx_hash).await?; diff --git a/src/lib.rs b/src/lib.rs index b4a0a5fe..88e66770 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub mod providers; pub use providers::HttpProvider; pub mod signers; -pub use signers::{AnyWallet, MainnetWallet}; +pub use signers::{AnyWallet, MainnetWallet, Signer}; /// Ethereum related datatypes pub mod types; diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 2af270b9..edab5267 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -6,6 +6,7 @@ mod http; use crate::{ + signers::{Client, Signer}, types::{Address, BlockNumber, Bytes, Transaction, TransactionRequest, TxHash, U256}, utils, }; @@ -37,6 +38,14 @@ pub struct Provider

(P); // JSON RPC bindings impl Provider

{ + /// Connects to a signer and returns a client + pub fn connect(&self, signer: S) -> Client { + Client { + signer, + provider: self, + } + } + pub async fn get_block_number(&self) -> Result { self.0.request("eth_blockNumber", None::<()>).await } diff --git a/src/signers/client.rs b/src/signers/client.rs index 9049eb27..dce3fd8f 100644 --- a/src/signers/client.rs +++ b/src/signers/client.rs @@ -6,7 +6,7 @@ use crate::{ #[derive(Clone, Debug)] pub struct Client<'a, S, P> { - pub(super) provider: &'a Provider

, + pub provider: &'a Provider

, pub signer: S, } diff --git a/src/signers/mod.rs b/src/signers/mod.rs index 0c42c4f4..abe38c97 100644 --- a/src/signers/mod.rs +++ b/src/signers/mod.rs @@ -1,3 +1,4 @@ +//! Sign and broadcast transactions mod networks; pub use networks::instantiated::*; use networks::Network; @@ -6,7 +7,7 @@ mod wallet; pub use wallet::Wallet; mod client; -use client::Client; +pub(crate) use client::Client; use crate::types::{Signature, Transaction, UnsignedTransaction}; diff --git a/src/types/signature.rs b/src/types/signature.rs index 60a5aa48..6676194e 100644 --- a/src/types/signature.rs +++ b/src/types/signature.rs @@ -4,12 +4,13 @@ use crate::{ utils::hash_message, }; +use rustc_hex::ToHex; use secp256k1::{ recovery::{RecoverableSignature, RecoveryId}, Error as Secp256k1Error, Message, Secp256k1, }; use serde::{Deserialize, Serialize}; -use std::convert::TryFrom; +use std::{convert::TryFrom, fmt}; use thiserror::Error; /// An error involving a signature. @@ -47,6 +48,13 @@ pub struct Signature { pub v: u8, } +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let sig = <[u8; 65]>::from(self); + write!(f, "{}", sig.to_hex::()) + } +} + impl Signature { /// Recovers the Ethereum address which was used to sign the given message. /// @@ -106,9 +114,7 @@ impl<'a> TryFrom<&'a [u8]> for Signature { /// Parses a raw signature which is expected to be 65 bytes long where /// the first 32 bytes is the `r` value, the second 32 bytes the `s` value /// and the final byte is the `v` value in 'Electrum' notation. - fn try_from(raw_signature: &'a [u8]) -> Result { - let bytes = raw_signature.as_ref(); - + fn try_from(bytes: &'a [u8]) -> Result { if bytes.len() != 65 { return Err(SignatureError::InvalidLength(bytes.len())); } diff --git a/src/types/transaction.rs b/src/types/transaction.rs index 0e0fe414..609effe2 100644 --- a/src/types/transaction.rs +++ b/src/types/transaction.rs @@ -87,7 +87,7 @@ impl UnsignedTransaction { rlp.begin_list(9); self.rlp_base(&mut rlp); - rlp.append(&chain_id.unwrap_or(U64::zero())); + rlp.append(&chain_id.unwrap_or_else(U64::zero)); rlp.append(&0u8); rlp.append(&0u8);