This commit is contained in:
Georgios Konstantopoulos 2020-05-26 13:24:19 +03:00
parent e7c9e19409
commit 2bec170968
No known key found for this signature in database
GPG Key ID: FA607837CD26EDBC
16 changed files with 126 additions and 119 deletions

29
Cargo.lock generated
View File

@ -74,6 +74,14 @@ name = "cfg-if"
version = "0.1.10" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.7.0" version = "0.7.0"
@ -158,6 +166,15 @@ dependencies = [
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "ethers-signers"
version = "0.1.0"
dependencies = [
"ethers-providers 0.1.0",
"ethers-types 0.1.0",
"ethers-utils 0.1.0",
]
[[package]] [[package]]
name = "ethers-types" name = "ethers-types"
version = "0.1.0" version = "0.1.0"
@ -169,6 +186,7 @@ dependencies = [
"rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"secp256k1 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "secp256k1 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -199,6 +217,11 @@ name = "fnv"
version = "1.0.7" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "fuchsia-zircon" name = "fuchsia-zircon"
version = "0.3.3" version = "0.3.3"
@ -617,7 +640,11 @@ name = "rand"
version = "0.5.6" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -1267,6 +1294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
"checksum cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" "checksum cc 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" "checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
@ -1278,6 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ethereum-types 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "473aecff686bd8e7b9db0165cbbb53562376b39bf35b427f0c60446a9e1634b0" "checksum ethereum-types 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "473aecff686bd8e7b9db0165cbbb53562376b39bf35b427f0c60446a9e1634b0"
"checksum fixed-hash 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" "checksum fixed-hash 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c"
"checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" "checksum fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" "checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"

View File

@ -6,7 +6,7 @@ members = [
# "./crates/ethers-contract", # "./crates/ethers-contract",
# "./crates/ethers-derive", # "./crates/ethers-derive",
"./crates/ethers-providers", "./crates/ethers-providers",
# "./crates/ethers-signers", "./crates/ethers-signers",
"./crates/ethers-types", "./crates/ethers-types",
"./crates/ethers-utils", "./crates/ethers-utils",
] ]

View File

@ -7,12 +7,11 @@ use reqwest::{Client, Error as ReqwestError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use std::{ use std::{
convert::TryFrom,
fmt, fmt,
sync::atomic::{AtomicU64, Ordering}, sync::atomic::{AtomicU64, Ordering},
}; };
use thiserror::Error; use thiserror::Error;
use url::{ParseError, Url}; use url::Url;
/// An HTTP Client /// An HTTP Client
#[derive(Debug)] #[derive(Debug)]
@ -69,14 +68,6 @@ impl Provider {
} }
} }
impl TryFrom<&str> for super::Provider<Provider> {
type Error = ParseError;
fn try_from(src: &str) -> Result<Self, Self::Error> {
Ok(super::Provider(Provider::new(Url::parse(src)?)))
}
}
impl Clone for Provider { impl Clone for Provider {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {

View File

@ -4,13 +4,15 @@
//! //!
//! TODO: WebSockets, multiple backends, popular APIs etc. //! TODO: WebSockets, multiple backends, popular APIs etc.
mod provider;
mod http; mod http;
mod provider;
use async_trait::async_trait; use async_trait::async_trait;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{error::Error, fmt::Debug}; use std::{error::Error, fmt::Debug};
pub use provider::Provider;
/// An HTTP provider for interacting with an Ethereum-compatible blockchain /// An HTTP provider for interacting with an Ethereum-compatible blockchain
pub type HttpProvider = Provider<http::Provider>; pub type HttpProvider = Provider<http::Provider>;
@ -26,4 +28,3 @@ pub trait JsonRpcClient: Debug {
params: Option<T>, params: Option<T>,
) -> Result<R, Self::Error>; ) -> Result<R, Self::Error>;
} }

View File

@ -4,9 +4,11 @@ use ethers_types::{
}; };
use ethers_utils as utils; use ethers_utils as utils;
use async_trait::async_trait; use crate::{http::Provider as HttpProvider, JsonRpcClient};
use serde::{Deserialize, Serialize}; use serde::Deserialize;
use std::{error::Error, fmt::Debug}; use url::{ParseError, Url};
use std::{convert::TryFrom, fmt::Debug};
/// An abstract provider for interacting with the [Ethereum JSON RPC /// An abstract provider for interacting with the [Ethereum JSON RPC
/// API](https://github.com/ethereum/wiki/wiki/JSON-RPC) /// API](https://github.com/ethereum/wiki/wiki/JSON-RPC)
@ -148,3 +150,11 @@ impl<P: JsonRpcClient> Provider<P> {
self.0.request("eth_getBalance", Some(&[from, block])).await self.0.request("eth_getBalance", Some(&[from, block])).await
} }
} }
impl TryFrom<&str> for Provider<HttpProvider> {
type Error = ParseError;
fn try_from(src: &str) -> Result<Self, Self::Error> {
Ok(Provider(HttpProvider::new(Url::parse(src)?)))
}
}

