//! Overrides for the `eth_call` rpc method use crate::{JsonRpcClient, PinBoxFut, Provider, ProviderError}; use ethers_core::{ types::{ transaction::eip2718::TypedTransaction, Address, BlockId, BlockNumber, Bytes, H256, U256, U64, }, utils, }; use pin_project::pin_project; use serde::{ser::SerializeTuple, Deserialize, Serialize}; use std::{ fmt, future::Future, pin::Pin, task::{Context, Poll}, }; pub use spoof::{balance, code, nonce, state, storage}; /// Provides methods for overriding parameters to the `eth_call` rpc method pub trait RawCall<'a> { /// Sets the block number to execute against fn block(self, id: BlockId) -> Self; /// Sets the [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set). /// Note that not all client implementations will support this as a parameter. fn state(self, state: &'a spoof::State) -> Self; /// Maps a closure `f` over the result of `.await`ing this call fn map(self, f: F) -> Map where Self: Sized, { Map::new(self, f) } } /// A builder which implements [`RawCall`] methods for overriding `eth_call` parameters. /// /// `CallBuilder` also implements [`std::future::Future`], so `.await`ing a `CallBuilder` will /// resolve to the result of executing the `eth_call`. #[must_use = "call_raw::CallBuilder does nothing unless you `.await` or poll it"] pub enum CallBuilder<'a, P> { /// The primary builder which exposes [`RawCall`] methods. Build(Caller<'a, P>), /// Used by the [`std::future::Future`] implementation. You are unlikely to encounter this /// variant unless you are constructing your own [`RawCall`] wrapper type. Wait(PinBoxFut<'a, Bytes>), } impl fmt::Debug for CallBuilder<'_, P> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Build(call) => f.debug_tuple("Build").field(call).finish(), Self::Wait(_) => f.debug_tuple("Wait").field(&"< Future >").finish(), } } } impl<'a, P> CallBuilder<'a, P> { pub fn new(provider: &'a Provider

, tx: &'a TypedTransaction) -> Self { Self::Build(Caller::new(provider, tx)) } /// Applies a closure `f` to a `CallBuilder::Build`. Does nothing for `CallBuilder::Wait`. pub fn map_input(self, f: F) -> Self where F: FnOnce(&mut Caller<'a, P>), { match self { Self::Build(mut call) => { f(&mut call); Self::Build(call) } wait => wait, } } /// Returns the inner `Caller` from a `CallBuilder::Build`. Panics if the `CallBuilder` future /// has already been polled. pub fn unwrap(self) -> Caller<'a, P> { match self { Self::Build(b) => b, _ => panic!("CallBuilder::unwrap on a Wait value"), } } } impl<'a, P> RawCall<'a> for CallBuilder<'a, P> { /// Sets the block number to execute against fn block(self, id: BlockId) -> Self { self.map_input(|mut call| call.input.block = Some(id)) } /// Sets the [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set). /// Note that not all client implementations will support this as a parameter. fn state(self, state: &'a spoof::State) -> Self { self.map_input(|mut call| call.input.state = Some(state)) } } impl<'a, P: JsonRpcClient> Future for CallBuilder<'a, P> { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let pin = self.get_mut(); loop { match pin { CallBuilder::Build(ref call) => { let fut = Box::pin(call.execute()); *pin = CallBuilder::Wait(fut); } CallBuilder::Wait(ref mut fut) => return fut.as_mut().poll(cx), } } } } /// Holds the inputs to the `eth_call` rpc method along with the rpc provider. /// This type is constructed by [`CallBuilder::new`]. #[derive(Clone, Debug)] pub struct Caller<'a, P> { provider: &'a Provider

, input: CallInput<'a>, } impl<'a, P> Caller<'a, P> { pub fn new(provider: &'a Provider

