Skip to content

Commit

Permalink
feat(cast): implement cast receipt (gakonst#535)
Browse files Browse the repository at this point in the history
* feat(cast): implement `cast receipt`

This commit implements `cast receipt`. If the receipt is not
found, it will poll until it is found. Similar to `seth`,
an optional field can be passed to only print a particular
field on the receipt response. Unlike `seth`, there is a
`--json/-j` flag that will render the receipt as JSON.

Example usage:

```bash
cast receipt --rpc-url https://mainnet.optimism.io \
    0xa9fc1761ecad57693d7000b7bd8aea8ae5c3c34a1fcf966097778068c82c86cc
```

* fix: use PendingTx to poll for a tx's receipt

Co-authored-by: Georgios Konstantopoulos <[email protected]>
  • Loading branch information
tynes and gakonst authored Jan 29, 2022
1 parent f803b70 commit 457fae7
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cast/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
- [x] `namehash`
- [x] `nonce`
- [x] `publish`
- [ ] `receipt`
- [x] `receipt`
- [x] `resolve-name`
- [ ] `run-tx`
- [x] `send` (partial)
Expand Down
60 changes: 60 additions & 0 deletions cast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,66 @@ where
if to_json { serde_json::to_string(&transaction)? } else { to_table(transaction) };
Ok(transaction)
}

/// ```no_run
/// use cast::Cast;
/// use ethers_providers::{Provider, Http};
/// use std::convert::TryFrom;
///
/// # async fn foo() -> eyre::Result<()> {
/// let provider = Provider::<Http>::try_from("http://localhost:8545")?;
/// let cast = Cast::new(provider);
/// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc";
/// let receipt = cast.receipt(tx_hash.to_string(), None, 1, false, false).await?;
/// println!("{}", receipt);
/// # Ok(())
/// # }
/// ```
pub async fn receipt(
&self,
tx_hash: String,
field: Option<String>,
confs: usize,
cast_async: bool,
to_json: bool,
) -> Result<String> {
let tx_hash = H256::from_str(&tx_hash)?;

// try to get the receipt
let receipt = self.provider.get_transaction_receipt(tx_hash).await?;

// if the async flag is provided, immediately exit if no tx is found,
// otherwise try to poll for it
let receipt = if cast_async {
match receipt {
Some(inner) => inner,
None => return Ok("receipt not found".to_string()),
}
} else {
match receipt {
Some(inner) => inner,
None => {
let tx = PendingTransaction::new(tx_hash, self.provider.provider());
match tx.confirmations(confs).await? {
Some(inner) => inner,
None => return Ok("receipt not found when polling pending tx. was the transaction dropped from the mempool?".to_string())
}
}
}
};

let receipt = if let Some(ref field) = field {
serde_json::to_value(&receipt)?
.get(field)
.cloned()
.ok_or_else(|| eyre::eyre!("field {} not found", field))?
} else {
serde_json::to_value(&receipt)?
};

let receipt = if to_json { serde_json::to_string(&receipt)? } else { to_table(receipt) };
Ok(receipt)
}
}

pub struct InterfaceSource {
Expand Down
9 changes: 9 additions & 0 deletions cli/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,15 @@ async fn main() -> eyre::Result<()> {
let value = provider.get_storage_at(address, slot, block).await?;
println!("{:?}", value);
}
Subcommands::Receipt { hash, field, to_json, rpc_url, cast_async, confirmations } => {
let provider = Provider::try_from(rpc_url)?;
println!(
"{}",
Cast::new(provider)
.receipt(hash, field, confirmations, cast_async, to_json)
.await?
);
}
Subcommands::Nonce { block, who, rpc_url } => {
let provider = Provider::try_from(rpc_url)?;
println!("{}", Cast::new(provider).nonce(who, block).await?);
Expand Down
23 changes: 21 additions & 2 deletions cli/src/opts/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ pub enum Subcommands {
ToUint256 { value: Option<String> },
#[clap(name = "--to-unit")]
#[clap(
about = r#"convert an ETH amount into a specified unit: ether, gwei or wei (default: wei).
Usage:
about = r#"convert an ETH amount into a specified unit: ether, gwei or wei (default: wei).
Usage:
- 1ether wei | converts 1 ether to wei
- "1 ether" wei | converts 1 ether to wei
- 1ether | converts 1 ether to wei
Expand Down Expand Up @@ -151,6 +151,25 @@ pub enum Subcommands {
#[clap(long, env = "ETH_RPC_URL")]
rpc_url: String,
},
#[clap(name = "receipt")]
#[clap(about = "Print information about the transaction receipt for <tx-hash>")]
Receipt {
hash: String,
field: Option<String>,
#[clap(
short,
long,
help = "the number of confirmations until the receipt is fetched",
default_value = "1"
)]
confirmations: usize,
#[clap(long, env = "CAST_ASYNC")]
cast_async: bool,
#[clap(long = "json", short = 'j')]
to_json: bool,
#[clap(long, env = "ETH_RPC_URL")]
rpc_url: String,
},
#[clap(name = "send")]
#[clap(about = "Publish a transaction signed by <from> to call <to> with <data>")]
SendTx {
Expand Down

0 comments on commit 457fae7

Please sign in to comment.