View File

@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ethers-types = { path = "../ethers-types" }
ethers-providers = { path = "../ethers-providers" }
ethers-utils = { path = "../ethers-utils" }

View File

@ -1,9 +1,7 @@
use crate::{ use crate::Signer;
providers::{JsonRpcClient, Provider},
signers::Signer, use ethers_providers::{JsonRpcClient, Provider};
types::{Address, BlockNumber, Overrides, TransactionRequest, TxHash}, use ethers_types::{Address, BlockNumber, TransactionRequest, TxHash};
utils,
};
use std::ops::Deref; use std::ops::Deref;
@ -79,40 +77,6 @@ impl<'a, S: Signer, P: JsonRpcClient> Client<'a, S, P> {
Ok(()) Ok(())
} }
/// client.call_contract(
/// addr,
/// "transfer(address,uint256)"
/// vec![0x1234, 100]
/// None,
/// None,
/// )
pub async fn call_contract(
&self,
to: impl Into<Address>,
signature: &str,
args: &[ethabi::Token],
overrides: Option<Overrides>,
block: Option<BlockNumber>,
) -> Result<TxHash, P::Error> {
// create the data field from the function signature and the arguments
let data = [&utils::id(signature)[..], &ethabi::encode(args)].concat();
let overrides = overrides.unwrap_or_default();
let tx = TransactionRequest {
to: Some(to.into()),
data: Some(data.into()),
// forward the overriden data
from: overrides.from, // let it figure it out itself
gas: overrides.gas,
gas_price: overrides.gas_price,
nonce: overrides.nonce,
value: overrides.value,
};
self.send_transaction(tx, block).await
}
pub fn address(&self) -> Address { pub fn address(&self) -> Address {
self.signer self.signer
.as_ref() .as_ref()

View File

@ -1,7 +1,39 @@
#[cfg(test)] //! Ethereum compatible signers
mod tests { //!
#[test] //! Currently supported:
fn it_works() { //! - [x] Private Key
assert_eq!(2 + 2, 4); //! - [ ] Encrypted Json
} //! - [ ] Ledger
//! - [ ] Trezor
//!
//! Implement the `Signer` trait to add support for new signers, e.g. with Ledger.
//!
//! TODO: We might need a `SignerAsync` trait for HSM use cases?
mod networks;
pub use networks::instantiated::*;
use networks::Network;
mod wallet;
pub use wallet::Wallet;
mod client;
pub(crate) use client::Client;
use ethers_types::{Address, Signature, Transaction, TransactionRequest};
use std::error::Error;
/// Trait for signing transactions and messages
///
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
pub trait Signer {
type Error: Error;
/// Signs the hash of the provided message after prefixing it
fn sign_message<S: AsRef<[u8]>>(&self, message: S) -> Signature;
/// Signs the transaction
fn sign_transaction(&self, message: TransactionRequest) -> Result<Transaction, Self::Error>;
/// Returns the signer's Ethereum Address
fn address(&self) -> Address;
} }

View File

@ -1,32 +0,0 @@
//! Sign and broadcast transactions
//!
//! Implement the `Signer` trait to add support for new signers, e.g. with Ledger.
//!
//! TODO: We might need a `SignerAsync` trait for HSM use cases?
mod networks;
pub use networks::instantiated::*;
use networks::Network;
mod wallet;
pub use wallet::Wallet;
mod client;
pub(crate) use client::Client;
use crate::types::{Address, Signature, Transaction, TransactionRequest};
use std::error::Error;
/// Trait for signing transactions and messages
///
/// Implement this trait to support different signing modes, e.g. Ledger, hosted etc.
pub trait Signer {
type Error: Error;
/// Signs the hash of the provided message after prefixing it
fn sign_message<S: AsRef<[u8]>>(&self, message: S) -> Signature;
/// Signs the transaction
fn sign_transaction(&self, message: TransactionRequest) -> Result<Transaction, Self::Error>;
/// Returns the signer's Ethereum Address
fn address(&self) -> Address;
}

View File

@ -2,7 +2,7 @@
//! a transaction that is designed to work with testnet does not accidentally work //! a transaction that is designed to work with testnet does not accidentally work
//! with mainnet because the URL was changed. //! with mainnet because the URL was changed.
use crate::types::U64; use ethers_types::U64;
pub trait Network { pub trait Network {
const CHAIN_ID: Option<U64>; const CHAIN_ID: Option<U64>;
@ -28,7 +28,7 @@ impl Network for EIP155Disabled {
pub mod instantiated { pub mod instantiated {
use super::*; use super::*;
use crate::signers::Wallet; use crate::Wallet;
/// A Wallet instantiated with chain_id = 1 for Ethereum Mainnet. /// A Wallet instantiated with chain_id = 1 for Ethereum Mainnet.
pub type MainnetWallet = Wallet<Mainnet>; pub type MainnetWallet = Wallet<Mainnet>;

View File

@ -1,10 +1,12 @@
use crate::{ use crate::{Client, Network, Signer};
providers::{JsonRpcClient, Provider},
signers::{Client, Network, Signer}, use ethers_providers::{JsonRpcClient, Provider};
types::{Address, PrivateKey, PublicKey, Signature, Transaction, TransactionRequest, TxError},
use ethers_types::{
rand::Rng, secp256k1, Address, PrivateKey, PublicKey, Signature, Transaction,
TransactionRequest, TxError,
}; };
use rand::Rng;
use std::{marker::PhantomData, str::FromStr}; use std::{marker::PhantomData, str::FromStr};
/// A keypair /// A keypair

View File

@ -4,8 +4,6 @@ version = "0.1.0"
authors = ["Georgios Konstantopoulos <me@gakonst.com>"] authors = ["Georgios Konstantopoulos <me@gakonst.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ethers-utils = { path = "../ethers-utils" } ethers-utils = { path = "../ethers-utils" }
@ -19,3 +17,7 @@ thiserror = { version = "1.0.19", default-features = false }
secp256k1 = { version = "0.17.2", default-features = false, features = ["std", "recovery", "rand"] } secp256k1 = { version = "0.17.2", default-features = false, features = ["std", "recovery", "rand"] }
rand = { version = "0.5.1", default-features = false } # this should be the same rand crate version as the one in secp rand = { version = "0.5.1", default-features = false } # this should be the same rand crate version as the one in secp
zeroize = { version = "1.1.0", default-features = false } zeroize = { version = "1.1.0", default-features = false }
[dev-dependencies]
serde_json = { version = "1.0.53", default-features = false }
rand = { version = "0.5.1" }

View File

@ -208,7 +208,7 @@ impl From<PrivateKey> for Address {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::types::Bytes; use crate::Bytes;
use rustc_hex::FromHex; use rustc_hex::FromHex;
#[test] #[test]

View File

@ -23,3 +23,10 @@ pub use block::{Block, BlockId, BlockNumber};
mod log; mod log;
pub use log::{Filter, Log, ValueOrArray}; pub use log::{Filter, Log, ValueOrArray};
// re-export the non-standard rand version so that other crates don't use the
// wrong one by accident
pub use rand;
// re-export libsecp
pub use secp256k1;

View File

@ -192,7 +192,7 @@ impl From<H256> for RecoveryMessage {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::types::PrivateKey; use crate::PrivateKey;
#[test] #[test]
fn recover_signature_from_message() { fn recover_signature_from_message() {

View File

@ -11,24 +11,24 @@ use std::str::FromStr;
pub struct Overrides { pub struct Overrides {
/// Sender address or ENS name /// Sender address or ENS name
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) from: Option<Address>, pub from: Option<Address>,
/// Supplied gas (None for sensible default) /// Supplied gas (None for sensible default)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) gas: Option<U256>, pub gas: Option<U256>,
/// Gas price (None for sensible default) /// Gas price (None for sensible default)
#[serde(rename = "gasPrice")] #[serde(rename = "gasPrice")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) gas_price: Option<U256>, pub gas_price: Option<U256>,
/// Transfered value (None for no transfer) /// Transfered value (None for no transfer)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) value: Option<U256>, pub value: Option<U256>,
/// Transaction nonce (None for next available nonce) /// Transaction nonce (None for next available nonce)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) nonce: Option<U256>, pub nonce: Option<U256>,
} }
/// Parameters for sending a transaction /// Parameters for sending a transaction
@ -36,33 +36,33 @@ pub struct Overrides {
pub struct TransactionRequest { pub struct TransactionRequest {
/// Sender address or ENS name /// Sender address or ENS name
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) from: Option<Address>, pub from: Option<Address>,
/// Recipient address (None for contract creation) /// Recipient address (None for contract creation)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) to: Option<Address>, pub to: Option<Address>,
/// Supplied gas (None for sensible default) /// Supplied gas (None for sensible default)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) gas: Option<U256>, pub gas: Option<U256>,
/// Gas price (None for sensible default) /// Gas price (None for sensible default)
#[serde(rename = "gasPrice")] #[serde(rename = "gasPrice")]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) gas_price: Option<U256>, pub gas_price: Option<U256>,
/// Transfered value (None for no transfer) /// Transfered value (None for no transfer)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) value: Option<U256>, pub value: Option<U256>,
/// The compiled code of a contract OR the first 4 bytes of the hash of the /// The compiled code of a contract OR the first 4 bytes of the hash of the
/// invoked method signature and encoded parameters. For details see Ethereum Contract ABI /// invoked method signature and encoded parameters. For details see Ethereum Contract ABI
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) data: Option<Bytes>, pub data: Option<Bytes>,
/// Transaction nonce (None for next available nonce) /// Transaction nonce (None for next available nonce)
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub(crate) nonce: Option<U256>, pub nonce: Option<U256>,
} }
impl TransactionRequest { impl TransactionRequest {