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:
parent
eede86df41
commit
dcf20022c6
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(¶ms);
|
||||
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) },
|
||||
|
|
|
@ -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]`
|
||||
|
|
|
@ -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,)*) })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(_) => {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(), ¶ms)?
|
||||
.into(),
|
||||
(Some(constructor), _) => {
|
||||
constructor.encode_input(self.bytecode.to_vec(), ¶ms)?.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 {
|
||||
|
|
|
@ -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"))]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]),
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,9 +77,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
hash,
|
||||
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2"
|
||||
.parse()
|
||||
.unwrap()
|
||||
"a1de988600a42c4b4ab089b619297c17d53cffae5d5120d82d8a92d0bb3b78f2".parse().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<_>>())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue