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

fix(BREAKING): return Option for txs/receipts/blocks #64

Merged
merged 7 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ethers-contract/tests/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ mod eth_tests {
let calldata = contract_call.calldata().unwrap();
let gas_estimate = contract_call.estimate_gas().await.unwrap();
let tx_hash = contract_call.send().await.unwrap();
let tx = client.get_transaction(tx_hash).await.unwrap();
let tx_receipt = client.get_transaction_receipt(tx_hash).await.unwrap();
let tx = client.get_transaction(tx_hash).await.unwrap().unwrap();
let tx_receipt = client
.get_transaction_receipt(tx_hash)
.await
.unwrap()
.unwrap();
assert_eq!(last_sender.clone().call().await.unwrap(), client2.address());
assert_eq!(get_value.clone().call().await.unwrap(), "hi");
assert_eq!(tx.input, calldata);
Expand Down
8 changes: 6 additions & 2 deletions ethers-providers/src/gas_oracle/etherscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ struct EtherscanResponseInner {
#[serde(deserialize_with = "deserialize_number_from_string")]
#[serde(rename = "ProposeGasPrice")]
propose_gas_price: u64,
#[serde(deserialize_with = "deserialize_number_from_string")]
#[serde(rename = "FastGasPrice")]
fast_gas_price: u64,
}

impl Etherscan {
pub fn new(api_key: Option<&'static str>) -> Self {
pub fn new(api_key: Option<&str>) -> Self {
let url = match api_key {
Some(key) => format!("{}&apikey={}", ETHERSCAN_URL_PREFIX, key),
None => ETHERSCAN_URL_PREFIX.to_string(),
Expand All @@ -60,7 +63,7 @@ impl Etherscan {
#[async_trait]
impl GasOracle for Etherscan {
async fn fetch(&self) -> Result<U256, GasOracleError> {
if matches!(self.gas_category, GasCategory::Fast | GasCategory::Fastest) {
if matches!(self.gas_category, GasCategory::Fastest) {
return Err(GasOracleError::GasCategoryNotSupported);
}

Expand All @@ -75,6 +78,7 @@ impl GasOracle for Etherscan {
match self.gas_category {
GasCategory::SafeLow => Ok(U256::from(res.result.safe_gas_price * GWEI_TO_WEI)),
GasCategory::Standard => Ok(U256::from(res.result.propose_gas_price * GWEI_TO_WEI)),
GasCategory::Fast => Ok(U256::from(res.result.fast_gas_price * GWEI_TO_WEI)),
_ => Err(GasOracleError::GasCategoryNotSupported),
}
}
Expand Down
8 changes: 6 additions & 2 deletions ethers-providers/src/pending_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ impl<'a, P: JsonRpcClient> Future for PendingTransaction<'a, P> {
}
PendingTxState::GettingReceipt(fut) => {
if let Ok(receipt) = futures_util::ready!(fut.as_mut().poll(ctx)) {
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
if let Some(receipt) = receipt {
*this.state = PendingTxState::CheckingReceipt(Box::new(receipt))
} else {
*this.state = PendingTxState::PausedGettingReceipt
}
} else {
*this.state = PendingTxState::PausedGettingReceipt
}
Expand Down Expand Up @@ -173,7 +177,7 @@ enum PendingTxState<'a> {
PausedGettingReceipt,

/// Polling the blockchain for the receipt
GettingReceipt(PinBoxFut<'a, TransactionReceipt>),
GettingReceipt(PinBoxFut<'a, Option<TransactionReceipt>>),

/// Waiting for interval to elapse before calling API again
PausedGettingBlockNumber(Box<TransactionReceipt>),
Expand Down
41 changes: 36 additions & 5 deletions ethers-providers/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ impl<P: JsonRpcClient> Provider<P> {
pub async fn get_block(
&self,
block_hash_or_number: impl Into<BlockId>,
) -> Result<Block<TxHash>, ProviderError> {
) -> Result<Option<Block<TxHash>>, ProviderError> {
Ok(self
.get_block_gen(block_hash_or_number.into(), false)
.await?)
Expand All @@ -102,7 +102,7 @@ impl<P: JsonRpcClient> Provider<P> {
pub async fn get_block_with_txs(
&self,
block_hash_or_number: impl Into<BlockId>,
) -> Result<Block<Transaction>, ProviderError> {
) -> Result<Option<Block<Transaction>>, ProviderError> {
Ok(self
.get_block_gen(block_hash_or_number.into(), true)
.await?)
Expand All @@ -112,7 +112,7 @@ impl<P: JsonRpcClient> Provider<P> {
&self,
id: BlockId,
include_txs: bool,
) -> Result<Block<Tx>, ProviderError> {
) -> Result<Option<Block<Tx>>, ProviderError> {
let include_txs = utils::serialize(&include_txs);

Ok(match id {
Expand All @@ -137,7 +137,7 @@ impl<P: JsonRpcClient> Provider<P> {
pub async fn get_transaction<T: Send + Sync + Into<TxHash>>(
&self,
transaction_hash: T,
) -> Result<Transaction, ProviderError> {
) -> Result<Option<Transaction>, ProviderError> {
let hash = transaction_hash.into();
Ok(self
.0
Expand All @@ -150,7 +150,7 @@ impl<P: JsonRpcClient> Provider<P> {
pub async fn get_transaction_receipt<T: Send + Sync + Into<TxHash>>(
&self,
transaction_hash: T,
) -> Result<TransactionReceipt, ProviderError> {
) -> Result<Option<TransactionReceipt>, ProviderError> {
let hash = transaction_hash.into();
Ok(self
.0
Expand Down Expand Up @@ -614,6 +614,7 @@ mod ens_tests {
#[cfg(test)]
mod tests {
use super::*;
use crate::Http;
use ethers_core::types::H256;
use futures_util::StreamExt;

Expand All @@ -636,6 +637,7 @@ mod tests {
let block = provider
.get_block(start_block + i as u64 + 1)
.await
.unwrap()
.unwrap();
assert_eq!(*hash, block.hash.unwrap());
}
Expand Down Expand Up @@ -673,4 +675,33 @@ mod tests {
let hashes: Vec<H256> = stream.take(num_txs).collect::<Vec<H256>>().await;
assert_eq!(tx_hashes, hashes);
}

#[tokio::test]
async fn receipt_on_unmined_tx() {
use ethers_core::{
types::TransactionRequest,
utils::{parse_ether, Ganache},
};
let ganache = Ganache::new().block_time(2u64).spawn();
let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();

let accounts = provider.get_accounts().await.unwrap();
let tx = TransactionRequest::pay(accounts[0], parse_ether(1u64).unwrap()).from(accounts[0]);
let tx_hash = provider.send_transaction(tx).await.unwrap();

assert!(provider
.get_transaction_receipt(tx_hash)
.await
.unwrap()
.is_none());

// couple of seconds pass
std::thread::sleep(std::time::Duration::new(3, 0));

assert!(provider
.get_transaction_receipt(tx_hash)
.await
.unwrap()
.is_some());
}
}
52 changes: 41 additions & 11 deletions ethers-providers/tests/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,39 @@ mod eth_tests {
use super::*;
use ethers::{
providers::JsonRpcClient,
types::TransactionRequest,
types::{BlockId, TransactionRequest, H256},
utils::{parse_ether, Ganache},
};

#[tokio::test]
async fn non_existing_data_works() {
let provider = Provider::<Http>::try_from(
"https://rinkeby.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27",
)
.unwrap();

assert!(provider
.get_transaction(H256::zero())
.await
.unwrap()
.is_none());
assert!(provider
.get_transaction_receipt(H256::zero())
.await
.unwrap()
.is_none());
assert!(provider
.get_block(BlockId::Hash(H256::zero()))
.await
.unwrap()
.is_none());
assert!(provider
.get_block_with_txs(BlockId::Hash(H256::zero()))
.await
.unwrap()
.is_none());
}

// Without TLS this would error with "TLS Support not compiled in"
#[test]
#[cfg(any(feature = "async-std-tls", feature = "tokio-tls"))]
Expand Down Expand Up @@ -83,14 +112,17 @@ mod eth_tests {
let data_1 = eth_gas_station_oracle.fetch().await;
assert!(data_1.is_ok());

let api_key = std::env::var("ETHERSCAN_API_KEY").unwrap();
let api_key = Some(api_key.as_str());

// initialize and fetch gas estimates from Etherscan
// since etherscan does not support `fastest` category, we expect an error
let etherscan_oracle = Etherscan::new(None).category(GasCategory::Fastest);
let etherscan_oracle = Etherscan::new(api_key).category(GasCategory::Fastest);
let data_2 = etherscan_oracle.fetch().await;
assert!(data_2.is_err());

// but fetching the `standard` gas price should work fine
let etherscan_oracle_2 = Etherscan::new(None).category(GasCategory::SafeLow);
let etherscan_oracle_2 = Etherscan::new(api_key).category(GasCategory::SafeLow);

let data_3 = etherscan_oracle_2.fetch().await;
assert!(data_3.is_ok());
Expand All @@ -100,12 +132,10 @@ mod eth_tests {
let data_4 = etherchain_oracle.fetch().await;
assert!(data_4.is_ok());

// TODO: Temporarily disabled SparkPool's GasOracle while the API is still
// evolving.
// // initialize and fetch gas estimates from SparkPool
// let gas_now_oracle = GasNow::new().category(GasCategory::Fastest);
// let data_5 = gas_now_oracle.fetch().await;
// assert!(data_5.is_ok());
// initialize and fetch gas estimates from SparkPool
let gas_now_oracle = GasNow::new().category(GasCategory::Fastest);
let data_5 = gas_now_oracle.fetch().await;
assert!(data_5.is_ok());
}

async fn generic_pending_txs_test<P: JsonRpcClient>(provider: Provider<P>) {
Expand Down Expand Up @@ -140,7 +170,7 @@ mod celo_tests {
let tx_hash = "c8496681d0ade783322980cce00c89419fce4b484635d9e09c79787a0f75d450"
.parse::<H256>()
.unwrap();
let tx = provider.get_transaction(tx_hash).await.unwrap();
let tx = provider.get_transaction(tx_hash).await.unwrap().unwrap();
assert!(tx.gateway_fee_recipient.is_none());
assert_eq!(tx.gateway_fee.unwrap(), 0.into());
assert_eq!(tx.hash, tx_hash);
Expand All @@ -152,7 +182,7 @@ mod celo_tests {
let provider =
Provider::<Http>::try_from("https://alfajores-forno.celo-testnet.org").unwrap();

let block = provider.get_block(447254).await.unwrap();
let block = provider.get_block(447254).await.unwrap().unwrap();
assert_eq!(
block.randomness,
Randomness {
Expand Down
3 changes: 2 additions & 1 deletion ethers-signers/tests/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ mod eth_tests {
.get_transaction(tx_hash)
.await
.unwrap()
.unwrap()
.nonce
.as_u64(),
);
Expand Down Expand Up @@ -148,7 +149,7 @@ mod eth_tests {
let tx = TransactionRequest::new().to(wallet2.address()).value(10000);
let tx_hash = client.send_transaction(tx, None).await.unwrap();

let tx = client.get_transaction(tx_hash).await.unwrap();
let tx = client.get_transaction(tx_hash).await.unwrap().unwrap();
assert_eq!(tx.gas_price, expected_gas_price);
}
}
Expand Down