ethcontract/transaction/
send.rs

1//! Implementation of a future for sending a transaction with optional
2//! confirmation.
3
4use crate::errors::ExecutionError;
5use crate::transaction::confirm;
6use crate::transaction::{ResolveCondition, Transaction, TransactionBuilder};
7use web3::types::{TransactionReceipt, H256, U64};
8use web3::Transport;
9
10impl<T: Transport> TransactionBuilder<T> {
11    /// Sign (if required) and send the transaction. Returns the transaction
12    /// hash that can be used to retrieve transaction information.
13    pub async fn send(mut self) -> Result<TransactionResult, ExecutionError> {
14        let web3 = self.web3.clone();
15        let resolve = self.resolve.take().unwrap_or_default();
16
17        let tx = self.build().await?;
18        let tx_hash = match tx {
19            Transaction::Request(tx) => web3.eth().send_transaction(tx).await?,
20            Transaction::Raw { bytes, hash } => {
21                let node_hash = web3.eth().send_raw_transaction(bytes).await?;
22                if node_hash != hash {
23                    return Err(ExecutionError::UnexpectedTransactionHash);
24                }
25                hash
26            }
27        };
28
29        let tx_receipt = match resolve {
30            ResolveCondition::Pending => return Ok(TransactionResult::Hash(tx_hash)),
31            ResolveCondition::Confirmed(params) => {
32                confirm::wait_for_confirmation(&web3, tx_hash, params).await
33            }
34        }?;
35
36        match tx_receipt.status {
37            Some(U64([1])) => Ok(TransactionResult::Receipt(tx_receipt)),
38            _ => Err(ExecutionError::Failure(Box::new(tx_receipt))),
39        }
40    }
41}
42
43/// Represents the result of a sent transaction that can either be a transaction
44/// hash, in the case the transaction was not confirmed, or a full transaction
45/// receipt if the `TransactionBuilder` was configured to wait for confirmation
46/// blocks.
47///
48/// Note that the result will always be a `TransactionResult::Hash` if
49/// `Confirm::Skip` was used and `TransactionResult::Receipt` if
50/// `Confirm::Blocks` was used.
51#[derive(Clone, Debug)]
52#[allow(clippy::large_enum_variant)]
53pub enum TransactionResult {
54    /// A transaction hash, this variant happens if and only if confirmation was
55    /// skipped.
56    Hash(H256),
57    /// A transaction receipt, this variant happens if and only if the
58    /// transaction was configured to wait for confirmations.
59    Receipt(TransactionReceipt),
60}
61
62impl TransactionResult {
63    /// Returns true if the `TransactionResult` is a `Hash` variant, i.e. it is
64    /// only a hash and does not contain the transaction receipt.
65    pub fn is_hash(&self) -> bool {
66        matches!(self, TransactionResult::Hash(_))
67    }
68
69    /// Get the transaction hash.
70    pub fn hash(&self) -> H256 {
71        match self {
72            TransactionResult::Hash(hash) => *hash,
73            TransactionResult::Receipt(tx) => tx.transaction_hash,
74        }
75    }
76
77    /// Returns true if the `TransactionResult` is a `Receipt` variant, i.e. the
78    /// transaction was confirmed and the full transaction receipt is available.
79    pub fn is_receipt(&self) -> bool {
80        self.as_receipt().is_some()
81    }
82
83    /// Extract a `TransactionReceipt` from the result. This will return `None`
84    /// if the result is only a hash and the transaction receipt is not
85    /// available.
86    pub fn as_receipt(&self) -> Option<&TransactionReceipt> {
87        match self {
88            TransactionResult::Receipt(ref tx) => Some(tx),
89            _ => None,
90        }
91    }
92}