Upgrade to Tokio 1.0 and remove async-std (#120)

* feat(providers): tokio 1.0

BREAKING: This removes async-std as a compatibility option

* feat: tokio 1.0 in rest of crates

* fix: patch Cargo.toml until deps are released

* fix(contract): load ws deps

* feat: bytes 1.0 (#121)

* feat(core): move to bytes::Bytes

* feat: adjust rest of crates to Bytes

* chore: bump deps

CI fails due to:
https://github.com/snapview/tokio-tungstenite/pull/142#discussion_r550445144

* chore: use latest tokio-tungstenite

* ci: split tests into jobs (#129)

* Switch to `hex` (#128)

* fix(core): replace rustc_hex with hex

* fix(providers): replace rustc_hex with hex

* chore: replace rustc-hex with hex

* chore: cargo fmt

* fix(ledger): copy address from string correctly

* chore: fix flaky tests

Fixes #105
This commit is contained in:
Georgios Konstantopoulos 2020-12-31 19:19:14 +02:00 committed by GitHub
parent 7eec705cec
commit 5c1f8f532a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 414 additions and 876 deletions

View File

@ -15,16 +15,12 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install ganache-cli
uses: actions/setup-node@v1
with:
node-version: 10
- name: Install ganache
run: npm install -g ganache-cli
- name: Install libusb (for Ledger)
run: sudo apt update && sudo apt install pkg-config libudev-dev
- name: Install Solc
run: |
mkdir -p "$HOME/bin"
@ -42,7 +38,6 @@ jobs:
chmod u+x "$HOME/bin/geth"
export PATH=$HOME/bin:$PATH
geth version
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
@ -50,19 +45,69 @@ jobs:
toolchain: stable
override: true
components: rustfmt, clippy
- name: cargo test
run: |
export PATH=$HOME/bin:$PATH
cargo test
feature-tests:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install ganache-cli
uses: actions/setup-node@v1
with:
node-version: 10
# TODO: can we combine these shared steps in github actions?
- name: Install ganache
run: npm install -g ganache-cli
- name: Install Solc
run: |
mkdir -p "$HOME/bin"
wget -q https://github.com/ethereum/solidity/releases/download/v0.6.6/solc-static-linux -O $HOME/bin/solc
chmod u+x "$HOME/bin/solc"
export PATH=$HOME/bin:$PATH
solc --version
- name: Install geth
run: |
mkdir -p "$HOME/bin"
wget -q https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.9.23-8c2f2715.tar.gz
tar -xvf geth-linux-amd64-1.9.23-8c2f2715.tar.gz
mv geth-linux-amd64-1.9.23-8c2f2715/geth $HOME/bin/geth
chmod u+x "$HOME/bin/geth"
export PATH=$HOME/bin:$PATH
geth version
- name: Install libusb (for Ledger)
run: sudo apt update && sudo apt install pkg-config libudev-dev
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: cargo test (Celo)
run: |
export PATH=$HOME/bin:$PATH
cargo test --all-features
lint:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: cargo fmt
run: cargo fmt --all -- --check
- name: cargo clippy
run: cargo clippy -- -D warnings

756
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,3 +8,7 @@ members = [
"./ethers-core",
"./ethers-middleware",
]
[patch.crates-io]
reqwest = { git = "https://github.com/seanmonstar/reqwest", branch = "master" }
tokio-tungstenite = { git = "https://github.com/dnaka91/tokio-tungstenite", branch = "tokio-1.0" }

View File

@ -17,16 +17,17 @@ ethers-contract-abigen = { version = "0.1.3", path = "ethers-contract-abigen", o
ethers-contract-derive = { version = "0.1.3", path = "ethers-contract-derive", optional = true }
serde = { version = "1.0.118", default-features = false }
serde_json = { version = "1.0.60", default-features = false }
rustc-hex = { version = "2.1.0", default-features = false }
thiserror = { version = "1.0.22", default-features = false }
serde_json = { version = "1.0.61", default-features = false }
thiserror = { version = "1.0.23", default-features = false }
once_cell = { version = "1.5.2", default-features = false }
pin-project = {version = "1.0.2", default-features = false }
futures-util = { version = "0.3.8", default-features = false }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
[dev-dependencies]
ethers = { version = "0.1.3", path = "../ethers" }
tokio = { version = "0.2.21", default-features = false, features = ["macros"] }
ethers-providers = { version = "0.1.3", path = "../ethers-providers", default-features = false, features = ["ws"] }
tokio = { version = "1.0", default-features = false, features = ["macros"] }
ethers-signers = { version = "0.1.3", path = "../ethers-signers" }
ethers-middleware = { version = "0.1.3", path = "../ethers-middleware" }

View File

@ -12,16 +12,15 @@ keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies]
ethers-core = { version = "0.1.3", path = "../../ethers-core" }
anyhow = "1.0.36"
anyhow = "1.0.37"
curl = "0.4"
Inflector = "0.11"
proc-macro2 = "1.0"
quote = "1.0"
syn = "1.0.12"
url = "2.1"
serde_json = "1.0.53"
once_cell = "1.3.1"
rustc-hex = { version = "2.1.0", default-features = false }
serde_json = "1.0.61"
hex = { version = "0.4.2", default-features = false, features = ["std"] }
[package.metadata.docs.rs]
all-features = true

View File

@ -8,7 +8,6 @@ use anyhow::{anyhow, Context as _, Result};
use inflector::Inflector;
use proc_macro2::{Literal, TokenStream};
use quote::quote;
use rustc_hex::ToHex;
use syn::Ident;
/// Expands a context into a method struct containing all the generated bindings
@ -46,7 +45,7 @@ fn expand_function(function: &Function, alias: Option<Ident>) -> Result<TokenStr
let doc = util::expand_doc(&format!(
"Calls the contract's `{}` (0x{}) function",
function.name,
function.selector().to_hex::<String>()
hex::encode(function.selector())
));
Ok(quote! {

View File

@ -8,7 +8,6 @@ use ethers_core::{
};
use ethers_providers::Middleware;
use rustc_hex::ToHex;
use std::{collections::HashMap, fmt::Debug, hash::Hash, sync::Arc};
use thiserror::Error;
@ -107,7 +106,7 @@ impl BaseContract {
.methods
.get(&signature)
.map(|(name, index)| &self.abi.functions[name][*index])
.ok_or_else(|| Error::InvalidName(signature.to_hex::<String>()))?)
.ok_or_else(|| Error::InvalidName(hex::encode(signature)))?)
}
/// Returns a reference to the contract's ABI
@ -139,7 +138,7 @@ pub(crate) fn decode_event<D: Detokenize>(
let tokens = event
.parse_log(RawLog {
topics,
data: data.0,
data: data.to_vec(),
})?
.params
.into_iter()
@ -199,7 +198,6 @@ where
mod tests {
use super::*;
use ethers_core::{abi::parse_abi, types::U256};
use rustc_hex::FromHex;
#[test]
fn can_parse_function_inputs() {
@ -214,7 +212,7 @@ mod tests {
let encoded = abi.encode("approve", (spender, amount)).unwrap();
assert_eq!(encoded.0.to_hex::<String>(), "095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert_eq!(hex::encode(&encoded), "095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
let (spender2, amount2): (Address, U256) = abi.decode("approve", encoded).unwrap();
assert_eq!(spender, spender2);
@ -239,8 +237,7 @@ mod tests {
.map(|hash| hash.parse::<H256>().unwrap())
.collect::<Vec<_>>();
let data = Bytes::from(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
.from_hex::<Vec<u8>>()
hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
.unwrap(),
);

View File

@ -10,7 +10,6 @@ use ethers_core::{
};
use ethers_providers::Middleware;
use rustc_hex::ToHex;
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
/// A Contract is an abstraction of an executable program on the Ethereum Blockchain.
@ -219,7 +218,7 @@ impl<M: Middleware> Contract<M> {
.methods
.get(&signature)
.map(|(name, index)| &self.base_contract.abi.functions[name][*index])
.ok_or_else(|| Error::InvalidName(signature.to_hex::<String>()))?;
.ok_or_else(|| Error::InvalidName(hex::encode(signature)))?;
self.method_func(function, args)
}

View File

@ -143,9 +143,9 @@ impl<M: Middleware> ContractFactory<M> {
return Err(ContractError::ConstructorError);
}
(None, true) => self.bytecode.clone(),
(Some(constructor), _) => {
Bytes(constructor.encode_input(self.bytecode.0.clone(), &params)?)
}
(Some(constructor), _) => constructor
.encode_input(self.bytecode.to_vec(), &params)?
.into(),
};
// create the tx object. Since we're deploying a contract, `to` is `None`

View File

@ -1,6 +1,6 @@
use ethers_core::{
abi::{Detokenize, Function, Token},
types::{Address, BlockNumber, NameOrAddress, TxHash, U256},
types::{Address, BlockNumber, Bytes, NameOrAddress, TxHash, U256},
};
use ethers_providers::Middleware;
@ -139,7 +139,7 @@ pub struct Multicall<M> {
/// with `data`
pub struct Call {
target: Address,
data: Vec<u8>,
data: Bytes,
function: Function,
}
@ -207,7 +207,7 @@ impl<M: Middleware> Multicall<M> {
(Some(NameOrAddress::Address(target)), Some(data)) => {
let call = Call {
target,
data: data.0,
data,
function: call.function,
};
self.calls.push(call);
@ -357,7 +357,7 @@ impl<M: Middleware> Multicall<M> {
let calls: Vec<(Address, Vec<u8>)> = self
.calls
.iter()
.map(|call| (call.target, call.data.clone()))
.map(|call| (call.target, call.data.to_vec()))
.collect();
// Construct the ContractCall for `aggregate` function to broadcast the transaction

View File

@ -384,9 +384,12 @@ mod celo_tests {
assert_eq!(value, "initial value");
// make a state mutating transaction
// gas estimation costs are sometimes under-reported on celo,
// so we manually set it to avoid failures
let call = contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap();
.unwrap()
.gas(100000);
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();

View File

@ -26,10 +26,11 @@ tiny-keccak = { version = "2.0.2", default-features = false }
# misc
serde = { version = "1.0.118", default-features = false, features = ["derive"] }
serde_json = { version = "1.0.60", default-features = false }
rustc-hex = { version = "2.1.0", default-features = false }
thiserror = { version = "1.0.22", default-features = false }
serde_json = { version = "1.0.61", default-features = false }
thiserror = { version = "1.0.23", default-features = false }
glob = { version = "0.3.0", default-features = false }
bytes = { version = "1.0.0", features = ["serde"] }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
[dev-dependencies]
ethers = { version = "0.1.3", path = "../ethers" }

View File

@ -161,7 +161,7 @@ impl Tokenizable for Bytes {
}
fn into_token(self) -> Token {
Token::Bytes(self.0)
Token::Bytes(self.to_vec())
}
}

View File

@ -1,33 +1,31 @@
use rustc_hex::{FromHex, ToHex};
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Wrapper type around Vec<u8> to deserialize/serialize "0x" prefixed ethereum hex strings
/// Wrapper type around Bytes to deserialize/serialize "0x" prefixed ethereum hex strings
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
pub struct Bytes(
#[serde(
serialize_with = "serialize_bytes",
deserialize_with = "deserialize_bytes"
)]
pub Vec<u8>,
pub bytes::Bytes,
);
impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] {
&self.0[..]
impl Bytes {
pub fn to_vec(&self) -> Vec<u8> {
self.as_ref().to_vec()
}
}
impl Bytes {
/// Returns an empty bytes vector
pub fn new() -> Self {
Bytes(vec![])
impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] {
&self.0.as_ref()
}
}
impl From<Vec<u8>> for Bytes {
fn from(src: Vec<u8>) -> Self {
Self(src)
Self(src.into())
}
}
@ -36,18 +34,18 @@ where
S: Serializer,
T: AsRef<[u8]>,
{
s.serialize_str(&format!("0x{}", x.as_ref().to_hex::<String>()))
s.serialize_str(&format!("0x{}", hex::encode(x.as_ref())))
}
pub fn deserialize_bytes<'de, D>(d: D) -> Result<Vec<u8>, D::Error>
pub fn deserialize_bytes<'de, D>(d: D) -> Result<bytes::Bytes, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(d)?;
if value.len() >= 2 && &value[0..2] == "0x" {
let bytes = FromHex::from_hex(&value[2..])
.map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?;
Ok(bytes)
let bytes: Vec<u8> =
hex::decode(&value[2..]).map_err(|e| Error::custom(format!("Invalid hex: {}", e)))?;
Ok(bytes.into())
} else {
Err(Error::invalid_value(Unexpected::Str(&value), &"0x prefix"))
}

View File

@ -4,7 +4,6 @@ use crate::{
utils::keccak256,
};
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use std::str::FromStr;
/// A log produced by a transaction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -147,11 +146,6 @@ impl Filter {
self
}
pub fn address_str(mut self, address: &str) -> Result<Self, rustc_hex::FromHexError> {
self.address = Some(Address::from_str(address)?);
Ok(self)
}
/// given the event in string form, it hashes it and adds it to the topics to monitor
pub fn event(self, event_name: &str) -> Self {
let hash = H256::from(keccak256(event_name.as_bytes()));
@ -259,7 +253,7 @@ mod tests {
let event = "ValueChanged(address,string,string)";
let t0 = H256::from(keccak256(event.as_bytes()));
let addr = Address::from_str("f817796F60D268A36a57b8D2dF1B97B14C0D0E1d").unwrap();
let addr: Address = "f817796F60D268A36a57b8D2dF1B97B14C0D0E1d".parse().unwrap();
let filter = Filter::new();
let ser = serialize(&filter.clone());

View File

@ -11,7 +11,7 @@ mod transaction;
pub use transaction::{Transaction, TransactionReceipt, TransactionRequest};
mod bytes;
pub use bytes::Bytes;
pub use self::bytes::Bytes;
mod block;
pub use block::{Block, BlockId, BlockNumber};

View File

@ -4,7 +4,6 @@ use crate::{
utils::hash_message,
};
use rustc_hex::{FromHex, ToHex};
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt, str::FromStr};
@ -26,7 +25,7 @@ pub enum SignatureError {
InvalidLength(usize),
/// When parsing a signature from string to hex
#[error(transparent)]
DecodingError(#[from] rustc_hex::FromHexError),
DecodingError(#[from] hex::FromHexError),
/// Thrown when signature verification failed (i.e. when the address that
/// produced the signature did not match the expected address)
#[error("Signature verification failed. Expected {0}, got {1}")]
@ -66,7 +65,7 @@ pub struct Signature {
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::<String>())
write!(f, "{}", hex::encode(sig))
}
}
@ -174,7 +173,7 @@ impl FromStr for Signature {
type Err = SignatureError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = s.from_hex::<Vec<u8>>()?;
let bytes = hex::decode(s)?;
Signature::try_from(&bytes[..])
}
}

View File

@ -6,7 +6,6 @@ use crate::{
use rlp::RlpStream;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
// Number of tx fields before signing
#[cfg(not(feature = "celo"))]
@ -94,12 +93,6 @@ impl TransactionRequest {
self
}
/// Sets the `to` field in the transaction to the provided value
pub fn send_to_str(mut self, to: &str) -> Result<Self, rustc_hex::FromHexError> {
self.to = Some(Address::from_str(to)?.into());
Ok(self)
}
/// Sets the `to` field in the transaction to the provided value
pub fn to<T: Into<NameOrAddress>>(mut self, to: T) -> Self {
self.to = Some(to.into());
@ -196,7 +189,7 @@ impl TransactionRequest {
rlp_opt(rlp, self.to.as_ref());
rlp_opt(rlp, self.value);
rlp_opt(rlp, self.data.as_ref().map(|d| &d.0[..]));
rlp_opt(rlp, self.data.as_ref().map(|d| d.as_ref()));
}
}
@ -328,7 +321,7 @@ impl Transaction {
}
pub fn hash(&self) -> H256 {
keccak256(&self.rlp().0).into()
keccak256(&self.rlp().as_ref()).into()
}
pub fn rlp(&self) -> Bytes {
@ -343,7 +336,7 @@ impl Transaction {
rlp_opt(&mut rlp, self.to);
rlp.append(&self.value);
rlp.append(&self.input.0);
rlp.append(&self.input.as_ref());
rlp.append(&self.v);
rlp.append(&self.r);
rlp.append(&self.s);

View File

@ -3,7 +3,6 @@ use crate::{
utils::{secret_key_to_address, unused_port},
};
use k256::{ecdsa::SigningKey, SecretKey as K256SecretKey};
use rustc_hex::FromHex;
use std::{
io::{BufRead, BufReader},
process::{Child, Command},
@ -161,9 +160,7 @@ impl Ganache {
if is_private_key && line.starts_with('(') {
let key_str = &line[6..line.len() - 1];
let key_hex = key_str
.from_hex::<Vec<u8>>()
.expect("could not parse as hex");
let key_hex = hex::decode(key_str).expect("could not parse as hex");
let key = K256SecretKey::from_bytes(&key_hex).expect("did not get private key");
addresses.push(secret_key_to_address(&SigningKey::from(&key)));
private_keys.push(key);

View File

@ -59,13 +59,12 @@ pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value {
#[cfg(test)]
mod tests {
use super::*;
use rustc_hex::ToHex;
#[test]
// from https://emn178.github.io/online-tools/keccak_256.html
fn test_keccak256() {
assert_eq!(
keccak256(b"hello").to_hex::<String>(),
hex::encode(keccak256(b"hello")),
"1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
);
}

View File

@ -25,7 +25,6 @@ pub use rlp;
use crate::types::{Address, Bytes, U256};
use k256::{ecdsa::SigningKey, EncodedPoint as K256PublicKey};
use rustc_hex::ToHex;
use std::convert::TryInto;
/// 1 Ether = 1e18 Wei == 0x0de0b6b3a7640000 Wei
@ -125,10 +124,10 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
Some(chain_id) => format!("{}0x{:x}", chain_id, addr),
None => format!("{:x}", addr),
};
let hash = keccak256(&prefixed_addr).to_hex::<String>();
let hash = hex::encode(keccak256(&prefixed_addr));
let hash = hash.as_bytes();
let addr_hex = addr.as_bytes().to_hex::<String>();
let addr_hex = hex::encode(addr.as_bytes());
let addr_hex = addr_hex.as_bytes();
addr_hex
@ -161,7 +160,6 @@ pub(crate) fn unused_port() -> u16 {
#[cfg(test)]
mod tests {
use super::*;
use rustc_hex::FromHex;
#[test]
fn wei_in_ether() {
@ -324,8 +322,8 @@ mod tests {
),
] {
let from = from.parse::<Address>().unwrap();
let salt = salt.from_hex::<Vec<u8>>().unwrap();
let init_code = init_code.from_hex::<Vec<u8>>().unwrap();
let salt = hex::decode(salt).unwrap();
let init_code = hex::decode(init_code).unwrap();
let expected = expected.parse::<Address>().unwrap();
assert_eq!(expected, get_create2_address(from, salt, init_code))
}

View File

@ -1,6 +1,5 @@
use crate::{abi::Abi, types::Bytes};
use glob::glob;
use rustc_hex::FromHex;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt, io::BufRead, path::PathBuf, process::Command};
use thiserror::Error;
@ -145,9 +144,7 @@ impl Solc {
.expect("could not parse `solc` abi, this should never happen");
// parse the bytecode
let bytecode = contract
.bin
.from_hex::<Vec<u8>>()
let bytecode = hex::decode(contract.bin)
.expect("solc did not produce valid bytecode")
.into();
(name, CompiledContract { abi, bytecode })

View File

@ -30,15 +30,13 @@ serde-aux = { version = "2.1.0", default-features = false }
reqwest = { version = "0.10.10", default-features = false, features = ["json", "rustls-tls"] }
url = { version = "2.2.0", default-features = false }
# optional for runtime
tokio = { version = "0.2.22", optional = true }
async-std = { version = "1.6.5", optional = true }
tokio = { version = "1.0" }
[dev-dependencies]
ethers = { version = "0.1.3", path = "../ethers" }
futures-executor = { version = "0.3.5", features = ["thread-pool"] }
rustc-hex = "2.1.0"
tokio = { version = "0.2.21", default-features = false, features = ["rt-core", "macros"] }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
tokio = { version = "1.0", default-features = false, features = ["rt", "macros", "time"] }
[features]
celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo"]

View File

@ -12,18 +12,8 @@ use std::sync::Arc;
use std::{pin::Pin, time::Instant};
use thiserror::Error;
#[cfg(any(feature = "async-std", feature = "tokio"))]
use tracing_futures::Instrument;
#[cfg(all(not(feature = "tokio"), feature = "async-std"))]
use async_std::task::spawn;
#[cfg(all(feature = "tokio", not(feature = "async-std")))]
use tokio::spawn;
#[cfg(all(feature = "tokio", all(feature = "async-std")))]
// this should never happen, used to silence clippy warnings
fn spawn<T>(_: T) {
unimplemented!("do not use both tokio and async-std!")
}
use tracing_futures::Instrument;
/// Trait for fetching updated gas prices after a transaction has been first
/// broadcast
@ -46,10 +36,6 @@ pub enum Frequency {
/// A Gas escalator allows bumping transactions' gas price to avoid getting them
/// stuck in the memory pool.
///
/// If the crate is compiled with the `tokio` or `async-std` features, it will
/// automatically start bumping transactions in the background. Otherwise, you need
/// to spawn the `escalate` call yourself with an executor of choice.
///
/// ```no_run
/// use ethers::{
/// providers::{Provider, Http},
@ -135,7 +121,6 @@ where
txs: Arc::new(Mutex::new(Vec::new())),
};
#[cfg(any(feature = "async-std", feature = "tokio"))]
{
let this2 = this.clone();
spawn(async move {

View File

@ -135,7 +135,7 @@ where
// Get the actual transaction hash
let rlp = tx.rlp_signed(&signature);
let hash = keccak256(&rlp.0);
let hash = keccak256(&rlp.as_ref());
// This function should not be called with ENS names
let to = tx.to.map(|to| match to {
@ -298,7 +298,6 @@ where
mod tests {
use super::*;
use ethers::{providers::Provider, signers::LocalWallet};
use rustc_hex::FromHex;
use std::convert::TryFrom;
#[tokio::test]
@ -337,7 +336,7 @@ mod tests {
.unwrap()
);
let expected_rlp = Bytes("f869808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a0c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895a0727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68".from_hex().unwrap());
let expected_rlp = Bytes::from(hex::decode("f869808504e3b29200831e848094f0109fc8df283027b6285cc889f5aa624eac1f55843b9aca008025a0c9cf86333bcb065d140032ecaab5d9281bde80f21b9687b3e94161de42d51895a0727a108a0b8d101465414033c3f705a9c7b826e596766046ee1183dbc8aeaa68").unwrap());
assert_eq!(tx.rlp(), expected_rlp);
}
}

View File

@ -43,7 +43,7 @@ async fn gas_escalator_live() {
.unwrap();
// Wait a bunch of seconds and refresh etherscan to see the transactions get bumped
tokio::time::delay_for(std::time::Duration::from_secs(100)).await;
tokio::time::sleep(std::time::Duration::from_secs(100)).await;
// TODO: Figure out how to test this behavior properly in a local network. If the gas price was bumped
// then the tx hash will be different

View File

@ -35,47 +35,16 @@ pin-project = { version = "1.0.2", default-features = false }
tracing = { version = "0.1.22", default-features = false }
tracing-futures = { version = "0.2.4", default-features = false }
# ws support async-std and tokio runtimes for the convenience methods
async-tungstenite = { version = "0.6.0", default-features = false, optional = true }
async-std = { version = "1.6.2", default-features = false, optional = true }
tokio = { version = "0.2.21", default-features = false, optional = true }
# needed for tls
real-tokio-native-tls = { package = "tokio-native-tls", version = "0.1.0", default-features = false, optional = true }
async-tls = { version = "0.7.0", optional = true }
# tokio
tokio = { version = "1.0", default-features = false, optional = true }
tokio-tungstenite = { version = "0.12.0", default-features = false, features = ["connect", "tls"], optional = true }
[dev-dependencies]
ethers = { version = "0.1.3", path = "../ethers" }
rustc-hex = "2.1.0"
tokio = { version = "0.2.21", default-features = false, features = ["rt-core", "macros"] }
async-std = { version = "1.6.2", default-features = false, features = ["attributes"] }
async-tungstenite = { version = "0.6.0", default-features = false, features = ["tokio-runtime"] }
tokio = { version = "1.0", default-features = false, features = ["rt", "macros"] }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
[features]
# slightly opinionated, but for convenience we default to tokio-tls
# to allow websockets w/ TLS support
default = ["tokio-tls"]
default = ["ws"]
celo = ["ethers-core/celo"]
ws = ["async-tungstenite"]
tokio-runtime = [
"ws",
"tokio",
"async-tungstenite/tokio-runtime"
]
tokio-tls = [
"tokio-runtime",
"async-tungstenite/tokio-native-tls",
"real-tokio-native-tls"
]
async-std-runtime = [
"ws",
"async-std",
"async-tungstenite/async-std-runtime"
]
async-std-tls = [
"async-std-runtime",
"async-tungstenite/async-tls",
"async-tls"
]
ws = ["tokio", "tokio-tungstenite"]

View File

@ -69,7 +69,6 @@ pub fn namehash(name: &str) -> H256 {
#[cfg(test)]
mod tests {
use super::*;
use rustc_hex::FromHex;
fn assert_hex(hash: H256, val: &str) {
let v = if val.starts_with("0x") {
@ -78,14 +77,11 @@ mod tests {
val
};
assert_eq!(hash.0.to_vec(), v.from_hex::<Vec<u8>>().unwrap());
assert_eq!(hash.0.to_vec(), hex::decode(v).unwrap());
}
#[test]
fn test_namehash() {
dbg!("00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
.from_hex::<Vec<u8>>()
.unwrap());
for (name, expected) in &[
(
"",

View File

@ -30,28 +30,9 @@
//!
//! # Websockets
//!
//! The crate has support for WebSockets. If none of the provided async runtime
//! features are enabled, you must manually instantiate the WS connection and wrap
//! it with with a [`Ws::new`](method@crate::Ws::new) call.
//! The crate has support for WebSockets via Tokio.
//!
//! ```ignore
//! use ethers::providers::Ws;
//!
//! let ws = Ws::new(...);
//! ```
//!
//! If you have compiled the library with any of the following features, you may
//! instantiate the websocket instance with the `connect` call and your URL:
//! - `tokio-runtime`: Uses `tokio` as the runtime
//! - `async-std-runtime`: Uses `async-std-runtime`
//!
//! ```no_run
//! # #[cfg(any(
//! # feature = "tokio-runtime",
//! # feature = "tokio-tls",
//! # feature = "async-std-runtime",
//! # feature = "async-std-tls",
//! # ))]
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
//! # use ethers::providers::Ws;
//! let ws = Ws::connect("ws://localhost:8545").await?;
@ -59,24 +40,6 @@
//! # }
//! ```
//!
//! TLS support is also provided via the following feature flags:
//! - `tokio-tls`
//! - `async-tls`
//!
//! ```no_run
//! # #[cfg(any(
//! # feature = "tokio-runtime",
//! # feature = "tokio-tls",
//! # feature = "async-std-runtime",
//! # feature = "async-std-tls",
//! # ))]
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
//! # use ethers::providers::Ws;
//! let ws = Ws::connect("wss://localhost:8545").await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Ethereum Name Service
//!
//! The provider may also be used to resolve [Ethereum Name Service](https://ens.domains) (ENS) names

View File

@ -685,11 +685,11 @@ impl<P: JsonRpcClient> Provider<P> {
}
}
#[cfg(any(feature = "tokio-runtime", feature = "async-std-runtime"))]
#[cfg(feature = "ws")]
impl Provider<crate::Ws> {
/// Direct connection to a websocket endpoint
pub async fn connect(
url: impl async_tungstenite::tungstenite::client::IntoClientRequest + Unpin,
url: impl tokio_tungstenite::tungstenite::client::IntoClientRequest + Unpin,
) -> Result<Self, ProviderError> {
let ws = crate::Ws::connect(url).await?;
Ok(Self::new(ws))
@ -730,8 +730,8 @@ impl Provider<MockProvider> {
///
/// If the provided bytes were not an interpretation of an address
fn decode_bytes<T: Detokenize>(param: ParamType, bytes: Bytes) -> T {
let tokens =
abi::decode(&[param], &bytes.0).expect("could not abi-decode bytes to address tokens");
let tokens = abi::decode(&[param], &bytes.as_ref())
.expect("could not abi-decode bytes to address tokens");
T::from_tokens(tokens).expect("could not parse tokens as address")
}

View File

@ -84,7 +84,7 @@ where
}
#[pinned_drop]
impl<'a, P, R> PinnedDrop for SubscriptionStream<'a, P, R>
impl<P, R> PinnedDrop for SubscriptionStream<'_, P, R>
where
P: PubsubClient,
R: DeserializeOwned,

View File

@ -6,7 +6,6 @@ use crate::{
use ethers_core::types::U256;
use async_trait::async_trait;
use async_tungstenite::tungstenite::{self, protocol::Message};
use futures_channel::{mpsc, oneshot};
use futures_util::{
sink::{Sink, SinkExt},
@ -22,53 +21,21 @@ use std::{
},
};
use thiserror::Error;
// `connect_async` adapter
#[cfg(all(feature = "async-std-runtime", not(feature = "tokio-runtime")))]
use async_tungstenite::async_std::connect_async;
#[cfg(feature = "tokio-runtime")]
use async_tungstenite::tokio::connect_async;
use tokio_tungstenite::{
connect_async,
tungstenite::{self, protocol::Message},
};
/// A JSON-RPC Client over Websockets.
///
/// If the library is not compiled with any runtime support, then you will have
/// to manually instantiate a websocket connection and call `Provider::new` on it.
///
/// ```ignore
/// use ethers::providers::Ws;
///
/// let ws = Ws::new(...)
/// ```
///
/// If you have compiled the library with any of the following features, you may
/// instantiate the websocket instance with the `connect` call and your URL:
/// - `tokio-runtime`: Uses `tokio` as the runtime
/// - `tokio-tls`: Same as `tokio-runtime` but with TLS support
/// - `async-std-runtime`: Uses `async-std-runtime`
/// - `async-tls`: Same as `async-std-runtime` but with TLS support
///
/// ```no_run
/// # #[cfg(any(
/// # feature = "tokio-runtime",
/// # feature = "tokio-tls",
/// # feature = "async-std-runtime",
/// # feature = "async-std-tls",
/// # ))]
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// use ethers::providers::Ws;
///
/// let ws = Ws::connect("ws://localhost:8545").await?;
///
/// // If built with TLS support (otherwise will get a "TLS Support not compiled in" error)
/// let ws = Ws::connect("wss://localhost:8545").await?;
/// # Ok(())
/// # }
/// ```
///
/// This feature is built using [`async-tungstenite`](https://docs.rs/async-tungstenite). If you need other runtimes,
/// consider importing `async-tungstenite` with the [corresponding feature
/// flag](https://github.com/sdroege/async-tungstenite/blob/master/Cargo.toml#L15-L22)
/// for your runtime.
#[derive(Clone)]
pub struct Ws {
id: Arc<AtomicU64>,
@ -123,8 +90,7 @@ impl Ws {
}
}
/// Initializes a new WebSocket Client, assuming usage of tokio or async-std
#[cfg(any(feature = "tokio-runtime", feature = "async-std-runtime"))]
/// Initializes a new WebSocket Client
pub async fn connect(
url: impl tungstenite::client::IntoClientRequest + Unpin,
) -> Result<Self, ClientError> {
@ -214,7 +180,6 @@ where
}
/// Spawns the event loop
#[allow(unused)]
fn spawn(mut self)
where
S: 'static,
@ -225,12 +190,7 @@ where
}
};
#[cfg(all(not(feature = "async-std-runtime"), feature = "tokio-runtime"))]
tokio::spawn(f);
// TODO: Ensure that this works with both async-std and tokio.
// Remove allow(unused) when fixed.
#[cfg(all(feature = "async-std-runtime", not(feature = "tokio-runtime")))]
async_std::task::spawn(f);
}
/// Processes 1 item selected from the incoming `requests` or `ws`

View File

@ -41,30 +41,17 @@ mod eth_tests {
}
// Without TLS this would error with "TLS Support not compiled in"
#[test]
#[cfg(any(feature = "async-std-tls", feature = "tokio-tls"))]
fn ssl_websocket() {
// this is extremely ugly but I couldn't figure out a better way of having
// a shared async test for both runtimes
#[cfg(feature = "async-std-tls")]
let block_on = async_std::task::block_on;
#[cfg(feature = "tokio-tls")]
let mut runtime = tokio::runtime::Runtime::new().unwrap();
#[cfg(feature = "tokio-tls")]
let mut block_on = |x| runtime.block_on(x);
#[tokio::test]
async fn ssl_websocket() {
use ethers::providers::Ws;
block_on(async move {
let ws = Ws::connect("wss://rinkeby.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
.await
.unwrap();
let provider = Provider::new(ws);
let _number = provider.get_block_number().await.unwrap();
});
let ws = Ws::connect("wss://rinkeby.infura.io/ws/v3/c60b0bb42f8a4c6481ecd229eddaca27")
.await
.unwrap();
let provider = Provider::new(ws);
let _number = provider.get_block_number().await.unwrap();
}
#[tokio::test]
#[cfg(feature = "tokio-runtime")]
async fn watch_blocks_websocket() {
use ethers::{
providers::{StreamExt, Ws},
@ -72,7 +59,7 @@ mod eth_tests {
};
let ganache = Ganache::new().block_time(2u64).spawn();
let (ws, _) = async_tungstenite::tokio::connect_async(ganache.ws_endpoint())
let (ws, _) = tokio_tungstenite::connect_async(ganache.ws_endpoint())
.await
.unwrap();
let provider = Provider::new(Ws::new(ws)).interval(Duration::from_millis(500u64));
@ -149,7 +136,6 @@ mod celo_tests {
use super::*;
use ethers::types::{Randomness, H256};
use futures_util::stream::StreamExt;
use rustc_hex::FromHex;
#[tokio::test]
// https://alfajores-blockscout.celo-testnet.org/tx/0x544ea96cddb16aeeaedaf90885c1e02be4905f3eb43d6db3f28cac4dbe76a625/internal_transactions
@ -176,14 +162,16 @@ mod celo_tests {
assert_eq!(
block.randomness,
Randomness {
committed: "003e12deb86292844274493e9ab6e57ed1e276202c16799d97af723eb0d3253f"
.from_hex::<Vec<u8>>()
.unwrap()
.into(),
revealed: "1333b3b45e0385da48a01b4459aeda7607867ef6a41167cfdeefa49b9fdce6d7"
.from_hex::<Vec<u8>>()
.unwrap()
.into(),
committed: hex::decode(
"003e12deb86292844274493e9ab6e57ed1e276202c16799d97af723eb0d3253f"
)
.unwrap()
.into(),
revealed: hex::decode(
"1333b3b45e0385da48a01b4459aeda7607867ef6a41167cfdeefa49b9fdce6d7"
)
.unwrap()
.into(),
}
);
}

View File

@ -16,23 +16,23 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
ethers-core = { version = "0.1.3", path = "../ethers-core" }
thiserror = { version = "1.0.22", default-features = false }
futures-util = { version = "0.3.8", default-features = false }
futures-executor = { version = "0.3.8", default-features = false }
serde = { version = "1.0.118", default-features = false }
coins-ledger = { git = "https://github.com/summa-tx/bitcoins-rs", default-features = false, optional = true, branch = "master" }
rustc-hex = { version = "2.1.0", default-features = false }
hex = { version = "0.4.2", default-features = false, features = ["std"] }
async-trait = { version = "0.1.40", default-features = false }
elliptic-curve = { version = "0.8.4", default-features = false }
sha2 = { version = "0.9.2", default-features = false }
rand = { version = "0.7.3", default-features = false }
yubihsm = { version = "0.37.0", features = ["secp256k1", "http", "usb"], optional = true }
futures-util = "0.3.8"
futures-executor = "0.3.8"
[dev-dependencies]
ethers = { version = "0.1.3", path = "../ethers" }
yubihsm = { version = "0.37.0", features = ["secp256k1", "usb", "mockhsm"] }
tokio = { version = "0.2.21", default-features = false, features = ["macros"] }
tokio = { version = "1.0", default-features = false, features = ["macros"] }
serde_json = { version = "1.0.55", default-features = false }
[features]

View File

@ -95,8 +95,10 @@ impl LedgerEthereum {
let address = {
// extract the address from the response
let offset = 1 + result[0] as usize;
let address = &result[offset + 1..offset + 1 + result[offset] as usize];
std::str::from_utf8(address)?.parse::<Address>()?
let address_str = &result[offset + 1..offset + 1 + result[offset] as usize];
let mut address = [0; 20];
address.copy_from_slice(&hex::decode(address_str)?);
Address::from(address)
};
Ok(address)
@ -207,7 +209,6 @@ mod tests {
use super::*;
use crate::Signer;
use ethers::prelude::*;
use rustc_hex::FromHex;
use std::str::FromStr;
#[tokio::test]
@ -239,11 +240,12 @@ mod tests {
.unwrap();
// approve uni v2 router 0xff
let data = "095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".from_hex::<Vec<u8>>().unwrap();
let data = hex::decode("095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let tx_req = TransactionRequest::new()
.send_to_str("2ed7afa17473e17ac59908f088b4371d28585476")
.unwrap()
.to("2ed7afa17473e17ac59908f088b4371d28585476"
.parse::<Address>()
.unwrap())
.gas(1000000)
.gas_price(400e9 as u64)
.nonce(5)

View File

@ -34,7 +34,7 @@ pub enum LedgerError {
UnexpectedNullResponse,
#[error(transparent)]
HexError(#[from] rustc_hex::FromHexError),
HexError(#[from] hex::FromHexError),
#[error("Error when decoding UTF8 Response: {0}")]
Utf8Error(#[from] std::str::Utf8Error),

View File

@ -9,7 +9,6 @@ use ethers_core::{
types::Address,
utils::keccak256,
};
use rustc_hex::FromHex;
use std::str::FromStr;
impl Clone for Wallet<SigningKey> {
@ -87,9 +86,7 @@ impl FromStr for Wallet<SigningKey> {
type Err = K256Error;
fn from_str(src: &str) -> Result<Self, Self::Err> {
let src = src
.from_hex::<Vec<u8>>()
.expect("invalid hex when reading PrivateKey");
let src = hex::decode(src).expect("invalid hex when reading PrivateKey");
let sk = SigningKey::from_bytes(&src).unwrap(); // TODO
Ok(sk.into())
}

View File

@ -67,13 +67,11 @@ impl From<YubiSigner<Secp256k1>> for Wallet<YubiSigner<Secp256k1>> {
mod tests {
use super::*;
use crate::Signer;
use rustc_hex::FromHex;
use std::str::FromStr;
#[tokio::test]
async fn from_key() {
let key = "2d8c44dc2dd2f0bea410e342885379192381e82d855b1b112f9b55544f1e0900"
.from_hex::<Vec<u8>>()
let key = hex::decode("2d8c44dc2dd2f0bea410e342885379192381e82d855b1b112f9b55544f1e0900")
.unwrap();
let connector = yubihsm::Connector::mockhsm();

View File

@ -20,16 +20,6 @@ rustdoc-args = ["--cfg", "docsrs"]
features = ["full"]
[features]
abigen = ["contract", "ethers-contract/abigen"]
default = ["full"]
full = [
"contract",
"providers",
"signers",
"core",
"middleware",
]
celo = [
"ethers-core/celo",
"ethers-providers/celo",
@ -38,27 +28,23 @@ celo = [
"ethers-middleware/celo",
]
core = ["ethers-core"]
contract = ["ethers-contract"]
providers = ["ethers-providers"]
middleware = ["ethers-middleware"]
signers = ["ethers-signers"]
ledger = ["ethers-signers/ledger"]
yubi = ["ethers-signers/yubi"]
ws = ["ethers-providers/ws"]
abigen = ["ethers-contract/abigen"]
[dependencies]
ethers-contract = { version = "0.1.3", path = "../ethers-contract", optional = true }
ethers-core = { version = "0.1.3", path = "../ethers-core", optional = true }
ethers-providers = { version = "0.1.3", path = "../ethers-providers", optional = true }
ethers-signers = { version = "0.1.3", path = "../ethers-signers", optional = true }
ethers-middleware = { version = "0.1.3", path = "../ethers-middleware", optional = true }
ethers-contract = { version = "0.1.3", path = "../ethers-contract" }
ethers-core = { version = "0.1.3", path = "../ethers-core" }
ethers-providers = { version = "0.1.3", path = "../ethers-providers" }
ethers-signers = { version = "0.1.3", path = "../ethers-signers" }
ethers-middleware = { version = "0.1.3", path = "../ethers-middleware" }
[dev-dependencies]
ethers-contract = { version = "0.1.3", path = "../ethers-contract", features = ["abigen"] }
ethers-providers = { version = "0.1.3", path = "../ethers-providers" }
anyhow = "1.0.31"
anyhow = "1.0.36"
rand = "0.7"
serde = { version = "1.0.110", features = ["derive"] }
serde_json = "1.0.53"
tokio = { version = "0.2.21", features = ["macros"] }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }

View File

@ -82,50 +82,28 @@
//! [`utils`]: core::utils
//! [`abi`]: core::abi
//! [`types`]: core::types
#[cfg(feature = "contract")]
pub use ethers_contract as contract;
#[cfg(feature = "providers")]
pub use ethers_providers as providers;
#[cfg(feature = "signers")]
pub use ethers_signers as signers;
#[cfg(feature = "core")]
pub use ethers_core as core;
#[cfg(feature = "middleware")]
pub use ethers_middleware as middleware;
pub use ethers_providers as providers;
pub use ethers_signers as signers;
// Re-export ethers_core::utils/types/abi
// We hide these docs so that the rustdoc links send the visitor
// to the corresponding crate, instead of the re-export
#[doc(hidden)]
#[cfg(feature = "core")]
pub use ethers_core::abi;
#[doc(hidden)]
#[cfg(feature = "core")]
pub use ethers_core::types;
#[doc(hidden)]
#[cfg(feature = "core")]
pub use ethers_core::utils;
/// Easy imports of frequently used type definitions and traits
#[doc(hidden)]
pub mod prelude {
#[cfg(feature = "contract")]
pub use ethers_contract::*;
#[cfg(feature = "providers")]
pub use ethers_providers::*;
#[cfg(feature = "signers")]
pub use ethers_signers::*;
#[cfg(feature = "middleware")]
pub use ethers_middleware::*;
#[cfg(feature = "core")]
pub use ethers_core::types::*;
pub use ethers_middleware::*;
pub use ethers_providers::*;
pub use ethers_signers::*;
}