ethers-rs/ethers-contract/src/call.rs

140 lines
4.6 KiB
Rust
Raw Normal View History

2020-12-31 19:08:12 +00:00
use super::base::{decode_function_data, AbiError};
2020-05-31 16:01:34 +00:00
use ethers_core::{
abi::{Detokenize, Function, InvalidOutputType},
types::{Address, BlockId, Bytes, TransactionRequest, U256},
};
use ethers_providers::{Middleware, PendingTransaction, ProviderError};
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
use thiserror::Error as ThisError;
2020-06-01 23:15:33 +00:00
#[derive(ThisError, Debug)]
2020-06-10 18:20:47 +00:00
/// An Error which is thrown when interacting with a smart contract
pub enum ContractError<M: Middleware> {
2020-06-10 18:20:47 +00:00
/// Thrown when the ABI decoding fails
2020-06-01 23:15:33 +00:00
#[error(transparent)]
DecodingError(#[from] ethers_core::abi::Error),
/// Thrown when the internal BaseContract errors
#[error(transparent)]
AbiError(#[from] AbiError),
2020-06-01 23:15:33 +00:00
2020-06-10 18:20:47 +00:00
/// Thrown when detokenizing an argument
2020-06-01 23:15:33 +00:00
#[error(transparent)]
DetokenizationError(#[from] InvalidOutputType),
/// Thrown when a middleware call fails
#[error("{0}")]
MiddlewareError(M::Error),
2020-06-01 23:15:33 +00:00
/// Thrown when a provider call fails
#[error("{0}")]
ProviderError(ProviderError),
2020-06-10 18:20:47 +00:00
/// Thrown during deployment if a constructor argument was passed in the `deploy`
/// call but a constructor was not present in the ABI
2020-06-01 23:15:33 +00:00
#[error("constructor is not defined in the ABI")]
ConstructorError,
2020-06-10 18:20:47 +00:00
/// Thrown if a contract address is not found in the deployment transaction's
/// receipt
2020-06-01 23:15:33 +00:00
#[error("Contract was not deployed")]
ContractNotDeployed,
}
#[derive(Debug, Clone)]
#[must_use = "contract calls do nothing unless you `send` or `call` them"]
2020-06-10 18:20:47 +00:00
/// Helper for managing a transaction before submitting it to a node
pub struct ContractCall<M, D> {
2020-06-10 18:20:47 +00:00
/// The raw transaction object
pub tx: TransactionRequest,
/// 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) datatype: PhantomData<D>,
}
impl<M, D: Detokenize> ContractCall<M, D> {
/// Sets the `from` field in the transaction to the provided value
pub fn from<T: Into<Address>>(mut self, from: T) -> Self {
self.tx.from = Some(from.into());
self
}
/// Sets the `gas` field in the transaction to the provided value
pub fn gas<T: Into<U256>>(mut self, gas: T) -> Self {
self.tx.gas = Some(gas.into());
self
}
/// Sets the `gas_price` field in the transaction to the provided value
pub fn gas_price<T: Into<U256>>(mut self, gas_price: T) -> Self {
self.tx.gas_price = Some(gas_price.into());
self
}
/// Sets the `value` field in the transaction to the provided value
pub fn value<T: Into<U256>>(mut self, value: T) -> Self {
self.tx.value = Some(value.into());
self
}
2020-06-01 23:15:33 +00:00
/// Sets the `block` field for sending the tx to the chain
pub fn block<T: Into<BlockId>>(mut self, block: T) -> Self {
2020-06-01 23:15:33 +00:00
self.block = Some(block.into());
self
}
}
impl<M, D> ContractCall<M, D>
where
M: Middleware,
2020-05-27 15:43:43 +00:00
D: Detokenize,
{
/// Returns the underlying transaction's ABI encoded data
pub fn calldata(&self) -> Option<Bytes> {
self.tx.data.clone()
}
/// Returns the estimated gas cost for the underlying transaction to be executed
pub async fn estimate_gas(&self) -> Result<U256, ContractError<M>> {
self.client
feat: typed txs provider / middleware changes (part 3) (#357) * feat(providers): add eth_feeHistory api * add access list * feat: fill transactions with access list / default sender info * feat: add helpers for operating on the txs enum * feat: send_transaction takes TypedTransaction now * fix(contract): temp wrap all contract txs as Legacy txs * feat(middleware): use TypedTransaction in Transformer * feat(signers): use TypedTransaction in Wallet/Ledger * feat(core): add helpers for setting typed tx fields * feat(signer): use typed transactions * fix(middleware): adjust nonce/gas/escalators for TypedTxs The GPO and the Escalators will throw an error if they are provided an EIP1559 transaction * fix(providers): ensure the correct account's nonce is filled * fix: add .into() to txs until we make the fn call more generic * Revert "fix: add .into() to txs until we make the fn call more generic" This reverts commit 04dc34b26d0e3f418ed3fc69ea35ad538b83dd50. * feat: generalize send_transaction interface * fix: only set the nonce manually in the Signer middleware * fix(transformer): fill the transaction after transformation * chore: fix compilation errors & lints * fix(signer): set the correct account's nonce * feat: make trace_call / call take TypedTransaction * fix: set sender to transaction in signer * chore: ethgasstation broke * chore: cargo fmt / lints * Fix(signer): pass the chain id * fix: final tx encoding fixes 1. Normalize v values for eip1559/2730 2. Make access lists mandatory for 1559 3. do not double-rlp on rlp_signed * fix: set access list only if available * test: check 1559 / 2930 txs * fix: do not prepend a 0 for Legacy txs & test * chore: code review comments * chore: fix aws signer signature
2021-08-09 00:31:11 +00:00
.estimate_gas(&self.tx.clone().into())
.await
.map_err(ContractError::MiddlewareError)
}
/// Queries the blockchain via an `eth_call` for the provided transaction.
///
/// If executed on a non-state mutating smart contract function (i.e. `view`, `pure`)
/// then it will return the raw data from the chain.
///
/// If executed on a mutating smart contract function, it will do a "dry run" of the call
/// and return the return type of the transaction without mutating the state
///
/// Note: this function _does not_ send a transaction from your account
pub async fn call(&self) -> Result<D, ContractError<M>> {
let bytes = self
.client
feat: typed txs provider / middleware changes (part 3) (#357) * feat(providers): add eth_feeHistory api * add access list * feat: fill transactions with access list / default sender info * feat: add helpers for operating on the txs enum * feat: send_transaction takes TypedTransaction now * fix(contract): temp wrap all contract txs as Legacy txs * feat(middleware): use TypedTransaction in Transformer * feat(signers): use TypedTransaction in Wallet/Ledger * feat(core): add helpers for setting typed tx fields * feat(signer): use typed transactions * fix(middleware): adjust nonce/gas/escalators for TypedTxs The GPO and the Escalators will throw an error if they are provided an EIP1559 transaction * fix(providers): ensure the correct account's nonce is filled * fix: add .into() to txs until we make the fn call more generic * Revert "fix: add .into() to txs until we make the fn call more generic" This reverts commit 04dc34b26d0e3f418ed3fc69ea35ad538b83dd50. * feat: generalize send_transaction interface * fix: only set the nonce manually in the Signer middleware * fix(transformer): fill the transaction after transformation * chore: fix compilation errors & lints * fix(signer): set the correct account's nonce * feat: make trace_call / call take TypedTransaction * fix: set sender to transaction in signer * chore: ethgasstation broke * chore: cargo fmt / lints * Fix(signer): pass the chain id * fix: final tx encoding fixes 1. Normalize v values for eip1559/2730 2. Make access lists mandatory for 1559 3. do not double-rlp on rlp_signed * fix: set access list only if available * test: check 1559 / 2930 txs * fix: do not prepend a 0 for Legacy txs & test * chore: code review comments * chore: fix aws signer signature
2021-08-09 00:31:11 +00:00
.call(&self.tx.clone().into(), self.block)
.await
.map_err(ContractError::MiddlewareError)?;
// decode output
2020-12-31 19:08:12 +00:00
let data = decode_function_data(&self.function, &bytes, false)?;
Ok(data)
}
/// Signs and broadcasts the provided transaction
pub async fn send(&self) -> Result<PendingTransaction<'_, M::Provider>, ContractError<M>> {
self.client
.send_transaction(self.tx.clone(), self.block)
.await
.map_err(ContractError::MiddlewareError)
}
}