diff --git a/ethcontract/src/contract/method.rs b/ethcontract/src/contract/method.rs index 8f84b10f..793b985a 100644 --- a/ethcontract/src/contract/method.rs +++ b/ethcontract/src/contract/method.rs @@ -2,11 +2,13 @@ //! intended to be used directly but to be used by a contract `Instance` with //! [Instance::method](ethcontract::contract::Instance::method). +use crate::state_overrides::StateOverrides; use crate::transaction::{Account, GasPrice, TransactionBuilder, TransactionResult}; use crate::{batch::CallBatch, errors::MethodError, tokens::Tokenize}; use ethcontract_common::abi::{Function, Token}; use std::marker::PhantomData; -use web3::types::{AccessList, Address, BlockId, Bytes, CallRequest, U256}; +use web3::helpers::CallFuture; +use web3::types::{AccessList, Address, BlockId, BlockNumber, Bytes, CallRequest, U256}; use web3::Transport; use web3::{api::Web3, BatchTransport}; @@ -152,6 +154,14 @@ impl MethodBuilder { pub async fn call(self) -> Result { self.view().call().await } + + /// Same as [`call`] but allows to also specify state overrides with the call. + pub async fn call_with_state_overrides( + self, + overrides: &StateOverrides, + ) -> Result { + self.view().call_with_state_overrides(overrides).await + } } /// Data used for building a contract method call. The view method builder can't @@ -243,6 +253,20 @@ impl ViewMethodBuilder { convert_response::<_, R>(future, function).await } + /// Same as [`call`] but also allows to specify state overrides for the call. + pub async fn call_with_state_overrides( + self, + overrides: &StateOverrides, + ) -> Result { + let transport = &self.m.web3.transport().clone(); + let (function, call, block) = self.decompose(); + let req = web3::helpers::serialize(&call); + let block = web3::helpers::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); + let overrides = web3::helpers::serialize(overrides); + let future = CallFuture::new(transport.execute("eth_call", vec![req, block, overrides])); + convert_response::<_, R>(future, function).await + } + /// Adds this view method to a batch. Allows execution with other contract calls in one roundtrip /// The returned future only resolve once `batch` is resolved. Panics, if `batch` is dropped before /// executing diff --git a/ethcontract/src/lib.rs b/ethcontract/src/lib.rs index ac25a248..2edd4d7b 100644 --- a/ethcontract/src/lib.rs +++ b/ethcontract/src/lib.rs @@ -99,6 +99,7 @@ pub mod errors; mod int; pub mod log; pub mod secret; +pub mod state_overrides; pub mod tokens; pub mod transaction; pub mod transport; diff --git a/ethcontract/src/state_overrides.rs b/ethcontract/src/state_overrides.rs new file mode 100644 index 00000000..196675ce --- /dev/null +++ b/ethcontract/src/state_overrides.rs @@ -0,0 +1,37 @@ +//! Contains data structures needed to specify +//! state overrides for `eth_call`s. + +use primitive_types::{H160, H256, U256}; +use serde::Serialize; +use std::collections::HashMap; +use web3::types::{Bytes, U64}; + +/// State overrides. +pub type StateOverrides = HashMap; + +/// State override object. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StateOverride { + /// Fake balance to set for the account before executing the call. + #[serde(skip_serializing_if = "Option::is_none")] + pub balance: Option, + + /// Fake nonce to set for the account before executing the call. + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, + + /// Fake EVM bytecode to inject into the account before executing the call. + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option, + + /// Fake key-value mapping to override **all** slots in the account storage + /// before executing the call. + #[serde(skip_serializing_if = "Option::is_none")] + pub state: Option>, + + /// Fake key-value mapping to override **individual** slots in the account + /// storage before executing the call. + #[serde(skip_serializing_if = "Option::is_none")] + pub state_diff: Option>, +}