, tx: &'a TypedTransaction) -> Self { Self { provider, input: CallInput::new(tx) } } } impl<'a, P: JsonRpcClient> Caller<'a, P> { /// Executes an `eth_call` rpc request with the overriden parameters. Returns a future that /// resolves to the result of the request. fn execute(&self) -> impl Future> + 'a { self.provider.request("eth_call", utils::serialize(&self.input)) } } /// The input parameters to the `eth_call` rpc method #[derive(Clone, Debug, PartialEq, Eq)] struct CallInput<'a> { tx: &'a TypedTransaction, block: Option, state: Option<&'a spoof::State>, } impl<'a> CallInput<'a> { fn new(tx: &'a TypedTransaction) -> Self { Self { tx, block: None, state: None } } } impl<'a> Serialize for CallInput<'a> { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::ser::Serializer, { let len = 2 + self.state.is_some() as usize; let mut tup = serializer.serialize_tuple(len)?; tup.serialize_element(self.tx)?; let block = self.block.unwrap_or_else(|| BlockNumber::Latest.into()); tup.serialize_element(&block)?; if let Some(state) = self.state { tup.serialize_element(state)?; } tup.end() } } /// An implementer of [`RawCall`] that maps a function `f` over the output of the inner future. /// /// This struct is created by the [`map`] method on [`RawCall`]. /// /// [`map`]: RawCall::map #[must_use = "call_raw::Map does nothing unless you `.await` or poll it"] #[derive(Clone)] #[pin_project] pub struct Map { #[pin] inner: T, f: F, } impl fmt::Debug for Map { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Map").field("inner", &self.inner).finish() } } impl Map { pub fn new(inner: T, f: F) -> Self { Self { inner, f } } } impl<'a, T, F> RawCall<'a> for Map where T: RawCall<'a>, { /// Sets the block number to execute against fn block(self, id: BlockId) -> Self { Self { inner: self.inner.block(id), f: self.f } } /// Sets the [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set). /// Note that not all client implementations will support this as a parameter. fn state(self, state: &'a spoof::State) -> Self { Self { inner: self.inner.state(state), f: self.f } } } impl Future for Map where T: Future, F: FnMut(T::Output) -> Y, { type Output = Y; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let pin = self.project(); let x = futures_util::ready!(pin.inner.poll(cx)); Poll::Ready((pin.f)(x)) } } /// Provides types and methods for constructing an `eth_call` /// [state override set](https://geth.ethereum.org/docs/rpc/ns-eth#3-object---state-override-set) pub mod spoof { use super::*; use std::collections::HashMap; /// The state elements to override for a particular account. #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct Account { #[serde(skip_serializing_if = "Option::is_none")] pub nonce: Option, #[serde(skip_serializing_if = "Option::is_none")] pub balance: Option, #[serde(skip_serializing_if = "Option::is_none")] pub code: Option, #[serde(flatten, skip_serializing_if = "Option::is_none")] pub storage: Option, } impl Account { /// Override the account nonce pub fn nonce(&mut self, nonce: U64) -> &mut Self { self.nonce = Some(nonce); self } /// Override the account balance pub fn balance(&mut self, bal: U256) -> &mut Self { self.balance = Some(bal); self } /// Override the code at the account pub fn code(&mut self, code: Bytes) -> &mut Self { self.code = Some(code); self } /// Override the value of the account storage at the given storage `key` pub fn store(&mut self, key: H256, val: H256) -> &mut Self { self.storage.get_or_insert_with(Default::default).insert(key, val); self } } /// Wraps a map from storage slot to the overriden value. /// /// Storage overrides can either replace the existing state of an account or they can be treated /// as a diff on the existing state. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Storage { #[serde(rename = "stateDiff")] Diff(HashMap), #[serde(rename = "state")] Replace(HashMap), } /// The default storage override is a diff on the existing state of the account. impl Default for Storage { fn default() -> Self { Self::Diff(Default::default()) } } impl std::ops::Deref for Storage { type Target = HashMap; fn deref(&self) -> &Self::Target { match self { Self::Diff(map) => map, Self::Replace(map) => map, } } } impl std::ops::DerefMut for Storage { fn deref_mut(&mut self) -> &mut Self::Target { match self { Self::Diff(map) => map, Self::Replace(map) => map, } } } /// A wrapper type that holds a complete state override set. #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct State(#[serde(skip_serializing_if = "HashMap::is_empty")] HashMap); impl State { /// Returns a mutable reference to the [`Account`] in the map. pub fn account(&mut self, adr: Address) -> &mut Account { self.0.entry(adr).or_default() } } /// Returns an empty state override set. /// /// # Example /// ```no_run /// # use ethers_core::{ /// # types::{Address, TransactionRequest, H256}, /// # utils::{parse_ether, Geth}, /// # }; /// # use ethers_providers::{Provider, Http, Middleware, call_raw::{spoof, RawCall}}; /// # use std::convert::TryFrom; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), Box> { /// let geth = Geth::new().spawn(); /// let provider = Provider::::try_from(geth.endpoint()).unwrap(); /// /// let adr1: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap(); /// let adr2: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse().unwrap(); /// let key = H256::from_low_u64_be(1); /// let val = H256::from_low_u64_be(17); /// /// let tx = TransactionRequest::default().to(adr2).from(adr1).into(); /// /// // override the storage at `adr2` /// let mut state = spoof::state(); /// state.account(adr2).store(key, val); /// /// // override the nonce at `adr1` /// state.account(adr1).nonce(2.into()); /// /// provider.call_raw(&tx).state(&state).await.unwrap(); /// # Ok(()) /// # } /// ``` pub fn state() -> State { Default::default() } /// Returns a state override set with a single element setting the balance of the address. /// /// # Example /// ```no_run /// # use ethers_core::{ /// # types::{Address, TransactionRequest, H256}, /// # utils::{parse_ether, Geth}, /// # }; /// # use ethers_providers::{Provider, Http, Middleware, call_raw::{RawCall, spoof}}; /// # use std::convert::TryFrom; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), Box> { /// let geth = Geth::new().spawn(); /// let provider = Provider::::try_from(geth.endpoint()).unwrap(); /// /// let adr1: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?; /// let adr2: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse()?; /// let pay_amt = parse_ether(1u64)?; /// /// // Not enough ether to pay for the transaction /// let tx = TransactionRequest::pay(adr2, pay_amt).from(adr1).into(); /// /// // override the sender's balance for the call /// let state = spoof::balance(adr1, pay_amt * 2); /// provider.call_raw(&tx).state(&state).await?; /// # Ok(()) /// # } /// ``` pub fn balance(adr: Address, bal: U256) -> State { let mut state = State::default(); state.account(adr).balance(bal); state } /// Returns a state override set with a single element setting the nonce of the address. /// /// # Example /// ```no_run /// # use ethers_core::{ /// # types::{Address, TransactionRequest, H256}, /// # utils::{parse_ether, Geth}, /// # }; /// # use ethers_providers::{Provider, Http, Middleware, call_raw::{RawCall, spoof}}; /// # use std::convert::TryFrom; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), Box> { /// let geth = Geth::new().spawn(); /// let provider = Provider::::try_from(geth.endpoint()).unwrap(); /// /// let adr: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?; /// let pay_amt = parse_ether(1u64)?; /// /// let tx = TransactionRequest::default().from(adr).into(); /// /// // override the sender's nonce for the call /// let state = spoof::nonce(adr, 72.into()); /// provider.call_raw(&tx).state(&state).await?; /// # Ok(()) /// # } /// ``` pub fn nonce(adr: Address, nonce: U64) -> State { let mut state = State::default(); state.account(adr).nonce(nonce); state } /// Returns a state override set with a single element setting the code at the address. /// /// # Example /// ```no_run /// # use ethers_core::{ /// # types::{Address, TransactionRequest, H256}, /// # utils::{parse_ether, Geth}, /// # }; /// # use ethers_providers::{Provider, Http, Middleware, call_raw::{RawCall, spoof}}; /// # use std::convert::TryFrom; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), Box> { /// let geth = Geth::new().spawn(); /// let provider = Provider::::try_from(geth.endpoint()).unwrap(); /// /// let adr: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?; /// let pay_amt = parse_ether(1u64)?; /// /// let tx = TransactionRequest::default().to(adr).into(); /// /// // override the code at the target address /// let state = spoof::code(adr, "0x00".parse()?); /// provider.call_raw(&tx).state(&state).await?; /// # Ok(()) /// # } /// ``` pub fn code(adr: Address, code: Bytes) -> State { let mut state = State::default(); state.account(adr).code(code); state } /// Returns a state override set with a single element setting the storage at the given address /// and key. /// /// # Example /// ```no_run /// # use ethers_core::{ /// # types::{Address, TransactionRequest, H256}, /// # utils::{parse_ether, Geth}, /// # }; /// # use ethers_providers::{Provider, Http, Middleware, call_raw::{RawCall, spoof}}; /// # use std::convert::TryFrom; /// # /// # #[tokio::main(flavor = "current_thread")] /// # async fn main() -> Result<(), Box> { /// let geth = Geth::new().spawn(); /// let provider = Provider::::try_from(geth.endpoint()).unwrap(); /// /// let adr: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?; /// let key = H256::from_low_u64_be(1); /// let val = H256::from_low_u64_be(17); /// /// let tx = TransactionRequest::default().to(adr).into(); /// /// // override the storage slot `key` at `adr` /// let state = spoof::storage(adr, key, val); /// provider.call_raw(&tx).state(&state).await?; /// # Ok(()) /// # } /// ``` pub fn storage(adr: Address, key: H256, val: H256) -> State { let mut state = State::default(); state.account(adr).store(key, val); state } } #[cfg(test)] mod tests { use super::*; use crate::{Http, Middleware, Provider}; use ethers_core::{ types::TransactionRequest, utils::{get_contract_address, keccak256, parse_ether, Anvil, Geth}, }; use std::convert::TryFrom; // Deserializes eth_call parameters as owned data for testing serialization #[derive(Debug, Deserialize)] struct CallInputOwned( TypedTransaction, Option, #[serde(default)] Option, ); impl<'a> From<&'a CallInputOwned> for CallInput<'a> { fn from(src: &'a CallInputOwned) -> Self { Self { tx: &src.0, block: src.1, state: src.2.as_ref() } } } // Tests "roundtrip" serialization of calls: deserialize(serialize(call)) == call fn test_encode<'a, P>(call: CallBuilder<'a, P>) { let input = call.unwrap().input; let ser = utils::serialize(&input).to_string(); let de: CallInputOwned = serde_json::from_str(&ser).unwrap(); let de = CallInput::from(&de); assert_eq!(input.tx, de.tx); assert_eq!(input.state, de.state); let block = input.block.or_else(|| Some(BlockNumber::Latest.into())); assert_eq!(block, de.block); } #[test] fn test_serialize() { let adr1: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap(); let adr2: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse().unwrap(); let k1 = utils::keccak256("foo").into(); let v1 = H256::from_low_u64_be(534); let k2 = utils::keccak256("bar").into(); let v2 = H256::from_low_u64_be(8675309); let tx = TypedTransaction::default(); let (provider, _) = Provider::mocked(); let call = provider.call_raw(&tx); test_encode(call); let mut state = spoof::state(); state.account(adr1).nonce(1.into()).balance(2.into()).store(k1, v1).store(k2, v2); let call = provider.call_raw(&tx).block(100.into()).state(&state); test_encode(call); let mut state = spoof::state(); state.account(adr1).nonce(1.into()); state.account(adr2).nonce(7.into()); let call = provider.call_raw(&tx).state(&state); test_encode(call); // State override with an empty acccount should be encoded as "0xab..": {} let mut state = spoof::state(); state.account(adr1); let call = provider.call_raw(&tx).state(&state); test_encode(call); } #[tokio::test] async fn test_state_overrides() { let geth = Geth::new().spawn(); let provider = Provider::::try_from(geth.endpoint()).unwrap(); let adr1: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap(); let adr2: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse().unwrap(); let pay_amt = parse_ether(1u64).unwrap(); // Not enough ether to pay for the transaction let tx = TransactionRequest::pay(adr2, pay_amt).from(adr1).into(); // assert that overriding the sender's balance works let state = spoof::balance(adr1, pay_amt * 2); provider.call_raw(&tx).state(&state).await.expect("eth_call success"); // bytecode that returns the result of the SELFBALANCE opcode const RETURN_BALANCE: &str = "0x4760005260206000f3"; let bytecode = RETURN_BALANCE.parse().unwrap(); let balance = 100.into(); let tx = TransactionRequest::default().to(adr2).into(); let mut state = spoof::state(); state.account(adr2).code(bytecode).balance(balance); // assert that overriding the code and balance at adr2 works let bytes = provider.call_raw(&tx).state(&state).await.unwrap(); assert_eq!(U256::from_big_endian(bytes.as_ref()), balance); // bytecode that deploys a contract and returns the deployed address const DEPLOY_CONTRACT: &str = "0x6000600052602060006000f060005260206000f3"; let bytecode = DEPLOY_CONTRACT.parse().unwrap(); let nonce = 17.into(); let mut state = spoof::state(); state.account(adr2).code(bytecode).nonce(nonce); // assert that overriding nonce works (contract is deployed to expected address) let bytes = provider.call_raw(&tx).state(&state).await.unwrap(); let deployed = Address::from_slice(&bytes.as_ref()[12..]); assert_eq!(deployed, get_contract_address(adr2, nonce.as_u64())); // bytecode that returns the value of storage slot 1 const RETURN_STORAGE: &str = "0x60015460005260206000f3"; let bytecode = RETURN_STORAGE.parse().unwrap(); let slot = H256::from_low_u64_be(1); let val = keccak256("foo").into(); let mut state = spoof::state(); state.account(adr2).code(bytecode).store(slot, val); // assert that overriding storage works let bytes = provider.call_raw(&tx).state(&state).await.unwrap(); assert_eq!(H256::from_slice(bytes.as_ref()), val); } }