Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
refactor: move BaseContract to own file
Browse files Browse the repository at this point in the history
  • Loading branch information
gakonst committed Oct 26, 2020
1 parent 496cc3d commit e9ec68f
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 68 deletions.
Empty file added ethers-contract/src/abi.rs
Empty file.
74 changes: 74 additions & 0 deletions ethers-contract/src/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::Contract;

use ethers_core::{
abi::{Abi, FunctionExt},
types::{Address, Selector},
};
use ethers_providers::Middleware;

use std::{collections::HashMap, fmt::Debug, hash::Hash, sync::Arc};

/// A reduced form of `Contract` which just takes the `abi` and produces
/// ABI encoded data for its functions.
#[derive(Debug, Clone)]
pub struct BaseContract {
pub(crate) abi: Abi,

/// A mapping from method signature to a name-index pair for accessing
/// functions in the contract ABI. This is used to avoid allocation when
/// searching for matching functions by signature.
// Adapted from: https://github.com/gnosis/ethcontract-rs/blob/master/src/contract.rs
pub(crate) methods: HashMap<Selector, (String, usize)>,
}

impl From<Abi> for BaseContract {
/// Creates a new `BaseContract` from the abi.
fn from(abi: Abi) -> Self {
let methods = create_mapping(&abi.functions, |function| function.selector());
Self { abi, methods }
}
}

impl BaseContract {
/// Returns a reference to the contract's ABI
pub fn abi(&self) -> &Abi {
&self.abi
}

/// Upgrades a `BaseContract` into a full fledged contract with an address and middleware.
pub fn into_contract<M: Middleware>(
self,
address: Address,
client: impl Into<Arc<M>>,
) -> Contract<M> {
Contract::new(address, self, client)
}
}

impl AsRef<Abi> for BaseContract {
fn as_ref(&self) -> &Abi {
self.abi()
}
}

/// Utility function for creating a mapping between a unique signature and a
/// name-index pair for accessing contract ABI items.
fn create_mapping<T, S, F>(
elements: &HashMap<String, Vec<T>>,
signature: F,
) -> HashMap<S, (String, usize)>
where
S: Hash + Eq,
F: Fn(&T) -> S,
{
let signature = &signature;
elements
.iter()
.flat_map(|(name, sub_elements)| {
sub_elements
.iter()
.enumerate()
.map(move |(index, element)| (signature(element), (name.to_owned(), index)))
})
.collect()
}
69 changes: 5 additions & 64 deletions ethers-contract/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use super::{call::ContractCall, event::Event};
use super::{base::BaseContract, call::ContractCall, event::Event};

use ethers_core::{
abi::{Abi, Detokenize, Error, EventExt, Function, FunctionExt, Tokenize},
abi::{Abi, Detokenize, Error, EventExt, Function, Tokenize},
types::{Address, Filter, NameOrAddress, Selector, TransactionRequest, TxHash},
};
use ethers_providers::{Middleware, PendingTransaction};

use rustc_hex::ToHex;
use std::{collections::HashMap, fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
use std::{fmt::Debug, marker::PhantomData, sync::Arc};

/// A Contract is an abstraction of an executable program on the Ethereum Blockchain.
/// It has code (called byte code) as well as allocated long-term memory
Expand Down Expand Up @@ -167,11 +167,9 @@ 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: Abi, client: impl Into<Arc<M>>) -> Self {
let base_contract = BaseContract::new(abi);

pub fn new(address: Address, abi: impl Into<BaseContract>, client: impl Into<Arc<M>>) -> Self {
Self {
base_contract,
base_contract: abi.into(),
client: client.into(),
address,
}
Expand Down Expand Up @@ -289,60 +287,3 @@ impl<M: Middleware> Contract<M> {
self.client.pending_transaction(tx_hash)
}
}

/// A reduced form of `Contract` which just takes the `abi` and produces ABI encoded data for its functions.
/// TODO(pawan): more docs
#[derive(Debug, Clone)]
pub struct BaseContract {
abi: Abi,

/// A mapping from method signature to a name-index pair for accessing
/// functions in the contract ABI. This is used to avoid allocation when
/// searching for matching functions by signature.
// Adapted from: https://github.com/gnosis/ethcontract-rs/blob/master/src/contract.rs
methods: HashMap<Selector, (String, usize)>,
}

impl BaseContract {
/// Creates a new `BaseContract` from the abi.
pub fn new(abi: Abi) -> Self {
let methods = create_mapping(&abi.functions, |function| function.selector());
Self { abi, methods }
}

/// Returns a reference to the contract's ABI
pub fn abi(&self) -> &Abi {
&self.abi
}

/// Upgrades a `BaseContract` into a full fledged contract with an address and middleware.
pub fn into_contract<M>(self, address: Address, client: impl Into<Arc<M>>) -> Contract<M> {
Contract {
base_contract: self,
address,
client: client.into(),
}
}
}

/// Utility function for creating a mapping between a unique signature and a
/// name-index pair for accessing contract ABI items.
fn create_mapping<T, S, F>(
elements: &HashMap<String, Vec<T>>,
signature: F,
) -> HashMap<S, (String, usize)>
where
S: Hash + Eq,
F: Fn(&T) -> S,
{
let signature = &signature;
elements
.iter()
.flat_map(|(name, sub_elements)| {
sub_elements
.iter()
.enumerate()
.map(move |(index, element)| (signature(element), (name.to_owned(), index)))
})
.collect()
}
5 changes: 4 additions & 1 deletion ethers-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
//! [`abigen`]: ./macro.abigen.html
//! [`Abigen` builder]: crate::Abigen
mod contract;
pub use contract::{BaseContract, Contract};
pub use contract::Contract;

mod base;
pub use base::BaseContract;

mod call;
pub use call::ContractError;
Expand Down
4 changes: 2 additions & 2 deletions ethers-contract/src/multicall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,13 @@ impl<M: Middleware> Multicall<M> {
///
/// ```no_run
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// # use ethers::prelude::*;
/// # use ethers::{abi::Abi, prelude::*};
/// # use std::{sync::Arc, convert::TryFrom};
/// #
/// # let client = Provider::<Http>::try_from("http://localhost:8545")?;
/// # let client = Arc::new(client);
/// #
/// # let abi = serde_json::from_str("")?;
/// # let abi: Abi = serde_json::from_str("")?;
/// # let address = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee".parse::<Address>()?;
/// # let contract = Contract::<Provider<Http>>::new(address, abi, client.clone());
/// #
Expand Down
1 change: 0 additions & 1 deletion ethers-providers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(intra_doc_link_resolution_failure)]
//! # Clients for interacting with Ethereum nodes
//!
//! This crate provides asynchronous [Ethereum JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC)
Expand Down

0 comments on commit e9ec68f

Please sign in to comment.