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

refactor: make contract abstract over Borrow #2082

Merged
merged 12 commits into from
Feb 6, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@

### Unreleased

- Make `Contract` objects generic over the borrow trait, to allow non-arc mware
[#2082](https://github.com/gakonst/ethers-rs/pull/2082)
- Return pending transaction from `Multicall::send`
[#2044](https://github.com/gakonst/ethers-rs/pull/2044)
- Add abigen to default features
Expand Down
16 changes: 8 additions & 8 deletions ethers-contract/src/base.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Contract;
use crate::contract::ContractInstance;

pub use ethers_core::abi::AbiError;
use ethers_core::{
Expand All @@ -8,10 +8,10 @@ use ethers_core::{
use ethers_providers::Middleware;

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

/// A reduced form of `Contract` which just takes the `abi` and produces
Expand Down Expand Up @@ -195,12 +195,12 @@ impl BaseContract {
}

/// 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)
pub fn into_contract<B, M>(self, address: Address, client: B) -> ContractInstance<B, M>
where
B: Borrow<M>,
M: Middleware,
{
ContractInstance::new(address, self, client)
}
}

Expand Down
56 changes: 41 additions & 15 deletions ethers-contract/src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@ use ethers_providers::{
};

use std::{
borrow::Cow,
borrow::{Borrow, Cow},
fmt::Debug,
future::{Future, IntoFuture},
marker::PhantomData,
pin::Pin,
sync::Arc,
};

use thiserror::Error as ThisError;
Expand Down Expand Up @@ -73,33 +72,49 @@ pub enum ContractError<M: Middleware> {
ContractNotDeployed,
}

/// `ContractCall` is a [`FunctionCall`] object with an [`std::sync::Arc`] middleware.
/// This type alias exists to preserve backwards compatibility with
/// less-abstract Contracts.
///
/// For full usage docs, see [`FunctionCall`].
pub type ContractCall<M, D> = FunctionCall<std::sync::Arc<M>, M, D>;

#[derive(Debug)]
#[must_use = "contract calls do nothing unless you `send` or `call` them"]
/// Helper for managing a transaction before submitting it to a node
pub struct ContractCall<M, D> {
pub struct FunctionCall<B, M, D> {
/// The raw transaction object
pub tx: TypedTransaction,
/// The ABI of the function being called
pub function: Function,
/// Optional block number to be used when calculating the transaction's gas and nonce
pub block: Option<BlockId>,
pub(crate) client: Arc<M>,
pub(crate) client: B,
pub(crate) datatype: PhantomData<D>,
pub(crate) _m: PhantomData<M>,
}

impl<M, D> Clone for ContractCall<M, D> {
impl<B, M, D> Clone for FunctionCall<B, M, D>
where
B: Clone,
{
fn clone(&self) -> Self {
ContractCall {
FunctionCall {
tx: self.tx.clone(),
function: self.function.clone(),
block: self.block,
client: self.client.clone(),
datatype: self.datatype,
_m: self._m,
}
}
}

impl<M, D: Detokenize> ContractCall<M, D> {
impl<B, M, D> FunctionCall<B, M, D>
where
B: Borrow<M>,
D: Detokenize,
{
/// Sets the `from` field in the transaction to the provided value
pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
self.tx.set_from(from.into());
Expand Down Expand Up @@ -145,8 +160,9 @@ impl<M, D: Detokenize> ContractCall<M, D> {
}
}

impl<M, D> ContractCall<M, D>
impl<B, M, D> FunctionCall<B, M, D>
where
B: Borrow<M>,
M: Middleware,
D: Detokenize,
{
Expand All @@ -157,7 +173,11 @@ 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, self.block).await.map_err(ContractError::MiddlewareError)
self.client
.borrow()
.estimate_gas(&self.tx, self.block)
.await
.map_err(ContractError::MiddlewareError)
}

/// Queries the blockchain via an `eth_call` for the provided transaction.
Expand All @@ -170,8 +190,12 @@ where
///
/// Note: this function _does not_ send a transaction from your account
pub async fn call(&self) -> Result<D, ContractError<M>> {
let bytes =
self.client.call(&self.tx, self.block).await.map_err(ContractError::MiddlewareError)?;
let bytes = self
.client
.borrow()
.call(&self.tx, self.block)
.await
.map_err(ContractError::MiddlewareError)?;

// decode output
let data = decode_function_data(&self.function, &bytes, false)?;
Expand Down Expand Up @@ -202,7 +226,7 @@ where
///
/// Note: this function _does not_ send a transaction from your account
pub fn call_raw_bytes(&self) -> CallBuilder<'_, M::Provider> {
let call = self.client.provider().call_raw(&self.tx);
let call = self.client.borrow().provider().call_raw(&self.tx);
if let Some(block) = self.block {
call.block(block)
} else {
Expand All @@ -213,17 +237,19 @@ where
/// Signs and broadcasts the provided transaction
pub async fn send(&self) -> Result<PendingTransaction<'_, M::Provider>, ContractError<M>> {
self.client
.borrow()
.send_transaction(self.tx.clone(), self.block)
.await
.map_err(ContractError::MiddlewareError)
}
}

/// [`ContractCall`] can be turned into [`Future`] automatically with `.await`.
/// Defaults to calling [`ContractCall::call`].
impl<M, D> IntoFuture for ContractCall<M, D>
/// [`FunctionCall`] can be turned into [`Future`] automatically with `.await`.
/// Defaults to calling [`FunctionCall::call`].
impl<B, M, D> IntoFuture for FunctionCall<B, M, D>
where
Self: 'static,
B: Borrow<M> + Send + Sync,
M: Middleware,
D: Detokenize + Send + Sync,
{
Expand Down
Loading