chore: add rustfmt.toml (#537)

* chore: add rustfmt.toml

* rustfmt

* chore: Update readme with fmt info

* ci: update ci

* chore: rustfmt

* rustfmt

* rustfmt

* ci: install libudev

* chore(clippy): make clippy happy

* chore(clippy): make clippy happy

* revert ci

* ci: install libudev
This commit is contained in:
Matthias Seitz 2021-10-29 14:29:35 +02:00 committed by GitHub
parent eede86df41
commit dcf20022c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 1262 additions and 3012 deletions

View File

@ -110,20 +110,22 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install stable toolchain
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
override: true
- name: Install Dependencies
run: sudo apt-get install libudev-dev
- uses: Swatinem/rust-cache@v1
with:
cache-on-failure: true
- name: cargo fmt
run: cargo fmt --all -- --check
run: cargo +nightly fmt --all -- --check
- name: cargo clippy
run: cargo clippy -- -D warnings
run: cargo +nightly clippy --all --all-features -- -D warnings
wasm:
name: WASM

20
Cargo.lock generated
View File

@ -373,9 +373,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.70"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "ccm"
@ -647,9 +647,9 @@ dependencies = [
[[package]]
name = "der"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adca118c71ecd9ae094d4b68257b3fdfcb711a612b9eec7b5a0d27a5a70a5b4"
checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
dependencies = [
"const-oid",
]
@ -2689,9 +2689,9 @@ dependencies = [
[[package]]
name = "sharded-slab"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
@ -2796,9 +2796,9 @@ dependencies = [
[[package]]
name = "synstructure"
version = "0.12.5"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
@ -3121,9 +3121,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"

View File

@ -98,7 +98,9 @@ Thanks for your help improving the project! We are so happy to have you! We have
[a contributing guide](https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md) to
help you get involved in the ethers-rs project.
If you make a Pull Request, do not forget to add your changes in the [CHANGELOG](CHANGELOG.md).
If you make a Pull Request, do not forget to add your changes in the [CHANGELOG](CHANGELOG.md) and ensure your code if
properly formatted with `cargo +nightly fmt` and clippy is happy `cargo clippy`, you can even try to let clippy fix simple
issues itself: `cargo +nightly clippy --fix -Z unstable-options`
## Related Projects

View File

@ -5,10 +5,8 @@ mod methods;
mod structs;
mod types;
use super::util;
use super::Abigen;
use crate::contract::structs::InternalStructs;
use crate::rawabi::RawAbi;
use super::{util, Abigen};
use crate::{contract::structs::InternalStructs, rawabi::RawAbi};
use anyhow::{anyhow, Context as _, Result};
use ethers_core::abi::{Abi, AbiParser};
@ -38,14 +36,8 @@ pub struct ExpandedContract {
impl ExpandedContract {
/// Merges everything into a single module
pub fn into_tokens(self) -> TokenStream {
let ExpandedContract {
module,
imports,
contract,
events,
call_structs,
abi_structs,
} = self;
let ExpandedContract { module, imports, contract, events, call_structs, abi_structs } =
self;
quote! {
// export all the created data types
pub use #module::*;
@ -96,10 +88,8 @@ impl Context {
/// Expands the whole rust contract
pub fn expand(&self) -> Result<ExpandedContract> {
let name = &self.contract_name;
let name_mod = util::ident(&format!(
"{}_mod",
self.contract_name.to_string().to_lowercase()
));
let name_mod =
util::ident(&format!("{}_mod", self.contract_name.to_string().to_lowercase()));
let abi_name = super::util::safe_ident(&format!("{}_ABI", name.to_string().to_uppercase()));
@ -182,16 +172,16 @@ impl Context {
};
// try to extract all the solidity structs from the normal JSON ABI
// we need to parse the json abi again because we need the internalType fields which are omitted by ethabi. If the ABI was defined as human readable we use the `internal_structs` from the Abi Parser
// we need to parse the json abi again because we need the internalType fields which are
// omitted by ethabi. If the ABI was defined as human readable we use the `internal_structs`
// from the Abi Parser
let internal_structs = if human_readable {
let mut internal_structs = InternalStructs::default();
// the types in the abi_parser are already valid rust types so simply clone them to make it consistent with the `RawAbi` variant
internal_structs.rust_type_names.extend(
abi_parser
.function_params
.values()
.map(|ty| (ty.clone(), ty.clone())),
);
// the types in the abi_parser are already valid rust types so simply clone them to make
// it consistent with the `RawAbi` variant
internal_structs
.rust_type_names
.extend(abi_parser.function_params.values().map(|ty| (ty.clone(), ty.clone())));
internal_structs.function_params = abi_parser.function_params.clone();
internal_structs.outputs = abi_parser.outputs.clone();
@ -212,10 +202,7 @@ impl Context {
for (signature, alias) in args.method_aliases.into_iter() {
let alias = syn::parse_str(&alias)?;
if method_aliases.insert(signature.clone(), alias).is_some() {
return Err(anyhow!(
"duplicate method signature '{}' in method aliases",
signature,
));
return Err(anyhow!("duplicate method signature '{}' in method aliases", signature,))
}
}

View File

@ -98,7 +98,7 @@ impl Context {
/// The name ident of the events enum
fn expand_event_enum_name(&self) -> Ident {
util::ident(&format!("{}Events", self.contract_name.to_string()))
util::ident(&format!("{}Events", self.contract_name))
}
/// Expands the `events` function that bundles all declared events of this contract
@ -112,10 +112,7 @@ impl Context {
let ty = if iter.next().is_some() {
self.expand_event_enum_name()
} else {
expand_struct_name(
event,
self.event_aliases.get(&event.abi_signature()).cloned(),
)
expand_struct_name(event, self.event_aliases.get(&event.abi_signature()).cloned())
};
quote! {
@ -149,7 +146,7 @@ impl Context {
.map(SolStruct::name)
.map(util::ident)
{
return Ok(quote! {::std::vec::Vec<#ty>});
return Ok(quote! {::std::vec::Vec<#ty>})
}
}
quote! { #ethers_core::types::H256 }
@ -165,19 +162,15 @@ impl Context {
.map(util::ident)
{
let size = Literal::usize_unsuffixed(*size);
return Ok(quote! {[#ty; #size]});
return Ok(quote! {[#ty; #size]})
}
}
quote! { #ethers_core::types::H256 }
}
(ParamType::Tuple(..), true) => {
// represents a struct
if let Some(ty) = self
.abi_parser
.structs
.get(&input.name)
.map(SolStruct::name)
.map(util::ident)
if let Some(ty) =
self.abi_parser.structs.get(&input.name).map(SolStruct::name).map(util::ident)
{
quote! {#ty}
} else {
@ -337,12 +330,8 @@ mod tests {
}
fn test_context_with_alias(sig: &str, alias: &str) -> Context {
Context::from_abigen(
Abigen::new("TestToken", "[]")
.unwrap()
.add_event_alias(sig, alias),
)
.unwrap()
Context::from_abigen(Abigen::new("TestToken", "[]").unwrap().add_event_alias(sig, alias))
.unwrap()
}
#[test]
@ -385,21 +374,9 @@ mod tests {
let event = Event {
name: "Transfer".into(),
inputs: vec![
EventParam {
name: "from".into(),
kind: ParamType::Address,
indexed: true,
},
EventParam {
name: "to".into(),
kind: ParamType::Address,
indexed: true,
},
EventParam {
name: "amount".into(),
kind: ParamType::Uint(256),
indexed: false,
},
EventParam { name: "from".into(), kind: ParamType::Address, indexed: true },
EventParam { name: "to".into(), kind: ParamType::Address, indexed: true },
EventParam { name: "amount".into(), kind: ParamType::Uint(256), indexed: false },
],
anonymous: false,
};
@ -417,16 +394,8 @@ mod tests {
let event = Event {
name: "Foo".into(),
inputs: vec![
EventParam {
name: "a".into(),
kind: ParamType::Bool,
indexed: false,
},
EventParam {
name: String::new(),
kind: ParamType::Address,
indexed: false,
},
EventParam { name: "a".into(), kind: ParamType::Bool, indexed: false },
EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
],
anonymous: false,
};
@ -449,16 +418,8 @@ mod tests {
let event = Event {
name: "Foo".into(),
inputs: vec![
EventParam {
name: "a".into(),
kind: ParamType::Bool,
indexed: false,
},
EventParam {
name: String::new(),
kind: ParamType::Address,
indexed: false,
},
EventParam { name: "a".into(), kind: ParamType::Bool, indexed: false },
EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
],
anonymous: false,
};
@ -482,16 +443,8 @@ mod tests {
let event = Event {
name: "Foo".into(),
inputs: vec![
EventParam {
name: String::new(),
kind: ParamType::Bool,
indexed: false,
},
EventParam {
name: String::new(),
kind: ParamType::Address,
indexed: false,
},
EventParam { name: String::new(), kind: ParamType::Bool, indexed: false },
EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
],
anonymous: false,
};
@ -511,16 +464,8 @@ mod tests {
let event = Event {
name: "Foo".into(),
inputs: vec![
EventParam {
name: String::new(),
kind: ParamType::Bool,
indexed: false,
},
EventParam {
name: String::new(),
kind: ParamType::Address,
indexed: false,
},
EventParam { name: String::new(), kind: ParamType::Bool, indexed: false },
EventParam { name: String::new(), kind: ParamType::Address, indexed: false },
],
anonymous: false,
};

View File

@ -6,9 +6,8 @@ use proc_macro2::{Literal, TokenStream};
use quote::quote;
use syn::Ident;
use ethers_core::abi::ParamType;
use ethers_core::{
abi::{Function, FunctionExt, Param},
abi::{Function, FunctionExt, Param, ParamType},
types::Selector,
};
@ -96,7 +95,7 @@ impl Context {
if struct_defs.len() <= 1 {
// no need for an enum
return Ok(struct_def_tokens);
return Ok(struct_def_tokens)
}
let ethers_core = util::ethers_core_crate();
@ -155,7 +154,7 @@ impl Context {
/// The name ident of the calls enum
fn expand_calls_enum_name(&self) -> Ident {
util::ident(&format!("{}Calls", self.contract_name.to_string()))
util::ident(&format!("{}Calls", self.contract_name))
}
/// Expands to the `name : type` pairs of the function's inputs
@ -217,9 +216,8 @@ impl Context {
Ok(quote! {[#ty; #size]})
}
ParamType::Tuple(_) => {
let ty = if let Some(rust_struct_name) = self
.internal_structs
.get_function_input_struct_type(&fun.name, param)
let ty = if let Some(rust_struct_name) =
self.internal_structs.get_function_input_struct_type(&fun.name, param)
{
let ident = util::ident(rust_struct_name);
quote! {#ident}
@ -245,10 +243,8 @@ impl Context {
let result = quote! { #ethers_contract::builders::ContractCall<M, #outputs> };
let contract_args = self.expand_contract_call_args(function)?;
let function_params = self
.expand_input_pairs(function)?
.into_iter()
.map(|(name, ty)| quote! { #name: #ty });
let function_params =
self.expand_input_pairs(function)?.into_iter().map(|(name, ty)| quote! { #name: #ty });
let function_params = quote! { #( , #function_params )* };
let doc = util::expand_doc(&format!(
@ -278,14 +274,9 @@ impl Context {
let mut aliases = self.method_aliases.clone();
// find all duplicates, where no aliases where provided
for functions in self.abi.functions.values() {
if functions
.iter()
.filter(|f| !aliases.contains_key(&f.abi_signature()))
.count()
<= 1
{
if functions.iter().filter(|f| !aliases.contains_key(&f.abi_signature())).count() <= 1 {
// no conflicts
continue;
continue
}
// sort functions by number of inputs asc
@ -476,11 +467,7 @@ mod tests {
// two inputs
let params = vec![
Param {
name: "arg_a".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param { name: "arg_a".to_string(), kind: ParamType::Address, internal_type: None },
Param {
name: "arg_b".to_string(),
kind: ParamType::Uint(256usize),
@ -492,21 +479,13 @@ mod tests {
// three inputs
let params = vec![
Param {
name: "arg_a".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param { name: "arg_a".to_string(), kind: ParamType::Address, internal_type: None },
Param {
name: "arg_b".to_string(),
kind: ParamType::Uint(128usize),
internal_type: None,
},
Param {
name: "arg_c".to_string(),
kind: ParamType::Bool,
internal_type: None,
},
Param { name: "arg_c".to_string(), kind: ParamType::Bool, internal_type: None },
];
let token_stream = expand_inputs_call_arg(&params);
assert_eq!(token_stream.to_string(), "(arg_a , arg_b , arg_c ,)");
@ -562,16 +541,8 @@ mod tests {
fn expand_fn_outputs_multiple() {
assert_quote!(
expand_fn_outputs(&[
Param {
name: "a".to_string(),
kind: ParamType::Bool,
internal_type: None,
},
Param {
name: "b".to_string(),
kind: ParamType::Address,
internal_type: None,
},
Param { name: "a".to_string(), kind: ParamType::Bool, internal_type: None },
Param { name: "b".to_string(), kind: ParamType::Address, internal_type: None },
],)
.unwrap(),
{ (bool, ethers_core::types::Address) },

View File

@ -12,9 +12,11 @@ use ethers_core::abi::{
ParamType, SolStruct,
};
use crate::contract::{types, Context};
use crate::rawabi::{Component, RawAbi};
use crate::util;
use crate::{
contract::{types, Context},
rawabi::{Component, RawAbi},
util,
};
impl Context {
/// Generate corresponding types for structs parsed from a human readable ABI
@ -50,11 +52,7 @@ impl Context {
/// Generates the type definition for the name that matches the given identifier
fn generate_internal_struct(&self, id: &str) -> Result<TokenStream> {
let sol_struct = self
.internal_structs
.structs
.get(id)
.context("struct not found")?;
let sol_struct = self.internal_structs.structs.get(id).context("struct not found")?;
let struct_name = self
.internal_structs
.rust_type_names
@ -105,17 +103,13 @@ impl Context {
"Mapping types in struct `{}` are not supported {:?}",
name,
field
));
))
}
}
}
let sig = if let ParamType::Tuple(ref tokens) = tuple {
tokens
.iter()
.map(|kind| kind.to_string())
.collect::<Vec<_>>()
.join(",")
tokens.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join(",")
} else {
"".to_string()
};
@ -140,11 +134,7 @@ impl Context {
}
fn generate_human_readable_struct(&self, name: &str) -> Result<TokenStream> {
let sol_struct = self
.abi_parser
.structs
.get(name)
.context("struct not found")?;
let sol_struct = self.abi_parser.structs.get(name).context("struct not found")?;
let mut fields = Vec::with_capacity(sol_struct.fields().len());
let mut param_types = Vec::with_capacity(sol_struct.fields().len());
for field in sol_struct.fields() {
@ -175,7 +165,7 @@ impl Context {
"Mapping types in struct `{}` are not supported {:?}",
name,
field
));
))
}
}
}
@ -183,11 +173,7 @@ impl Context {
let abi_signature = format!(
"{}({})",
name,
param_types
.iter()
.map(|kind| kind.to_string())
.collect::<Vec<_>>()
.join(","),
param_types.iter().map(|kind| kind.to_string()).collect::<Vec<_>>().join(","),
);
let abi_signature_doc = util::expand_doc(&format!("`{}`", abi_signature));
@ -223,10 +209,12 @@ impl Context {
/// Helper to match `ethabi::Param`s with structs and nested structs
///
/// This is currently used to get access to all the unique solidity structs used as function in/output until `ethabi` supports it as well.
/// This is currently used to get access to all the unique solidity structs used as function
/// in/output until `ethabi` supports it as well.
#[derive(Debug, Clone, Default)]
pub struct InternalStructs {
/// (function name, param name) -> struct which are the identifying properties we get the name from ethabi.
/// (function name, param name) -> struct which are the identifying properties we get the name
/// from ethabi.
pub(crate) function_params: HashMap<(String, String), String>,
/// (function name) -> Vec<structs> all structs the function returns
@ -327,7 +315,9 @@ impl InternalStructs {
}
}
/// This will determine the name of the rust type and will make sure that possible collisions are resolved by adjusting the actual Rust name of the structure, e.g. `LibraryA.Point` and `LibraryB.Point` to `LibraryAPoint` and `LibraryBPoint`.
/// This will determine the name of the rust type and will make sure that possible collisions are
/// resolved by adjusting the actual Rust name of the structure, e.g. `LibraryA.Point` and
/// `LibraryB.Point` to `LibraryAPoint` and `LibraryBPoint`.
fn insert_rust_type_name(
type_names: &mut HashMap<String, (String, Vec<String>)>,
mut name: String,
@ -338,11 +328,7 @@ fn insert_rust_type_name(
let mut other_name = name.clone();
// name collision `A.name` `B.name`, rename to `AName`, `BName`
if !other_projections.is_empty() {
other_name = format!(
"{}{}",
other_projections.remove(0).to_pascal_case(),
other_name
);
other_name = format!("{}{}", other_projections.remove(0).to_pascal_case(), other_name);
}
insert_rust_type_name(type_names, other_name, other_projections, other_id);
@ -357,7 +343,10 @@ fn insert_rust_type_name(
/// Tries to determine the `ParamType::Tuple` for every struct.
///
/// If a structure has nested structures, these must be determined first, essentially starting with structures consisting of only elementary types before moving on to higher level structures, for example `Proof {point: Point}, Point {x:int, y:int}` start by converting Point into a tuple of `x` and `y` and then substituting `point` with this within `Proof`.
/// If a structure has nested structures, these must be determined first, essentially starting with
/// structures consisting of only elementary types before moving on to higher level structures, for
/// example `Proof {point: Point}, Point {x:int, y:int}` start by converting Point into a tuple of
/// `x` and `y` and then substituting `point` with this within `Proof`.
fn resolve_struct_tuples(all_structs: &HashMap<String, SolStruct>) -> HashMap<String, ParamType> {
let mut params = HashMap::new();
let mut structs: VecDeque<_> = all_structs.iter().collect();
@ -366,7 +355,7 @@ fn resolve_struct_tuples(all_structs: &HashMap<String, SolStruct>) -> HashMap<St
let mut sequential_retries = 0;
'outer: while let Some((id, ty)) = structs.pop_front() {
if sequential_retries > structs.len() {
break;
break
}
if let Some(tuple) = ty.as_tuple() {
params.insert(id.to_string(), tuple);
@ -396,7 +385,7 @@ fn resolve_struct_tuples(all_structs: &HashMap<String, SolStruct>) -> HashMap<St
// struct field needs to be resolved first
structs.push_back((id, ty));
sequential_retries += 1;
continue 'outer;
continue 'outer
}
}
_ => {
@ -413,27 +402,21 @@ fn resolve_struct_tuples(all_structs: &HashMap<String, SolStruct>) -> HashMap<St
params
}
/// turns the tuple component into a struct if it's still missing in the map, including all inner structs
/// turns the tuple component into a struct if it's still missing in the map, including all inner
/// structs
fn insert_structs(structs: &mut HashMap<String, SolStruct>, tuple: &Component) {
if let Some(internal_ty) = tuple.internal_type.as_ref() {
let ident = struct_type_identifier(internal_ty);
if structs.contains_key(ident) {
return;
return
}
if let Some(fields) = tuple
.components
.iter()
.map(|f| {
Reader::read(&f.type_field)
.ok()
.and_then(|kind| field(structs, f, kind))
})
.map(|f| Reader::read(&f.type_field).ok().and_then(|kind| field(structs, f, kind)))
.collect::<Option<Vec<_>>>()
{
let s = SolStruct {
name: ident.to_string(),
fields,
};
let s = SolStruct { name: ident.to_string(), fields };
structs.insert(ident.to_string(), s);
}
}
@ -512,10 +495,7 @@ fn struct_type_name(name: &str) -> &str {
/// `Pairing.G2Point` -> `Pairing.G2Point`
fn struct_type_identifier(name: &str) -> &str {
name.trim_start_matches("struct ")
.split('[')
.next()
.unwrap()
name.trim_start_matches("struct ").split('[').next().unwrap()
}
/// `struct Pairing.Nested.G2Point[]` -> `[Pairing, Nested]`

View File

@ -49,13 +49,10 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
}
ParamType::Tuple(members) => {
if members.is_empty() {
return Err(anyhow!("Tuple must have at least 1 member"));
return Err(anyhow!("Tuple must have at least 1 member"))
}
let members = members
.iter()
.map(|member| expand(member))
.collect::<Result<Vec<_>, _>>()?;
let members = members.iter().map(expand).collect::<Result<Vec<_>, _>>()?;
Ok(quote! { (#(#members,)*) })
}
}

View File

@ -1,4 +1,5 @@
//! This is a basic representation of a contract ABI that does no post processing but contains the raw content of the ABI.
//! This is a basic representation of a contract ABI that does no post processing but contains the
//! raw content of the ABI.
#![allow(missing_docs)]
use serde::{
@ -86,11 +87,7 @@ pub struct Item {
/// Either
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Component {
#[serde(
rename = "internalType",
default,
skip_serializing_if = "Option::is_none"
)]
#[serde(rename = "internalType", default, skip_serializing_if = "Option::is_none")]
pub internal_type: Option<String>,
pub name: String,
#[serde(rename = "type")]

View File

@ -1,18 +1,18 @@
//! This module implements basic `rustfmt` code formatting.
use anyhow::{anyhow, Result};
use std::io::Write;
use std::process::{Command, Stdio};
use std::{
io::Write,
process::{Command, Stdio},
};
/// Format the raw input source string and return formatted output.
pub fn format<S>(source: S) -> Result<String>
where
S: AsRef<str>,
{
let mut rustfmt = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let mut rustfmt =
Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?;
{
let stdin = rustfmt
@ -28,7 +28,7 @@ where
"`rustfmt` exited with code {}:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr),
));
))
}
let stdout = String::from_utf8(output.stdout)?;

View File

@ -45,26 +45,24 @@ impl Source {
/// This relative path is rooted in the current working directory.
/// To specify the root for relative paths, use `Source::with_root`.
///
/// - `/absolute/path/to/Contract.json` or
/// `file:///absolute/path/to/Contract.json`: an absolute path or file URL
/// to an ABI JSON file.
/// - `/absolute/path/to/Contract.json` or `file:///absolute/path/to/Contract.json`: an absolute
/// path or file URL to an ABI JSON file.
///
/// - `http(s)://...` an HTTP url to a contract ABI.
///
/// - `etherscan:0xXX..XX` or `https://etherscan.io/address/0xXX..XX`: a
/// address or URL of a verified contract on Etherscan.
/// - `etherscan:0xXX..XX` or `https://etherscan.io/address/0xXX..XX`: a address or URL of a
/// verified contract on Etherscan.
///
/// - `npm:@org/package@1.0.0/path/to/contract.json` an npmjs package with
/// an optional version and path (defaulting to the latest version and
/// `index.js`). The contract ABI will be retrieved through
/// `unpkg.io`.
/// - `npm:@org/package@1.0.0/path/to/contract.json` an npmjs package with an optional version
/// and path (defaulting to the latest version and `index.js`). The contract ABI will be
/// retrieved through `unpkg.io`.
pub fn parse<S>(source: S) -> Result<Self>
where
S: AsRef<str>,
{
let source = source.as_ref();
if matches!(source.chars().next(), Some('[' | '{')) {
return Ok(Source::String(source.to_owned()));
return Ok(Source::String(source.to_owned()))
}
let root = env::current_dir()?.canonicalize()?;
Source::with_root(root, source)
@ -197,10 +195,8 @@ fn get_local_contract(path: &Path) -> Result<String> {
Cow::Borrowed(path)
};
let json = fs::read_to_string(&path).context(format!(
"failed to read artifact JSON file with path {}",
&path.display()
))?;
let json = fs::read_to_string(&path)
.context(format!("failed to read artifact JSON file with path {}", &path.display()))?;
Ok(json)
}
@ -220,9 +216,8 @@ fn get_etherscan_contract(address: Address) -> Result<String> {
// same bytecode is unreliable as the libraries have already linked and
// probably don't reference anything when deploying on other networks.
let api_key = env::var("ETHERSCAN_API_KEY")
.map(|key| format!("&apikey={}", key))
.unwrap_or_default();
let api_key =
env::var("ETHERSCAN_API_KEY").map(|key| format!("&apikey={}", key)).unwrap_or_default();
let abi_url = format!(
"http://api.etherscan.io/api\
@ -251,14 +246,8 @@ mod tests {
fn parse_source() {
let root = "/rooted";
for (url, expected) in &[
(
"relative/Contract.json",
Source::local("/rooted/relative/Contract.json"),
),
(
"/absolute/Contract.json",
Source::local("/absolute/Contract.json"),
),
("relative/Contract.json", Source::local("/rooted/relative/Contract.json")),
("/absolute/Contract.json", Source::local("/absolute/Contract.json")),
(
"https://my.domain.eth/path/to/Contract.json",
Source::http("https://my.domain.eth/path/to/Contract.json").unwrap(),

View File

@ -62,13 +62,12 @@ pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
.ok()
.and_then(|metadata| {
metadata.root_package().and_then(|pkg| {
pkg.dependencies
.iter()
.filter(|dep| dep.kind == DependencyKind::Normal)
.find_map(|dep| {
pkg.dependencies.iter().filter(|dep| dep.kind == DependencyKind::Normal).find_map(
|dep| {
(dep.name == "ethers")
.then(|| ("ethers::core", "ethers::contract", "ethers::providers"))
})
},
)
})
})
.unwrap_or(("ethers_core", "ethers_contract", "ethers_providers"));
@ -128,7 +127,7 @@ where
{
let address_str = address_str.as_ref();
if !address_str.starts_with("0x") {
return Err(anyhow!("address must start with '0x'"));
return Err(anyhow!("address must start with '0x'"))
}
Ok(address_str[2..].parse()?)
}
@ -174,12 +173,8 @@ mod tests {
#[test]
fn parse_address_ok() {
let expected = Address::from([
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
]);
assert_eq!(
parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(),
expected
);
let expected =
Address::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
assert_eq!(parse_address("0x000102030405060708090a0b0c0d0e0f10111213").unwrap(), expected);
}
}

View File

@ -3,8 +3,7 @@
use ethers_contract_abigen::ethers_core_crate;
use proc_macro2::{Ident, Literal, TokenStream};
use quote::{quote, quote_spanned};
use syn::spanned::Spanned as _;
use syn::{parse::Error, Data, DeriveInput, Fields, Variant};
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Variant};
/// Generates the tokenize implementation
pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream {
@ -49,12 +48,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
});
let into_token_impl = quote! { #(#into_token,)* };
(
tokenize_predicates,
fields.named.len(),
init_struct_impl,
into_token_impl,
)
(tokenize_predicates, fields.named.len(), init_struct_impl, into_token_impl)
}
Fields::Unnamed(ref fields) => {
let tokenize_predicates = fields.unnamed.iter().map(|f| {
@ -74,12 +68,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
});
let into_token_impl = quote! { #(#into_token,)* };
(
tokenize_predicates,
fields.unnamed.len(),
init_struct_impl,
into_token_impl,
)
(tokenize_predicates, fields.unnamed.len(), init_struct_impl, into_token_impl)
}
Fields::Unit => return tokenize_unit_type(&input.ident),
},
@ -91,7 +80,7 @@ pub fn derive_tokenizeable_impl(input: &DeriveInput) -> proc_macro2::TokenStream
}
Data::Union(_) => {
return Error::new(input.span(), "EthAbiType cannot be derived for unions")
.to_compile_error();
.to_compile_error()
}
};
@ -218,7 +207,7 @@ fn tokenize_enum<'a>(
return Err(Error::new(
variant.span(),
"EthAbiType cannot be derived for enum variants with multiple fields",
));
))
} else if variant.fields.is_empty() {
let value = Literal::u8_unsuffixed(idx as u8);
from_tokens.extend(quote! {

View File

@ -8,11 +8,17 @@ use ethers_core::abi::{Function, FunctionExt, Param, StateMutability};
use ethers_contract_abigen::contract::{Context, ExpandedContract};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use std::collections::{HashMap, HashSet};
use std::error::Error;
use syn::ext::IdentExt;
use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult};
use syn::{braced, parenthesized, Ident, LitStr, Path, Token};
use std::{
collections::{HashMap, HashSet},
error::Error,
};
use syn::{
braced,
ext::IdentExt,
parenthesized,
parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult},
Ident, LitStr, Path, Token,
};
/// A series of `ContractArgs` separated by `;`
#[cfg_attr(test, derive(Debug))]
@ -63,9 +69,7 @@ impl Contracts {
for contract in dirty {
let (expanded, ctx) = &mut expansions[contract];
expanded.abi_structs = ctx.abi_structs().unwrap();
expanded
.imports
.extend(quote!( pub use super::#shared_types_mdoule::*;));
expanded.imports.extend(quote!( pub use super::#shared_types_mdoule::*;));
}
tokens.extend(quote! {
pub mod #shared_types_mdoule {
@ -111,9 +115,9 @@ impl ContractArgs {
for parameter in self.parameters.into_iter() {
builder = match parameter {
Parameter::Methods(methods) => methods.into_iter().fold(builder, |builder, m| {
builder.add_method_alias(m.signature, m.alias)
}),
Parameter::Methods(methods) => methods
.into_iter()
.fold(builder, |builder, m| builder.add_method_alias(m.signature, m.alias)),
Parameter::EventDerives(derives) => derives
.into_iter()
.fold(builder, |builder, derive| builder.add_event_derive(derive)),
@ -149,11 +153,11 @@ impl ParseInner for ContractArgs {
loop {
if input.is_empty() {
break;
break
}
let lookahead = input.lookahead1();
if lookahead.peek(Token![;]) {
break;
break
}
let param = Parameter::parse(input)?;
parameters.push(param);
@ -164,14 +168,7 @@ impl ParseInner for ContractArgs {
}
}
Ok((
span,
ContractArgs {
name,
abi,
parameters,
},
))
Ok((span, ContractArgs { name, abi, parameters }))
}
}
@ -201,13 +198,13 @@ impl Parse for Parameter {
return Err(ParseError::new(
method.span(),
"duplicate method signature in `abigen!` macro invocation",
));
))
}
if !aliases.insert(method.alias.clone()) {
return Err(ParseError::new(
method.span(),
"duplicate method alias in `abigen!` macro invocation",
));
))
}
methods.push(method.into_inner())
}
@ -259,11 +256,7 @@ impl Parse for Method {
.map(|ident| {
let kind = serde_json::from_value(serde_json::json!(&ident.to_string()))
.map_err(|err| ParseError::new(ident.span(), err))?;
Ok(Param {
name: "".into(),
kind,
internal_type: None,
})
Ok(Param { name: "".into(), kind, internal_type: None })
})
.collect::<ParseResult<Vec<_>>>()?;
@ -319,21 +312,12 @@ mod tests {
#[allow(unused)]
fn method(signature: &str, alias: &str) -> Method {
Method {
signature: signature.into(),
alias: alias.into(),
}
Method { signature: signature.into(), alias: alias.into() }
}
fn parse_contracts(s: TokenStream2) -> Vec<ContractArgs> {
use syn::parse::Parser;
Contracts::parse
.parse2(s)
.unwrap()
.inner
.into_iter()
.map(|(_, c)| c)
.collect::<Vec<_>>()
Contracts::parse.parse2(s).unwrap().inner.into_iter().map(|(_, c)| c).collect::<Vec<_>>()
}
#[test]

View File

@ -3,13 +3,11 @@
use ethers_contract_abigen::{ethers_contract_crate, ethers_core_crate};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::spanned::Spanned as _;
use syn::{parse::Error, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
use syn::{parse::Error, spanned::Spanned as _, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
use ethers_core::abi::{param_type::Reader, AbiParser, Function, FunctionExt, Param, ParamType};
use crate::abi_ty;
use crate::utils;
use crate::{abi_ty, utils};
/// Generates the `ethcall` trait support
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
@ -23,10 +21,8 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
Err(errors) => return errors,
};
let function_call_name = attributes
.name
.map(|(s, _)| s)
.unwrap_or_else(|| input.ident.to_string());
let function_call_name =
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
let mut function = if let Some((src, span)) = attributes.abi {
// try to parse as solidity function
@ -44,11 +40,7 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
ParamType::Tuple(params) => Some(
params
.into_iter()
.map(|kind| Param {
name: "".to_string(),
kind,
internal_type: None,
})
.map(|kind| Param { name: "".to_string(), kind, internal_type: None })
.collect(),
),
_ => None,
@ -63,7 +55,7 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
}
} else {
return Error::new(span, format!("Unable to determine ABI: {}", src))
.to_compile_error();
.to_compile_error()
}
}
} else {
@ -128,10 +120,7 @@ pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> TokenStream {
fn derive_decode_impl(function: &Function) -> TokenStream {
let core_crate = ethers_core_crate();
let contract_crate = ethers_contract_crate();
let data_types = function
.inputs
.iter()
.map(|input| utils::param_type_quote(&input.kind));
let data_types = function.inputs.iter().map(|input| utils::param_type_quote(&input.kind));
let data_types_init = quote! {let data_types = [#( #data_types ),*];};
@ -153,11 +142,7 @@ fn derive_abi_function_from_fields(input: &DeriveInput) -> Result<Function, Erro
name: "".to_string(),
inputs: utils::derive_abi_inputs_from_fields(input, "EthCall")?
.into_iter()
.map(|(name, kind)| Param {
name,
kind,
internal_type: None,
})
.map(|(name, kind)| Param { name, kind, internal_type: None })
.collect(),
outputs: vec![],
constant: false,
@ -188,14 +173,14 @@ fn parse_call_attributes(input: &DeriveInput) -> Result<EthCallAttributes, Token
path.span(),
"unrecognized ethcall parameter",
)
.to_compile_error());
.to_compile_error())
}
Meta::List(meta) => {
return Err(Error::new(
meta.path.span(),
"unrecognized ethcall parameter",
)
.to_compile_error());
.to_compile_error())
}
Meta::NameValue(meta) => {
if meta.path.is_ident("name") {
@ -208,14 +193,14 @@ fn parse_call_attributes(input: &DeriveInput) -> Result<EthCallAttributes, Token
meta.span(),
"name already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"name must be a string",
)
.to_compile_error());
.to_compile_error())
}
} else if meta.path.is_ident("abi") {
if let Lit::Str(ref lit_str) = meta.lit {
@ -227,21 +212,21 @@ fn parse_call_attributes(input: &DeriveInput) -> Result<EthCallAttributes, Token
meta.span(),
"abi already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"abi must be a string",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"unrecognized ethcall parameter",
)
.to_compile_error());
.to_compile_error())
}
}
}

View File

@ -2,8 +2,7 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned as _;
use syn::{parse::Error, Data, DeriveInput, Fields, Index};
use syn::{parse::Error, spanned::Spanned as _, Data, DeriveInput, Fields, Index};
use ethers_contract_abigen::ethers_core_crate;
use ethers_core::abi::ParamType;
@ -21,30 +20,20 @@ pub(crate) fn derive_eth_display_impl(input: DeriveInput) -> Result<TokenStream,
}
},
Data::Enum(_) => {
return Err(Error::new(
input.span(),
"Enum types are not supported by EthDisplay",
))
return Err(Error::new(input.span(), "Enum types are not supported by EthDisplay"))
}
Data::Union(_) => {
return Err(Error::new(
input.span(),
"Union types are not supported by EthDisplay",
))
return Err(Error::new(input.span(), "Union types are not supported by EthDisplay"))
}
};
let core_crate = ethers_core_crate();
let hex_encode = quote! {#core_crate::utils::hex::encode};
let mut fmts = TokenStream::new();
for (idx, field) in fields.iter().enumerate() {
let ident = field
.ident
.clone()
.map(|id| quote! {#id})
.unwrap_or_else(|| {
let idx = Index::from(idx);
quote! {#idx}
});
let ident = field.ident.clone().map(|id| quote! {#id}).unwrap_or_else(|| {
let idx = Index::from(idx);
quote! {#idx}
});
let tokens = if let Ok(param) = utils::find_parameter_type(&field.ty) {
match param {
ParamType::Address | ParamType::Uint(_) | ParamType::Int(_) => {

View File

@ -3,14 +3,15 @@
use ethers_contract_abigen::{ethers_contract_crate, ethers_core_crate, Source};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::spanned::Spanned as _;
use syn::{parse::Error, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta};
use syn::{
parse::Error, spanned::Spanned as _, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta,
NestedMeta,
};
use ethers_core::abi::{param_type::Reader, AbiParser, Event, EventExt, EventParam, ParamType};
use hex::FromHex;
use crate::abi_ty;
use crate::utils;
use crate::{abi_ty, utils};
/// Generates the `EthEvent` trait support
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
@ -24,10 +25,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
Err(errors) => return errors,
};
let event_name = attributes
.name
.map(|(s, _)| s)
.unwrap_or_else(|| input.ident.to_string());
let event_name = attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
let mut event = if let Some((src, span)) = attributes.abi {
// try to parse as solidity event
@ -36,29 +34,19 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> TokenStream {
} else {
// try as tuple
if let Some(inputs) = Reader::read(
src.trim_start_matches("event ")
.trim_start()
.trim_start_matches(&event_name),
src.trim_start_matches("event ").trim_start().trim_start_matches(&event_name),
)
.ok()
.and_then(|param| match param {
ParamType::Tuple(params) => Some(
params
.into_iter()
.map(|kind| EventParam {
name: "".to_string(),
indexed: false,
kind,
})
.map(|kind| EventParam { name: "".to_string(), indexed: false, kind })
.collect(),
),
_ => None,
}) {
Event {
name: event_name.clone(),
inputs,
anonymous: false,
}
Event { name: event_name.clone(), inputs, anonymous: false }
} else {
match src.parse::<Source>().and_then(|s| s.get()) {
Ok(abi) => {
@ -169,7 +157,7 @@ fn derive_decode_from_log_impl(
event.name,
event.abi_signature()
),
));
))
}
fields.named.iter().collect()
}
@ -182,7 +170,7 @@ fn derive_decode_from_log_impl(
event.name,
event.abi_signature()
),
));
))
}
fields.unnamed.iter().collect()
}
@ -190,20 +178,14 @@ fn derive_decode_from_log_impl(
return Err(Error::new(
input.span(),
"EthEvent cannot be derived for empty structs and unit",
));
))
}
},
Data::Enum(_) => {
return Err(Error::new(
input.span(),
"EthEvent cannot be derived for enums",
));
return Err(Error::new(input.span(), "EthEvent cannot be derived for enums"))
}
Data::Union(_) => {
return Err(Error::new(
input.span(),
"EthEvent cannot be derived for unions",
));
return Err(Error::new(input.span(), "EthEvent cannot be derived for unions"))
}
};
@ -215,16 +197,10 @@ fn derive_decode_from_log_impl(
if indexed {
param.indexed = true;
}
let topic_name = param
.indexed
.then(|| topic_name.or_else(|| Some(param.name.clone())))
.flatten();
let topic_name =
param.indexed.then(|| topic_name.or_else(|| Some(param.name.clone()))).flatten();
event_fields.push(EventField {
topic_name,
index,
param,
});
event_fields.push(EventField { topic_name, index, param });
}
// convert fields to params list
@ -327,11 +303,7 @@ fn derive_abi_event_from_fields(input: &DeriveInput) -> Result<Event, Error> {
name: "".to_string(),
inputs: utils::derive_abi_inputs_from_fields(input, "EthEvent")?
.into_iter()
.map(|(name, kind)| EventParam {
name,
kind,
indexed: false,
})
.map(|(name, kind)| EventParam { name, kind, indexed: false })
.collect(),
anonymous: false,
};
@ -355,14 +327,14 @@ fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error
return Err(Error::new(
path.span(),
"unrecognized ethevent parameter",
));
))
}
}
Meta::List(meta) => {
return Err(Error::new(
meta.path.span(),
"unrecognized ethevent parameter",
));
))
}
Meta::NameValue(meta) => {
if meta.path.is_ident("name") {
@ -372,7 +344,7 @@ fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error
return Err(Error::new(
meta.span(),
"name attribute must be a string",
));
))
}
}
}
@ -424,13 +396,13 @@ fn parse_event_attributes(
if &*name.to_string() == "anonymous" {
if result.anonymous.is_none() {
result.anonymous = Some((true, name.span()));
continue;
continue
} else {
return Err(Error::new(
name.span(),
"anonymous already specified",
)
.to_compile_error());
.to_compile_error())
}
}
}
@ -438,14 +410,14 @@ fn parse_event_attributes(
path.span(),
"unrecognized ethevent parameter",
)
.to_compile_error());
.to_compile_error())
}
Meta::List(meta) => {
return Err(Error::new(
meta.path.span(),
"unrecognized ethevent parameter",
)
.to_compile_error());
.to_compile_error())
}
Meta::NameValue(meta) => {
if meta.path.is_ident("anonymous") {
@ -458,14 +430,14 @@ fn parse_event_attributes(
meta.span(),
"anonymous already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"name must be a string",
)
.to_compile_error());
.to_compile_error())
}
} else if meta.path.is_ident("name") {
if let Lit::Str(ref lit_str) = meta.lit {
@ -477,14 +449,14 @@ fn parse_event_attributes(
meta.span(),
"name already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"name must be a string",
)
.to_compile_error());
.to_compile_error())
}
} else if meta.path.is_ident("abi") {
if let Lit::Str(ref lit_str) = meta.lit {
@ -496,14 +468,14 @@ fn parse_event_attributes(
meta.span(),
"abi already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"abi must be a string",
)
.to_compile_error());
.to_compile_error())
}
} else if meta.path.is_ident("signature") {
if let Lit::Str(ref lit_str) = meta.lit {
@ -521,7 +493,7 @@ fn parse_event_attributes(
err
),
)
.to_compile_error());
.to_compile_error())
}
}
} else {
@ -529,21 +501,21 @@ fn parse_event_attributes(
meta.span(),
"signature already specified",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"signature must be a hex string",
)
.to_compile_error());
.to_compile_error())
}
} else {
return Err(Error::new(
meta.span(),
"unrecognized ethevent parameter",
)
.to_compile_error());
.to_compile_error())
}
}
}

View File

@ -43,12 +43,11 @@ pub(crate) mod utils;
///
/// Currently the proc macro accepts additional parameters to configure some
/// aspects of the code generation. Specifically it accepts:
/// - `methods`: A list of mappings from method signatures to method names
/// allowing methods names to be explicitely set for contract methods. This
/// also provides a workaround for generating code for contracts with multiple
/// methods with the same name.
/// - `event_derives`: A list of additional derives that should be added to
/// contract event structs and enums.
/// - `methods`: A list of mappings from method signatures to method names allowing methods names to
/// be explicitely set for contract methods. This also provides a workaround for generating code
/// for contracts with multiple methods with the same name.
/// - `event_derives`: A list of additional derives that should be added to contract event structs
/// and enums.
///
/// # Example
///
@ -87,10 +86,7 @@ pub(crate) mod utils;
pub fn abigen(input: TokenStream) -> TokenStream {
let contracts = parse_macro_input!(input as Contracts);
contracts
.expand()
.unwrap_or_else(|err| err.to_compile_error())
.into()
contracts.expand().unwrap_or_else(|err| err.to_compile_error()).into()
}
/// Derives the `Tokenizable` trait for the labeled type.
@ -144,14 +140,11 @@ pub fn derive_eth_display(input: TokenStream) -> TokenStream {
///
/// For the struct:
///
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default
/// is the
/// - `name`, `name = "..."`: Overrides the generated `EthEvent` name, default is the
/// struct's name.
/// - `signature`, `signature = "..."`: The signature as hex string to override
/// the
/// - `signature`, `signature = "..."`: The signature as hex string to override the
/// event's signature.
/// - `abi`, `abi = "..."`: The ABI signature for the event this event's data
/// corresponds to.
/// - `abi`, `abi = "..."`: The ABI signature for the event this event's data corresponds to.
/// The `abi` should be solidity event definition or a tuple of the event's
/// types in case the event has non elementary (other `EthAbiType`) types as
/// members
@ -160,8 +153,7 @@ pub fn derive_eth_display(input: TokenStream) -> TokenStream {
/// For fields:
///
/// - `indexed`: flag to mark a field as an indexed event input
/// - `name`: override the name of an indexed event input, default is the rust
/// field name
/// - `name`: override the name of an indexed event input, default is the rust field name
///
/// # Example
/// ```ignore
@ -196,11 +188,9 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream {
///
/// For the struct:
///
/// - `name`, `name = "..."`: Overrides the generated `EthCall` function name, default
/// is the
/// - `name`, `name = "..."`: Overrides the generated `EthCall` function name, default is the
/// struct's name.
/// - `abi`, `abi = "..."`: The ABI signature for the function this call's data
/// corresponds to.
/// - `abi`, `abi = "..."`: The ABI signature for the function this call's data corresponds to.
///
/// NOTE: in order to successfully parse the `abi` (`<name>(<args>,...)`) the `<name`>
/// must match either the struct name or the name attribute: `#[ethcall(name ="<name>"]`
@ -249,7 +239,6 @@ pub fn derive_abi_event(input: TokenStream) -> TokenStream {
/// "foo(address,(address,string),string)"
/// );
/// ```
///
#[proc_macro_derive(EthCall, attributes(ethcall))]
pub fn derive_abi_call(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

View File

@ -1,11 +1,10 @@
use ethers_contract_abigen::ethers_core_crate;
use ethers_core::abi::ParamType;
use ethers_core::types::Selector;
use ethers_core::{abi::ParamType, types::Selector};
use proc_macro2::Literal;
use quote::quote;
use syn::spanned::Spanned as _;
use syn::{
parse::Error, Data, DeriveInput, Expr, Fields, GenericArgument, Lit, PathArguments, Type,
parse::Error, spanned::Spanned as _, Data, DeriveInput, Expr, Fields, GenericArgument, Lit,
PathArguments, Type,
};
pub fn signature(hash: &[u8]) -> proc_macro2::TokenStream {
@ -21,12 +20,7 @@ pub fn selector(selector: Selector) -> proc_macro2::TokenStream {
/// Parses an int type from its string representation
pub fn parse_int_param_type(s: &str) -> Option<ParamType> {
let size = s
.chars()
.skip(1)
.collect::<String>()
.parse::<usize>()
.ok()?;
let size = s.chars().skip(1).collect::<String>().parse::<usize>().ok()?;
if s.starts_with('u') {
Some(ParamType::Uint(size))
} else if s.starts_with('i') {
@ -43,11 +37,11 @@ pub fn parse_int_param_type(s: &str) -> Option<ParamType> {
pub fn topic_param_type_quote(kind: &ParamType) -> proc_macro2::TokenStream {
let core_crate = ethers_core_crate();
match kind {
ParamType::String
| ParamType::Bytes
| ParamType::Array(_)
| ParamType::FixedArray(_, _)
| ParamType::Tuple(_) => quote! {#core_crate::abi::ParamType::FixedBytes(32)},
ParamType::String |
ParamType::Bytes |
ParamType::Array(_) |
ParamType::FixedArray(_, _) |
ParamType::Tuple(_) => quote! {#core_crate::abi::ParamType::FixedBytes(32)},
ty => param_type_quote(ty),
}
}
@ -111,18 +105,16 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
if let Expr::Lit(ref expr) = ty.len {
if let Lit::Int(ref len) = expr.lit {
if let Ok(size) = len.base10_parse::<usize>() {
return Ok(ParamType::FixedArray(Box::new(param), size));
return Ok(ParamType::FixedArray(Box::new(param), size))
}
}
}
Err(Error::new(
ty.span(),
"Failed to derive proper ABI from array field",
))
Err(Error::new(ty.span(), "Failed to derive proper ABI from array field"))
}
Type::Path(ty) => {
if let Some(ident) = ty.path.get_ident() {
return match ident.to_string().to_lowercase().as_str() {
let ident = ident.to_string().to_lowercase();
return match ident.as_str() {
"address" => Ok(ParamType::Address),
"string" => Ok(ParamType::String),
"bool" => Ok(ParamType::Bool),
@ -133,7 +125,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
s => parse_int_param_type(s).ok_or_else(|| {
Error::new(ty.span(), "Failed to derive proper ABI from fields")
}),
};
}
}
// check for `Vec`
if ty.path.segments.len() == 1 && ty.path.segments[0].ident == "Vec" {
@ -141,29 +133,19 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
if args.args.len() == 1 {
if let GenericArgument::Type(ref ty) = args.args.iter().next().unwrap() {
let kind = find_parameter_type(ty)?;
return Ok(ParamType::Array(Box::new(kind)));
return Ok(ParamType::Array(Box::new(kind)))
}
}
}
}
Err(Error::new(
ty.span(),
"Failed to derive proper ABI from fields",
))
Err(Error::new(ty.span(), "Failed to derive proper ABI from fields"))
}
Type::Tuple(ty) => {
let params = ty
.elems
.iter()
.map(find_parameter_type)
.collect::<Result<Vec<_>, _>>()?;
let params = ty.elems.iter().map(find_parameter_type).collect::<Result<Vec<_>, _>>()?;
Ok(ParamType::Tuple(params))
}
_ => Err(Error::new(
ty.span(),
"Failed to derive proper ABI from fields",
)),
_ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")),
}
}
@ -179,10 +161,7 @@ pub fn derive_abi_inputs_from_fields(
Fields::Unit => {
return Err(Error::new(
input.span(),
format!(
"{} cannot be derived for empty structs and unit",
trait_name
),
format!("{} cannot be derived for empty structs and unit", trait_name),
))
}
},
@ -190,24 +169,21 @@ pub fn derive_abi_inputs_from_fields(
return Err(Error::new(
input.span(),
format!("{} cannot be derived for enums", trait_name),
));
))
}
Data::Union(_) => {
return Err(Error::new(
input.span(),
format!("{} cannot be derived for unions", trait_name),
));
))
}
};
fields
.iter()
.map(|f| {
let name = f
.ident
.as_ref()
.map(|name| name.to_string())
.unwrap_or_else(|| "".to_string());
let name =
f.ident.as_ref().map(|name| name.to_string()).unwrap_or_else(|| "".to_string());
find_parameter_type(&f.ty).map(|ty| (name, ty))
})
.collect()

View File

@ -125,10 +125,7 @@ pub(crate) fn decode_event<D: Detokenize>(
data: Bytes,
) -> Result<D, AbiError> {
let tokens = event
.parse_log(RawLog {
topics,
data: data.to_vec(),
})?
.parse_log(RawLog { topics, data: data.to_vec() })?
.params
.into_iter()
.map(|param| param.value)
@ -151,7 +148,7 @@ pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>(
let bytes = bytes.as_ref();
let tokens = if is_input {
if bytes.len() < 4 || bytes[..4] != function.selector() {
return Err(AbiError::WrongSelector);
return Err(AbiError::WrongSelector)
}
function.decode_input(&bytes[4..])?
} else {
@ -194,9 +191,7 @@ mod tests {
"function approve(address _spender, uint256 value) external view returns (bool, bool)"
]).unwrap());
let spender = "7a250d5630b4cf539739df2c5dacb4c659f2488d"
.parse::<Address>()
.unwrap();
let spender = "7a250d5630b4cf539739df2c5dacb4c659f2488d".parse::<Address>().unwrap();
let amount = U256::MAX;
let encoded = abi.encode("approve", (spender, amount)).unwrap();
@ -233,17 +228,7 @@ mod tests {
let (owner, spender, value): (Address, Address, U256) =
abi.decode_event("Approval", topics, data).unwrap();
assert_eq!(value, U256::MAX);
assert_eq!(
owner,
"e4e60fdf9bf188fa57b7a5022230363d5bd56d08"
.parse::<Address>()
.unwrap()
);
assert_eq!(
spender,
"7a250d5630b4cf539739df2c5dacb4c659f2488d"
.parse::<Address>()
.unwrap()
);
assert_eq!(owner, "e4e60fdf9bf188fa57b7a5022230363d5bd56d08".parse::<Address>().unwrap());
assert_eq!(spender, "7a250d5630b4cf539739df2c5dacb4c659f2488d".parse::<Address>().unwrap());
}
}

View File

@ -133,10 +133,7 @@ where
/// Returns the estimated gas cost for the underlying transaction to be executed
pub async fn estimate_gas(&self) -> Result<U256, ContractError<M>> {
self.client
.estimate_gas(&self.tx)
.await
.map_err(ContractError::MiddlewareError)
self.client.estimate_gas(&self.tx).await.map_err(ContractError::MiddlewareError)
}
/// Queries the blockchain via an `eth_call` for the provided transaction.

View File

@ -143,7 +143,6 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc};
/// println!("{:?}", logs);
/// # Ok(())
/// # }
///
/// ```
///
/// _Disclaimer: these above docs have been adapted from the corresponding [ethers.js page](https://docs.ethers.io/ethers.js/html/api-contract.html)_
@ -162,11 +161,7 @@ pub struct Contract<M> {
impl<M: Middleware> Contract<M> {
/// Creates a new contract from the provided client, abi and address
pub fn new(address: Address, abi: impl Into<BaseContract>, client: impl Into<Arc<M>>) -> Self {
Self {
base_contract: abi.into(),
client: client.into(),
address,
}
Self { base_contract: abi.into(), client: client.into(), address }
}
/// Returns an [`Event`](crate::builders::Event) builder for the provided event.

View File

@ -5,8 +5,7 @@ use ethers_core::{
types::{BlockNumber, Filter, Log, ValueOrArray, H256},
};
use ethers_providers::{FilterWatcher, Middleware, PubsubClient, SubscriptionStream};
use std::borrow::Cow;
use std::marker::PhantomData;
use std::{borrow::Cow, marker::PhantomData};
/// A trait for implementing event bindings
pub trait EthEvent: Detokenize + Send + Sync {
@ -36,11 +35,7 @@ pub trait EthEvent: Detokenize + Send + Sync {
Self: Sized,
{
let filter = filter.event(&Self::abi_signature());
Event {
filter,
provider,
datatype: PhantomData,
}
Event { filter, provider, datatype: PhantomData }
}
}
@ -127,16 +122,9 @@ where
EventStream<'a, FilterWatcher<'a, M::Provider, Log>, D, ContractError<M>>,
ContractError<M>,
> {
let filter = self
.provider
.watch(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(
filter.id,
filter,
Box::new(move |log| self.parse_log(log)),
))
let filter =
self.provider.watch(&self.filter).await.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(filter.id, filter, Box::new(move |log| self.parse_log(log))))
}
}
@ -159,11 +147,7 @@ where
.subscribe_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
Ok(EventStream::new(
filter.id,
filter,
Box::new(move |log| self.parse_log(log)),
))
Ok(EventStream::new(filter.id, filter, Box::new(move |log| self.parse_log(log))))
}
}
@ -175,11 +159,8 @@ where
/// Queries the blockchain for the selected filter and returns a vector of matching
/// event logs
pub async fn query(&self) -> Result<Vec<D>, ContractError<M>> {
let logs = self
.provider
.get_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
let logs =
self.provider.get_logs(&self.filter).await.map_err(ContractError::MiddlewareError)?;
let events = logs
.into_iter()
.map(|log| self.parse_log(log))
@ -190,11 +171,8 @@ where
/// Queries the blockchain for the selected filter and returns a vector of logs
/// along with their metadata
pub async fn query_with_meta(&self) -> Result<Vec<(D, LogMeta)>, ContractError<M>> {
let logs = self
.provider
.get_logs(&self.filter)
.await
.map_err(ContractError::MiddlewareError)?;
let logs =
self.provider.get_logs(&self.filter).await.map_err(ContractError::MiddlewareError)?;
let events = logs
.into_iter()
.map(|log| {
@ -207,10 +185,6 @@ where
}
fn parse_log(&self, log: Log) -> Result<D, ContractError<M>> {
D::decode_log(&RawLog {
topics: log.topics,
data: log.data.to_vec(),
})
.map_err(From::from)
D::decode_log(&RawLog { topics: log.topics, data: log.data.to_vec() }).map_err(From::from)
}
}

View File

@ -62,9 +62,7 @@ impl<M: Middleware> Deployer<M> {
.await
.map_err(|_| ContractError::ContractNotDeployed)?
.ok_or(ContractError::ContractNotDeployed)?;
let address = receipt
.contract_address
.ok_or(ContractError::ContractNotDeployed)?;
let address = receipt.contract_address.ok_or(ContractError::ContractNotDeployed)?;
let contract = Contract::new(address, self.abi.clone(), self.client);
Ok(contract)
@ -134,11 +132,7 @@ impl<M: Middleware> ContractFactory<M> {
/// constructor defined in the abi. The client will be used to send any deployment
/// transaction.
pub fn new(abi: Abi, bytecode: Bytes, client: Arc<M>) -> Self {
Self {
client,
abi,
bytecode,
}
Self { client, abi, bytecode }
}
/// Constructs the deployment transaction based on the provided constructor
@ -153,30 +147,20 @@ impl<M: Middleware> ContractFactory<M> {
// Encode the constructor args & concatenate with the bytecode if necessary
let params = constructor_args.into_tokens();
let data: Bytes = match (self.abi.constructor(), params.is_empty()) {
(None, false) => {
return Err(ContractError::ConstructorError);
}
(None, false) => return Err(ContractError::ConstructorError),
(None, true) => self.bytecode.clone(),
(Some(constructor), _) => constructor
.encode_input(self.bytecode.to_vec(), &params)?
.into(),
(Some(constructor), _) => {
constructor.encode_input(self.bytecode.to_vec(), &params)?.into()
}
};
// create the tx object. Since we're deploying a contract, `to` is `None`
// We default to EIP-1559 transactions, but the sender can convert it back
// to a legacy one
#[cfg(feature = "legacy")]
let tx = TransactionRequest {
to: None,
data: Some(data),
..Default::default()
};
let tx = TransactionRequest { to: None, data: Some(data), ..Default::default() };
#[cfg(not(feature = "legacy"))]
let tx = Eip1559TransactionRequest {
to: None,
data: Some(data),
..Default::default()
};
let tx = Eip1559TransactionRequest { to: None, data: Some(data), ..Default::default() };
let tx = tx.into();
Ok(Deployer {

View File

@ -39,9 +39,7 @@ pub use multicall::Multicall;
/// This module exposes low lever builder structures which are only consumed by the
/// type-safe ABI bindings generators.
pub mod builders {
pub use super::call::ContractCall;
pub use super::event::Event;
pub use super::factory::Deployer;
pub use super::{call::ContractCall, event::Event, factory::Deployer};
}
#[cfg(any(test, feature = "abigen"))]

View File

@ -1,7 +1,8 @@
//! Mod of types for ethereum logs
use ethers_core::abi::Error;
use ethers_core::abi::RawLog;
use ethers_core::types::{Address, Log, TxHash, H256, U256, U64};
use ethers_core::{
abi::{Error, RawLog},
types::{Address, Log, TxHash, H256, U256, U64},
};
/// A trait for types (events) that can be decoded from a `RawLog`
pub trait EthLogDecode: Send + Sync {

View File

@ -22,26 +22,11 @@ pub static ADDRESS_BOOK: Lazy<HashMap<U256, Address>> = Lazy::new(|| {
}
[
(
Chain::Mainnet.into(),
decode_address("eefba1e63905ef1d7acba5a8513c70307c1ce441"),
),
(
Chain::Rinkeby.into(),
decode_address("42ad527de7d4e9d9d011ac45b31d8551f8fe9821"),
),
(
Chain::Goerli.into(),
decode_address("77dca2c955b15e9de4dbbcf1246b4b85b651e50e"),
),
(
Chain::Kovan.into(),
decode_address("2cc8688c5f75e365aaeeb4ea8d6a480405a48d2a"),
),
(
Chain::XDai.into(),
decode_address("b5b692a88bdfc81ca69dcb1d924f59f0413a602a"),
),
(Chain::Mainnet.into(), decode_address("eefba1e63905ef1d7acba5a8513c70307c1ce441")),
(Chain::Rinkeby.into(), decode_address("42ad527de7d4e9d9d011ac45b31d8551f8fe9821")),
(Chain::Goerli.into(), decode_address("77dca2c955b15e9de4dbbcf1246b4b85b651e50e")),
(Chain::Kovan.into(), decode_address("2cc8688c5f75e365aaeeb4ea8d6a480405a48d2a")),
(Chain::XDai.into(), decode_address("b5b692a88bdfc81ca69dcb1d924f59f0413a602a")),
]
.into()
});
@ -168,10 +153,8 @@ impl<M: Middleware> Multicall<M> {
let address: Address = match address {
Some(addr) => addr,
None => {
let chain_id = client
.get_chainid()
.await
.map_err(ContractError::MiddlewareError)?;
let chain_id =
client.get_chainid().await.map_err(ContractError::MiddlewareError)?;
match ADDRESS_BOOK.get(&chain_id) {
Some(addr) => *addr,
None => panic!(
@ -184,12 +167,7 @@ impl<M: Middleware> Multicall<M> {
// Instantiate the multicall contract
let contract = MulticallContract::new(address, client);
Ok(Self {
calls: vec![],
block: None,
contract,
legacy: false,
})
Ok(Self { calls: vec![], block: None, contract, legacy: false })
}
/// Makes a legacy transaction instead of an EIP-1559 one
@ -211,19 +189,11 @@ impl<M: Middleware> Multicall<M> {
/// If more than the maximum number of supported calls are added. The maximum
/// limits is constrained due to tokenization/detokenization support for tuples
pub fn add_call<D: Detokenize>(&mut self, call: ContractCall<M, D>) -> &mut Self {
assert!(
!(self.calls.len() >= 16),
"Cannot support more than {} calls",
16
);
assert!(!(self.calls.len() >= 16), "Cannot support more than {} calls", 16);
match (call.tx.to(), call.tx.data()) {
(Some(NameOrAddress::Address(target)), Some(data)) => {
let call = Call {
target: *target,
data: data.clone(),
function: call.function,
};
let call = Call { target: *target, data: data.clone(), function: call.function };
self.calls.push(call);
self
}
@ -373,11 +343,8 @@ impl<M: Middleware> Multicall<M> {
fn as_contract_call(&self) -> ContractCall<M, (U256, Vec<Vec<u8>>)> {
// Map the Multicall struct into appropriate types for `aggregate` function
let calls: Vec<(Address, Vec<u8>)> = self
.calls
.iter()
.map(|call| (call.target, call.data.to_vec()))
.collect();
let calls: Vec<(Address, Vec<u8>)> =
self.calls.iter().map(|call| (call.target, call.data.to_vec())).collect();
// Construct the ContractCall for `aggregate` function to broadcast the transaction
let mut contract_call = self.contract.aggregate(calls);

View File

@ -26,9 +26,7 @@ mod multicallcontract_mod {
}
impl<M: Middleware> std::fmt::Debug for MulticallContract<M> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_tuple(stringify!(MulticallContract))
.field(&self.address())
.finish()
f.debug_tuple(stringify!(MulticallContract)).field(&self.address()).finish()
}
}
impl<'a, M: Middleware> MulticallContract<M> {

View File

@ -2,8 +2,10 @@ use crate::LogMeta;
use ethers_core::types::{Log, U256};
use futures_util::stream::{Stream, StreamExt};
use pin_project::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{
pin::Pin,
task::{Context, Poll},
};
type MapEvent<'a, R, E> = Box<dyn Fn(Log) -> Result<R, E> + 'a + Send + Sync>;

View File

@ -1,12 +1,13 @@
#![cfg(feature = "abigen")]
//! Test cases to validate the `abigen!` macro
use ethers_contract::{abigen, EthEvent};
use ethers_core::abi::{AbiDecode, AbiEncode, Address, Tokenizable};
use ethers_core::types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, U256};
use ethers_core::utils::Solc;
use ethers_core::{
abi::{AbiDecode, AbiEncode, Address, Tokenizable},
types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest, U256},
utils::Solc,
};
use ethers_providers::Provider;
use std::convert::TryFrom;
use std::sync::Arc;
use std::{convert::TryFrom, sync::Arc};
#[test]
fn can_gen_human_readable() {
@ -18,10 +19,7 @@ fn can_gen_human_readable() {
event_derives(serde::Deserialize, serde::Serialize)
);
assert_eq!("ValueChanged", ValueChangedFilter::name());
assert_eq!(
"ValueChanged(address,string,string)",
ValueChangedFilter::abi_signature()
);
assert_eq!("ValueChanged(address,string,string)", ValueChangedFilter::abi_signature());
}
#[test]
@ -40,15 +38,9 @@ fn can_gen_human_readable_multiple() {
event_derives(serde::Deserialize, serde::Serialize)
);
assert_eq!("ValueChanged1", ValueChanged1Filter::name());
assert_eq!(
"ValueChanged1(address,string,string)",
ValueChanged1Filter::abi_signature()
);
assert_eq!("ValueChanged1(address,string,string)", ValueChanged1Filter::abi_signature());
assert_eq!("ValueChanged2", ValueChanged2Filter::name());
assert_eq!(
"ValueChanged2(address,string,string)",
ValueChanged2Filter::abi_signature()
);
assert_eq!("ValueChanged2(address,string,string)", ValueChanged2Filter::abi_signature());
}
#[test]
@ -130,14 +122,8 @@ fn can_generate_internal_structs_multiple() {
let (provider, _) = Provider::mocked();
let client = Arc::new(provider);
let g1 = G1Point {
x: U256::zero(),
y: U256::zero(),
};
let g2 = G2Point {
x: [U256::zero(), U256::zero()],
y: [U256::zero(), U256::zero()],
};
let g1 = G1Point { x: U256::zero(), y: U256::zero() };
let g2 = G2Point { x: [U256::zero(), U256::zero()], y: [U256::zero(), U256::zero()] };
let vk = VerifyingKey {
alfa_1: g1.clone(),
beta_2: g2.clone(),
@ -145,11 +131,7 @@ fn can_generate_internal_structs_multiple() {
delta_2: g2.clone(),
ic: vec![g1.clone()],
};
let proof = Proof {
a: g1.clone(),
b: g2,
c: g1,
};
let proof = Proof { a: g1.clone(), b: g2, c: g1 };
// ensure both contracts use the same types
let contract = VerifierContract::new(Address::zero(), client.clone());
@ -177,11 +159,7 @@ fn can_gen_human_readable_with_structs() {
let f = Foo { x: 100u64.into() };
let _ = contract.foo(f);
let call = BarCall {
x: 1u64.into(),
y: 0u64.into(),
addr: Address::random(),
};
let call = BarCall { x: 1u64.into(), y: 0u64.into(), addr: Address::random() };
let encoded_call = contract.encode("bar", (call.x, call.y, call.addr)).unwrap();
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = BarCall::decode(encoded_call.as_ref()).unwrap();
@ -236,13 +214,9 @@ fn can_handle_overloaded_functions() {
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().into());
let call = GetValueWithOtherValueCall {
other_value: 420u64.into(),
};
let call = GetValueWithOtherValueCall { other_value: 420u64.into() };
let encoded_call = contract
.encode_with_selector([15, 244, 201, 22], call.other_value)
.unwrap();
let encoded_call = contract.encode_with_selector([15, 244, 201, 22], call.other_value).unwrap();
assert_eq!(encoded_call, call.clone().encode().into());
let decoded_call = GetValueWithOtherValueCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);
@ -252,14 +226,11 @@ fn can_handle_overloaded_functions() {
assert_eq!(contract_call, decoded_enum);
assert_eq!(encoded_call, contract_call.encode().into());
let call = GetValueWithOtherValueAndAddrCall {
other_value: 420u64.into(),
addr: Address::random(),
};
let call =
GetValueWithOtherValueAndAddrCall { other_value: 420u64.into(), addr: Address::random() };
let encoded_call = contract
.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr))
.unwrap();
let encoded_call =
contract.encode_with_selector([14, 97, 29, 56], (call.other_value, call.addr)).unwrap();
let decoded_call = GetValueWithOtherValueAndAddrCall::decode(encoded_call.as_ref()).unwrap();
assert_eq!(call, decoded_call);
@ -290,23 +261,14 @@ async fn can_handle_underscore_functions() {
.interval(std::time::Duration::from_millis(10));
let client = Arc::new(provider);
let compiled = Solc::new("./tests/solidity-contracts/SimpleStorage.sol")
.build()
.unwrap();
let compiled = Solc::new("./tests/solidity-contracts/SimpleStorage.sol").build().unwrap();
let compiled = compiled.get("SimpleStorage").unwrap();
let factory = ethers_contract::ContractFactory::new(
compiled.abi.clone(),
compiled.bytecode.clone(),
client.clone(),
);
let addr = factory
.deploy("hi".to_string())
.unwrap()
.legacy()
.send()
.await
.unwrap()
.address();
let addr = factory.deploy("hi".to_string()).unwrap().legacy().send().await.unwrap().address();
// connect to the contract
let contract = SimpleStorage::new(addr, client.clone());
@ -314,18 +276,8 @@ async fn can_handle_underscore_functions() {
let res = contract.hash_puzzle().call().await.unwrap();
let res2 = contract2.hash_puzzle().call().await.unwrap();
let res3 = contract
.method::<_, U256>("_hashPuzzle", ())
.unwrap()
.call()
.await
.unwrap();
let res4 = contract2
.method::<_, U256>("_hashPuzzle", ())
.unwrap()
.call()
.await
.unwrap();
let res3 = contract.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap();
let res4 = contract2.method::<_, U256>("_hashPuzzle", ()).unwrap().call().await.unwrap();
// Manual call construction
use ethers_providers::Middleware;

View File

@ -1,8 +1,8 @@
use ethers_contract::EthLogDecode;
use ethers_contract::{abigen, EthAbiType, EthCall, EthDisplay, EthEvent};
use ethers_core::abi::{RawLog, Tokenizable};
use ethers_core::types::Address;
use ethers_core::types::{H160, H256, I256, U128, U256};
use ethers_contract::{abigen, EthAbiType, EthCall, EthDisplay, EthEvent, EthLogDecode};
use ethers_core::{
abi::{RawLog, Tokenizable},
types::{Address, H160, H256, I256, U128, U256},
};
fn assert_tokenizeable<T: Tokenizable>() {}
fn assert_ethcall<T: EthCall>() {}
@ -165,10 +165,7 @@ fn can_set_eth_event_name_attribute() {
}
assert_eq!("MyEvent", ValueChangedEvent::name());
assert_eq!(
"MyEvent(address,address,string,string)",
ValueChangedEvent::abi_signature()
);
assert_eq!("MyEvent(address,address,string,string)", ValueChangedEvent::abi_signature());
}
#[test]
@ -268,10 +265,7 @@ fn can_generate_ethevent_from_json() {
}
);
assert_eq!(
"Created(address,address,address,address)",
CreatedFilter::abi_signature()
);
assert_eq!("Created(address,address,address,address)", CreatedFilter::abi_signature());
assert_eq!(
H256([
@ -294,11 +288,9 @@ fn can_decode_event_with_no_topics() {
}
// https://etherscan.io/tx/0xb7ba825294f757f8b8b6303b2aef542bcaebc9cc0217ddfaf822200a00594ed9#eventlog index 141
let log = RawLog {
topics: vec![
"298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"
.parse()
.unwrap(),
],
topics: vec!["298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52"
.parse()
.unwrap()],
data: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 205, 0, 29, 173, 151, 238, 5, 127, 91, 31,
197, 154, 221, 40, 175, 143, 32, 26, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, 129,
@ -324,12 +316,8 @@ fn can_decode_event_single_param() {
let log = RawLog {
topics: vec![
"bd9bb67345a2fcc8ef3b0857e7e2901f5a0dcfc7fe5e3c10dc984f02842fb7ba"
.parse()
.unwrap(),
"000000000000000000000000000000000000000000000000000000000000007b"
.parse()
.unwrap(),
"bd9bb67345a2fcc8ef3b0857e7e2901f5a0dcfc7fe5e3c10dc984f02842fb7ba".parse().unwrap(),
"000000000000000000000000000000000000000000000000000000000000007b".parse().unwrap(),
],
data: vec![],
};
@ -345,12 +333,8 @@ fn can_decode_event_tuple_single_param() {
let log = RawLog {
topics: vec![
"bd9bb67345a2fcc8ef3b0857e7e2901f5a0dcfc7fe5e3c10dc984f02842fb7ba"
.parse()
.unwrap(),
"000000000000000000000000000000000000000000000000000000000000007b"
.parse()
.unwrap(),
"bd9bb67345a2fcc8ef3b0857e7e2901f5a0dcfc7fe5e3c10dc984f02842fb7ba".parse().unwrap(),
"000000000000000000000000000000000000000000000000000000000000007b".parse().unwrap(),
],
data: vec![],
};
@ -365,11 +349,9 @@ fn can_decode_event_with_no_params() {
pub struct NoParam {}
let log = RawLog {
topics: vec![
"59a6f900daaeb7581ff830f3a97097fa6372db29b0b50c6d1818ede9d1daaa0c"
.parse()
.unwrap(),
],
topics: vec!["59a6f900daaeb7581ff830f3a97097fa6372db29b0b50c6d1818ede9d1daaa0c"
.parse()
.unwrap()],
data: vec![],
};
@ -424,9 +406,7 @@ fn eth_display_works_for_human_readable() {
let log = LogFilter("abc".to_string());
assert_eq!("abc".to_string(), format!("{}", log));
let log = Log2Filter {
x: "abc".to_string(),
};
let log = Log2Filter { x: "abc".to_string() };
assert_eq!("abc".to_string(), format!("{}", log));
}
@ -456,10 +436,7 @@ fn can_derive_ethcall() {
old_value: String,
new_value: String,
}
assert_eq!(
MyCall::abi_signature().as_ref(),
"my_call(address,string,string)"
);
assert_eq!(MyCall::abi_signature().as_ref(), "my_call(address,string,string)");
assert_tokenizeable::<MyCall>();
assert_ethcall::<MyCall>();
@ -481,10 +458,7 @@ fn can_derive_ethcall_with_nested_structs() {
new_value: String,
}
assert_eq!(
FooCall::abi_signature().as_ref(),
"foo(address,(address,string),string)"
);
assert_eq!(FooCall::abi_signature().as_ref(), "foo(address,(address,string),string)");
assert_tokenizeable::<FooCall>();
assert_ethcall::<FooCall>();
@ -502,8 +476,5 @@ fn can_derive_for_enum() {
assert_tokenizeable::<ActionChoices>();
let token = ActionChoices::GoLeft.into_token();
assert_eq!(
ActionChoices::GoLeft,
ActionChoices::from_token(token).unwrap()
);
assert_eq!(ActionChoices::GoLeft, ActionChoices::from_token(token).unwrap());
}

View File

@ -10,8 +10,11 @@ use ethers_contract::EthEvent;
mod derive;
use ethers_contract::{Contract, ContractFactory};
use ethers_core::utils::{GanacheInstance, Solc};
use ethers_core::{abi::Abi, types::Bytes};
use ethers_core::{
abi::Abi,
types::Bytes,
utils::{GanacheInstance, Solc},
};
use ethers_providers::{Http, Middleware, Provider};
use std::{convert::TryFrom, sync::Arc, time::Duration};
@ -30,9 +33,7 @@ pub struct ValueChanged {
/// compiles the given contract and returns the ABI and Bytecode
pub fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
let compiled = Solc::new(&format!("./tests/solidity-contracts/{}", filename))
.build()
.unwrap();
let compiled = Solc::new(&format!("./tests/solidity-contracts/{}", filename)).build().unwrap();
let contract = compiled.get(name).expect("could not find contract");
(contract.abi.clone(), contract.bytecode.clone())
}
@ -50,11 +51,5 @@ pub fn connect(ganache: &GanacheInstance, idx: usize) -> Arc<Provider<Http>> {
/// Launches a ganache instance and deploys the SimpleStorage contract
pub async fn deploy<M: Middleware>(client: Arc<M>, abi: Abi, bytecode: Bytes) -> Contract<M> {
let factory = ContractFactory::new(abi, bytecode, client);
factory
.deploy("initial value".to_string())
.unwrap()
.legacy()
.send()
.await
.unwrap()
factory.deploy("initial value".to_string()).unwrap().legacy().send().await.unwrap()
}

View File

@ -38,10 +38,7 @@ mod eth_tests {
// `send` consumes the deployer so it must be cloned for later re-use
// (practically it's not expected that you'll need to deploy multiple instances of
// the _same_ deployer, so it's fine to clone here from a dev UX vs perf tradeoff)
let deployer = factory
.deploy("initial value".to_string())
.unwrap()
.legacy();
let deployer = factory.deploy("initial value".to_string()).unwrap().legacy();
let contract = deployer.clone().send().await.unwrap();
let get_value = contract.method::<_, String>("getValue", ()).unwrap();
@ -73,18 +70,10 @@ mod eth_tests {
// (useful when interacting with multiple ERC20s for example)
let contract2_addr = deployer.send().await.unwrap().address();
let contract2 = contract.at(contract2_addr);
let init_value: String = contract2
.method::<_, String>("getValue", ())
.unwrap()
.call()
.await
.unwrap();
let init_address = contract2
.method::<_, Address>("lastSender", ())
.unwrap()
.call()
.await
.unwrap();
let init_value: String =
contract2.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
let init_address =
contract2.method::<_, Address>("lastSender", ()).unwrap().call().await.unwrap();
assert_eq!(init_address, Address::zero());
assert_eq!(init_value, "initial value");
@ -110,10 +99,7 @@ mod eth_tests {
let contract = deploy(client.clone(), abi, bytecode).await;
// make a call with `client`
let func = contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap()
.legacy();
let func = contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().legacy();
let tx = func.send().await.unwrap();
let _receipt = tx.await.unwrap();
@ -184,13 +170,8 @@ mod eth_tests {
let deployed_block = client.get_block_number().await.unwrap();
// assert initial state
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.legacy()
.call()
.await
.unwrap();
let value =
contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap();
assert_eq!(value, "initial value");
// make a call with `client`
@ -203,13 +184,8 @@ mod eth_tests {
.unwrap();
// assert new value
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.legacy()
.call()
.await
.unwrap();
let value =
contract.method::<_, String>("getValue", ()).unwrap().legacy().call().await.unwrap();
assert_eq!(value, "hi");
// assert previous value
@ -250,39 +226,19 @@ mod eth_tests {
let deployed_block = client.get_block_number().await.unwrap();
// assert initial state
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.call()
.await
.unwrap();
let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "initial value");
// make a call with `client`
let _tx_hash = *contract
.method::<_, H256>("setValue", "hi".to_owned())
.unwrap()
.send()
.await
.unwrap();
let _tx_hash =
*contract.method::<_, H256>("setValue", "hi".to_owned()).unwrap().send().await.unwrap();
// assert new value
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
.call()
.await
.unwrap();
let value = contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "hi");
// assert previous value using block hash
let hash = client
.get_block(deployed_block)
.await
.unwrap()
.unwrap()
.hash
.unwrap();
let hash = client.get_block(deployed_block).await.unwrap().unwrap().hash.unwrap();
let value = contract
.method::<_, String>("getValue", ())
.unwrap()
@ -321,10 +277,7 @@ mod eth_tests {
// and we make a few calls
let num = client.get_block_number().await.unwrap();
for i in 0..num_calls {
let call = contract
.method::<_, H256>("setValue", i.to_string())
.unwrap()
.legacy();
let call = contract.method::<_, H256>("setValue", i.to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
}
@ -338,13 +291,7 @@ mod eth_tests {
assert_eq!(log.new_value, log2.new_value);
assert_eq!(log.new_value, i.to_string());
assert_eq!(meta.block_number, num + i + 1);
let hash = client
.get_block(num + i + 1)
.await
.unwrap()
.unwrap()
.hash
.unwrap();
let hash = client.get_block(num + i + 1).await.unwrap().unwrap().hash.unwrap();
assert_eq!(meta.block_hash, hash);
}
}
@ -358,24 +305,16 @@ mod eth_tests {
let contract_2 = deploy(client.clone(), abi.clone(), bytecode).await;
let ws = Provider::connect(ganache.ws_endpoint()).await.unwrap();
let filter = Filter::new().address(ValueOrArray::Array(vec![
contract_1.address(),
contract_2.address(),
]));
let filter = Filter::new()
.address(ValueOrArray::Array(vec![contract_1.address(), contract_2.address()]));
let mut stream = ws.subscribe_logs(&filter).await.unwrap();
// and we make a few calls
let call = contract_1
.method::<_, H256>("setValue", "1".to_string())
.unwrap()
.legacy();
let call = contract_1.method::<_, H256>("setValue", "1".to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
let call = contract_2
.method::<_, H256>("setValue", "2".to_string())
.unwrap()
.legacy();
let call = contract_2.method::<_, H256>("setValue", "2".to_string()).unwrap().legacy();
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
@ -413,12 +352,8 @@ mod eth_tests {
.unwrap()
.await
.unwrap();
let value: String = contract
.method::<_, String>("getValue", ())
.unwrap()
.call()
.await
.unwrap();
let value: String =
contract.method::<_, String>("getValue", ()).unwrap().call().await.unwrap();
assert_eq!(value, "hi");
}
@ -458,13 +393,8 @@ mod eth_tests {
let not_so_simple_factory =
ContractFactory::new(not_so_simple_abi, not_so_simple_bytecode, client3.clone());
let multicall_contract = multicall_factory
.deploy(())
.unwrap()
.legacy()
.send()
.await
.unwrap();
let multicall_contract =
multicall_factory.deploy(()).unwrap().legacy().send().await.unwrap();
let addr = multicall_contract.address();
let simple_contract = simple_factory
@ -502,24 +432,15 @@ mod eth_tests {
// get the calls for `value` and `last_sender` for both SimpleStorage contracts
let value = simple_contract.method::<_, String>("getValue", ()).unwrap();
let value2 = not_so_simple_contract
.method::<_, (String, Address)>("getValues", ())
.unwrap();
let last_sender = simple_contract
.method::<_, Address>("lastSender", ())
.unwrap();
let last_sender2 = not_so_simple_contract
.method::<_, Address>("lastSender", ())
.unwrap();
let value2 =
not_so_simple_contract.method::<_, (String, Address)>("getValues", ()).unwrap();
let last_sender = simple_contract.method::<_, Address>("lastSender", ()).unwrap();
let last_sender2 = not_so_simple_contract.method::<_, Address>("lastSender", ()).unwrap();
// initiate the Multicall instance and add calls one by one in builder style
let mut multicall = Multicall::new(client4.clone(), Some(addr)).await.unwrap();
multicall
.add_call(value)
.add_call(value2)
.add_call(last_sender)
.add_call(last_sender2);
multicall.add_call(value).add_call(value2).add_call(last_sender).add_call(last_sender2);
let return_data: (String, (String, Address), Address, Address) =
multicall.call().await.unwrap();
@ -545,16 +466,11 @@ mod eth_tests {
// go. Now we will use the `.send()` functionality to broadcast a batch of transactions
// in one go
let mut multicall_send = multicall.clone();
multicall_send
.clear_calls()
.add_call(broadcast)
.add_call(broadcast2);
multicall_send.clear_calls().add_call(broadcast).add_call(broadcast2);
// broadcast the transaction and wait for it to be mined
let tx_hash = multicall_send.legacy().send().await.unwrap();
let _tx_receipt = PendingTransaction::new(tx_hash, client.provider())
.await
.unwrap();
let _tx_receipt = PendingTransaction::new(tx_hash, client.provider()).await.unwrap();
// Do another multicall to check the updated return values
// The `getValue` calls should return the last value we set in the batched broadcast
@ -659,10 +575,7 @@ mod eth_tests {
out: foo_bar.out.clone(),
};
let sig = wallet
.sign_typed_data(&foo_bar)
.await
.expect("failed to sign typed data");
let sig = wallet.sign_typed_data(&foo_bar).await.expect("failed to sign typed data");
let r = <[u8; 32]>::try_from(sig.r)
.expect("failed to parse 'r' value from signature into [u8; 32]");
@ -675,11 +588,8 @@ mod eth_tests {
.call()
.await
.expect("failed to retrieve domain_separator from contract");
let type_hash = contract
.type_hash()
.call()
.await
.expect("failed to retrieve type_hash from contract");
let type_hash =
contract.type_hash().call().await.expect("failed to retrieve type_hash from contract");
let struct_hash = contract
.struct_hash(derived_foo_bar.clone())
.call()

View File

@ -59,7 +59,6 @@
//!
//! There is an Inner helper attribute `#[eip712]` for fields that will eventually be used to
//! determine if there is a nested eip712 struct. However, this work is not yet complete.
//!
use std::convert::TryFrom;
use ethers_core::types::transaction::eip712;
@ -95,7 +94,7 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
Err(e) => {
return TokenStream::from(
syn::Error::new(ast.ident.span(), e.to_string()).to_compile_error(),
);
)
}
};
@ -106,10 +105,8 @@ fn impl_eip_712_macro(ast: &syn::DeriveInput) -> TokenStream {
};
// Compute the type hash for the derived struct using the parsed fields from above;
let type_hash = hex::encode(eip712::make_type_hash(
primary_type.clone().to_string(),
&parsed_fields,
));
let type_hash =
hex::encode(eip712::make_type_hash(primary_type.clone().to_string(), &parsed_fields));
let implementation = quote! {
impl Eip712 for #primary_type {

View File

@ -93,8 +93,8 @@ fn test_derive_eip712_nested() {
foo: String,
bar: U256,
addr: Address,
// #[eip712] // Todo: Support nested Eip712 structs
// nested: MyNestedStruct,
/* #[eip712] // Todo: Support nested Eip712 structs
* nested: MyNestedStruct, */
}
#[derive(Debug, Clone, Eip712, EthAbiType)]
@ -114,11 +114,11 @@ fn test_derive_eip712_nested() {
foo: "foo".to_string(),
bar: U256::from(1),
addr: Address::from(&[0; 20]),
// nested: MyNestedStruct {
// foo: "foo".to_string(),
// bar: U256::from(1),
// addr: Address::from(&[0; 20]),
// },
/* nested: MyNestedStruct {
* foo: "foo".to_string(),
* bar: U256::from(1),
* addr: Address::from(&[0; 20]),
* }, */
};
let hash = my_struct.struct_hash().expect("failed to hash struct");
@ -147,12 +147,8 @@ fn test_uniswap_v2_permit_hash() {
}
let permit = Permit {
owner: "0x617072Cb2a1897192A9d301AC53fC541d35c4d9D"
.parse()
.unwrap(),
spender: "0x2819c144D5946404C0516B6f817a960dB37D4929"
.parse()
.unwrap(),
owner: "0x617072Cb2a1897192A9d301AC53fC541d35c4d9D".parse().unwrap(),
spender: "0x2819c144D5946404C0516B6f817a960dB37D4929".parse().unwrap(),
value: parse_ether(10).unwrap(),
nonce: U256::from(1),
deadline: U256::from(3133728498 as u32),

View File

@ -1,5 +1,7 @@
use crate::abi::{AbiArrayType, AbiError, AbiType, Detokenize, Tokenizable, TokenizableItem};
use crate::types::{Address, H256, U128, U256};
use crate::{
abi::{AbiArrayType, AbiError, AbiType, Detokenize, Tokenizable, TokenizableItem},
types::{Address, H256, U128, U256},
};
/// Trait for ABI encoding
pub trait AbiEncode {
@ -208,11 +210,7 @@ mod tests {
fn u8_codec() {
assert_codec(random::<u8>());
assert_codec((random::<u8>(), random::<u8>()));
assert_codec(
std::iter::repeat_with(|| random::<u8>())
.take(10)
.collect::<Vec<_>>(),
);
assert_codec(std::iter::repeat_with(random::<u8>).take(10).collect::<Vec<_>>());
assert_codec([random::<u8>(); 10]);
}

View File

@ -1,10 +1,10 @@
use std::collections::{BTreeMap, HashMap, VecDeque};
use crate::abi::error::{bail, format_err, ParseError, Result};
use crate::abi::struct_def::{FieldType, StructFieldType};
use crate::abi::{
param_type::Reader, Abi, Constructor, Event, EventParam, Function, Param, ParamType, SolStruct,
StateMutability,
error::{bail, format_err, ParseError, Result},
param_type::Reader,
struct_def::{FieldType, StructFieldType},
Abi, Constructor, Event, EventParam, Function, Param, ParamType, SolStruct, StateMutability,
};
/// A parser that turns a "human readable abi" into a `Abi`
@ -13,7 +13,8 @@ pub struct AbiParser {
pub structs: HashMap<String, SolStruct>,
/// solidity structs as tuples
pub struct_tuples: HashMap<String, Vec<ParamType>>,
/// (function name, param name) -> struct which are the identifying properties we get the name from ethabi.
/// (function name, param name) -> struct which are the identifying properties we get the name
/// from ethabi.
pub function_params: HashMap<(String, String), String>,
/// (function name) -> Vec<structs> all structs the function returns
pub outputs: HashMap<String, Vec<String>>,
@ -34,11 +35,7 @@ impl AbiParser {
/// ```
pub fn parse_str(&mut self, s: &str) -> Result<Abi> {
self.parse(
&s.trim()
.trim_start_matches('[')
.trim_end_matches(']')
.lines()
.collect::<Vec<_>>(),
&s.trim().trim_start_matches('[').trim_end_matches(']').lines().collect::<Vec<_>>(),
)
}
@ -82,10 +79,7 @@ impl AbiParser {
line = line.trim_start();
if line.starts_with("event") {
let event = self.parse_event(line)?;
abi.events
.entry(event.name.clone())
.or_default()
.push(event);
abi.events.entry(event.name.clone()).or_default().push(event);
} else if line.starts_with("constructor") {
let inputs = self
.constructor_inputs(line)?
@ -109,10 +103,7 @@ impl AbiParser {
Ok(function) => function,
Err(_) => bail!("Illegal abi `{}`", line),
};
abi.functions
.entry(function.name.clone())
.or_default()
.push(function);
abi.functions.entry(function.name.clone()).or_default().push(function);
}
}
Ok(abi)
@ -134,7 +125,7 @@ impl AbiParser {
tuple.push(ty.as_param(ParamType::Tuple(param)))
} else {
resolved = false;
break;
break
}
}
FieldType::Mapping(_) => {
@ -162,10 +153,7 @@ impl AbiParser {
/// Link additional structs for parsing
pub fn with_structs(structs: Vec<SolStruct>) -> Self {
Self {
structs: structs
.into_iter()
.map(|s| (s.name().to_string(), s))
.collect(),
structs: structs.into_iter().map(|s| (s.name().to_string(), s)).collect(),
struct_tuples: HashMap::new(),
function_params: Default::default(),
outputs: Default::default(),
@ -207,15 +195,9 @@ impl AbiParser {
.map(|e| self.parse_event_arg(e))
.collect::<Result<Vec<_>, _>>()?
};
return Ok(Event {
name,
inputs,
anonymous,
});
}
Some(' ') | Some('\t') => {
continue;
return Ok(Event { name, inputs, anonymous })
}
Some(' ') | Some('\t') => continue,
Some(c) => {
bail!("Illegal char `{}` at `{}`", c, s)
}
@ -227,9 +209,8 @@ impl AbiParser {
fn parse_event_arg(&self, input: &str) -> Result<EventParam> {
let mut iter = input.trim().rsplitn(3, is_whitespace);
let mut indexed = false;
let mut name = iter
.next()
.ok_or_else(|| format_err!("Empty event param at `{}`", input))?;
let mut name =
iter.next().ok_or_else(|| format_err!("Empty event param at `{}`", input))?;
let type_str;
if let Some(mid) = iter.next() {
@ -251,11 +232,7 @@ impl AbiParser {
name = "";
}
Ok(EventParam {
name: name.to_string(),
indexed,
kind: self.parse_type(type_str)?.0,
})
Ok(EventParam { name: name.to_string(), indexed, kind: self.parse_type(type_str)?.0 })
}
pub fn parse_function(&mut self, s: &str) -> Result<Function> {
@ -339,13 +316,7 @@ impl AbiParser {
Ok(
#[allow(deprecated)]
Function {
name,
inputs,
outputs,
state_mutability,
constant: false,
},
Function { name, inputs, outputs, state_mutability, constant: false },
)
}
@ -356,7 +327,8 @@ impl AbiParser {
.collect::<Result<Vec<_>, _>>()
}
/// Returns the `ethabi` `ParamType` for the function parameter and the aliased struct type, if it is a user defined struct
/// Returns the `ethabi` `ParamType` for the function parameter and the aliased struct type, if
/// it is a user defined struct
fn parse_type(&self, type_str: &str) -> Result<(ParamType, Option<String>)> {
if let Ok(kind) = Reader::read(type_str) {
Ok((kind, None))
@ -386,11 +358,7 @@ impl AbiParser {
}
pub fn parse_constructor(&self, s: &str) -> Result<Constructor> {
let inputs = self
.constructor_inputs(s)?
.into_iter()
.map(|s| s.0)
.collect();
let inputs = self.constructor_inputs(s)?.into_iter().map(|s| s.0).collect();
Ok(Constructor { inputs })
}
@ -415,9 +383,7 @@ impl AbiParser {
fn parse_param(&self, param: &str) -> Result<(Param, Option<String>)> {
let mut iter = param.trim().rsplitn(3, is_whitespace);
let mut name = iter
.next()
.ok_or(ParseError::ParseError(super::Error::InvalidData))?;
let mut name = iter.next().ok_or(ParseError::ParseError(super::Error::InvalidData))?;
let type_str;
if let Some(ty) = iter.last() {
@ -430,14 +396,7 @@ impl AbiParser {
name = "";
}
let (kind, user_struct) = self.parse_type(type_str)?;
Ok((
Param {
name: name.to_string(),
kind,
internal_type: None,
},
user_struct,
))
Ok((Param { name: name.to_string(), kind, internal_type: None }, user_struct))
}
}
@ -471,9 +430,7 @@ pub fn parse_str(input: &str) -> Result<Abi> {
pub(crate) fn parse_identifier(input: &mut &str) -> Result<String> {
let mut chars = input.trim_start().chars();
let mut name = String::new();
let c = chars
.next()
.ok_or_else(|| format_err!("Empty identifier in `{}`", input))?;
let c = chars.next().ok_or_else(|| format_err!("Empty identifier in `{}`", input))?;
if is_first_ident_char(c) {
name.push(c);
loop {
@ -487,9 +444,7 @@ pub(crate) fn parse_identifier(input: &mut &str) -> Result<String> {
}
}
if name.is_empty() {
return Err(ParseError::ParseError(super::Error::InvalidName(
input.to_string(),
)));
return Err(ParseError::ParseError(super::Error::InvalidName(input.to_string())))
}
*input = chars.as_str();
Ok(name)
@ -546,10 +501,7 @@ mod tests {
let parsed = AbiParser::default().parse_function(fn_str).unwrap();
assert_eq!(parsed.name, "foo");
assert_eq!(parsed.inputs[0].name, "x");
assert_eq!(
parsed.inputs[0].kind,
ParamType::Array(Box::new(ParamType::Uint(32)))
);
assert_eq!(parsed.inputs[0].kind, ParamType::Array(Box::new(ParamType::Uint(32))));
assert_eq!(parsed.outputs[0].name, "");
assert_eq!(parsed.outputs[0].kind, ParamType::Address);
}
@ -594,11 +546,7 @@ mod tests {
anonymous: false,
name: "Foo".to_string(),
inputs: vec![
EventParam {
name: "x".to_string(),
kind: ParamType::Address,
indexed: true,
},
EventParam { name: "x".to_string(), kind: ParamType::Address, indexed: true },
EventParam {
name: "y".to_string(),
kind: ParamType::Uint(256),
@ -617,23 +565,15 @@ mod tests {
#[test]
fn parses_anonymous_event() {
assert_eq!(
AbiParser::default()
.parse_event("event Foo() anonymous")
.unwrap(),
Event {
anonymous: true,
name: "Foo".to_string(),
inputs: vec![],
}
AbiParser::default().parse_event("event Foo() anonymous").unwrap(),
Event { anonymous: true, name: "Foo".to_string(), inputs: vec![] }
);
}
#[test]
fn parses_unnamed_event() {
assert_eq!(
AbiParser::default()
.parse_event("event Foo(address)")
.unwrap(),
AbiParser::default().parse_event("event Foo(address)").unwrap(),
Event {
anonymous: false,
name: "Foo".to_string(),
@ -649,9 +589,7 @@ mod tests {
#[test]
fn parses_unnamed_indexed_event() {
assert_eq!(
AbiParser::default()
.parse_event("event Foo(address indexed)")
.unwrap(),
AbiParser::default().parse_event("event Foo(address indexed)").unwrap(),
Event {
anonymous: false,
name: "Foo".to_string(),
@ -667,23 +605,13 @@ mod tests {
#[test]
fn parse_event_input() {
assert_eq!(
AbiParser::default()
.parse_event_arg("address indexed x")
.unwrap(),
EventParam {
name: "x".to_string(),
kind: ParamType::Address,
indexed: true,
}
AbiParser::default().parse_event_arg("address indexed x").unwrap(),
EventParam { name: "x".to_string(), kind: ParamType::Address, indexed: true }
);
assert_eq!(
AbiParser::default().parse_event_arg("address x").unwrap(),
EventParam {
name: "x".to_string(),
kind: ParamType::Address,
indexed: false,
}
EventParam { name: "x".to_string(), kind: ParamType::Address, indexed: false }
);
}
@ -797,10 +725,7 @@ mod tests {
EventParam {
name: "m2".to_string(),
kind: ParamType::FixedArray(
Box::new(ParamType::Tuple(vec![
ParamType::Int(256),
ParamType::Address
])),
Box::new(ParamType::Tuple(vec![ParamType::Int(256), ParamType::Address])),
10
),
indexed: false

View File

@ -2,8 +2,7 @@
// Adapted from [Gnosis' ethcontract](https://github.com/gnosis/ethcontract-rs/blob/master/common/src/abiext.rs)
use crate::{types::Selector, utils::id};
pub use ethabi::Contract as Abi;
pub use ethabi::*;
pub use ethabi::{Contract as Abi, *};
mod tokens;
pub use tokens::{Detokenize, InvalidOutputType, Tokenizable, TokenizableItem, Tokenize};
@ -60,11 +59,7 @@ impl EventExt for Event {
format!(
"{}({}){}",
self.name,
self.inputs
.iter()
.map(|input| input.kind.to_string())
.collect::<Vec<_>>()
.join(","),
self.inputs.iter().map(|input| input.kind.to_string()).collect::<Vec<_>>().join(","),
if self.anonymous { " anonymous" } else { "" },
)
}
@ -226,10 +221,7 @@ mod tests {
r#"{"name":"baz","inputs":[{"name":"a","type":"uint256"}],"anonymous":true}"#,
"baz(uint256) anonymous",
),
(
r#"{"name":"bax","inputs":[],"anonymous":true}"#,
"bax() anonymous",
),
(r#"{"name":"bax","inputs":[],"anonymous":true}"#, "bax() anonymous"),
] {
let event: Event = serde_json::from_str(e).expect("invalid event JSON");
let signature = event.abi_signature();
@ -240,19 +232,13 @@ mod tests {
#[test]
fn abi_type_works() {
assert_eq!(ParamType::Bytes, Vec::<u8>::param_type());
assert_eq!(
ParamType::Array(Box::new(ParamType::Bytes)),
Vec::<Vec<u8>>::param_type()
);
assert_eq!(ParamType::Array(Box::new(ParamType::Bytes)), Vec::<Vec<u8>>::param_type());
assert_eq!(
ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bytes)))),
Vec::<Vec<Vec<u8>>>::param_type()
);
assert_eq!(
ParamType::Array(Box::new(ParamType::Uint(16))),
Vec::<u16>::param_type()
);
assert_eq!(ParamType::Array(Box::new(ParamType::Uint(16))), Vec::<u16>::param_type());
assert_eq!(
ParamType::Tuple(vec![ParamType::Bytes, ParamType::Address]),

View File

@ -1,7 +1,10 @@
//! Solidity struct definition parsing support
use crate::abi::error::{bail, format_err, Result};
use crate::abi::human_readable::{is_whitespace, parse_identifier};
use crate::abi::{param_type::Reader, ParamType};
use crate::abi::{
error::{bail, format_err, Result},
human_readable::{is_whitespace, parse_identifier},
param_type::Reader,
ParamType,
};
/// A field declaration inside a struct
#[derive(Debug, Clone, PartialEq)]
@ -171,10 +174,7 @@ impl StructFieldType {
}
}
Some(']') => {
let ty = StructType {
name: ty,
projections,
};
let ty = StructType { name: ty, projections };
return if size.is_empty() {
Ok(FieldType::Struct(StructFieldType::Array(Box::new(
@ -188,7 +188,7 @@ impl StructFieldType {
Box::new(StructFieldType::Type(ty)),
size,
)))
};
}
}
Some(c) => {
if c.is_numeric() {
@ -260,11 +260,9 @@ impl SolStruct {
.map(parse_struct_field)
.collect::<Result<Vec<_>, _>>()?
};
return Ok(SolStruct { name, fields });
}
Some(' ') | Some('\t') => {
continue;
return Ok(SolStruct { name, fields })
}
Some(' ') | Some('\t') => continue,
Some(c) => {
bail!("Illegal char `{}` at `{}`", c, s)
}
@ -282,14 +280,15 @@ impl SolStruct {
&self.fields
}
/// If the struct only consists of elementary fields, this will return `ParamType::Tuple` with all those fields
/// If the struct only consists of elementary fields, this will return `ParamType::Tuple` with
/// all those fields
pub fn as_tuple(&self) -> Option<ParamType> {
let mut params = Vec::with_capacity(self.fields.len());
for field in self.fields() {
if let FieldType::Elementary(ref param) = field.ty {
params.push(param.clone())
} else {
return None;
return None
}
}
Some(ParamType::Tuple(params))
@ -303,10 +302,8 @@ fn strip_field_identifier(input: &mut &str) -> Result<String> {
.next()
.ok_or_else(|| format_err!("Expected field identifier"))
.map(|mut s| parse_identifier(&mut s))??;
*input = iter
.next()
.ok_or_else(|| format_err!("Expected field type in `{}`", input))?
.trim_end();
*input =
iter.next().ok_or_else(|| format_err!("Expected field type in `{}`", input))?.trim_end();
Ok(name)
}
@ -323,16 +320,13 @@ fn parse_struct_field(s: &str) -> Result<FieldDeclaration> {
.trim_end();
}
let name = strip_field_identifier(&mut input)?;
Ok(FieldDeclaration {
name,
ty: parse_field_type(input)?,
})
Ok(FieldDeclaration { name, ty: parse_field_type(input)? })
}
fn parse_field_type(s: &str) -> Result<FieldType> {
let mut input = s.trim_start();
if input.starts_with("mapping") {
return Ok(FieldType::Mapping(Box::new(parse_mapping(input)?)));
return Ok(FieldType::Mapping(Box::new(parse_mapping(input)?)))
}
if input.ends_with(" payable") {
// special case for `address payable`
@ -353,10 +347,7 @@ fn parse_mapping(s: &str) -> Result<MappingType> {
bail!("Not a mapping `{}`", input)
}
input = input[7..].trim_start();
let mut iter = input
.trim_start_matches('(')
.trim_end_matches(')')
.splitn(2, "=>");
let mut iter = input.trim_start_matches('(').trim_end_matches(')').splitn(2, "=>");
let key_type = iter
.next()
.ok_or_else(|| format_err!("Expected mapping key type at `{}`", input))
@ -364,11 +355,7 @@ fn parse_mapping(s: &str) -> Result<MappingType> {
.map(Reader::read)??;
if let ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Tuple(_) = &key_type {
bail!(
"Expected elementary mapping key type at `{}` got {:?}",
input,
key_type
)
bail!("Expected elementary mapping key type at `{}` got {:?}", input, key_type)
}
let value_type = iter
@ -377,10 +364,7 @@ fn parse_mapping(s: &str) -> Result<MappingType> {
.map(str::trim)
.map(parse_field_type)??;
Ok(MappingType {
key_type,
value_type,
})
Ok(MappingType { key_type, value_type })
}
#[cfg(test)]

View File

@ -137,10 +137,7 @@ impl Tokenizable for String {
fn from_token(token: Token) -> Result<Self, InvalidOutputType> {
match token {
Token::String(s) => Ok(s),
other => Err(InvalidOutputType(format!(
"Expected `String`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `String`, got {:?}", other))),
}
}
@ -153,10 +150,7 @@ impl Tokenizable for Bytes {
fn from_token(token: Token) -> Result<Self, InvalidOutputType> {
match token {
Token::Bytes(s) => Ok(s.into()),
other => Err(InvalidOutputType(format!(
"Expected `Bytes`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `Bytes`, got {:?}", other))),
}
}
@ -170,7 +164,7 @@ impl Tokenizable for H256 {
match token {
Token::FixedBytes(mut s) => {
if s.len() != 32 {
return Err(InvalidOutputType(format!("Expected `H256`, got {:?}", s)));
return Err(InvalidOutputType(format!("Expected `H256`, got {:?}", s)))
}
let mut data = [0; 32];
for (idx, val) in s.drain(..).enumerate() {
@ -178,10 +172,7 @@ impl Tokenizable for H256 {
}
Ok(data.into())
}
other => Err(InvalidOutputType(format!(
"Expected `H256`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `H256`, got {:?}", other))),
}
}
@ -194,10 +185,7 @@ impl Tokenizable for Address {
fn from_token(token: Token) -> Result<Self, InvalidOutputType> {
match token {
Token::Address(data) => Ok(data),
other => Err(InvalidOutputType(format!(
"Expected `Address`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `Address`, got {:?}", other))),
}
}
@ -214,11 +202,10 @@ macro_rules! eth_uint_tokenizable {
Token::Int(data) | Token::Uint(data) => {
Ok(::std::convert::TryInto::try_into(data).unwrap())
}
other => Err(InvalidOutputType(format!(
"Expected `{}`, got {:?}",
$name, other
))
.into()),
other => {
Err(InvalidOutputType(format!("Expected `{}`, got {:?}", $name, other))
.into())
}
}
}
@ -278,10 +265,7 @@ impl Tokenizable for bool {
fn from_token(token: Token) -> Result<Self, InvalidOutputType> {
match token {
Token::Bool(data) => Ok(data),
other => Err(InvalidOutputType(format!(
"Expected `bool`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `bool`, got {:?}", other))),
}
}
fn into_token(self) -> Token {
@ -338,10 +322,7 @@ impl Tokenizable for Vec<u8> {
match token {
Token::Bytes(data) => Ok(data),
Token::FixedBytes(data) => Ok(data),
other => Err(InvalidOutputType(format!(
"Expected `bytes`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `bytes`, got {:?}", other))),
}
}
fn into_token(self) -> Token {
@ -355,10 +336,7 @@ impl<T: TokenizableItem> Tokenizable for Vec<T> {
Token::FixedArray(tokens) | Token::Array(tokens) => {
tokens.into_iter().map(Tokenizable::from_token).collect()
}
other => Err(InvalidOutputType(format!(
"Expected `Array`, got {:?}",
other
))),
other => Err(InvalidOutputType(format!("Expected `Array`, got {:?}", other))),
}
}
@ -378,18 +356,17 @@ impl<const N: usize> Tokenizable for [u8; N] {
"Expected `FixedBytes({})`, got FixedBytes({})",
N,
bytes.len()
)));
)))
}
let mut arr = [0; N];
arr.copy_from_slice(&bytes);
Ok(arr)
}
other => Err(InvalidOutputType(format!(
"Expected `FixedBytes({})`, got {:?}",
N, other
))
.into()),
other => {
Err(InvalidOutputType(format!("Expected `FixedBytes({})`, got {:?}", N, other))
.into())
}
}
}
@ -409,7 +386,7 @@ impl<T: TokenizableItem + Clone, const N: usize> Tokenizable for [T; N] {
"Expected `FixedArray({})`, got FixedArray({})",
N,
tokens.len()
)));
)))
}
let mut arr = ArrayVec::<T, N>::new();
@ -423,21 +400,15 @@ impl<T: TokenizableItem + Clone, const N: usize> Tokenizable for [T; N] {
Err(_) => panic!("All elements inserted so the array is full; qed"),
}
}
other => Err(InvalidOutputType(format!(
"Expected `FixedArray({})`, got {:?}",
N, other
))
.into()),
other => {
Err(InvalidOutputType(format!("Expected `FixedArray({})`, got {:?}", N, other))
.into())
}
}
}
fn into_token(self) -> Token {
Token::FixedArray(
ArrayVec::from(self)
.into_iter()
.map(T::into_token)
.collect(),
)
Token::FixedArray(ArrayVec::from(self).into_iter().map(T::into_token).collect())
}
}

View File

@ -216,14 +216,10 @@ mod tests {
#[test]
fn serde_block_number() {
for b in vec![
BlockNumber::Latest,
BlockNumber::Earliest,
BlockNumber::Pending,
] {
for b in &[BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending] {
let b_ser = serde_json::to_string(&b).unwrap();
let b_de: BlockNumber = serde_json::from_str(&b_ser).unwrap();
assert_eq!(b_de, b);
assert_eq!(b_de, *b);
}
let b = BlockNumber::Number(1042u64.into());

View File

@ -1,14 +1,13 @@
use serde::de::{Error, Unexpected};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer, Serialize, Serializer,
};
/// 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 bytes::Bytes,
#[serde(serialize_with = "serialize_bytes", deserialize_with = "deserialize_bytes")]
pub bytes::Bytes,
);
impl Bytes {

View File

@ -114,9 +114,6 @@ mod tests {
let addr = "f02c1c8e6114b1dbe8937a39260b5b0a374432bb".parse().unwrap();
let union = NameOrAddress::Address(addr);
assert_eq!(
bincode::serialize(&addr).unwrap(),
bincode::serialize(&union).unwrap(),
);
assert_eq!(bincode::serialize(&addr).unwrap(), bincode::serialize(&union).unwrap(),);
}
}

View File

@ -1,17 +1,17 @@
//! This module contains an 256-bit signed integer implementation.
//! This module was derived for ethers-core via https://github.com/gnosis/ethcontract-rs/
#![allow(clippy::wrong_self_convention)]
use crate::abi::{InvalidOutputType, Token, Tokenizable};
use crate::types::U256;
use crate::{
abi::{InvalidOutputType, Token, Tokenizable},
types::U256,
};
use ethabi::ethereum_types::FromDecStrErr;
use serde::{Deserialize, Serialize};
use std::cmp;
use std::convert::{TryFrom, TryInto};
use std::fmt;
use std::iter;
use std::ops;
use std::str;
use std::{i128, i64, u64};
use std::{
cmp,
convert::{TryFrom, TryInto},
fmt, i128, i64, iter, ops, str, u64,
};
use thiserror::Error;
/// The error type that is returned when conversion to or from a 256-bit integer
@ -314,7 +314,7 @@ impl I256 {
// NOTE: Do the hex conversion here as `U256` implementation can panic.
if value.len() > 64 {
return Err(ParseI256Error::IntegerOverflow);
return Err(ParseI256Error::IntegerOverflow)
}
let mut abs = U256::zero();
for (i, word) in value.as_bytes().rchunks(16).enumerate() {
@ -464,8 +464,7 @@ impl I256 {
// NOTE: We need to deal with two special cases:
// - the number is 0
// - the number is a negative power of `2`. These numbers are written
// as `0b11..1100..00`.
// - the number is a negative power of `2`. These numbers are written as `0b11..1100..00`.
// In the case of a negative power of two, the number of bits required
// to represent the negative signed value is equal to the number of
// bits required to represent its absolute value as an unsigned
@ -550,8 +549,8 @@ impl I256 {
// the result.
let overflow = matches!(
(self.sign(), rhs.sign(), result.sign()),
(Sign::Positive, Sign::Positive, Sign::Negative)
| (Sign::Negative, Sign::Negative, Sign::Positive)
(Sign::Positive, Sign::Positive, Sign::Negative) |
(Sign::Negative, Sign::Negative, Sign::Positive)
);
(result, overflow)
@ -603,8 +602,8 @@ impl I256 {
// the result.
let overflow = matches!(
(self.sign(), rhs.sign(), result.sign()),
(Sign::Positive, Sign::Negative, Sign::Negative)
| (Sign::Negative, Sign::Positive, Sign::Positive)
(Sign::Positive, Sign::Negative, Sign::Negative) |
(Sign::Negative, Sign::Positive, Sign::Positive)
);
(result, overflow)
@ -746,24 +745,22 @@ impl I256 {
///
/// This computes the integer `n` such that `self = n * rhs + self.rem_euclid(rhs)`,
/// with `0 <= self.rem_euclid(rhs) < rhs`.
/// In other words, the result is `self / rhs` rounded to the integer `n` such that `self >= n * rhs`:
/// In other words, the result is `self / rhs` rounded to the integer `n` such that `self >= n *
/// rhs`:
/// * If `self > 0`, this is equal to round towards zero (the default in Rust);
/// * If `self < 0`, this is equal to round towards +/- infinity.
pub fn div_euclid(self, rhs: Self) -> Self {
let q = self / rhs;
if (self % rhs).is_negative() {
return if rhs.is_positive() {
q - I256::one()
} else {
q + I256::one()
};
return if rhs.is_positive() { q - I256::one() } else { q + I256::one() }
}
q
}
/// Calculates the least non-negative remainder of self (mod rhs).
/// This is done as if by the _Euclidean division algorithm_
/// given `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r, and 0 <= r < abs(rhs)`.
/// given `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r, and 0 <= r <
/// abs(rhs)`.
pub fn rem_euclid(self, rhs: Self) -> Self {
let r = self % rhs;
if r < Self::zero() {
@ -1410,22 +1407,10 @@ mod tests {
assert_eq!(format!("{:+x}", positive), format!("+{:x}", unsigned));
assert_eq!(format!("{:+x}", negative), format!("-{:x}", unsigned));
assert_eq!(
format!("{:X}", positive),
format!("{:x}", unsigned).to_uppercase()
);
assert_eq!(
format!("{:X}", negative),
format!("-{:x}", unsigned).to_uppercase()
);
assert_eq!(
format!("{:+X}", positive),
format!("+{:x}", unsigned).to_uppercase()
);
assert_eq!(
format!("{:+X}", negative),
format!("-{:x}", unsigned).to_uppercase()
);
assert_eq!(format!("{:X}", positive), format!("{:x}", unsigned).to_uppercase());
assert_eq!(format!("{:X}", negative), format!("-{:x}", unsigned).to_uppercase());
assert_eq!(format!("{:+X}", positive), format!("+{:x}", unsigned).to_uppercase());
assert_eq!(format!("{:+X}", negative), format!("-{:x}", unsigned).to_uppercase());
}
#[test]
@ -1456,24 +1441,19 @@ mod tests {
assert!(I256::zero().is_zero());
assert_eq!(
I256::from_dec_str("314159265358979323846264338327950288419716")
.unwrap()
.signum(),
I256::from_dec_str("314159265358979323846264338327950288419716").unwrap().signum(),
I256::one(),
);
assert_eq!(
I256::from_dec_str("-314159265358979323846264338327950288419716")
.unwrap()
.signum(),
I256::from_dec_str("-314159265358979323846264338327950288419716").unwrap().signum(),
I256::minus_one(),
);
}
#[test]
fn abs() {
let positive = I256::from_dec_str("314159265358979323846264338327950288419716")
.unwrap()
.signum();
let positive =
I256::from_dec_str("314159265358979323846264338327950288419716").unwrap().signum();
let negative = -positive;
assert_eq!(positive.abs(), positive);
@ -1487,9 +1467,8 @@ mod tests {
#[test]
fn neg() {
let positive = I256::from_dec_str("314159265358979323846264338327950288419716")
.unwrap()
.signum();
let positive =
I256::from_dec_str("314159265358979323846264338327950288419716").unwrap().signum();
let negative = -positive;
assert_eq!(-positive, negative);
@ -1525,10 +1504,7 @@ mod tests {
assert_eq!(I256::MIN.overflowing_add(I256::MIN), (I256::zero(), true));
assert_eq!(I256::MAX.overflowing_add(I256::MAX), (I256::from(-2), true));
assert_eq!(
I256::MIN.overflowing_add(I256::minus_one()),
(I256::MAX, true)
);
assert_eq!(I256::MIN.overflowing_add(I256::minus_one()), (I256::MAX, true));
assert_eq!(I256::MAX.overflowing_add(I256::one()), (I256::MIN, true));
assert_eq!(I256::MAX + I256::MIN, I256::minus_one());
@ -1544,16 +1520,10 @@ mod tests {
#[allow(clippy::eq_op)]
fn subtraction() {
assert_eq!(I256::MIN.overflowing_sub(I256::MAX), (I256::one(), true));
assert_eq!(
I256::MAX.overflowing_sub(I256::MIN),
(I256::minus_one(), true)
);
assert_eq!(I256::MAX.overflowing_sub(I256::MIN), (I256::minus_one(), true));
assert_eq!(I256::MIN.overflowing_sub(I256::one()), (I256::MAX, true));
assert_eq!(
I256::MAX.overflowing_sub(I256::minus_one()),
(I256::MIN, true)
);
assert_eq!(I256::MAX.overflowing_sub(I256::minus_one()), (I256::MIN, true));
assert_eq!(I256::zero().overflowing_sub(I256::MIN), (I256::MIN, true));
@ -1623,10 +1593,7 @@ mod tests {
assert_eq!((-a).div_euclid(-b), I256::from(2)); // -7 >= -4 * 2
// Overflowing
assert_eq!(
I256::MIN.overflowing_div_euclid(-I256::one()),
(I256::MIN, true)
);
assert_eq!(I256::MIN.overflowing_div_euclid(-I256::one()), (I256::MIN, true));
// Wrapping
assert_eq!(I256::MIN.wrapping_div_euclid(-I256::one()), I256::MIN);
// // Checked
@ -1646,20 +1613,11 @@ mod tests {
// Overflowing
assert_eq!(a.overflowing_rem_euclid(b), (I256::from(3), false));
assert_eq!(
I256::min_value().overflowing_rem_euclid(-I256::one()),
(I256::zero(), true)
);
assert_eq!(I256::min_value().overflowing_rem_euclid(-I256::one()), (I256::zero(), true));
// Wrapping
assert_eq!(
I256::from(100).wrapping_rem_euclid(I256::from(10)),
I256::zero()
);
assert_eq!(
I256::min_value().wrapping_rem_euclid(-I256::one()),
I256::zero()
);
assert_eq!(I256::from(100).wrapping_rem_euclid(I256::from(10)), I256::zero());
assert_eq!(I256::min_value().wrapping_rem_euclid(-I256::one()), I256::zero());
// Checked
assert_eq!(a.checked_rem_euclid(b), Some(I256::from(3)));
@ -1689,10 +1647,7 @@ mod tests {
#[test]
fn remainder() {
// The only case for overflow.
assert_eq!(
I256::MIN.overflowing_rem(I256::from(-1)),
(I256::zero(), true)
);
assert_eq!(I256::MIN.overflowing_rem(I256::from(-1)), (I256::zero(), true));
assert_eq!(I256::from(-5) % I256::from(-2), I256::from(-1));
assert_eq!(I256::from(5) % I256::from(-2), I256::one());
assert_eq!(I256::from(-5) % I256::from(2), I256::from(-1));
@ -1728,13 +1683,7 @@ mod tests {
assert_eq!(I256::from(42).into_token(), 42i32.into_token());
assert_eq!(I256::minus_one().into_token(), Token::Int(U256::MAX),);
assert_eq!(
I256::from_token(42i32.into_token()).unwrap(),
I256::from(42),
);
assert_eq!(
I256::from_token(U256::MAX.into_token()).unwrap(),
I256::minus_one(),
);
assert_eq!(I256::from_token(42i32.into_token()).unwrap(), I256::from(42),);
assert_eq!(I256::from_token(U256::MAX.into_token()).unwrap(), I256::minus_one(),);
}
}

View File

@ -65,20 +65,14 @@ pub struct Log {
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum FilterBlockOption {
Range {
from_block: Option<BlockNumber>,
to_block: Option<BlockNumber>,
},
Range { from_block: Option<BlockNumber>, to_block: Option<BlockNumber> },
AtBlockHash(H256),
}
impl From<BlockNumber> for FilterBlockOption {
fn from(block: BlockNumber) -> Self {
let block = Some(block);
FilterBlockOption::Range {
from_block: block,
to_block: block,
}
FilterBlockOption::Range { from_block: block, to_block: block }
}
}
@ -98,30 +92,21 @@ impl<T: Into<BlockNumber>> From<Range<T>> for FilterBlockOption {
fn from(r: Range<T>) -> Self {
let from_block = Some(r.start.into());
let to_block = Some(r.end.into());
FilterBlockOption::Range {
from_block,
to_block,
}
FilterBlockOption::Range { from_block, to_block }
}
}
impl<T: Into<BlockNumber>> From<RangeTo<T>> for FilterBlockOption {
fn from(r: RangeTo<T>) -> Self {
let to_block = Some(r.end.into());
FilterBlockOption::Range {
from_block: Some(BlockNumber::Earliest),
to_block,
}
FilterBlockOption::Range { from_block: Some(BlockNumber::Earliest), to_block }
}
}
impl<T: Into<BlockNumber>> From<RangeFrom<T>> for FilterBlockOption {
fn from(r: RangeFrom<T>) -> Self {
let from_block = Some(r.start.into());
FilterBlockOption::Range {
from_block,
to_block: Some(BlockNumber::Latest),
}
FilterBlockOption::Range { from_block, to_block: Some(BlockNumber::Latest) }
}
}
@ -133,38 +118,23 @@ impl From<H256> for FilterBlockOption {
impl Default for FilterBlockOption {
fn default() -> Self {
FilterBlockOption::Range {
from_block: None,
to_block: None,
}
FilterBlockOption::Range { from_block: None, to_block: None }
}
}
impl FilterBlockOption {
pub fn set_from_block(&self, block: BlockNumber) -> Self {
let to_block = if let FilterBlockOption::Range { to_block, .. } = self {
*to_block
} else {
None
};
let to_block =
if let FilterBlockOption::Range { to_block, .. } = self { *to_block } else { None };
FilterBlockOption::Range {
from_block: Some(block),
to_block,
}
FilterBlockOption::Range { from_block: Some(block), to_block }
}
pub fn set_to_block(&self, block: BlockNumber) -> Self {
let from_block = if let FilterBlockOption::Range { from_block, .. } = self {
*from_block
} else {
None
};
let from_block =
if let FilterBlockOption::Range { from_block, .. } = self { *from_block } else { None };
FilterBlockOption::Range {
from_block,
to_block: Some(block),
}
FilterBlockOption::Range { from_block, to_block: Some(block) }
}
pub fn set_hash(&self, hash: H256) -> Self {
@ -199,10 +169,7 @@ impl Serialize for Filter {
{
let mut s = serializer.serialize_struct("Filter", 5)?;
match self.block_option {
FilterBlockOption::Range {
from_block,
to_block,
} => {
FilterBlockOption::Range { from_block, to_block } => {
if let Some(ref from_block) = from_block {
s.serialize_field("fromBlock", from_block)?;
}
@ -426,9 +393,7 @@ mod tests {
#[test]
fn filter_serialization_test() {
let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7"
.parse::<Address>()
.unwrap();
let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::<Address>().unwrap();
let t2 = H256::from([0; 32]);
let t3 = U256::from(123);
@ -468,37 +433,22 @@ mod tests {
// 3
let ser = serialize(&filter.clone().topic3(t3));
assert_eq!(
ser,
json!({ "address" : addr, "topics": [t0, null, null, t3_padded]})
);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, null, t3_padded]}));
// 1 & 2
let ser = serialize(&filter.clone().topic1(t1).topic2(t2));
assert_eq!(
ser,
json!({ "address" : addr, "topics": [t0, t1_padded, t2]})
);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2]}));
// 1 & 3
let ser = serialize(&filter.clone().topic1(t1).topic3(t3));
assert_eq!(
ser,
json!({ "address" : addr, "topics": [t0, t1_padded, null, t3_padded]})
);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, null, t3_padded]}));
// 2 & 3
let ser = serialize(&filter.clone().topic2(t2).topic3(t3));
assert_eq!(
ser,
json!({ "address" : addr, "topics": [t0, null, t2, t3_padded]})
);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, null, t2, t3_padded]}));
// 1 & 2 & 3
let ser = serialize(&filter.topic1(t1).topic2(t2).topic3(t3));
assert_eq!(
ser,
json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]})
);
assert_eq!(ser, json!({ "address" : addr, "topics": [t0, t1_padded, t2, t3_padded]}));
}
}

View File

@ -11,11 +11,13 @@ use thiserror::Error;
use elliptic_curve::consts::U32;
use generic_array::GenericArray;
use k256::ecdsa::{
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
Error as K256SignatureError, Signature as K256Signature,
use k256::{
ecdsa::{
recoverable::{Id as RecoveryId, Signature as RecoverableSignature},
Error as K256SignatureError, Signature as K256Signature,
},
EncodedPoint as K256PublicKey,
};
use k256::EncodedPoint as K256PublicKey;
/// An error involving a signature.
#[derive(Debug, Error)]
@ -79,7 +81,7 @@ impl Signature {
let address = address.into();
let recovered = self.recover(message)?;
if recovered != address {
return Err(SignatureError::VerificationError(address, recovered));
return Err(SignatureError::VerificationError(address, recovered))
}
Ok(())
@ -163,7 +165,7 @@ impl<'a> TryFrom<&'a [u8]> for Signature {
/// and the final byte is the `v` value in 'Electrum' notation.
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
if bytes.len() != 65 {
return Err(SignatureError::InvalidLength(bytes.len()));
return Err(SignatureError::InvalidLength(bytes.len()))
}
let v = bytes[64];

View File

@ -40,28 +40,22 @@ pub struct Eip1559TransactionRequest {
#[serde(rename = "accessList", default)]
pub access_list: AccessList,
#[serde(
rename = "maxPriorityFeePerGas",
default,
skip_serializing_if = "Option::is_none"
)]
#[serde(rename = "maxPriorityFeePerGas", default, skip_serializing_if = "Option::is_none")]
/// Represents the maximum tx fee that will go to the miner as part of the user's
/// fee payment. It serves 3 purposes:
/// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a block;
/// 2. Allows users with high opportunity costs to pay a premium to miners;
/// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a
/// block; 2. Allows users with high opportunity costs to pay a premium to miners;
/// 3. In times where demand exceeds the available block space (i.e. 100% full, 30mm gas),
/// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the priority fee.
/// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the
/// priority fee.
///
/// More context [here](https://hackmd.io/@q8X_WM2nTfu6nuvAzqXiTQ/1559-wallets)
pub max_priority_fee_per_gas: Option<U256>,
#[serde(
rename = "maxFeePerGas",
default,
skip_serializing_if = "Option::is_none"
)]
/// Represents the maximum amount that a user is willing to pay for their tx (inclusive of baseFeePerGas and maxPriorityFeePerGas).
/// The difference between maxFeePerGas and baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user.
#[serde(rename = "maxFeePerGas", default, skip_serializing_if = "Option::is_none")]
/// Represents the maximum amount that a user is willing to pay for their tx (inclusive of
/// baseFeePerGas and maxPriorityFeePerGas). The difference between maxFeePerGas and
/// baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user.
pub max_fee_per_gas: Option<U256>,
}

View File

@ -19,7 +19,6 @@ use serde::{Deserialize, Serialize};
/// the `legacy` crate feature. This will disable the `type` flag in the
/// serialized transaction, and cause contract calls and other common actions
/// to default to using the legacy transaction type.
///
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
#[cfg_attr(not(feature = "legacy"), serde(tag = "type"))]
#[cfg_attr(feature = "legacy", serde(untagged))]
@ -252,9 +251,7 @@ mod tests {
#[test]
fn serde_legacy_tx() {
let tx = TransactionRequest::new()
.to(Address::zero())
.value(U256::from(100));
let tx = TransactionRequest::new().to(Address::zero()).value(U256::from(100));
let tx: TypedTransaction = tx.into();
let serialized = serde_json::to_string(&tx).unwrap();

View File

@ -104,9 +104,7 @@ mod tests {
.nonce(3)
.gas_price(1)
.gas(25000)
.to("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"
.parse::<Address>()
.unwrap())
.to("b94f5374fce5edbc8e2a8697c15331677e6ebf0b".parse::<Address>().unwrap())
.value(10)
.data(vec![0x55, 0x44])
.with_access_list(vec![])
@ -116,9 +114,7 @@ mod tests {
let sig: Signature = "c9519f4f2b30335884581971573fadf60c6204f59a911df35ee8a540456b266032f1e8e2c5dd761f9e4f88f41c8310aeaba26a8bfcdacfedfa12ec3862d3752101".parse().unwrap();
assert_eq!(
hash,
"49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3"
.parse()
.unwrap()
"49b486f0ec0a60dfbbca2d30cb07c9e8ffb2a2ff41f29a1ab6737475f6ff69f3".parse().unwrap()
);
let enc = rlp::encode(&tx.rlp_signed(1, &sig).as_ref());
@ -129,10 +125,8 @@ mod tests {
#[test]
#[cfg_attr(feature = "legacy", ignore)]
fn serde_eip2930_tx() {
let access_list = vec![AccessListItem {
address: Address::zero(),
storage_keys: vec![H256::zero()],
}];
let access_list =
vec![AccessListItem { address: Address::zero(), storage_keys: vec![H256::zero()] }];
let tx = TransactionRequest::new()
.to(Address::zero())
.value(U256::from(100))

View File

@ -1,10 +1,9 @@
use convert_case::{Case, Casing};
use core::convert::TryFrom;
use proc_macro2::TokenStream;
use syn::spanned::Spanned as _;
use syn::{
parse::Error, AttrStyle, Data, DeriveInput, Expr, Fields, GenericArgument, Lit, NestedMeta,
PathArguments, Type,
parse::Error, spanned::Spanned as _, AttrStyle, Data, DeriveInput, Expr, Fields,
GenericArgument, Lit, NestedMeta, PathArguments, Type,
};
use crate::{
@ -16,8 +15,8 @@ use crate::{
/// Pre-computed value of the following statement:
///
/// `ethers_core::utils::keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`
///
/// `ethers_core::utils::keccak256("EIP712Domain(string name,string version,uint256 chainId,address
/// verifyingContract)")`
pub const EIP712_DOMAIN_TYPE_HASH: [u8; 32] = [
139, 115, 195, 198, 155, 184, 254, 61, 81, 46, 204, 76, 247, 89, 204, 121, 35, 159, 123, 23,
155, 15, 250, 202, 169, 167, 93, 82, 43, 57, 64, 15,
@ -25,8 +24,8 @@ pub const EIP712_DOMAIN_TYPE_HASH: [u8; 32] = [
/// Pre-computed value of the following statement:
///
/// `ethers_core::utils::keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")`
///
/// `ethers_core::utils::keccak256("EIP712Domain(string name,string version,uint256 chainId,address
/// verifyingContract,bytes32 salt)")`
pub const EIP712_DOMAIN_TYPE_HASH_WITH_SALT: [u8; 32] = [
216, 124, 214, 239, 121, 212, 226, 185, 94, 21, 206, 138, 191, 115, 45, 181, 30, 199, 113, 241,
202, 46, 220, 207, 34, 164, 108, 114, 154, 197, 100, 114,
@ -108,16 +107,19 @@ pub struct EIP712Domain {
/// The user readable name of signing domain, i.e. the name of the DApp or the protocol.
pub name: String,
/// The current major version of the signing domain. Signatures from different versions are not compatible.
/// The current major version of the signing domain. Signatures from different versions are not
/// compatible.
pub version: String,
/// The EIP-155 chain id. The user-agent should refuse signing if it does not match the currently active chain.
/// The EIP-155 chain id. The user-agent should refuse signing if it does not match the
/// currently active chain.
pub chain_id: U256,
/// The address of the contract that will verify the signature.
pub verifying_contract: Address,
/// A disambiguating salt for the protocol. This can be used as a domain separator of last resort.
/// A disambiguating salt for the protocol. This can be used as a domain separator of last
/// resort.
pub salt: Option<[u8; 32]>,
}
@ -159,18 +161,13 @@ where
impl<T: Eip712 + Clone> EIP712WithDomain<T> {
pub fn new(inner: T) -> Result<Self, Eip712Error> {
let domain = inner
.domain()
.map_err(|e| Eip712Error::Inner(e.to_string()))?;
let domain = inner.domain().map_err(|e| Eip712Error::Inner(e.to_string()))?;
Ok(Self { domain, inner })
}
pub fn set_domain(self, domain: EIP712Domain) -> Self {
Self {
domain,
inner: self.inner,
}
Self { domain, inner: self.inner }
}
}
@ -187,11 +184,8 @@ impl<T: Eip712 + Clone> Eip712 for EIP712WithDomain<T> {
}
fn struct_hash(&self) -> Result<[u8; 32], Self::Error> {
let struct_hash = self
.inner
.clone()
.struct_hash()
.map_err(|e| Self::Error::Inner(e.to_string()))?;
let struct_hash =
self.inner.clone().struct_hash().map_err(|e| Self::Error::Inner(e.to_string()))?;
Ok(struct_hash)
}
}
@ -229,7 +223,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain name already specified",
)
.to_compile_error());
.to_compile_error())
}
domain.name = lit_str.value();
@ -239,7 +233,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain name must be a string",
)
.to_compile_error());
.to_compile_error())
}
},
"version" => match meta.lit {
@ -249,7 +243,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain version already specified",
)
.to_compile_error());
.to_compile_error())
}
domain.version = lit_str.value();
@ -259,7 +253,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain version must be a string",
)
.to_compile_error());
.to_compile_error())
}
},
"chain_id" => match meta.lit {
@ -269,7 +263,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain chain_id already specified",
)
.to_compile_error());
.to_compile_error())
}
domain.chain_id = lit_int
@ -325,10 +319,11 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain salt already specified",
)
.to_compile_error());
.to_compile_error())
}
// keccak256(<string>) to compute bytes32 encoded domain salt
// keccak256(<string>) to compute bytes32
// encoded domain salt
let salt = keccak256(lit_str.value());
domain.salt = Some(salt);
@ -338,7 +333,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"domain salt must be a string",
)
.to_compile_error());
.to_compile_error())
}
},
_ => {
@ -355,14 +350,14 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
path.span(),
"unrecognized eip712 parameter",
)
.to_compile_error());
.to_compile_error())
}
syn::Meta::List(meta) => {
return Err(Error::new(
meta.path.span(),
"unrecognized eip712 parameter",
)
.to_compile_error());
.to_compile_error())
}
}
}
@ -373,21 +368,21 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
meta.path.span(),
"missing required domain attribute: 'name'".to_string(),
)
.to_compile_error());
.to_compile_error())
}
if domain.version == String::default() {
return Err(Error::new(
meta.path.span(),
"missing required domain attribute: 'version'".to_string(),
)
.to_compile_error());
.to_compile_error())
}
if domain.chain_id == U256::default() {
return Err(Error::new(
meta.path.span(),
"missing required domain attribute: 'chain_id'".to_string(),
)
.to_compile_error());
.to_compile_error())
}
if domain.verifying_contract == H160::default() {
return Err(Error::new(
@ -395,7 +390,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
"missing required domain attribute: 'verifying_contract'"
.to_string(),
)
.to_compile_error());
.to_compile_error())
}
}
}
@ -407,7 +402,7 @@ impl TryFrom<&syn::DeriveInput> for EIP712Domain {
input,
"missing required derive attribute: '#[eip712( ... )]'".to_string(),
)
.to_compile_error());
.to_compile_error())
}
Ok(domain)
@ -425,21 +420,20 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, TokenStream> {
if let Lit::Int(ref len) = expr.lit {
if let Ok(size) = len.base10_parse::<usize>() {
if let ParamType::Uint(_) = param {
return Ok(ParamType::FixedBytes(size));
return Ok(ParamType::FixedBytes(size))
}
return Ok(ParamType::FixedArray(Box::new(param), size));
return Ok(ParamType::FixedArray(Box::new(param), size))
}
}
}
Err(
Error::new(ty.span(), "Failed to derive proper ABI from array field")
.to_compile_error(),
)
Err(Error::new(ty.span(), "Failed to derive proper ABI from array field")
.to_compile_error())
}
Type::Path(ty) => {
if let Some(ident) = ty.path.get_ident() {
return match ident.to_string().to_lowercase().as_str() {
let ident = ident.to_string().to_lowercase();
return match ident.as_str() {
"address" => Ok(ParamType::Address),
"string" => Ok(ParamType::String),
"bool" => Ok(ParamType::Bool),
@ -455,7 +449,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, TokenStream> {
)
.to_compile_error()
}),
};
}
}
// check for `Vec`
if ty.path.segments.len() == 1 && ty.path.segments[0].ident == "Vec" {
@ -467,11 +461,11 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, TokenStream> {
// Check if byte array is found
if let ParamType::Uint(size) = kind {
if size == 8 {
return Ok(ParamType::Bytes);
return Ok(ParamType::Bytes)
}
}
return Ok(ParamType::Array(Box::new(kind)));
return Ok(ParamType::Array(Box::new(kind)))
}
}
}
@ -480,11 +474,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, TokenStream> {
Err(Error::new(ty.span(), "Failed to derive proper ABI from fields").to_compile_error())
}
Type::Tuple(ty) => {
let params = ty
.elems
.iter()
.map(find_parameter_type)
.collect::<Result<Vec<_>, _>>()?;
let params = ty.elems.iter().map(find_parameter_type).collect::<Result<Vec<_>, _>>()?;
Ok(ParamType::Tuple(params))
}
_ => {
@ -494,12 +484,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, TokenStream> {
}
fn parse_int_param_type(s: &str) -> Option<ParamType> {
let size = s
.chars()
.skip(1)
.collect::<String>()
.parse::<usize>()
.ok()?;
let size = s.chars().skip(1).collect::<String>().parse::<usize>().ok()?;
if s.starts_with('u') {
Some(ParamType::Uint(size))
} else if s.starts_with('i') {
@ -527,37 +512,27 @@ pub fn parse_fields(ast: &DeriveInput) -> Result<Vec<(String, ParamType)>, Token
let named_fields = match &data.fields {
Fields::Named(name) => name,
_ => {
return Err(
Error::new(ast.span(), "unnamed fields are not supported").to_compile_error()
)
return Err(Error::new(ast.span(), "unnamed fields are not supported").to_compile_error())
}
};
for f in named_fields.named.iter() {
let field_name = f
.ident
.clone()
.map(|i| i.to_string().to_case(Case::Camel))
.ok_or_else(|| {
let field_name =
f.ident.clone().map(|i| i.to_string().to_case(Case::Camel)).ok_or_else(|| {
Error::new(named_fields.span(), "fields must be named").to_compile_error()
})?;
let field_type = match f
.attrs
.iter()
.find(|a| a.path.segments.iter().any(|s| s.ident == "eip712"))
{
// Found nested Eip712 Struct
// TODO: Implement custom
Some(a) => {
return Err(
Error::new(a.span(), "nested Eip712 struct are not yet supported")
.to_compile_error(),
)
}
// Not a nested eip712 struct, return the field param type;
None => find_parameter_type(&f.ty)?,
};
let field_type =
match f.attrs.iter().find(|a| a.path.segments.iter().any(|s| s.ident == "eip712")) {
// Found nested Eip712 Struct
// TODO: Implement custom
Some(a) => {
return Err(Error::new(a.span(), "nested Eip712 struct are not yet supported")
.to_compile_error())
}
// Not a nested eip712 struct, return the field param type;
None => find_parameter_type(&f.ty)?,
};
fields.push((field_name, field_type));
}
@ -567,11 +542,8 @@ pub fn parse_fields(ast: &DeriveInput) -> Result<Vec<(String, ParamType)>, Token
/// Convert hash map of field names and types into a type hash corresponding to enc types;
pub fn make_type_hash(primary_type: String, fields: &[(String, ParamType)]) -> [u8; 32] {
let parameters = fields
.iter()
.map(|(k, v)| format!("{} {}", v, k))
.collect::<Vec<String>>()
.join(",");
let parameters =
fields.iter().map(|(k, v)| format!("{} {}", v, k)).collect::<Vec<String>>().join(",");
let sig = format!("{}({})", primary_type, parameters);
@ -595,16 +567,10 @@ pub fn encode_eip712_type(token: Token) -> Token {
Token::Uint(t)
}
Token::Array(tokens) => Token::Uint(U256::from(keccak256(abi::encode(
&tokens
.into_iter()
.map(encode_eip712_type)
.collect::<Vec<Token>>(),
&tokens.into_iter().map(encode_eip712_type).collect::<Vec<Token>>(),
)))),
Token::FixedArray(tokens) => Token::Uint(U256::from(keccak256(abi::encode(
&tokens
.into_iter()
.map(encode_eip712_type)
.collect::<Vec<Token>>(),
&tokens.into_iter().map(encode_eip712_type).collect::<Vec<Token>>(),
)))),
_ => {
// Return the ABI encoded token;

View File

@ -69,11 +69,7 @@ impl TransactionRequest {
/// Convenience function for sending a new payment transaction to the receiver.
pub fn pay<T: Into<NameOrAddress>, V: Into<U256>>(to: T, value: V) -> Self {
TransactionRequest {
to: Some(to.into()),
value: Some(value.into()),
..Default::default()
}
TransactionRequest { to: Some(to.into()), value: Some(value.into()), ..Default::default() }
}
// Builder pattern helpers

View File

@ -70,10 +70,7 @@ pub struct Transaction {
/// Gateway fee recipient (None for no gateway fee paid)
#[cfg(feature = "celo")]
#[cfg_attr(docsrs, doc(cfg(feature = "celo")))]
#[serde(
skip_serializing_if = "Option::is_none",
rename = "gatewayFeeRecipient"
)]
#[serde(skip_serializing_if = "Option::is_none", rename = "gatewayFeeRecipient")]
pub gateway_fee_recipient: Option<Address>,
/// Gateway fee amount (None for no gateway fee paid)
@ -89,35 +86,25 @@ pub struct Transaction {
pub transaction_type: Option<U64>,
// EIP2930
#[serde(
rename = "accessList",
default,
skip_serializing_if = "Option::is_none"
)]
#[serde(rename = "accessList", default, skip_serializing_if = "Option::is_none")]
pub access_list: Option<AccessList>,
#[serde(
rename = "maxPriorityFeePerGas",
default,
skip_serializing_if = "Option::is_none"
)]
#[serde(rename = "maxPriorityFeePerGas", default, skip_serializing_if = "Option::is_none")]
/// Represents the maximum tx fee that will go to the miner as part of the user's
/// fee payment. It serves 3 purposes:
/// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a block;
/// 2. Allows users with high opportunity costs to pay a premium to miners;
/// 1. Compensates miners for the uncle/ommer risk + fixed costs of including transaction in a
/// block; 2. Allows users with high opportunity costs to pay a premium to miners;
/// 3. In times where demand exceeds the available block space (i.e. 100% full, 30mm gas),
/// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the priority fee.
/// this component allows first price auctions (i.e. the pre-1559 fee model) to happen on the
/// priority fee.
///
/// More context [here](https://hackmd.io/@q8X_WM2nTfu6nuvAzqXiTQ/1559-wallets)
pub max_priority_fee_per_gas: Option<U256>,
#[serde(
rename = "maxFeePerGas",
default,
skip_serializing_if = "Option::is_none"
)]
/// Represents the maximum amount that a user is willing to pay for their tx (inclusive of baseFeePerGas and maxPriorityFeePerGas).
/// The difference between maxFeePerGas and baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user.
#[serde(rename = "maxFeePerGas", default, skip_serializing_if = "Option::is_none")]
/// Represents the maximum amount that a user is willing to pay for their tx (inclusive of
/// baseFeePerGas and maxPriorityFeePerGas). The difference between maxFeePerGas and
/// baseFeePerGas + maxPriorityFeePerGas is “refunded” to the user.
pub max_fee_per_gas: Option<U256>,
#[serde(rename = "chainId", default, skip_serializing_if = "Option::is_none")]
@ -255,13 +242,9 @@ pub struct TransactionReceipt {
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub transaction_type: Option<U64>,
/// The price paid post-execution by the transaction (i.e. base fee + priority fee).
/// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the amount
/// that's actually paid by users can only be determined post-execution
#[serde(
rename = "effectiveGasPrice",
default,
skip_serializing_if = "Option::is_none"
)]
/// Both fields in 1559-style transactions are *maximums* (max fee + max priority fee), the
/// amount that's actually paid by users can only be determined post-execution
#[serde(rename = "effectiveGasPrice", default, skip_serializing_if = "Option::is_none")]
pub effective_gas_price: Option<U256>,
}
@ -328,9 +311,7 @@ mod tests {
let tx: Transaction = serde_json::from_value(serde_json::json!({"accessList":[{"address":"0x8ba1f109551bd432803012645ac136ddd64dba72","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000042"]}],"blockHash":"0x55ae43d3511e327dc532855510d110676d340aa1bbba369b4b98896d86559586","blockNumber":"0xa3d322","chainId":"0x3","from":"0x541d6a0e9ca9e7a083e41e2e178eef9f22d7492e","gas":"0x6a40","gasPrice":"0x3b9aca07","hash":"0x824384376c5972498c6fcafe71fd8cad1689f64e7d5e270d025a898638c0c34d","input":"0x","maxFeePerGas":"0x3b9aca0e","maxPriorityFeePerGas":"0x3b9aca00","nonce":"0x2","r":"0xf13b5088108f783f4b6048d4be456971118aabfb88be96bb541d734b6c2b20dc","s":"0x13fb7eb25a7d5df42a176cd4c6a086e19163ed7cd8ffba015f939d24f66bc17a","to":"0x8210357f377e901f18e45294e86a2a32215cc3c9","transactionIndex":"0xd","type":"0x2","v":"0x1","value":"0x7b"})).unwrap();
assert_eq!(tx.transaction_type.unwrap().as_u64(), 2);
let lst = AccessList(vec![AccessListItem {
address: "0x8ba1f109551bd432803012645ac136ddd64dba72"
.parse()
.unwrap(),
address: "0x8ba1f109551bd432803012645ac136ddd64dba72".parse().unwrap(),
storage_keys: vec![
"0x0000000000000000000000000000000000000000000000000000000000000000"
.parse()

View File

@ -44,27 +44,19 @@ impl<'de> Visitor<'de> for TxpoolInspectSummaryVisitor {
{
let addr_split: Vec<&str> = value.split(": ").collect();
if addr_split.len() != 2 {
return Err(de::Error::custom(
"invalid format for TxpoolInspectSummary: to",
));
return Err(de::Error::custom("invalid format for TxpoolInspectSummary: to"))
}
let value_split: Vec<&str> = addr_split[1].split(" wei + ").collect();
if value_split.len() != 2 {
return Err(de::Error::custom(
"invalid format for TxpoolInspectSummary: gasLimit",
));
return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gasLimit"))
}
let gas_split: Vec<&str> = value_split[1].split(" gas × ").collect();
if gas_split.len() != 2 {
return Err(de::Error::custom(
"invalid format for TxpoolInspectSummary: gas",
));
return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gas"))
}
let gas_price_split: Vec<&str> = gas_split[1].split(" wei").collect();
if gas_price_split.len() != 2 {
return Err(de::Error::custom(
"invalid format for TxpoolInspectSummary: gas_price",
));
return Err(de::Error::custom("invalid format for TxpoolInspectSummary: gas_price"))
}
let addr = match addr_split[0] {
"" => None,
@ -77,12 +69,7 @@ impl<'de> Visitor<'de> for TxpoolInspectSummaryVisitor {
let gas = U256::from(u64::from_str(gas_split[0]).map_err(de::Error::custom)?);
let gas_price = U256::from(u64::from_str(gas_price_split[0]).map_err(de::Error::custom)?);
Ok(TxpoolInspectSummary {
to: addr,
value,
gas,
gas_price,
})
Ok(TxpoolInspectSummary { to: addr, value, gas, gas_price })
}
}
@ -103,7 +90,6 @@ impl<'de> Deserialize<'de> for TxpoolInspectSummary {
/// as the ones that are being scheduled for future execution only.
///
/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) for more details
///
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct TxpoolContent {
/// pending tx
@ -121,7 +107,6 @@ pub struct TxpoolContent {
/// transactions in the pool and find any potential issues.
///
/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) for more details
///
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
pub struct TxpoolInspect {
/// pending tx
@ -137,7 +122,6 @@ pub struct TxpoolInspect {
/// are being scheduled for future execution only.
///
/// See [here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status) for more details
///
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct TxpoolStatus {
/// number of pending tx
@ -260,10 +244,7 @@ mod tests {
}"#;
let deserialized: TxpoolContent = serde_json::from_str(txpool_content_json).unwrap();
let serialized: String = serde_json::to_string(&deserialized).unwrap();
assert_eq!(
deserialized,
serde_json::from_str::<TxpoolContent>(&serialized).unwrap()
);
assert_eq!(deserialized, serde_json::from_str::<TxpoolContent>(&serialized).unwrap());
}
#[test]
@ -403,9 +384,6 @@ mod tests {
queued_map_inner,
);
TxpoolInspect {
pending: pending_map,
queued: queued_map,
}
TxpoolInspect { pending: pending_map, queued: queued_map }
}
}

View File

@ -143,11 +143,7 @@ impl Ganache {
pub fn spawn(self) -> GanacheInstance {
let mut cmd = Command::new("ganache-cli");
cmd.stdout(std::process::Stdio::piped());
let port = if let Some(port) = self.port {
port
} else {
unused_port()
};
let port = if let Some(port) = self.port { port } else { unused_port() };
cmd.arg("-p").arg(port.to_string());
if let Some(mnemonic) = self.mnemonic {
@ -166,9 +162,7 @@ impl Ganache {
let mut child = cmd.spawn().expect("couldnt start ganache-cli");
let stdout = child
.stdout
.expect("Unable to get stdout for ganache child process");
let stdout = child.stdout.expect("Unable to get stdout for ganache child process");
let start = Instant::now();
let mut reader = BufReader::new(stdout);
@ -182,11 +176,9 @@ impl Ganache {
}
let mut line = String::new();
reader
.read_line(&mut line)
.expect("Failed to read line from ganache process");
reader.read_line(&mut line).expect("Failed to read line from ganache process");
if line.starts_with("Listening on") {
break;
break
}
if line.starts_with("Private Keys") {
@ -204,11 +196,6 @@ impl Ganache {
child.stdout = Some(reader.into_inner());
GanacheInstance {
pid: child,
private_keys,
addresses,
port,
}
GanacheInstance { pid: child, private_keys, addresses, port }
}
}

View File

@ -110,11 +110,7 @@ impl Geth {
let mut cmd = Command::new(GETH);
// geth uses stderr for its logs
cmd.stderr(std::process::Stdio::piped());
let port = if let Some(port) = self.port {
port
} else {
unused_port()
};
let port = if let Some(port) = self.port { port } else { unused_port() };
// Open the HTTP API
cmd.arg("--http");
@ -138,9 +134,7 @@ impl Geth {
let mut child = cmd.spawn().expect("couldnt start geth");
let stdout = child
.stderr
.expect("Unable to get stderr for geth child process");
let stdout = child.stderr.expect("Unable to get stderr for geth child process");
let start = Instant::now();
let mut reader = BufReader::new(stdout);
@ -151,22 +145,16 @@ impl Geth {
}
let mut line = String::new();
reader
.read_line(&mut line)
.expect("Failed to read line from geth process");
reader.read_line(&mut line).expect("Failed to read line from geth process");
// geth 1.9.23 uses "server started" while 1.9.18 uses "endpoint opened"
if line.contains("HTTP endpoint opened") || line.contains("HTTP server started") {
break;
break
}
}
child.stderr = Some(reader.into_inner());
GethInstance {
pid: child,
port,
ipc: self.ipc_path,
}
GethInstance { pid: child, port, ipc: self.ipc_path }
}
}

View File

@ -77,9 +77,7 @@ mod tests {
assert_eq!(
hash,
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2"
.parse()
.unwrap()
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2".parse().unwrap()
);
}

View File

@ -206,13 +206,9 @@ pub fn get_create2_address_from_hash(
salt: impl Into<Bytes>,
init_code_hash: impl Into<Bytes>,
) -> Address {
let bytes = [
&[0xff],
from.into().as_bytes(),
salt.into().as_ref(),
init_code_hash.into().as_ref(),
]
.concat();
let bytes =
[&[0xff], from.into().as_bytes(), salt.into().as_ref(), init_code_hash.into().as_ref()]
.concat();
let hash = keccak256(&bytes);
@ -244,17 +240,14 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
let addr_hex = hex::encode(addr.as_bytes());
let addr_hex = addr_hex.as_bytes();
addr_hex
.iter()
.zip(hash)
.fold("0x".to_owned(), |mut encoded, (addr, hash)| {
encoded.push(if *hash >= 56 {
addr.to_ascii_uppercase() as char
} else {
addr.to_ascii_lowercase() as char
});
encoded
})
addr_hex.iter().zip(hash).fold("0x".to_owned(), |mut encoded, (addr, hash)| {
encoded.push(if *hash >= 56 {
addr.to_ascii_uppercase() as char
} else {
addr.to_ascii_lowercase() as char
});
encoded
})
}
/// Returns a bytes32 string representation of text. If the length of text exceeds 32 bytes,
@ -262,7 +255,7 @@ pub fn to_checksum(addr: &Address, chain_id: Option<u8>) -> String {
pub fn format_bytes32_string(text: &str) -> Result<[u8; 32], FormatBytes32StringError> {
let str_bytes: &[u8] = text.as_bytes();
if str_bytes.len() > 32 {
return Err(FormatBytes32StringError::TextTooLong);
return Err(FormatBytes32StringError::TextTooLong)
}
let mut bytes32: [u8; 32] = [0u8; 32];
@ -302,16 +295,13 @@ pub fn eip1559_default_estimator(base_fee_per_gas: U256, rewards: Vec<Vec<U256>>
}
fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {
let mut rewards: Vec<U256> = rewards
.iter()
.map(|r| r[0])
.filter(|r| *r > U256::zero())
.collect();
let mut rewards: Vec<U256> =
rewards.iter().map(|r| r[0]).filter(|r| *r > U256::zero()).collect();
if rewards.is_empty() {
return U256::zero();
return U256::zero()
}
if rewards.len() == 1 {
return rewards[0];
return rewards[0]
}
// Sort the rewards as we will eventually take the median.
rewards.sort();
@ -336,15 +326,12 @@ fn estimate_priority_fee(rewards: Vec<Vec<U256>>) -> U256 {
// Fetch the max of the percentage change, and that element's index.
let max_change = percentage_change.iter().max().unwrap();
let max_change_index = percentage_change
.iter()
.position(|&c| c == *max_change)
.unwrap();
let max_change_index = percentage_change.iter().position(|&c| c == *max_change).unwrap();
// If we encountered a big change in fees at a certain position, then consider only
// the values >= it.
let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE
&& (max_change_index >= (rewards.len() / 2))
let values = if *max_change >= EIP1559_FEE_ESTIMATION_THRESHOLD_MAX_CHANGE &&
(max_change_index >= (rewards.len() / 2))
{
rewards[max_change_index..].to_vec()
} else {
@ -376,9 +363,8 @@ pub(crate) fn unused_port() -> u16 {
let listener = std::net::TcpListener::bind("127.0.0.1:0")
.expect("Failed to create TCP listener to find unused port");
let local_addr = listener
.local_addr()
.expect("Failed to read TCP listener local_addr to find unused port");
let local_addr =
listener.local_addr().expect("Failed to read TCP listener local_addr to find unused port");
local_addr.port()
}
@ -407,16 +393,10 @@ mod tests {
assert_eq!(gwei.as_u64(), 15e8 as u64);
let eth_dec_float = parse_units(1.39563324, "ether").unwrap();
assert_eq!(
eth_dec_float,
U256::from_dec_str("1395633240000000000").unwrap()
);
assert_eq!(eth_dec_float, U256::from_dec_str("1395633240000000000").unwrap());
let eth_dec_string = parse_units("1.39563324", "ether").unwrap();
assert_eq!(
eth_dec_string,
U256::from_dec_str("1395633240000000000").unwrap()
);
assert_eq!(eth_dec_string, U256::from_dec_str("1395633240000000000").unwrap());
let eth = parse_units(1, "ether").unwrap();
assert_eq!(eth, WEI_IN_ETHER);
@ -513,9 +493,7 @@ mod tests {
#[test]
fn contract_address() {
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
let from = "6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"
.parse::<Address>()
.unwrap();
let from = "6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0".parse::<Address>().unwrap();
for (nonce, expected) in [
"cd234a471b72ba2f1ccf0a70fcaba648a5eecd8d",
"343c43a37d37dff08ae8c4a11544c718abb4fcf8",
@ -593,14 +571,8 @@ mod tests {
#[test]
fn bytes32_string_parsing() {
let text_bytes_list = vec![
(
"",
hex!("0000000000000000000000000000000000000000000000000000000000000000"),
),
(
"A",
hex!("4100000000000000000000000000000000000000000000000000000000000000"),
),
("", hex!("0000000000000000000000000000000000000000000000000000000000000000")),
("A", hex!("4100000000000000000000000000000000000000000000000000000000000000")),
(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
@ -619,14 +591,8 @@ mod tests {
#[test]
fn bytes32_string_formatting() {
let text_bytes_list = vec![
(
"",
hex!("0000000000000000000000000000000000000000000000000000000000000000"),
),
(
"A",
hex!("4100000000000000000000000000000000000000000000000000000000000000"),
),
("", hex!("0000000000000000000000000000000000000000000000000000000000000000")),
("A", hex!("4100000000000000000000000000000000000000000000000000000000000000")),
(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345",
hex!("4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435"),
@ -657,10 +623,7 @@ mod tests {
let base_fee_per_gas = U256::from(EIP1559_FEE_ESTIMATION_PRIORITY_FEE_TRIGGER) - 1;
let rewards: Vec<Vec<U256>> = vec![vec![]];
let (base_fee, priority_fee) = eip1559_default_estimator(base_fee_per_gas, rewards);
assert_eq!(
priority_fee,
U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE)
);
assert_eq!(priority_fee, U256::from(EIP1559_FEE_ESTIMATION_DEFAULT_PRIORITY_FEE));
assert_eq!(base_fee, base_fee_surged(base_fee_per_gas));
// If the base fee is above the triggering base fee, we calculate the priority fee using

View File

@ -1,7 +1,9 @@
//! Setup utilities to start necessary infrastructure
use crate::utils::solc::{CompiledContract, SolcError};
use crate::utils::{Ganache, GanacheInstance, Geth, GethInstance, Solc};
use crate::utils::{
solc::{CompiledContract, SolcError},
Ganache, GanacheInstance, Geth, GethInstance, Solc,
};
use std::collections::HashMap;
/// Builds the contracts and returns a hashmap for each named contract
@ -15,9 +17,7 @@ pub async fn compile(solc: Solc) -> Result<HashMap<String, CompiledContract>, So
///
/// Same as [crate::utils::Ganache::spawn] but async
pub async fn launch_ganache(ganache: Ganache) -> GanacheInstance {
tokio::task::spawn_blocking(|| ganache.spawn())
.await
.unwrap()
tokio::task::spawn_blocking(|| ganache.spawn()).await.unwrap()
}
/// Compiles the contracts and launches a [crate::utils::GanacheInstance]

View File

@ -145,10 +145,7 @@ impl Solc {
}
if let Some(runs) = self.optimizer {
command
.arg("--optimize")
.arg("--optimize-runs")
.arg(runs.to_string());
command.arg("--optimize").arg("--optimize-runs").arg(runs.to_string());
}
command.args(self.args);
@ -160,9 +157,7 @@ impl Solc {
let command = command.output().expect("could not run `solc`");
if !command.status.success() {
return Err(SolcError::SolcError(
String::from_utf8_lossy(&command.stderr).to_string(),
));
return Err(SolcError::SolcError(String::from_utf8_lossy(&command.stderr).to_string()))
}
// Deserialize the output
@ -180,11 +175,7 @@ impl Solc {
for (name, contract) in contract_values {
if let serde_json::Value::String(bin) = contract["bin"].take() {
let name = name
.rsplit(':')
.next()
.expect("could not strip fname")
.to_owned();
let name = name.rsplit(':').next().expect("could not strip fname").to_owned();
// abi could be an escaped string (solc<=0.7) or an array (solc>=0.8)
let abi = match contract["abi"].take() {
@ -204,18 +195,9 @@ impl Solc {
} else {
panic!("no runtime bytecode found")
};
contracts.insert(
name,
CompiledContractStr {
abi,
bin,
runtime_bin,
},
);
contracts.insert(name, CompiledContractStr { abi, bin, runtime_bin });
} else {
return Err(SolcError::SolcError(
"could not find `bin` in solc output".to_string(),
));
return Err(SolcError::SolcError("could not find `bin` in solc output".to_string()))
}
}
@ -234,22 +216,14 @@ impl Solc {
.expect("could not parse `solc` abi, this should never happen");
// parse the bytecode
let bytecode = hex::decode(contract.bin)
.expect("solc did not produce valid bytecode")
.into();
let bytecode =
hex::decode(contract.bin).expect("solc did not produce valid bytecode").into();
// parse the runtime bytecode
let runtime_bytecode = hex::decode(contract.runtime_bin)
.expect("solc did not produce valid runtime-bytecode")
.into();
(
name,
CompiledContract {
abi,
bytecode,
runtime_bytecode,
},
)
(name, CompiledContract { abi, bytecode, runtime_bytecode })
})
.collect::<HashMap<String, CompiledContract>>();
@ -293,7 +267,8 @@ impl Solc {
}
/// Sets the `combined-json` option, by default this is set to `abi,bin,bin-runtime`
/// NOTE: In order to get the `CompiledContract` from `Self::build`, this _must_ contain `abi,bin`.
/// NOTE: In order to get the `CompiledContract` from `Self::build`, this _must_ contain
/// `abi,bin`.
pub fn combined_json(mut self, combined_json: impl Into<String>) -> Self {
self.combined_json = Some(combined_json.into());
self
@ -541,9 +516,7 @@ where
{
let value = String::deserialize(d)?;
Ok(hex::decode(&value)
.map_err(|e| serde::de::Error::custom(e.to_string()))?
.into())
Ok(hex::decode(&value).map_err(|e| serde::de::Error::custom(e.to_string()))?.into())
}
fn de_from_json_opt<'de, D, T>(deserializer: D) -> std::result::Result<Option<T>, D::Error>
@ -583,23 +556,11 @@ mod tests {
("0.4.20", EvmVersion::Homestead, None),
// Constantinople clipping
("0.4.21", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
(
"0.4.21",
EvmVersion::Constantinople,
Some(EvmVersion::Constantinople),
),
(
"0.4.21",
EvmVersion::London,
Some(EvmVersion::Constantinople),
),
("0.4.21", EvmVersion::Constantinople, Some(EvmVersion::Constantinople)),
("0.4.21", EvmVersion::London, Some(EvmVersion::Constantinople)),
// Petersburg
("0.5.5", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
(
"0.5.5",
EvmVersion::Petersburg,
Some(EvmVersion::Petersburg),
),
("0.5.5", EvmVersion::Petersburg, Some(EvmVersion::Petersburg)),
("0.5.5", EvmVersion::London, Some(EvmVersion::Petersburg)),
// Istanbul
("0.5.14", EvmVersion::Homestead, Some(EvmVersion::Homestead)),

View File

@ -22,10 +22,7 @@ pub struct VerifyContract {
#[serde(skip_serializing_if = "Option::is_none")]
pub runs: Option<String>,
/// NOTE: there is a typo in the etherscan API `constructorArguements`
#[serde(
rename = "constructorArguements",
skip_serializing_if = "Option::is_none"
)]
#[serde(rename = "constructorArguements", skip_serializing_if = "Option::is_none")]
pub constructor_arguments: Option<String>,
#[serde(rename = "evmversion")]
pub evm_version: Option<String>,
@ -151,11 +148,7 @@ impl ContractMetadata {
/// Combined source code of all contracts
pub fn source_code(&self) -> String {
self.items
.iter()
.map(|c| c.source_code.as_str())
.collect::<Vec<_>>()
.join("\n")
self.items.iter().map(|c| c.source_code.as_str()).collect::<Vec<_>>().join("\n")
}
}
@ -249,15 +242,10 @@ impl Client {
/// # }
/// ```
pub async fn contract_source_code(&self, address: Address) -> Result<ContractMetadata> {
let query = self.create_query(
"contract",
"getsourcecode",
HashMap::from([("address", address)]),
);
let query =
self.create_query("contract", "getsourcecode", HashMap::from([("address", address)]));
let response: Response<Vec<Metadata>> = self.get_json(&query).await?;
Ok(ContractMetadata {
items: response.result,
})
Ok(ContractMetadata { items: response.result })
}
}
@ -272,11 +260,7 @@ mod tests {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let _abi = client
.contract_abi(
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"
.parse()
.unwrap(),
)
.contract_abi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".parse().unwrap())
.await
.unwrap();
}
@ -287,11 +271,7 @@ mod tests {
let client = Client::new_from_env(Chain::Mainnet).unwrap();
let _meta = client
.contract_source_code(
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"
.parse()
.unwrap(),
)
.contract_source_code("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413".parse().unwrap())
.await
.unwrap();
}
@ -303,9 +283,7 @@ mod tests {
// https://etherscan.io/address/0x9e744c9115b74834c0f33f4097f40c02a9ac5c33#code
let contract = include_str!("../resources/UniswapExchange.sol");
let address = "0x9e744c9115b74834c0f33f4097f40c02a9ac5c33"
.parse()
.unwrap();
let address = "0x9e744c9115b74834c0f33f4097f40c02a9ac5c33".parse().unwrap();
let compiler_version = "v0.5.17+commit.d19bba13";
let constructor_args = "0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000000000007596179537761700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035941590000000000000000000000000000000000000000000000000000000000";

View File

@ -29,10 +29,9 @@ impl Client {
/// Create a new client with the correct endpoints based on the chain and provided API key
pub fn new(chain: Chain, api_key: impl Into<String>) -> Result<Self> {
let (etherscan_api_url, etherscan_url) = match chain {
Chain::Mainnet => (
Url::parse("https://api.etherscan.io/api"),
Url::parse("https://etherscan.io"),
),
Chain::Mainnet => {
(Url::parse("https://api.etherscan.io/api"), Url::parse("https://etherscan.io"))
}
Chain::Ropsten | Chain::Kovan | Chain::Rinkeby | Chain::Goerli => {
let chain_name = chain.to_string().to_lowercase();

View File

@ -32,9 +32,7 @@ impl Client {
if response.result.is_error == "0" {
Ok(())
} else {
Err(EtherscanError::ExecutionFailed(
response.result.err_description,
))
Err(EtherscanError::ExecutionFailed(response.result.err_description))
}
}
@ -85,10 +83,7 @@ mod tests {
.unwrap_err();
assert!(matches!(err, EtherscanError::ExecutionFailed(_)));
assert_eq!(
err.to_string(),
"contract execution call failed: Bad jump destination"
);
assert_eq!(err.to_string(), "contract execution call failed: Bad jump destination");
}
#[tokio::test]

View File

@ -4,8 +4,8 @@ use ethers_core::types::U256;
/// Linearly increasing gas price.
///
///
/// Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs` seconds
/// until the transaction gets confirmed. There is an optional upper limit.
/// Start with `initial_price`, then increase it by fixed amount `increase_by` every `every_secs`
/// seconds until the transaction gets confirmed. There is an optional upper limit.
///
/// https://github.com/makerdao/pymaker/blob/master/pymaker/gas.py#L129
#[derive(Clone, Debug)]

View File

@ -10,8 +10,7 @@ use ethers_core::types::{BlockId, TransactionRequest, TxHash, U256};
use ethers_providers::{interval, FromErr, Middleware, PendingTransaction, StreamExt};
use futures_util::lock::Mutex;
use instant::Instant;
use std::pin::Pin;
use std::sync::Arc;
use std::{pin::Pin, sync::Arc};
use thiserror::Error;
#[cfg(not(target_arch = "wasm32"))]
@ -141,11 +140,7 @@ where
{
let this2 = this.clone();
spawn(async move {
this2
.escalate()
.instrument(tracing::trace_span!("gas-escalation"))
.await
.unwrap();
this2.escalate().instrument(tracing::trace_span!("gas-escalation")).await.unwrap();
});
}
@ -192,10 +187,7 @@ where
replacement_tx.gas_price = Some(new_gas_price);
// the tx hash will be different so we need to update it
match self
.inner()
.send_transaction(replacement_tx.clone(), priority)
.await
match self.inner().send_transaction(replacement_tx.clone(), priority).await
{
Ok(new_tx_hash) => {
let new_tx_hash = *new_tx_hash;
@ -215,9 +207,9 @@ where
// gas price tx when one of the previous ones
// was already mined (meaning we also do not
// push it back to the pending txs vector)
continue;
continue
} else {
return Err(GasEscalatorError::MiddlewareError(err));
return Err(GasEscalatorError::MiddlewareError(err))
}
}
}

View File

@ -69,11 +69,7 @@ impl EthGasStation {
let url = Url::parse(&url).expect("invalid url");
EthGasStation {
client: Client::new(),
url,
gas_category: GasCategory::Standard,
}
EthGasStation { client: Client::new(), url, gas_category: GasCategory::Standard }
}
/// Sets the gas price category to be used when fetching the gas price.
@ -83,13 +79,7 @@ impl EthGasStation {
}
pub async fn query(&self) -> Result<EthGasStationResponse, GasOracleError> {
Ok(self
.client
.get(self.url.as_ref())
.send()
.await?
.json::<EthGasStationResponse>()
.await?)
Ok(self.client.get(self.url.as_ref()).send().await?.json::<EthGasStationResponse>().await?)
}
}

View File

@ -40,11 +40,7 @@ impl Etherchain {
pub fn new() -> Self {
let url = Url::parse(ETHERCHAIN_URL).expect("invalid url");
Etherchain {
client: Client::new(),
url,
gas_category: GasCategory::Standard,
}
Etherchain { client: Client::new(), url, gas_category: GasCategory::Standard }
}
/// Sets the gas price category to be used when fetching the gas price.
@ -54,13 +50,7 @@ impl Etherchain {
}
pub async fn query(&self) -> Result<EtherchainResponse, GasOracleError> {
Ok(self
.client
.get(self.url.as_ref())
.send()
.await?
.json::<EtherchainResponse>()
.await?)
Ok(self.client.get(self.url.as_ref()).send().await?.json::<EtherchainResponse>().await?)
}
}

View File

@ -67,11 +67,7 @@ impl Etherscan {
let url = Url::parse(&url).expect("invalid url");
Etherscan {
client: Client::new(),
url,
gas_category: GasCategory::Standard,
}
Etherscan { client: Client::new(), url, gas_category: GasCategory::Standard }
}
/// Sets the gas price category to be used when fetching the gas price.
@ -97,7 +93,7 @@ impl Etherscan {
impl GasOracle for Etherscan {
async fn fetch(&self) -> Result<U256, GasOracleError> {
if matches!(self.gas_category, GasCategory::Fastest) {
return Err(GasOracleError::GasCategoryNotSupported);
return Err(GasOracleError::GasCategoryNotSupported)
}
let res = self.query().await?;

View File

@ -0,0 +1,84 @@
use ethers_core::types::U256;
use async_trait::async_trait;
use reqwest::Client;
use serde::Deserialize;
use url::Url;
use crate::gas_oracle::{GasCategory, GasOracle, GasOracleError};
const GAS_NOW_URL: &str = "https://www.gasnow.org/api/v3/gas/price";
/// A client over HTTP for the [GasNow](https://www.gasnow.org/api/v1/gas/price) gas tracker API
/// that implements the `GasOracle` trait
#[derive(Clone, Debug)]
pub struct GasNow {
client: Client,
url: Url,
gas_category: GasCategory,
}
impl Default for GasNow {
fn default() -> Self {
Self::new()
}
}
#[derive(Deserialize)]
struct GasNowResponseWrapper {
data: GasNowResponse,
}
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd)]
pub struct GasNowResponse {
pub rapid: u64,
pub fast: u64,
pub standard: u64,
pub slow: u64,
}
impl GasNow {
/// Creates a new [GasNow](https://gasnow.org) gas price oracle.
pub fn new() -> Self {
let url = Url::parse(GAS_NOW_URL).expect("invalid url");
Self { client: Client::new(), url, gas_category: GasCategory::Standard }
}
/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category;
self
}
pub async fn query(&self) -> Result<GasNowResponse, GasOracleError> {
let res = self
.client
.get(self.url.as_ref())
.send()
.await?
.json::<GasNowResponseWrapper>()
.await?;
Ok(res.data)
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GasOracle for GasNow {
async fn fetch(&self) -> Result<U256, GasOracleError> {
let res = self.query().await?;
let gas_price = match self.gas_category {
GasCategory::SafeLow => U256::from(res.slow),
GasCategory::Standard => U256::from(res.standard),
GasCategory::Fast => U256::from(res.fast),
GasCategory::Fastest => U256::from(res.rapid),
};
Ok(gas_price)
}
async fn estimate_eip1559_fees(&self) -> Result<(U256, U256), GasOracleError> {
Err(GasOracleError::Eip1559EstimationNotSupported)
}
}

View File

@ -98,9 +98,6 @@ where
}
}
};
self.inner
.send_transaction(tx, block)
.await
.map_err(MiddlewareError::MiddlewareError)
self.inner.send_transaction(tx, block).await.map_err(MiddlewareError::MiddlewareError)
}
}

View File

@ -63,8 +63,8 @@ pub mod gas_escalator;
/// [`GasOracle`](crate::gas_oracle::GasOracle) trait.
pub mod gas_oracle;
/// The [Nonce Manager](crate::NonceManagerMiddleware) is used to locally calculate nonces instead of
/// using eth_getTransactionCount
/// The [Nonce Manager](crate::NonceManagerMiddleware) is used to locally calculate nonces instead
/// of using eth_getTransactionCount
pub mod nonce_manager;
pub use nonce_manager::NonceManagerMiddleware;

View File

@ -1,6 +1,5 @@
use async_trait::async_trait;
use ethers_core::types::transaction::eip2718::TypedTransaction;
use ethers_core::types::*;
use ethers_core::types::{transaction::eip2718::TypedTransaction, *};
use ethers_providers::{FromErr, Middleware, PendingTransaction};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use thiserror::Error;
@ -22,12 +21,7 @@ where
/// Instantiates the nonce manager with a 0 nonce. The `address` should be the
/// address which you'll be sending transactions from
pub fn new(inner: M, address: Address) -> Self {
Self {
initialized: false.into(),
nonce: 0.into(),
inner,
address,
}
Self { initialized: false.into(), nonce: 0.into(), inner, address }
}
/// Returns the next nonce to be used
@ -106,10 +100,7 @@ where
// was a nonce mismatch
self.nonce.store(nonce.as_u64(), Ordering::SeqCst);
tx.set_nonce(nonce);
self.inner
.send_transaction(tx, block)
.await
.map_err(FromErr::from)
self.inner.send_transaction(tx, block).await.map_err(FromErr::from)
} else {
// propagate the error otherwise
Err(FromErr::from(err))

View File

@ -107,9 +107,6 @@ where
.ensure_can_send(tx.into())
.await
.map_err(PolicyMiddlewareError::PolicyError)?;
self.inner
.send_transaction(tx, block)
.await
.map_err(PolicyMiddlewareError::MiddlewareError)
self.inner.send_transaction(tx, block).await.map_err(PolicyMiddlewareError::MiddlewareError)
}
}

View File

@ -56,7 +56,6 @@ use thiserror::Error;
///
/// # Ok(())
/// # }
///
/// ```
///
/// [`Provider`]: ethers_providers::Provider
@ -106,11 +105,7 @@ where
/// Creates a new client from the provider and signer.
pub fn new(inner: M, signer: S) -> Self {
let address = signer.address();
SignerMiddleware {
inner,
signer,
address,
}
SignerMiddleware { inner, signer, address }
}
/// Signs and returns the RLP encoding of the signed transaction
@ -118,11 +113,8 @@ where
&self,
tx: TypedTransaction,
) -> Result<Bytes, SignerMiddlewareError<M, S>> {
let signature = self
.signer
.sign_transaction(&tx)
.await
.map_err(SignerMiddlewareError::SignerError)?;
let signature =
self.signer.sign_transaction(&tx).await.map_err(SignerMiddlewareError::SignerError)?;
// Return the raw rlp-encoded signed transaction
Ok(tx.rlp_signed(self.signer.chain_id(), &signature))
@ -217,7 +209,7 @@ where
.inner
.send_transaction(tx, block)
.await
.map_err(SignerMiddlewareError::MiddlewareError);
.map_err(SignerMiddlewareError::MiddlewareError)
}
// if we have a nonce manager set, we should try handling the result in
@ -238,10 +230,7 @@ where
data: T,
_: &Address,
) -> Result<Signature, Self::Error> {
self.signer
.sign_message(data.into())
.await
.map_err(SignerMiddlewareError::SignerError)
self.signer.sign_message(data.into()).await.map_err(SignerMiddlewareError::SignerError)
}
}
@ -262,12 +251,7 @@ mod tests {
// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-accounts.html#eth-accounts-signtransaction
let tx = TransactionRequest {
from: None,
to: Some(
"F0109fC8DF283027b6285cc889F5aA624EaC1F55"
.parse::<Address>()
.unwrap()
.into(),
),
to: Some("F0109fC8DF283027b6285cc889F5aA624EaC1F55".parse::<Address>().unwrap().into()),
value: Some(1_000_000_000.into()),
gas: Some(2_000_000.into()),
nonce: Some(0.into()),
@ -316,30 +300,21 @@ mod tests {
// signing a TransactionRequest with a from field of None should yield
// a signed transaction from the signer address
let request_from_none = request.clone();
let hash = *client
.send_transaction(request_from_none, None)
.await
.unwrap();
let hash = *client.send_transaction(request_from_none, None).await.unwrap();
let tx = client.get_transaction(hash).await.unwrap().unwrap();
assert_eq!(tx.from, client.address());
// signing a TransactionRequest with the signer as the from address
// should yield a signed transaction from the signer
let request_from_signer = request.clone().from(client.address());
let hash = *client
.send_transaction(request_from_signer, None)
.await
.unwrap();
let hash = *client.send_transaction(request_from_signer, None).await.unwrap();
let tx = client.get_transaction(hash).await.unwrap().unwrap();
assert_eq!(tx.from, client.address());
// signing a TransactionRequest with a from address that is not the
// signer should result in the default ganache account being used
let request_from_other = request.from(acc);
let hash = *client
.send_transaction(request_from_other, None)
.await
.unwrap();
let hash = *client.send_transaction(request_from_other, None).await.unwrap();
let tx = client.get_transaction(hash).await.unwrap().unwrap();
assert_eq!(tx.from, acc);
}

View File

@ -73,10 +73,9 @@ where
block_option: FilterBlockOption,
) -> TimeLagResult<FilterBlockOption, M> {
match block_option {
FilterBlockOption::Range {
from_block: _,
to_block: None,
} => Ok(block_option.set_to_block(self.get_block_number().await?.into())),
FilterBlockOption::Range { from_block: _, to_block: None } => {
Ok(block_option.set_to_block(self.get_block_number().await?.into()))
}
_ => Ok(block_option),
}
}
@ -112,10 +111,7 @@ where
block: Option<BlockId>,
) -> Result<ethers_providers::PendingTransaction<'_, Self::Provider>, Self::Error> {
let block = self.normalize_block_id(block).await?;
self.inner()
.send_transaction(tx, block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().send_transaction(tx, block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_block<T: Into<BlockId> + Send + Sync>(
@ -127,10 +123,7 @@ where
.await?
.expect("Cannot return None if Some is passed in");
self.inner()
.get_block(block_hash_or_number)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().get_block(block_hash_or_number).await.map_err(ethers_providers::FromErr::from)
}
async fn get_block_with_txs<T: Into<BlockId> + Send + Sync>(
@ -199,10 +192,7 @@ where
) -> Result<Bytes, Self::Error> {
let block = self.normalize_block_id(block).await?;
self.inner()
.call(tx, block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().call(tx, block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_balance<T: Into<NameOrAddress> + Send + Sync>(
@ -211,10 +201,7 @@ where
block: Option<BlockId>,
) -> Result<U256, Self::Error> {
let block = self.normalize_block_id(block).await?;
self.inner()
.get_balance(from, block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().get_balance(from, block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_transaction_receipt<T: Send + Sync + Into<TxHash>>(
@ -228,12 +215,12 @@ where
.map_err(ethers_providers::FromErr::from)?;
if receipt.is_none() {
return Ok(None);
return Ok(None)
}
let receipt = receipt.expect("checked is_none");
if receipt.block_number.is_none() {
return Ok(Some(receipt));
return Ok(Some(receipt))
}
let number = receipt.block_number.expect("checked is_none");
@ -252,10 +239,7 @@ where
) -> Result<Bytes, Self::Error> {
let block = self.normalize_block_id(block).await?;
self.inner()
.get_code(at, block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().get_code(at, block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_storage_at<T: Into<NameOrAddress> + Send + Sync>(
@ -277,10 +261,7 @@ where
block: Option<BlockId>,
) -> Result<(), Self::Error> {
let block = self.normalize_block_id(block).await?;
self.inner()
.fill_transaction(tx, block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().fill_transaction(tx, block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_block_receipts<T: Into<BlockNumber> + Send + Sync>(
@ -293,10 +274,7 @@ where
.await?
.expect("Cannot return None if Some is passed in");
self.inner()
.get_block_receipts(block)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().get_block_receipts(block).await.map_err(ethers_providers::FromErr::from)
}
async fn get_logs(
@ -306,10 +284,7 @@ where
let mut filter = filter.clone();
filter.block_option = self.normalize_filter_range(filter.block_option).await?;
self.inner()
.get_logs(&filter)
.await
.map_err(ethers_providers::FromErr::from)
self.inner().get_logs(&filter).await.map_err(ethers_providers::FromErr::from)
}
async fn new_filter(

View File

@ -26,7 +26,6 @@ pub static ADDRESS_BOOK: Lazy<HashMap<U256, Address>> = Lazy::new(|| {
/// }
/// );
/// ```
///
// Auto-generated type-safe bindings
pub use dsproxyfactory_mod::*;
#[allow(clippy::too_many_arguments)]
@ -57,9 +56,7 @@ mod dsproxyfactory_mod {
}
impl<M: Middleware> std::fmt::Debug for DsProxyFactory<M> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_tuple(stringify!(DsProxyFactory))
.field(&self.address())
.finish()
f.debug_tuple(stringify!(DsProxyFactory)).field(&self.address()).finish()
}
}
impl<'a, M: Middleware> DsProxyFactory<M> {
@ -104,7 +101,8 @@ mod dsproxyfactory_mod {
self.0.event()
}
/// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this contract
/// Returns an [`Event`](ethers_contract::builders::Event) builder for all events of this
/// contract
pub fn events(&self) -> ethers_contract::builders::Event<M, CreatedFilter> {
self.0.event_with_filter(Default::default())
}

View File

@ -4,7 +4,9 @@ use factory::{CreatedFilter, DsProxyFactory, ADDRESS_BOOK};
use super::{Transformer, TransformerError};
use ethers_contract::{builders::ContractCall, BaseContract, ContractError};
use ethers_core::{
abi::parse_abi, types::transaction::eip2718::TypedTransaction, types::*, utils::id,
abi::parse_abi,
types::{transaction::eip2718::TypedTransaction, *},
utils::id,
};
use ethers_providers::Middleware;
use std::sync::Arc;
@ -92,10 +94,8 @@ impl DsProxy {
let factory: Address = match factory {
Some(addr) => addr,
None => {
let chain_id = client
.get_chainid()
.await
.map_err(ContractError::MiddlewareError)?;
let chain_id =
client.get_chainid().await.map_err(ContractError::MiddlewareError)?;
match ADDRESS_BOOK.get(&chain_id) {
Some(addr) => *addr,
None => panic!(
@ -134,10 +134,7 @@ impl DsProxy {
let contract = parse_abi(&[DS_PROXY_EXECUTE_TARGET, DS_PROXY_EXECUTE_CODE])
.expect("could not parse ABI")
.into();
Ok(Self {
address: created_filter.proxy,
contract,
})
Ok(Self { address: created_filter.proxy, contract })
} else {
Err(ContractError::ContractNotDeployed)
}
@ -157,10 +154,7 @@ impl DsProxy {
data: Bytes,
) -> Result<ContractCall<M, Bytes>, ContractError<M>> {
// construct the full contract using DsProxy's address and the injected client.
let ds_proxy = self
.contract
.clone()
.into_contract(self.address, client.into());
let ds_proxy = self.contract.clone().into_contract(self.address, client.into());
match target.into() {
// handle the case when the target is an address to a deployed contract.
@ -193,9 +187,7 @@ impl Transformer for DsProxy {
// encode data as the ABI encoded data for DSProxy's execute method.
let selector = id("execute(address,bytes)");
let encoded_data = self
.contract
.encode_with_selector(selector, (*target, data))?;
let encoded_data = self.contract.encode_with_selector(selector, (*target, data))?;
// update appropriate fields of the proxy tx.
tx.set_data(encoded_data);

View File

@ -30,22 +30,13 @@ async fn gas_escalator_live() {
let tx = TransactionRequest::pay(Address::zero(), 1u64).gas_price(10_000_000);
// broadcast 3 txs
provider
.send_transaction(tx.clone().nonce(nonce), None)
.await
.unwrap();
provider
.send_transaction(tx.clone().nonce(nonce + 1), None)
.await
.unwrap();
provider
.send_transaction(tx.clone().nonce(nonce + 2), None)
.await
.unwrap();
provider.send_transaction(tx.clone().nonce(nonce), None).await.unwrap();
provider.send_transaction(tx.clone().nonce(nonce + 1), None).await.unwrap();
provider.send_transaction(tx.clone().nonce(nonce + 2), None).await.unwrap();
// Wait a bunch of seconds and refresh etherscan to see the transactions get bumped
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
// 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

@ -38,18 +38,13 @@ async fn using_gas_oracle() {
let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
// assign a gas oracle to use
let gas_oracle = FakeGasOracle {
gas_price: 1337.into(),
};
let gas_oracle = FakeGasOracle { gas_price: 1337.into() };
let expected_gas_price = gas_oracle.fetch().await.unwrap();
let provider = GasOracleMiddleware::new(provider, gas_oracle);
// broadcast a transaction
let tx = TransactionRequest::new()
.from(from)
.to(Address::zero())
.value(10000);
let tx = TransactionRequest::new().from(from).to(Address::zero()).value(10000);
let tx_hash = provider.send_transaction(tx, None).await.unwrap();
let tx = provider.get_transaction(*tx_hash).await.unwrap().unwrap();

View File

@ -6,8 +6,7 @@ async fn nonce_manager() {
use ethers_middleware::{nonce_manager::NonceManagerMiddleware, signer::SignerMiddleware};
use ethers_providers::{Http, Middleware, Provider};
use ethers_signers::{LocalWallet, Signer};
use std::convert::TryFrom;
use std::time::Duration;
use std::{convert::TryFrom, time::Duration};
let provider =
Provider::<Http>::try_from("https://rinkeby.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778")
@ -36,10 +35,7 @@ async fn nonce_manager() {
let mut tx_hashes = Vec::new();
for _ in 0..10 {
let tx = provider
.send_transaction(
Eip1559TransactionRequest::new().to(address).value(100u64),
None,
)
.send_transaction(Eip1559TransactionRequest::new().to(address).value(100u64), None)
.await
.unwrap();
tx_hashes.push(*tx);
@ -50,15 +46,7 @@ async fn nonce_manager() {
let mut nonces = Vec::new();
for tx_hash in tx_hashes {
nonces.push(
provider
.get_transaction(tx_hash)
.await
.unwrap()
.unwrap()
.nonce
.as_u64(),
);
nonces.push(provider.get_transaction(tx_hash).await.unwrap().unwrap().nonce.as_u64());
}
assert_eq!(nonces, (nonce..nonce + 10).collect::<Vec<_>>())

View File

@ -41,18 +41,12 @@ async fn send_eth() {
// craft the transaction
let tx = TransactionRequest::new().to(wallet2.address()).value(10000);
let balance_before = provider
.get_balance(provider.address(), None)
.await
.unwrap();
let balance_before = provider.get_balance(provider.address(), None).await.unwrap();
// send it!
provider.send_transaction(tx, None).await.unwrap();
let balance_after = provider
.get_balance(provider.address(), None)
.await
.unwrap();
let balance_after = provider.get_balance(provider.address(), None).await.unwrap();
assert!(balance_before > balance_after);
}
@ -124,46 +118,23 @@ async fn typed_txs() {
) {
let provider = pending_tx.provider();
let receipt = pending_tx.await.unwrap().unwrap();
let tx = provider
.get_transaction(receipt.transaction_hash)
.await
.unwrap()
.unwrap();
let tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
assert_eq!(receipt.transaction_type, Some(expected.into()));
assert_eq!(tx.transaction_type, Some(expected.into()));
}
let mut nonce = provider.get_transaction_count(address, None).await.unwrap();
let tx = TransactionRequest::new()
.from(address)
.to(address)
.nonce(nonce);
let tx = TransactionRequest::new().from(address).to(address).nonce(nonce);
nonce += 1.into();
let tx1 = provider
.send_transaction(tx.clone(), Some(BlockNumber::Pending.into()))
.await
.unwrap();
let tx1 =
provider.send_transaction(tx.clone(), Some(BlockNumber::Pending.into())).await.unwrap();
let tx = tx
.clone()
.nonce(nonce)
.from(address)
.to(address)
.with_access_list(vec![]);
let tx = tx.clone().nonce(nonce).from(address).to(address).with_access_list(vec![]);
nonce += 1.into();
let tx2 = provider
.send_transaction(tx, Some(BlockNumber::Pending.into()))
.await
.unwrap();
let tx2 = provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap();
let tx = Eip1559TransactionRequest::new()
.from(address)
.to(address)
.nonce(nonce);
let tx3 = provider
.send_transaction(tx, Some(BlockNumber::Pending.into()))
.await
.unwrap();
let tx = Eip1559TransactionRequest::new().from(address).to(address).nonce(nonce);
let tx3 = provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap();
futures_util::join!(check_tx(tx1, 0), check_tx(tx2, 1), check_tx(tx3, 2),);
}
@ -187,13 +158,7 @@ async fn test_send_transaction() {
let balance_before = client.get_balance(client.address(), None).await.unwrap();
let tx = TransactionRequest::pay(client.address(), 100);
let _receipt = client
.send_transaction(tx, None)
.await
.unwrap()
.confirmations(3)
.await
.unwrap();
let _receipt = client.send_transaction(tx, None).await.unwrap().confirmations(3).await.unwrap();
let balance_after = client.get_balance(client.address(), None).await.unwrap();
assert!(balance_before > balance_after);
}
@ -217,54 +182,27 @@ async fn send_transaction_handles_tx_from_field() {
// sending a TransactionRequest with a from field of None should result
// in a transaction from the signer address
let request_from_none = TransactionRequest::new();
let receipt = provider
.send_transaction(request_from_none, None)
.await
.unwrap()
.await
.unwrap()
.unwrap();
let sent_tx = provider
.get_transaction(receipt.transaction_hash)
.await
.unwrap()
.unwrap();
let receipt =
provider.send_transaction(request_from_none, None).await.unwrap().await.unwrap().unwrap();
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
assert_eq!(sent_tx.from, signer.address());
// sending a TransactionRequest with the signer as the from address should
// result in a transaction from the signer address
let request_from_signer = TransactionRequest::new().from(signer.address());
let receipt = provider
.send_transaction(request_from_signer, None)
.await
.unwrap()
.await
.unwrap()
.unwrap();
let sent_tx = provider
.get_transaction(receipt.transaction_hash)
.await
.unwrap()
.unwrap();
let receipt =
provider.send_transaction(request_from_signer, None).await.unwrap().await.unwrap().unwrap();
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
assert_eq!(sent_tx.from, signer.address());
// sending a TransactionRequest with a from address that is not the signer
// should result in a transaction from the specified address
let request_from_other = TransactionRequest::new().from(other.address());
let receipt = provider
.send_transaction(request_from_other, None)
.await
.unwrap()
.await
.unwrap()
.unwrap();
let sent_tx = provider
.get_transaction(receipt.transaction_hash)
.await
.unwrap()
.unwrap();
let receipt =
provider.send_transaction(request_from_other, None).await.unwrap().await.unwrap().unwrap();
let sent_tx = provider.get_transaction(receipt.transaction_hash).await.unwrap().unwrap();
assert_eq!(sent_tx.from, other.address());
}
@ -281,9 +219,8 @@ async fn deploy_and_call_contract() {
use std::sync::Arc;
fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
let compiled = Solc::new(&format!("./tests/solidity-contracts/{}", filename))
.build()
.unwrap();
let compiled =
Solc::new(&format!("./tests/solidity-contracts/{}", filename)).build().unwrap();
let contract = compiled.get(name).expect("could not find contract");
(contract.abi.clone(), contract.bytecode.clone())
}
@ -314,10 +251,7 @@ async fn deploy_and_call_contract() {
// 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", U256::from(1))
.unwrap()
.gas(100000);
let call = contract.method::<_, H256>("setValue", U256::from(1)).unwrap().gas(100000);
let pending_tx = call.send().await.unwrap();
let _receipt = pending_tx.await.unwrap();
@ -335,9 +269,7 @@ impl TestWallets {
/// Helper for funding the wallets with an instantiated provider
#[allow(unused)]
pub async fn fund<T: JsonRpcClient, U: Into<u32>>(&self, provider: &Provider<T>, n: U) {
let addrs = (0..n.into())
.map(|i| self.get(i).address())
.collect::<Vec<_>>();
let addrs = (0..n.into()).map(|i| self.get(i).address()).collect::<Vec<_>>();
// hardcoded funder address private key, rinkeby
let signer = "39aa18eeb5d12c071e5f19d8e9375a872e90cb1f2fa640384ffd8800a2f3e8f1"
.parse::<LocalWallet>()
@ -356,10 +288,7 @@ impl TestWallets {
// 0.1 eth per wallet
.value(parse_units("1", 18).unwrap());
pending_txs.push(
provider
.send_transaction(tx, Some(BlockNumber::Pending.into()))
.await
.unwrap(),
provider.send_transaction(tx, Some(BlockNumber::Pending.into())).await.unwrap(),
);
nonce += 1.into();
}

View File

@ -84,12 +84,7 @@ mod tests {
for _ in 0..10 {
let pending = provider.send_transaction(tx.clone(), None).await.unwrap();
let hash = *pending;
let gas_price = provider
.get_transaction(hash)
.await
.unwrap()
.unwrap()
.gas_price;
let gas_price = provider.get_transaction(hash).await.unwrap().unwrap().gas_price;
dbg!(gas_price);
pending_txs.push(pending);
}

View File

@ -38,9 +38,7 @@ async fn ds_proxy_transformer() {
let compiled = Solc::new("./tests/solidity-contracts/DSProxy.sol")
.build()
.expect("could not compile DSProxyFactory");
let contract = compiled
.get("DSProxyFactory")
.expect("could not find DSProxyFactory");
let contract = compiled.get("DSProxyFactory").expect("could not find DSProxyFactory");
let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
@ -63,9 +61,7 @@ async fn ds_proxy_transformer() {
let compiled = Solc::new("./tests/solidity-contracts/SimpleStorage.sol")
.build()
.expect("could not compile SimpleStorage");
let contract = compiled
.get("SimpleStorage")
.expect("could not find SimpleStorage");
let contract = compiled.get("SimpleStorage").expect("could not find SimpleStorage");
let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
@ -82,25 +78,13 @@ async fn ds_proxy_transformer() {
let calldata = simple_storage
.encode("setValue", U256::from(expected_value))
.expect("could not get ABI encoded data");
let tx = TransactionRequest::new()
.to(simple_storage.address())
.data(calldata);
provider
.send_transaction(tx, None)
.await
.unwrap()
.await
.unwrap();
let tx = TransactionRequest::new().to(simple_storage.address()).data(calldata);
provider.send_transaction(tx, None).await.unwrap().await.unwrap();
// verify that DsProxy's state was updated.
let last_sender = provider
.get_storage_at(ds_proxy_addr, H256::zero(), None)
.await
.unwrap();
let last_value = provider
.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None)
.await
.unwrap();
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
let last_value =
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
assert_eq!(last_sender, wallet_addr.into());
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
}
@ -127,9 +111,7 @@ async fn ds_proxy_code() {
let compiled = Solc::new("./tests/solidity-contracts/DSProxy.sol")
.build()
.expect("could not compile DSProxyFactory");
let contract = compiled
.get("DSProxyFactory")
.expect("could not find DSProxyFactory");
let contract = compiled.get("DSProxyFactory").expect("could not find DSProxyFactory");
let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
@ -152,9 +134,7 @@ async fn ds_proxy_code() {
let compiled = Solc::new("./tests/solidity-contracts/SimpleStorage.sol")
.build()
.expect("could not compile SimpleStorage");
let ss = compiled
.get("SimpleStorage")
.expect("could not find SimpleStorage");
let ss = compiled.get("SimpleStorage").expect("could not find SimpleStorage");
let ss_base_contract: BaseContract = ss.abi.clone().into();
let expected_value: u64 = rng.gen();
let calldata = ss_base_contract
@ -175,14 +155,9 @@ async fn ds_proxy_code() {
.unwrap();
// verify that DsProxy's state was updated.
let last_sender = provider
.get_storage_at(ds_proxy_addr, H256::zero(), None)
.await
.unwrap();
let last_value = provider
.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None)
.await
.unwrap();
let last_sender = provider.get_storage_at(ds_proxy_addr, H256::zero(), None).await.unwrap();
let last_value =
provider.get_storage_at(ds_proxy_addr, H256::from_low_u64_be(1u64), None).await.unwrap();
assert_eq!(last_sender, wallet_addr.into());
assert_eq!(last_value, H256::from_low_u64_be(expected_value));
}

View File

@ -56,14 +56,12 @@ pub fn reverse_address(addr: Address) -> String {
/// Returns the ENS namehash as specified in [EIP-137](https://eips.ethereum.org/EIPS/eip-137)
pub fn namehash(name: &str) -> H256 {
if name.is_empty() {
return H256::zero();
return H256::zero()
}
// iterate in reverse
name.rsplit('.')
.fold([0u8; 32], |node, label| {
keccak256(&[node, keccak256(label.as_bytes())].concat())
})
.fold([0u8; 32], |node, label| keccak256(&[node, keccak256(label.as_bytes())].concat()))
.into()
}
@ -72,11 +70,7 @@ mod tests {
use super::*;
fn assert_hex(hash: H256, val: &str) {
let v = if let Some(stripped) = val.strip_prefix("0x") {
stripped
} else {
val
};
let v = if let Some(stripped) = val.strip_prefix("0x") { stripped } else { val };
assert_eq!(hash.0.to_vec(), hex::decode(v).unwrap());
}
@ -84,22 +78,10 @@ mod tests {
#[test]
fn test_namehash() {
for (name, expected) in &[
(
"",
"0000000000000000000000000000000000000000000000000000000000000000",
),
(
"foo.eth",
"de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f",
),
(
"eth",
"0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae",
),
(
"alice.eth",
"0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec",
),
("", "0000000000000000000000000000000000000000000000000000000000000000"),
("foo.eth", "de9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f"),
("eth", "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"),
("alice.eth", "0x787192fc5378cc32aa956ddfdedbf26b24e8d78e40109add0eea2c1a012c3dec"),
] {
assert_hex(namehash(name), expected);
}

View File

@ -132,9 +132,9 @@ where
/// A middleware allows customizing requests send and received from an ethereum node.
///
/// Writing a middleware is as simple as:
/// 1. implementing the [`inner`](crate::Middleware::inner) method to point to the next layer in the "middleware onion",
/// 2. implementing the [`FromErr`](crate::FromErr) trait on your middleware's error type
/// 3. implementing any of the methods you want to override
/// 1. implementing the [`inner`](crate::Middleware::inner) method to point to the next layer in the
/// "middleware onion", 2. implementing the [`FromErr`](crate::FromErr) trait on your middleware's
/// error type 3. implementing any of the methods you want to override
///
/// ```rust
/// use ethers_providers::{Middleware, FromErr};
@ -280,54 +280,36 @@ pub trait Middleware: Sync + Send + Debug {
tx: T,
block: Option<BlockId>,
) -> Result<PendingTransaction<'_, Self::Provider>, Self::Error> {
self.inner()
.send_transaction(tx, block)
.await
.map_err(FromErr::from)
self.inner().send_transaction(tx, block).await.map_err(FromErr::from)
}
async fn resolve_name(&self, ens_name: &str) -> Result<Address, Self::Error> {
self.inner()
.resolve_name(ens_name)
.await
.map_err(FromErr::from)
self.inner().resolve_name(ens_name).await.map_err(FromErr::from)
}
async fn lookup_address(&self, address: Address) -> Result<String, Self::Error> {
self.inner()
.lookup_address(address)
.await
.map_err(FromErr::from)
self.inner().lookup_address(address).await.map_err(FromErr::from)
}
async fn get_block<T: Into<BlockId> + Send + Sync>(
&self,
block_hash_or_number: T,
) -> Result<Option<Block<TxHash>>, Self::Error> {
self.inner()
.get_block(block_hash_or_number)
.await
.map_err(FromErr::from)
self.inner().get_block(block_hash_or_number).await.map_err(FromErr::from)
}
async fn get_block_with_txs<T: Into<BlockId> + Send + Sync>(
&self,
block_hash_or_number: T,
) -> Result<Option<Block<Transaction>>, Self::Error> {
self.inner()
.get_block_with_txs(block_hash_or_number)
.await
.map_err(FromErr::from)
self.inner().get_block_with_txs(block_hash_or_number).await.map_err(FromErr::from)
}
async fn get_uncle_count<T: Into<BlockId> + Send + Sync>(
&self,
block_hash_or_number: T,
) -> Result<U256, Self::Error> {
self.inner()
.get_uncle_count(block_hash_or_number)
.await
.map_err(FromErr::from)
self.inner().get_uncle_count(block_hash_or_number).await.map_err(FromErr::from)
}
async fn get_uncle<T: Into<BlockId> + Send + Sync>(
@ -335,10 +317,7 @@ pub trait Middleware: Sync + Send + Debug {
block_hash_or_number: T,
idx: U64,
) -> Result<Option<Block<H256>>, Self::Error> {
self.inner()
.get_uncle(block_hash_or_number, idx)
.await
.map_err(FromErr::from)
self.inner().get_uncle(block_hash_or_number, idx).await.map_err(FromErr::from)
}
async fn get_transaction_count<T: Into<NameOrAddress> + Send + Sync>(
@ -346,10 +325,7 @@ pub trait Middleware: Sync + Send + Debug {
from: T,
block: Option<BlockId>,
) -> Result<U256, Self::Error> {
self.inner()
.get_transaction_count(from, block)
.await
.map_err(FromErr::from)
self.inner().get_transaction_count(from, block).await.map_err(FromErr::from)
}
async fn estimate_gas(&self, tx: &TypedTransaction) -> Result<U256, Self::Error> {
@ -373,40 +349,28 @@ pub trait Middleware: Sync + Send + Debug {
from: T,
block: Option<BlockId>,
) -> Result<U256, Self::Error> {
self.inner()
.get_balance(from, block)
.await
.map_err(FromErr::from)
self.inner().get_balance(from, block).await.map_err(FromErr::from)
}
async fn get_transaction<T: Send + Sync + Into<TxHash>>(
&self,
transaction_hash: T,
) -> Result<Option<Transaction>, Self::Error> {
self.inner()
.get_transaction(transaction_hash)
.await
.map_err(FromErr::from)
self.inner().get_transaction(transaction_hash).await.map_err(FromErr::from)
}
async fn get_transaction_receipt<T: Send + Sync + Into<TxHash>>(
&self,
transaction_hash: T,
) -> Result<Option<TransactionReceipt>, Self::Error> {
self.inner()
.get_transaction_receipt(transaction_hash)
.await
.map_err(FromErr::from)
self.inner().get_transaction_receipt(transaction_hash).await.map_err(FromErr::from)
}
async fn get_block_receipts<T: Into<BlockNumber> + Send + Sync>(
&self,
block: T,
) -> Result<Vec<TransactionReceipt>, Self::Error> {
self.inner()
.get_block_receipts(block)
.await
.map_err(FromErr::from)
self.inner().get_block_receipts(block).await.map_err(FromErr::from)
}
async fn get_gas_price(&self) -> Result<U256, Self::Error> {
@ -417,10 +381,7 @@ pub trait Middleware: Sync + Send + Debug {
&self,
estimator: Option<fn(U256, Vec<Vec<U256>>) -> (U256, U256)>,
) -> Result<(U256, U256), Self::Error> {
self.inner()
.estimate_eip1559_fees(estimator)
.await
.map_err(FromErr::from)
self.inner().estimate_eip1559_fees(estimator).await.map_err(FromErr::from)
}
async fn get_accounts(&self) -> Result<Vec<Address>, Self::Error> {
@ -431,10 +392,7 @@ pub trait Middleware: Sync + Send + Debug {
&'a self,
tx: Bytes,
) -> Result<PendingTransaction<'a, Self::Provider>, Self::Error> {
self.inner()
.send_raw_transaction(tx)
.await
.map_err(FromErr::from)
self.inner().send_raw_transaction(tx).await.map_err(FromErr::from)
}
/// This returns true if either the middleware stack contains a `SignerMiddleware`, or the
@ -466,10 +424,7 @@ pub trait Middleware: Sync + Send + Debug {
&self,
id: T,
) -> Result<bool, Self::Error> {
self.inner()
.uninstall_filter(id)
.await
.map_err(FromErr::from)
self.inner().uninstall_filter(id).await.map_err(FromErr::from)
}
async fn watch<'a>(
@ -482,10 +437,7 @@ pub trait Middleware: Sync + Send + Debug {
async fn watch_pending_transactions(
&self,
) -> Result<FilterWatcher<'_, Self::Provider, H256>, Self::Error> {
self.inner()
.watch_pending_transactions()
.await
.map_err(FromErr::from)
self.inner().watch_pending_transactions().await.map_err(FromErr::from)
}
async fn get_filter_changes<T, R>(&self, id: T) -> Result<Vec<R>, Self::Error>
@ -493,10 +445,7 @@ pub trait Middleware: Sync + Send + Debug {
T: Into<U256> + Send + Sync,
R: Serialize + DeserializeOwned + Send + Sync + Debug,
{
self.inner()
.get_filter_changes(id)
.await
.map_err(FromErr::from)
self.inner().get_filter_changes(id).await.map_err(FromErr::from)
}
async fn watch_blocks(&self) -> Result<FilterWatcher<'_, Self::Provider, H256>, Self::Error> {
@ -508,10 +457,7 @@ pub trait Middleware: Sync + Send + Debug {
at: T,
block: Option<BlockId>,
) -> Result<Bytes, Self::Error> {
self.inner()
.get_code(at, block)
.await
.map_err(FromErr::from)
self.inner().get_code(at, block).await.map_err(FromErr::from)
}
async fn get_storage_at<T: Into<NameOrAddress> + Send + Sync>(
@ -520,10 +466,7 @@ pub trait Middleware: Sync + Send + Debug {
location: H256,
block: Option<BlockId>,
) -> Result<H256, Self::Error> {
self.inner()
.get_storage_at(from, location, block)
.await
.map_err(FromErr::from)
self.inner().get_storage_at(from, location, block).await.map_err(FromErr::from)
}
async fn get_proof<T: Into<NameOrAddress> + Send + Sync>(
@ -532,10 +475,7 @@ pub trait Middleware: Sync + Send + Debug {
locations: Vec<H256>,
block: Option<BlockId>,
) -> Result<EIP1186ProofResponse, Self::Error> {
self.inner()
.get_proof(from, locations, block)
.await
.map_err(FromErr::from)
self.inner().get_proof(from, locations, block).await.map_err(FromErr::from)
}
// Mempool inspection for Geth's API
@ -561,10 +501,7 @@ pub trait Middleware: Sync + Send + Debug {
trace_type: Vec<TraceType>,
block: Option<BlockNumber>,
) -> Result<BlockTrace, Self::Error> {
self.inner()
.trace_call(req, trace_type, block)
.await
.map_err(FromErr::from)
self.inner().trace_call(req, trace_type, block).await.map_err(FromErr::from)
}
/// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces
@ -573,10 +510,7 @@ pub trait Middleware: Sync + Send + Debug {
data: Bytes,
trace_type: Vec<TraceType>,
) -> Result<BlockTrace, Self::Error> {
self.inner()
.trace_raw_transaction(data, trace_type)
.await
.map_err(FromErr::from)
self.inner().trace_raw_transaction(data, trace_type).await.map_err(FromErr::from)
}
/// Replays a transaction, returning the traces
@ -585,10 +519,7 @@ pub trait Middleware: Sync + Send + Debug {
hash: H256,
trace_type: Vec<TraceType>,
) -> Result<BlockTrace, Self::Error> {
self.inner()
.trace_replay_transaction(hash, trace_type)
.await
.map_err(FromErr::from)
self.inner().trace_replay_transaction(hash, trace_type).await.map_err(FromErr::from)
}
/// Replays all transactions in a block returning the requested traces for each transaction
@ -597,10 +528,7 @@ pub trait Middleware: Sync + Send + Debug {
block: BlockNumber,
trace_type: Vec<TraceType>,
) -> Result<Vec<BlockTrace>, Self::Error> {
self.inner()
.trace_replay_block_transactions(block, trace_type)
.await
.map_err(FromErr::from)
self.inner().trace_replay_block_transactions(block, trace_type).await.map_err(FromErr::from)
}
/// Returns traces created at given block
@ -610,10 +538,7 @@ pub trait Middleware: Sync + Send + Debug {
/// Return traces matching the given filter
async fn trace_filter(&self, filter: TraceFilter) -> Result<Vec<Trace>, Self::Error> {
self.inner()
.trace_filter(filter)
.await
.map_err(FromErr::from)
self.inner().trace_filter(filter).await.map_err(FromErr::from)
}
/// Returns trace at the given position
@ -622,18 +547,12 @@ pub trait Middleware: Sync + Send + Debug {
hash: H256,
index: Vec<T>,
) -> Result<Trace, Self::Error> {
self.inner()
.trace_get(hash, index)
.await
.map_err(FromErr::from)
self.inner().trace_get(hash, index).await.map_err(FromErr::from)
}
/// Returns all traces of a given transaction
async fn trace_transaction(&self, hash: H256) -> Result<Vec<Trace>, Self::Error> {
self.inner()
.trace_transaction(hash)
.await
.map_err(FromErr::from)
self.inner().trace_transaction(hash).await.map_err(FromErr::from)
}
// Parity namespace
@ -643,10 +562,7 @@ pub trait Middleware: Sync + Send + Debug {
&self,
block: T,
) -> Result<Vec<TransactionReceipt>, Self::Error> {
self.inner()
.parity_block_receipts(block)
.await
.map_err(FromErr::from)
self.inner().parity_block_receipts(block).await.map_err(FromErr::from)
}
async fn subscribe<T, R>(
@ -684,10 +600,7 @@ pub trait Middleware: Sync + Send + Debug {
where
<Self as Middleware>::Provider: PubsubClient,
{
self.inner()
.subscribe_pending_txs()
.await
.map_err(FromErr::from)
self.inner().subscribe_pending_txs().await.map_err(FromErr::from)
}
async fn subscribe_logs<'a>(
@ -697,10 +610,7 @@ pub trait Middleware: Sync + Send + Debug {
where
<Self as Middleware>::Provider: PubsubClient,
{
self.inner()
.subscribe_logs(filter)
.await
.map_err(FromErr::from)
self.inner().subscribe_logs(filter).await.map_err(FromErr::from)
}
async fn fee_history<T: Into<U256> + serde::Serialize + Send + Sync>(
@ -720,10 +630,7 @@ pub trait Middleware: Sync + Send + Debug {
tx: &TypedTransaction,
block: Option<BlockId>,
) -> Result<AccessListWithGasUsed, Self::Error> {
self.inner()
.create_access_list(tx, block)
.await
.map_err(FromErr::from)
self.inner().create_access_list(tx, block).await.map_err(FromErr::from)
}
}
@ -765,9 +672,6 @@ pub trait CeloMiddleware: Middleware {
&self,
block_id: T,
) -> Result<Vec<String>, ProviderError> {
self.provider()
.get_validators_bls_public_keys(block_id)
.await
.map_err(FromErr::from)
self.provider().get_validators_bls_public_keys(block_id).await.map_err(FromErr::from)
}
}

View File

@ -1,7 +1,6 @@
use crate::Middleware;
use crate::{
stream::{interval, DEFAULT_POLL_INTERVAL},
JsonRpcClient, PinBoxFut, Provider, ProviderError,
JsonRpcClient, Middleware, PinBoxFut, Provider, ProviderError,
};
use ethers_core::types::{Transaction, TransactionReceipt, TxHash, U64};
use futures_core::stream::Stream;
@ -81,7 +80,7 @@ macro_rules! rewake_with_new_state {
($ctx:ident, $this:ident, $new_state:expr) => {
*$this.state = $new_state;
$ctx.waker().wake_by_ref();
return Poll::Pending;
return Poll::Pending
};
}
@ -130,7 +129,7 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
if tx_opt.is_none() {
tracing::debug!("Dropped from mempool, pending tx {:?}", *this.tx_hash);
*this.state = PendingTxState::Completed;
return Poll::Ready(Ok(None));
return Poll::Ready(Ok(None))
}
// If it hasn't confirmed yet, poll again later
@ -175,10 +174,7 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
// If we requested more than 1 confirmation, we need to compare the receipt's
// block number and the current block
if *this.confirmations > 1 {
tracing::debug!(
"Waiting on confirmations for pending tx {:?}",
*this.tx_hash
);
tracing::debug!("Waiting on confirmations for pending tx {:?}", *this.tx_hash);
let fut = Box::pin(this.provider.get_block_number());
*this.state = PendingTxState::GettingBlockNumber(fut, receipt.take());
@ -188,7 +184,7 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
} else {
let receipt = receipt.take();
*this.state = PendingTxState::Completed;
return Poll::Ready(Ok(receipt));
return Poll::Ready(Ok(receipt))
}
}
PendingTxState::PausedGettingBlockNumber(receipt) => {
@ -219,7 +215,7 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
if current_block > inclusion_block + *this.confirmations - 1 {
let receipt = Some(receipt);
*this.state = PendingTxState::Completed;
return Poll::Ready(Ok(receipt));
return Poll::Ready(Ok(receipt))
} else {
tracing::trace!(tx_hash = ?this.tx_hash, "confirmations {}/{}", current_block - inclusion_block + 1, this.confirmations);
*this.state = PendingTxState::PausedGettingBlockNumber(Some(receipt));
@ -313,8 +309,6 @@ impl<'a> fmt::Debug for PendingTxState<'a> {
PendingTxState::Completed => "Completed",
};
f.debug_struct("PendingTxState")
.field("state", &state)
.finish()
f.debug_struct("PendingTxState").field("state", &state).finish()
}
}

View File

@ -108,12 +108,7 @@ pub enum FilterKind<'a> {
impl<P: JsonRpcClient> Provider<P> {
/// Instantiate a new provider with a backend.
pub fn new(provider: P) -> Self {
Self {
inner: provider,
ens: None,
interval: None,
from: None,
}
Self { inner: provider, ens: None, interval: None, from: None }
}
pub fn with_sender(mut self, address: impl Into<Address>) -> Self {
@ -131,11 +126,7 @@ impl<P: JsonRpcClient> Provider<P> {
// https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code
let res = async move {
trace!("tx");
let res: R = self
.inner
.request(method, params)
.await
.map_err(Into::into)?;
let res: R = self.inner.request(method, params).await.map_err(Into::into)?;
trace!(rx = ?serde_json::to_string(&res)?);
Ok::<_, ProviderError>(res)
}
@ -154,13 +145,11 @@ impl<P: JsonRpcClient> Provider<P> {
Ok(match id {
BlockId::Hash(hash) => {
let hash = utils::serialize(&hash);
self.request("eth_getBlockByHash", [hash, include_txs])
.await?
self.request("eth_getBlockByHash", [hash, include_txs]).await?
}
BlockId::Number(num) => {
let num = utils::serialize(&num);
self.request("eth_getBlockByNumber", [num, include_txs])
.await?
self.request("eth_getBlockByNumber", [num, include_txs]).await?
}
})
}
@ -175,8 +164,7 @@ impl<P: JsonRpcClient> CeloMiddleware for Provider<P> {
block_id: T,
) -> Result<Vec<String>, ProviderError> {
let block_id = utils::serialize(&block_id.into());
self.request("istanbul_getValidatorsBLSPublicKeys", [block_id])
.await
self.request("istanbul_getValidatorsBLSPublicKeys", [block_id]).await
}
}
@ -242,8 +230,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
}
BlockId::Number(num) => {
let num = utils::serialize(&num);
self.request("eth_getUncleCountByBlockNumber", [num])
.await?
self.request("eth_getUncleCountByBlockNumber", [num]).await?
}
})
}
@ -259,13 +246,11 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
Ok(match blk_id {
BlockId::Hash(hash) => {
let hash = utils::serialize(&hash);
self.request("eth_getUncleByBlockHashAndIndex", [hash, idx])
.await?
self.request("eth_getUncleByBlockHashAndIndex", [hash, idx]).await?
}
BlockId::Number(num) => {
let num = utils::serialize(&num);
self.request("eth_getUncleByBlockNumberAndIndex", [num, idx])
.await?
self.request("eth_getUncleByBlockNumberAndIndex", [num, idx]).await?
}
})
}
@ -382,8 +367,9 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
//
// These are relatively low-level calls. The Contracts API should usually be used instead.
/// Sends the read-only (constant) transaction to a single Ethereum node and return the result (as bytes) of executing it.
/// This is free, since it does not change any state on the blockchain.
/// Sends the read-only (constant) transaction to a single Ethereum node and return the result
/// (as bytes) of executing it. This is free, since it does not change any state on the
/// blockchain.
async fn call(
&self,
tx: &TypedTransaction,
@ -394,9 +380,10 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
self.request("eth_call", [tx, block]).await
}
/// Sends a transaction to a single Ethereum node and return the estimated amount of gas required (as a U256) to send it
/// This is free, but only an estimate. Providing too little gas will result in a transaction being rejected
/// (while still consuming all provided gas).
/// Sends a transaction to a single Ethereum node and return the estimated amount of gas
/// required (as a U256) to send it This is free, but only an estimate. Providing too little
/// gas will result in a transaction being rejected (while still consuming all provided
/// gas).
async fn estimate_gas(&self, tx: &TypedTransaction) -> Result<U256, ProviderError> {
self.request("eth_estimateGas", [tx]).await
}
@ -425,8 +412,8 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
Ok(PendingTransaction::new(tx_hash, self).interval(self.get_interval()))
}
/// Send the raw RLP encoded transaction to the entire Ethereum network and returns the transaction's hash
/// This will consume gas from the account that signed the transaction.
/// Send the raw RLP encoded transaction to the entire Ethereum network and returns the
/// transaction's hash This will consume gas from the account that signed the transaction.
async fn send_raw_transaction<'a>(
&'a self,
tx: Bytes,
@ -557,9 +544,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
// get the hex encoded value.
let value: String = self
.request("eth_getStorageAt", [from, location, block])
.await?;
let value: String = self.request("eth_getStorageAt", [from, location, block]).await?;
// get rid of the 0x prefix and left pad it with zeroes.
let value = format!("{:0>64}", value.replace("0x", ""));
Ok(H256::from_slice(&Vec::from_hex(value)?))
@ -595,10 +580,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
};
let from = utils::serialize(&from);
let locations = locations
.iter()
.map(|location| utils::serialize(&location))
.collect();
let locations = locations.iter().map(|location| utils::serialize(&location)).collect();
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
self.request("eth_getProof", [from, locations, block]).await
@ -609,7 +591,8 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
// be assigned to Ethereum addresses. Any provider operation which takes an address
// may also take an ENS name.
//
// ENS also provides the ability for a reverse lookup, which determines the name for an address if it has been configured.
// ENS also provides the ability for a reverse lookup, which determines the name for an address
// if it has been configured.
/// Returns the address that the `ens_name` resolves to (or None if not configured).
///
@ -618,8 +601,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
/// If the bytes returned from the ENS registrar/resolver cannot be interpreted as
/// an address. This should theoretically never happen.
async fn resolve_name(&self, ens_name: &str) -> Result<Address, ProviderError> {
self.query_resolver(ParamType::Address, ens_name, ens::ADDR_SELECTOR)
.await
self.query_resolver(ParamType::Address, ens_name, ens::ADDR_SELECTOR).await
}
/// Returns the ENS name the `address` resolves to (or None if not configured).
@ -629,8 +611,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
/// a string. This should theoretically never happen.
async fn lookup_address(&self, address: Address) -> Result<String, ProviderError> {
let ens_name = ens::reverse_address(address);
self.query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR)
.await
self.query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR).await
}
/// Returns the details of all transactions currently pending for inclusion in the next
@ -676,8 +657,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
) -> Result<BlockTrace, ProviderError> {
let data = utils::serialize(&data);
let trace_type = utils::serialize(&trace_type);
self.request("trace_rawTransaction", [data, trace_type])
.await
self.request("trace_rawTransaction", [data, trace_type]).await
}
/// Replays a transaction, returning the traces
@ -688,8 +668,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
) -> Result<BlockTrace, ProviderError> {
let hash = utils::serialize(&hash);
let trace_type = utils::serialize(&trace_type);
self.request("trace_replayTransaction", [hash, trace_type])
.await
self.request("trace_replayTransaction", [hash, trace_type]).await
}
/// Replays all transactions in a block returning the requested traces for each transaction
@ -700,8 +679,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
) -> Result<Vec<BlockTrace>, ProviderError> {
let block = utils::serialize(&block);
let trace_type = utils::serialize(&trace_type);
self.request("trace_replayBlockTransactions", [block, trace_type])
.await
self.request("trace_replayBlockTransactions", [block, trace_type]).await
}
/// Returns traces created at given block
@ -739,8 +717,7 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
&self,
block: T,
) -> Result<Vec<TransactionReceipt>, Self::Error> {
self.request("parity_getBlockReceipts", vec![block.into()])
.await
self.request("parity_getBlockReceipts", vec![block.into()]).await
}
async fn subscribe<T, R>(
@ -808,21 +785,13 @@ impl<P: JsonRpcClient> Middleware for Provider<P> {
// decode the param from client side would fallback to the old API spec.
self.request(
"eth_feeHistory",
[
utils::serialize(&block_count),
last_block.clone(),
reward_percentiles.clone(),
],
[utils::serialize(&block_count), last_block.clone(), reward_percentiles.clone()],
)
.await
.or(self
.request(
"eth_feeHistory",
[
utils::serialize(&block_count.into().as_u64()),
last_block,
reward_percentiles,
],
[utils::serialize(&block_count.into().as_u64()), last_block, reward_percentiles],
)
.await)
}
@ -840,22 +809,16 @@ impl<P: JsonRpcClient> Provider<P> {
// first get the resolver responsible for this name
// the call will return a Bytes array which we convert to an address
let data = self
.call(&ens::get_resolver(ens_addr, ens_name).into(), None)
.await?;
let data = self.call(&ens::get_resolver(ens_addr, ens_name).into(), None).await?;
let resolver_address: Address = decode_bytes(ParamType::Address, data);
if resolver_address == Address::zero() {
return Err(ProviderError::EnsError(ens_name.to_owned()));
return Err(ProviderError::EnsError(ens_name.to_owned()))
}
// resolve
let data = self
.call(
&ens::resolve(resolver_address, selector, ens_name).into(),
None,
)
.await?;
let data =
self.call(&ens::resolve(resolver_address, selector, ens_name).into(), None).await?;
Ok(decode_bytes(param, data))
}
@ -864,10 +827,7 @@ impl<P: JsonRpcClient> Provider<P> {
/// ganache-only function for mining empty blocks
pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> {
for _ in 0..num_blocks {
self.inner
.request::<_, U256>("evm_mine", None::<()>)
.await
.map_err(Into::into)?;
self.inner.request::<_, U256>("evm_mine", None::<()>).await.map_err(Into::into)?;
}
Ok(())
}
@ -989,8 +949,10 @@ impl TryFrom<String> for Provider<HttpProvider> {
mod tests {
use super::*;
use crate::Http;
use ethers_core::types::{TransactionRequest, H256};
use ethers_core::utils::Geth;
use ethers_core::{
types::{TransactionRequest, H256},
utils::Geth,
};
use futures_util::StreamExt;
const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
@ -1000,23 +962,14 @@ mod tests {
async fn mainnet_resolve_name() {
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap();
let addr = provider
.resolve_name("registrar.firefly.eth")
.await
.unwrap();
assert_eq!(
addr,
"6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()
);
let addr = provider.resolve_name("registrar.firefly.eth").await.unwrap();
assert_eq!(addr, "6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap());
// registrar not found
provider.resolve_name("asdfasdffads").await.unwrap_err();
// name not found
provider
.resolve_name("asdfasdf.registrar.firefly.eth")
.await
.unwrap_err();
provider.resolve_name("asdfasdf.registrar.firefly.eth").await.unwrap_err();
}
#[tokio::test]
@ -1052,11 +1005,7 @@ mod tests {
let hashes: Vec<H256> = stream.take(num_blocks).collect::<Vec<H256>>().await;
for (i, hash) in hashes.iter().enumerate() {
let block = provider
.get_block(start_block + i as u64 + 1)
.await
.unwrap()
.unwrap();
let block = provider.get_block(start_block + i as u64 + 1).await.unwrap().unwrap();
assert_eq!(*hash, block.hash.unwrap());
}
}
@ -1096,17 +1045,10 @@ mod tests {
.interval(Duration::from_millis(1000));
let accounts = provider.get_accounts().await.unwrap();
let stream = provider
.watch_pending_transactions()
.await
.unwrap()
.stream();
let stream = provider.watch_pending_transactions().await.unwrap().stream();
let mut tx_hashes = Vec::new();
let tx = TransactionRequest::new()
.from(accounts[0])
.to(accounts[0])
.value(1e18 as u64);
let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64);
for _ in 0..num_txs {
tx_hashes.push(provider.send_transaction(tx.clone(), None).await.unwrap());
@ -1129,11 +1071,7 @@ mod tests {
let tx = TransactionRequest::pay(accounts[0], parse_ether(1u64).unwrap()).from(accounts[0]);
let pending_tx = provider.send_transaction(tx, None).await.unwrap();
assert!(provider
.get_transaction_receipt(*pending_tx)
.await
.unwrap()
.is_none());
assert!(provider.get_transaction_receipt(*pending_tx).await.unwrap().is_none());
let hash = *pending_tx;
let receipt = pending_tx.await.unwrap().unwrap();
@ -1161,11 +1099,7 @@ mod tests {
let provider = Provider::connect(ganache.ws_endpoint()).await.unwrap();
let stream = provider.subscribe_blocks().await.unwrap();
let blocks = stream
.take(3)
.map(|x| x.number.unwrap().as_u64())
.collect::<Vec<_>>()
.await;
let blocks = stream.take(3).map(|x| x.number.unwrap().as_u64()).collect::<Vec<_>>().await;
assert_eq!(blocks, vec![1, 2, 3]);
}
@ -1177,10 +1111,8 @@ mod tests {
)
.unwrap();
let history = provider
.fee_history(10u64, BlockNumber::Latest, &[10.0, 40.0])
.await
.unwrap();
let history =
provider.fee_history(10u64, BlockNumber::Latest, &[10.0, 40.0]).await.unwrap();
dbg!(&history);
}
}

View File

@ -54,12 +54,7 @@ where
pub fn new(id: U256, provider: &'a Provider<P>) -> Result<Self, P::Error> {
// Call the underlying PubsubClient's subscribe
let rx = provider.as_ref().subscribe(id)?;
Ok(Self {
id,
provider,
rx,
ret: PhantomData,
})
Ok(Self { id, provider, rx, ret: PhantomData })
}
/// Unsubscribes from the subscription.
@ -108,7 +103,8 @@ impl<'a, P> SubscriptionStream<'a, P, TxHash>
where
P: PubsubClient,
{
/// Returns a stream that yields the `Transaction`s for the transaction hashes this stream yields.
/// Returns a stream that yields the `Transaction`s for the transaction hashes this stream
/// yields.
///
/// This internally calls `Provider::get_transaction` with every new transaction.
/// No more than n futures will be buffered at any point in time, and less than n may also be

View File

@ -2,14 +2,12 @@ use crate::{JsonRpcClient, Middleware, PinBoxFut, Provider, ProviderError};
use ethers_core::types::{Transaction, TxHash, U256};
use futures_core::stream::Stream;
use futures_core::Future;
use futures_util::stream::FuturesUnordered;
use futures_util::{stream, FutureExt, StreamExt};
use futures_core::{stream::Stream, Future};
use futures_util::{stream, stream::FuturesUnordered, FutureExt, StreamExt};
use pin_project::pin_project;
use serde::{de::DeserializeOwned, Serialize};
use std::collections::VecDeque;
use std::{
collections::VecDeque,
fmt::Debug,
pin::Pin,
task::{Context, Poll},
@ -132,7 +130,8 @@ impl<'a, P> FilterWatcher<'a, P, TxHash>
where
P: JsonRpcClient,
{
/// Returns a stream that yields the `Transaction`s for the transaction hashes this stream yields.
/// Returns a stream that yields the `Transaction`s for the transaction hashes this stream
/// yields.
///
/// This internally calls `Provider::get_transaction` with every new transaction.
/// No more than n futures will be buffered at any point in time, and less than n may also be
@ -194,14 +193,11 @@ impl<'a, P: JsonRpcClient, St> TransactionStream<'a, P, St> {
/// Push a future into the set
fn push_tx(&mut self, tx: TxHash) {
let fut = self
.provider
.get_transaction(tx)
.then(move |res| match res {
Ok(Some(tx)) => futures_util::future::ok(tx),
Ok(None) => futures_util::future::err(GetTransactionError::NotFound(tx)),
Err(err) => futures_util::future::err(GetTransactionError::ProviderError(tx, err)),
});
let fut = self.provider.get_transaction(tx).then(move |res| match res {
Ok(Some(tx)) => futures_util::future::ok(tx),
Ok(None) => futures_util::future::err(GetTransactionError::NotFound(tx)),
Err(err) => futures_util::future::err(GetTransactionError::ProviderError(tx, err)),
});
self.pending.push(Box::pin(fut));
}
}
@ -221,7 +217,7 @@ where
if let Some(tx) = this.buffered.pop_front() {
this.push_tx(tx);
} else {
break;
break
}
}
@ -237,7 +233,7 @@ where
}
Poll::Ready(None) => {
stream_done = true;
break;
break
}
_ => break,
}
@ -245,12 +241,12 @@ where
// poll running futures
if let tx @ Poll::Ready(Some(_)) = this.pending.poll_next_unpin(cx) {
return tx;
return tx
}
if stream_done && this.pending.is_empty() {
// all done
return Poll::Ready(None);
return Poll::Ready(None)
}
Poll::Pending
@ -267,8 +263,7 @@ mod tests {
utils::{Ganache, Geth},
};
use futures_util::{FutureExt, StreamExt};
use std::collections::HashSet;
use std::convert::TryFrom;
use std::{collections::HashSet, convert::TryFrom};
#[tokio::test]
async fn can_stream_pending_transactions() {
@ -281,20 +276,11 @@ mod tests {
let ws_provider = Provider::new(ws);
let accounts = provider.get_accounts().await.unwrap();
let tx = TransactionRequest::new()
.from(accounts[0])
.to(accounts[0])
.value(1e18 as u64);
let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64);
let mut sending = futures_util::future::join_all(
std::iter::repeat(tx.clone()).take(num_txs).map(|tx| async {
provider
.send_transaction(tx, None)
.await
.unwrap()
.await
.unwrap()
.unwrap()
provider.send_transaction(tx, None).await.unwrap().await.unwrap().unwrap()
}),
)
.fuse();
@ -306,12 +292,8 @@ mod tests {
.transactions_unordered(num_txs)
.fuse();
let mut sub_tx_stream = ws_provider
.subscribe_pending_txs()
.await
.unwrap()
.transactions_unordered(2)
.fuse();
let mut sub_tx_stream =
ws_provider.subscribe_pending_txs().await.unwrap().transactions_unordered(2).fuse();
let mut sent: Option<Vec<TransactionReceipt>> = None;
let mut watch_received: Vec<Transaction> = Vec::with_capacity(num_txs);
@ -328,13 +310,11 @@ mod tests {
if watch_received.len() == num_txs && sub_received.len() == num_txs {
if let Some(ref sent) = sent {
assert_eq!(sent.len(), watch_received.len());
let sent_txs = sent
.iter()
.map(|tx| tx.transaction_hash)
.collect::<HashSet<_>>();
let sent_txs =
sent.iter().map(|tx| tx.transaction_hash).collect::<HashSet<_>>();
assert_eq!(sent_txs, watch_received.iter().map(|tx| tx.hash).collect());
assert_eq!(sent_txs, sub_received.iter().map(|tx| tx.hash).collect());
break;
break
}
}
}
@ -349,19 +329,11 @@ mod tests {
let accounts = provider.get_accounts().await.unwrap();
let tx = TransactionRequest::new()
.from(accounts[0])
.to(accounts[0])
.value(1e18 as u64);
let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64);
let txs =
futures_util::future::join_all(std::iter::repeat(tx.clone()).take(3).map(|tx| async {
provider
.send_transaction(tx, None)
.await
.unwrap()
.await
.unwrap()
provider.send_transaction(tx, None).await.unwrap().await.unwrap()
}))
.await;
@ -370,19 +342,13 @@ mod tests {
stream::iter(txs.iter().cloned().map(|tx| tx.unwrap().transaction_hash)),
10,
);
let res = stream
.collect::<Vec<_>>()
.await
.into_iter()
.collect::<Result<Vec<_>, _>>()
.unwrap();
let res =
stream.collect::<Vec<_>>().await.into_iter().collect::<Result<Vec<_>, _>>().unwrap();
assert_eq!(res.len(), txs.len());
assert_eq!(
res.into_iter().map(|tx| tx.hash).collect::<HashSet<_>>(),
txs.into_iter()
.map(|tx| tx.unwrap().transaction_hash)
.collect()
txs.into_iter().map(|tx| tx.unwrap().transaction_hash).collect()
);
}
}

View File

@ -18,11 +18,7 @@ pub struct JsonRpcError {
impl fmt::Display for JsonRpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"(code: {}, message: {}, data: {:?})",
self.code, self.message, self.data
)
write!(f, "(code: {}, message: {}, data: {:?})", self.code, self.message, self.data)
}
}
@ -57,12 +53,7 @@ pub struct Subscription<R> {
impl<'a, T> Request<'a, T> {
/// Creates a new JSON RPC request
pub fn new(id: u64, method: &'a str, params: T) -> Self {
Self {
id,
jsonrpc: "2.0",
method,
params,
}
Self { id, jsonrpc: "2.0", method, params }
}
}

View File

@ -47,10 +47,7 @@ pub enum ClientError {
#[error("Deserialization Error: {err}. Response: {text}")]
/// Serde JSON Error
SerdeJson {
err: serde_json::Error,
text: String,
},
SerdeJson { err: serde_json::Error, text: String },
}
impl From<ClientError> for ProviderError {
@ -76,12 +73,7 @@ impl JsonRpcClient for Provider {
let payload = Request::new(next_id, method, params);
let res = self
.client
.post(self.url.as_ref())
.json(&payload)
.send()
.await?;
let res = self.client.post(self.url.as_ref()).json(&payload).send().await?;
let text = res.text().await?;
let res: Response<R> =
serde_json::from_str(&text).map_err(|err| ClientError::SerdeJson { err, text })?;
@ -103,11 +95,7 @@ impl Provider {
/// let provider = Http::new(url);
/// ```
pub fn new(url: impl Into<Url>) -> Self {
Self {
id: AtomicU64::new(0),
client: Client::new(),
url: url.into(),
}
Self { id: AtomicU64::new(0), client: Client::new(), url: url.into() }
}
}
@ -122,10 +110,6 @@ impl FromStr for Provider {
impl Clone for Provider {
fn clone(&self) -> Self {
Self {
id: AtomicU64::new(0),
client: self.client.clone(),
url: self.url.clone(),
}
Self { id: AtomicU64::new(0), client: self.client.clone(), url: self.url.clone() }
}
}

View File

@ -35,10 +35,7 @@ impl JsonRpcClient for MockProvider {
method: &str,
input: T,
) -> Result<R, MockError> {
self.requests
.lock()
.unwrap()
.push_back((method.to_owned(), serde_json::to_value(input)?));
self.requests.lock().unwrap().push_back((method.to_owned(), serde_json::to_value(input)?));
let mut data = self.responses.lock().unwrap();
let element = data.pop_back().ok_or(MockError::EmptyResponses)?;
let res: R = serde_json::from_value(element)?;
@ -54,17 +51,9 @@ impl MockProvider {
method: &str,
data: T,
) -> Result<(), MockError> {
let (m, inp) = self
.requests
.lock()
.unwrap()
.pop_front()
.ok_or(MockError::EmptyRequests)?;
let (m, inp) = self.requests.lock().unwrap().pop_front().ok_or(MockError::EmptyRequests)?;
assert_eq!(m, method);
assert_eq!(
serde_json::to_value(data).expect("could not serialize data"),
inp
);
assert_eq!(serde_json::to_value(data).expect("could not serialize data"), inp);
Ok(())
}
@ -123,10 +112,7 @@ mod tests {
async fn empty_responses() {
let mock = MockProvider::new();
// tries to get a response without pushing a response
let err = mock
.request::<_, ()>("eth_blockNumber", ())
.await
.unwrap_err();
let err = mock.request::<_, ()>("eth_blockNumber", ()).await.unwrap_err();
match err {
MockError::EmptyResponses => {}
_ => panic!("expected empty responses"),

View File

@ -74,10 +74,7 @@ impl<T> QuorumProvider<T> {
}
pub fn new(quorum: Quorum, providers: impl IntoIterator<Item = WeightedProvider<T>>) -> Self {
Self::builder()
.add_providers(providers)
.quorum(quorum)
.build()
Self::builder().add_providers(providers).quorum(quorum).build()
}
pub fn providers(&self) -> &[WeightedProvider<T>] {
@ -103,10 +100,7 @@ pub struct QuorumProviderBuilder<T> {
impl<T> Default for QuorumProviderBuilder<T> {
fn default() -> Self {
Self {
quorum: Default::default(),
providers: Vec::new(),
}
Self { quorum: Default::default(), providers: Vec::new() }
}
}
@ -133,11 +127,7 @@ impl<T> QuorumProviderBuilder<T> {
pub fn build(self) -> QuorumProvider<T> {
let quorum_weight = self.quorum.weight(&self.providers);
QuorumProvider {
quorum: self.quorum,
quorum_weight,
providers: self.providers,
}
QuorumProvider { quorum: self.quorum, quorum_weight, providers: self.providers }
}
}
@ -147,10 +137,7 @@ impl<T: JsonRpcClientWrapper> QuorumProvider<T> {
/// This is the minimum of all provider's block numbers
async fn get_minimum_block_number(&self) -> Result<U64, ProviderError> {
let mut numbers = join_all(self.providers.iter().map(|provider| async move {
let block = provider
.inner
.request("eth_blockNumber", serde_json::json!(()))
.await?;
let block = provider.inner.request("eth_blockNumber", serde_json::json!(())).await?;
serde_json::from_value::<U64>(block).map_err(ProviderError::from)
}))
.await
@ -167,13 +154,13 @@ impl<T: JsonRpcClientWrapper> QuorumProvider<T> {
/// Normalizes the request payload depending on the call
async fn normalize_request(&self, method: &str, params: &mut Value) {
match method {
"eth_call"
| "eth_createAccessList"
| "eth_getStorageAt"
| "eth_getCode"
| "eth_getProof"
| "trace_call"
| "trace_block" => {
"eth_call" |
"eth_createAccessList" |
"eth_getStorageAt" |
"eth_getCode" |
"eth_getProof" |
"trace_call" |
"trace_block" => {
// calls that include the block number in the params at the last index of json array
if let Some(block) = params.as_array_mut().and_then(|arr| arr.last_mut()) {
if Some("latest") == block.as_str() {
@ -265,12 +252,7 @@ struct QuorumRequest<'a, T> {
impl<'a, T> QuorumRequest<'a, T> {
fn new(inner: &'a QuorumProvider<T>, requests: Vec<PendingRequest<'a>>) -> Self {
Self {
responses: Vec::new(),
errors: Vec::new(),
inner,
requests,
}
Self { responses: Vec::new(), errors: Vec::new(), inner, requests }
}
}
@ -289,13 +271,13 @@ impl<'a, T> Future for QuorumRequest<'a, T> {
*weight += response_weight;
if *weight >= this.inner.quorum_weight {
// reached quorum with multiple responses
return Poll::Ready(Ok(val));
return Poll::Ready(Ok(val))
} else {
this.responses.push((val, response_weight));
}
} else if response_weight >= this.inner.quorum_weight {
// reached quorum with single response
return Poll::Ready(Ok(val));
return Poll::Ready(Ok(val))
} else {
this.responses.push((val, response_weight));
}
@ -310,10 +292,7 @@ impl<'a, T> Future for QuorumRequest<'a, T> {
if this.requests.is_empty() {
// No more requests and no quorum reached
this.responses.sort_by(|a, b| b.1.cmp(&a.1));
let values = std::mem::take(&mut this.responses)
.into_iter()
.map(|r| r.0)
.collect();
let values = std::mem::take(&mut this.responses).into_iter().map(|r| r.0).collect();
let errors = std::mem::take(&mut this.errors);
Poll::Ready(Err(QuorumError::NoQuorumReached { values, errors }))
} else {
@ -345,10 +324,7 @@ impl<T> WeightedProvider<T> {
/// Error thrown when sending an HTTP request
pub enum QuorumError {
#[error("No Quorum reached.")]
NoQuorumReached {
values: Vec<Value>,
errors: Vec<ProviderError>,
},
NoQuorumReached { values: Vec<Value>, errors: Vec<ProviderError> },
}
impl From<QuorumError> for ProviderError {
@ -376,9 +352,7 @@ pub trait PubsubClientWrapper: JsonRpcClientWrapper {
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C: JsonRpcClient> JsonRpcClientWrapper for C {
async fn request(&self, method: &str, params: Value) -> Result<Value, ProviderError> {
Ok(JsonRpcClient::request(self, method, params)
.await
.map_err(C::Error::into)?)
Ok(JsonRpcClient::request(self, method, params).await.map_err(C::Error::into)?)
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@ -402,9 +376,7 @@ where
<C as PubsubClient>::NotificationStream: 'static,
{
fn subscribe(&self, id: U256) -> Result<NotificationStream, ProviderError> {
Ok(Box::new(
PubsubClient::subscribe(self, id).map_err(C::Error::into)?,
))
Ok(Box::new(PubsubClient::subscribe(self, id).map_err(C::Error::into)?))
}
fn unsubscribe(&self, id: U256) -> Result<(), ProviderError> {
@ -444,10 +416,7 @@ where
.enumerate()
.map(|(idx, provider)| {
let params = params.clone();
let fut = provider
.inner
.request(method, params)
.map(move |res| (res, idx));
let fut = provider.inner.request(method, params).map(move |res| (res, idx));
Box::pin(fut) as PendingRequest
})
.collect::<Vec<_>>();
@ -477,12 +446,7 @@ pub struct QuorumStream {
impl QuorumStream {
fn new(quorum_weight: u64, notifications: Vec<WeightedNotificationStream>) -> Self {
Self {
quorum_weight,
responses: Vec::new(),
active: notifications,
benched: Vec::new(),
}
Self { quorum_weight, responses: Vec::new(), active: notifications, benched: Vec::new() }
}
}
@ -506,14 +470,14 @@ impl Stream for QuorumStream {
if *weight >= this.quorum_weight {
// reached quorum with multiple notification
this.benched.push(stream);
return Poll::Ready(Some(val));
return Poll::Ready(Some(val))
} else {
this.responses.push((val, response_weight));
}
} else if response_weight >= this.quorum_weight {
// reached quorum with single notification
this.benched.push(stream);
return Poll::Ready(Some(val));
return Poll::Ready(Some(val))
} else {
this.responses.push((val, response_weight));
}
@ -528,7 +492,7 @@ impl Stream for QuorumStream {
}
if this.active.is_empty() && this.benched.is_empty() {
return Poll::Ready(None);
return Poll::Ready(None)
}
Poll::Pending
}
@ -578,10 +542,7 @@ mod tests {
providers.push(WeightedProvider::new(mock.clone()));
mocked.push(mock);
}
let quorum = QuorumProvider::builder()
.add_providers(providers)
.quorum(q)
.build();
let quorum = QuorumProvider::builder().add_providers(providers).quorum(q).build();
let quorum_weight = quorum.quorum_weight;
let provider = Provider::quorum(quorum);
@ -589,10 +550,8 @@ mod tests {
assert_eq!(blk, value);
// count the number of providers that returned a value
let requested = mocked
.iter()
.filter(|mock| mock.assert_request("eth_blockNumber", ()).is_ok())
.count();
let requested =
mocked.iter().filter(|mock| mock.assert_request("eth_blockNumber", ()).is_ok()).count();
match q {
Quorum::All => {

View File

@ -12,9 +12,8 @@ use futures_util::{
stream::{Fuse, Stream, StreamExt},
};
use serde::{de::DeserializeOwned, Serialize};
use std::collections::btree_map::Entry;
use std::{
collections::BTreeMap,
collections::{btree_map::Entry, BTreeMap},
fmt::{self, Debug},
sync::{
atomic::{AtomicU64, Ordering},
@ -69,11 +68,7 @@ type Subscription = mpsc::UnboundedSender<serde_json::Value>;
/// Instructions for the `WsServer`.
enum Instruction {
/// JSON-RPC request
Request {
id: u64,
request: String,
sender: Pending,
},
Request { id: u64, request: String, sender: Pending },
/// Create a new subscription
Subscribe { id: U256, sink: Subscription },
/// Cancel an existing subscription
@ -105,9 +100,7 @@ pub struct Ws {
impl Debug for Ws {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WebsocketProvider")
.field("id", &self.id)
.finish()
f.debug_struct("WebsocketProvider").field("id", &self.id).finish()
}
}
@ -123,10 +116,7 @@ impl Ws {
// Spawn the server
WsServer::new(ws, stream).spawn();
Self {
id: Arc::new(AtomicU64::new(0)),
instructions: sink,
}
Self { id: Arc::new(AtomicU64::new(0)), instructions: sink }
}
/// Returns true if the WS connection is active, false otherwise
@ -137,9 +127,7 @@ impl Ws {
/// Initializes a new WebSocket Client
#[cfg(target_arch = "wasm32")]
pub async fn connect(url: &str) -> Result<Self, ClientError> {
let (_, wsio) = WsMeta::connect(url, None)
.await
.expect_throw("Could not create websocket");
let (_, wsio) = WsMeta::connect(url, None).await.expect_throw("Could not create websocket");
Ok(Self::new(wsio))
}
@ -154,9 +142,7 @@ impl Ws {
}
fn send(&self, msg: Instruction) -> Result<(), ClientError> {
self.instructions
.unbounded_send(msg)
.map_err(to_client_error)
self.instructions.unbounded_send(msg).map_err(to_client_error)
}
}
@ -199,10 +185,7 @@ impl PubsubClient for Ws {
fn subscribe<T: Into<U256>>(&self, id: T) -> Result<Self::NotificationStream, ClientError> {
let (sink, stream) = mpsc::unbounded();
self.send(Instruction::Subscribe {
id: id.into(),
sink,
})?;
self.send(Instruction::Subscribe { id: id.into(), sink })?;
Ok(stream)
}
@ -252,12 +235,12 @@ where
loop {
if self.is_done() {
debug!("work complete");
break;
break
}
match self.tick().await {
Err(ClientError::UnexpectedClose) => {
error!("{}", ClientError::UnexpectedClose);
break;
break
}
Err(e) => {
panic!("WS Server panic: {}", e);
@ -303,10 +286,7 @@ where
/// Dispatch a unsubscribe request
async fn service_unsubscribe(&mut self, id: U256) -> Result<(), ClientError> {
if self.subscriptions.remove(&id).is_none() {
warn!(
"Unsubscribing from non-existent subscription with id {:?}",
id
);
warn!("Unsubscribing from non-existent subscription with id {:?}", id);
}
Ok(())
}
@ -314,11 +294,9 @@ where
/// Dispatch an outgoing message
async fn service(&mut self, instruction: Instruction) -> Result<(), ClientError> {
match instruction {
Instruction::Request {
id,
request,
sender,
} => self.service_request(id, request, sender).await,
Instruction::Request { id, request, sender } => {
self.service_request(id, request, sender).await
}
Instruction::Subscribe { id, sink } => self.service_subscribe(id, sink).await,
Instruction::Unsubscribe { id } => self.service_unsubscribe(id).await,
}
@ -335,9 +313,7 @@ where
Err(_) => {}
Ok(Incoming::Response(resp)) => {
if let Some(request) = self.pending.remove(&resp.id) {
request
.send(resp.data.into_result())
.map_err(to_client_error)?;
request.send(resp.data.into_result()).map_err(to_client_error)?;
}
}
Ok(Incoming::Notification(notification)) => {
@ -348,7 +324,7 @@ where
// subscription channel was closed on the receiver end
stream.remove();
}
return Err(to_client_error(err));
return Err(to_client_error(err))
}
}
}
@ -477,8 +453,10 @@ impl From<ClientError> for ProviderError {
#[cfg(not(target_arch = "wasm32"))]
mod tests {
use super::*;
use ethers_core::types::{Block, TxHash, U256};
use ethers_core::utils::Ganache;
use ethers_core::{
types::{Block, TxHash, U256},
utils::Ganache,
};
#[tokio::test]
async fn request() {

View File

@ -17,26 +17,10 @@ mod eth_tests {
)
.unwrap();
assert!(provider
.get_transaction(H256::zero())
.await
.unwrap()
.is_none());
assert!(provider
.get_transaction_receipt(H256::zero())
.await
.unwrap()
.is_none());
assert!(provider
.get_block(BlockId::Hash(H256::zero()))
.await
.unwrap()
.is_none());
assert!(provider
.get_block_with_txs(BlockId::Hash(H256::zero()))
.await
.unwrap()
.is_none());
assert!(provider.get_transaction(H256::zero()).await.unwrap().is_none());
assert!(provider.get_transaction_receipt(H256::zero()).await.unwrap().is_none());
assert!(provider.get_block(BlockId::Hash(H256::zero())).await.unwrap().is_none());
assert!(provider.get_block_with_txs(BlockId::Hash(H256::zero())).await.unwrap().is_none());
}
#[tokio::test]
@ -71,9 +55,7 @@ mod eth_tests {
use ethers_providers::{StreamExt, Ws};
let ganache = Ganache::new().block_time(2u64).spawn();
let (ws, _) = tokio_tungstenite::connect_async(ganache.ws_endpoint())
.await
.unwrap();
let (ws, _) = tokio_tungstenite::connect_async(ganache.ws_endpoint()).await.unwrap();
let provider = Provider::new(Ws::new(ws)).interval(Duration::from_millis(500u64));
let stream = provider.watch_blocks().await.unwrap().stream();

View File

@ -14,11 +14,8 @@ async fn txpool() {
let account = provider.get_accounts().await.unwrap()[0];
let value: u64 = 42;
let gas_price = U256::from_dec_str("221435145689").unwrap();
let mut tx = TransactionRequest::new()
.to(account)
.from(account)
.value(value)
.gas_price(gas_price);
let mut tx =
TransactionRequest::new().to(account).from(account).value(value).gas_price(gas_price);
// send a few transactions
let mut txs = Vec::new();

View File

@ -3,8 +3,8 @@
use ethers_core::{
k256::ecdsa::{Error as K256Error, Signature as KSig, VerifyingKey},
types::{
transaction::eip2718::TypedTransaction, transaction::eip712::Eip712, Address,
Signature as EthSig, H256,
transaction::{eip2718::TypedTransaction, eip712::Eip712},
Address, Signature as EthSig, H256,
},
utils::hash_message,
};
@ -116,10 +116,7 @@ where
{
debug!("Dispatching get_public_key");
let req = GetPublicKeyRequest {
grant_tokens: None,
key_id: key_id.as_ref().to_owned(),
};
let req = GetPublicKeyRequest { grant_tokens: None, key_id: key_id.as_ref().to_owned() };
trace!("{:?}", &req);
let resp = kms.get_public_key(req).await;
trace!("{:?}", &resp);
@ -163,9 +160,7 @@ impl<'a> AwsSigner<'a> {
where
T: AsRef<str>,
{
let pubkey = request_get_pubkey(kms, &key_id)
.await
.map(utils::decode_pubkey)??;
let pubkey = request_get_pubkey(kms, &key_id).await.map(utils::decode_pubkey)??;
let address = verifying_key_to_address(&pubkey);
debug!(
@ -174,13 +169,7 @@ impl<'a> AwsSigner<'a> {
hex::encode(&address)
);
Ok(Self {
kms,
chain_id,
key_id: key_id.as_ref().to_owned(),
pubkey,
address,
})
Ok(Self { kms, chain_id, key_id: key_id.as_ref().to_owned(), pubkey, address })
}
/// Fetch the pubkey associated with a key id
@ -188,9 +177,7 @@ impl<'a> AwsSigner<'a> {
where
T: AsRef<str>,
{
Ok(request_get_pubkey(self.kms, key_id)
.await
.map(utils::decode_pubkey)??)
Ok(request_get_pubkey(self.kms, key_id).await.map(utils::decode_pubkey)??)
}
/// Fetch the pubkey associated with this signer's key ID
@ -207,9 +194,7 @@ impl<'a> AwsSigner<'a> {
where
T: AsRef<str>,
{
Ok(request_sign_digest(self.kms, key_id, digest)
.await
.map(utils::decode_signature)??)
Ok(request_sign_digest(self.kms, key_id, digest).await.map(utils::decode_signature)??)
}
/// Sign a digest with this signer's key
@ -258,9 +243,7 @@ impl<'a> super::Signer for AwsSigner<'a> {
&self,
payload: &T,
) -> Result<EthSig, Self::Error> {
let hash = payload
.encode_eip712()
.map_err(|e| Self::Error::Eip712Error(e.to_string()))?;
let hash = payload.encode_eip712().map_err(|e| Self::Error::Eip712Error(e.to_string()))?;
let digest = self.sign_digest_with_eip155(hash.into()).await?;
@ -296,10 +279,7 @@ mod tests {
#[allow(dead_code)]
fn setup_tracing() {
tracing_subscriber::fmt()
.with_max_level(LevelFilter::DEBUG)
.try_init()
.unwrap();
tracing_subscriber::fmt().with_max_level(LevelFilter::DEBUG).try_init().unwrap();
}
#[allow(dead_code)]

View File

@ -47,12 +47,7 @@ impl LedgerEthereum {
let transport = Ledger::init().await?;
let address = Self::get_address_with_path_transport(&transport, &derivation).await?;
Ok(Self {
transport: Mutex::new(transport),
derivation,
chain_id,
address,
})
Ok(Self { transport: Mutex::new(transport), derivation, chain_id, address })
}
/// Consume self and drop the ledger mutex
@ -150,17 +145,13 @@ impl LedgerEthereum {
// Enforce app version is greater than EIP712_MIN_VERSION
if !req.matches(&version) {
return Err(LedgerError::UnsupportedAppVersion(
EIP712_MIN_VERSION.to_string(),
));
return Err(LedgerError::UnsupportedAppVersion(EIP712_MIN_VERSION.to_string()))
}
let domain_separator = payload
.domain_separator()
.map_err(|e| LedgerError::Eip712Error(e.to_string()))?;
let struct_hash = payload
.struct_hash()
.map_err(|e| LedgerError::Eip712Error(e.to_string()))?;
let domain_separator =
payload.domain_separator().map_err(|e| LedgerError::Eip712Error(e.to_string()))?;
let struct_hash =
payload.struct_hash().map_err(|e| LedgerError::Eip712Error(e.to_string()))?;
let mut payload = Self::path_to_bytes(&self.derivation);
payload.extend_from_slice(&domain_separator);
@ -169,7 +160,8 @@ impl LedgerEthereum {
self.sign_payload(INS::SIGN_ETH_EIP_712, payload).await
}
// Helper function for signing either transaction data, personal messages or EIP712 derived structs
// Helper function for signing either transaction data, personal messages or EIP712 derived
// structs
pub async fn sign_payload(
&self,
command: INS,
@ -193,10 +185,7 @@ impl LedgerEthereum {
command.data = APDUData::new(&data);
let answer = block_on(transport.exchange(&command))?;
result = answer
.data()
.ok_or(LedgerError::UnexpectedNullResponse)?
.to_vec();
result = answer.data().ok_or(LedgerError::UnexpectedNullResponse)?.to_vec();
// We need more data
command.p1 = P1::MORE as u8;
@ -262,18 +251,13 @@ mod tests {
// Replace this with your ETH addresses.
async fn test_get_address() {
// Instantiate it with the default ledger derivation path
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1)
.await
.unwrap();
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1).await.unwrap();
assert_eq!(
ledger.get_address().await.unwrap(),
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()
);
assert_eq!(
ledger
.get_address_with_path(&DerivationType::Legacy(0))
.await
.unwrap(),
ledger.get_address_with_path(&DerivationType::Legacy(0)).await.unwrap(),
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse().unwrap()
);
}
@ -281,17 +265,13 @@ mod tests {
#[tokio::test]
#[ignore]
async fn test_sign_tx() {
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1)
.await
.unwrap();
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1).await.unwrap();
// approve uni v2 router 0xff
let data = hex::decode("095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let tx_req = TransactionRequest::new()
.to("2ed7afa17473e17ac59908f088b4371d28585476"
.parse::<Address>()
.unwrap())
.to("2ed7afa17473e17ac59908f088b4371d28585476".parse::<Address>().unwrap())
.gas(1000000)
.gas_price(400e9 as u64)
.nonce(5)
@ -304,9 +284,7 @@ mod tests {
#[tokio::test]
#[ignore]
async fn test_version() {
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1)
.await
.unwrap();
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1).await.unwrap();
let version = ledger.version().await.unwrap();
assert_eq!(version, "1.3.7");
@ -315,9 +293,7 @@ mod tests {
#[tokio::test]
#[ignore]
async fn test_sign_message() {
let ledger = LedgerEthereum::new(DerivationType::Legacy(0), 1)
.await
.unwrap();
let ledger = LedgerEthereum::new(DerivationType::Legacy(0), 1).await.unwrap();
let message = "hello world";
let sig = ledger.sign_message(message).await.unwrap();
let addr = ledger.get_address().await.unwrap();
@ -327,9 +303,7 @@ mod tests {
#[tokio::test]
#[ignore]
async fn test_sign_eip712_struct() {
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1u64)
.await
.unwrap();
let ledger = LedgerEthereum::new(DerivationType::LedgerLive(0), 1u64).await.unwrap();
let foo_bar = FooBar {
foo: I256::from(10),
@ -340,10 +314,7 @@ mod tests {
out: Address::from([0; 20]),
};
let sig = ledger
.sign_typed_struct(&foo_bar)
.await
.expect("failed to sign typed data");
let sig = ledger.sign_typed_struct(&foo_bar).await.expect("failed to sign typed data");
let foo_bar_hash = foo_bar.encode_eip712().unwrap();
sig.verify(foo_bar_hash, ledger.address).unwrap();
}

View File

@ -5,7 +5,8 @@ use crate::Signer;
use app::LedgerEthereum;
use async_trait::async_trait;
use ethers_core::types::{
transaction::eip2718::TypedTransaction, transaction::eip712::Eip712, Address, Signature,
transaction::{eip2718::TypedTransaction, eip712::Eip712},
Address, Signature,
};
use types::LedgerError;

View File

@ -70,7 +70,8 @@ pub use aws::{AwsSigner, AwsSignerError};
use async_trait::async_trait;
use ethers_core::types::{
transaction::eip2718::TypedTransaction, transaction::eip712::Eip712, Address, Signature,
transaction::{eip2718::TypedTransaction, eip712::Eip712},
Address, Signature,
};
use std::error::Error;

View File

@ -180,11 +180,7 @@ impl<W: Wordlist> MnemonicBuilder<W> {
let signer = SigningKey::from_bytes(&key.to_bytes())?;
let address = secret_key_to_address(&signer);
Ok(Wallet::<SigningKey> {
signer,
address,
chain_id: 1,
})
Ok(Wallet::<SigningKey> { signer, address, chain_id: 1 })
}
}
@ -227,26 +223,24 @@ mod tests {
"0xFB78b25f69A8e941036fEE2A5EeAf349D81D4ccc",
),
];
TESTCASES
.iter()
.for_each(|(phrase, index, password, expected_addr)| {
let wallet = match password {
Some(psswd) => MnemonicBuilder::<English>::default()
.phrase(*phrase)
.index(*index)
.unwrap()
.password(psswd)
.build()
.unwrap(),
None => MnemonicBuilder::<English>::default()
.phrase(*phrase)
.index(*index)
.unwrap()
.build()
.unwrap(),
};
assert_eq!(&to_checksum(&wallet.address, None), expected_addr);
})
TESTCASES.iter().for_each(|(phrase, index, password, expected_addr)| {
let wallet = match password {
Some(psswd) => MnemonicBuilder::<English>::default()
.phrase(*phrase)
.index(*index)
.unwrap()
.password(psswd)
.build()
.unwrap(),
None => MnemonicBuilder::<English>::default()
.phrase(*phrase)
.index(*index)
.unwrap()
.build()
.unwrap(),
};
assert_eq!(&to_checksum(&wallet.address, None), expected_addr);
})
}
#[tokio::test]

Some files were not shown because too many files have changed in this diff Show More