Skip to content

Commit 457fae7

Browse files
tynesgakonst
andauthored
feat(cast): implement cast receipt (#535)
* 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]>
1 parent f803b70 commit 457fae7

File tree

4 files changed

+91
-3
lines changed

4 files changed

+91
-3
lines changed

cast/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
- [x] `namehash`
5151
- [x] `nonce`
5252
- [x] `publish`
53-
- [ ] `receipt`
53+
- [x] `receipt`
5454
- [x] `resolve-name`
5555
- [ ] `run-tx`
5656
- [x] `send` (partial)

cast/src/lib.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,66 @@ where
522522
if to_json { serde_json::to_string(&transaction)? } else { to_table(transaction) };
523523
Ok(transaction)
524524
}
525+
526+
/// ```no_run
527+
/// use cast::Cast;
528+
/// use ethers_providers::{Provider, Http};
529+
/// use std::convert::TryFrom;
530+
///
531+
/// # async fn foo() -> eyre::Result<()> {
532+
/// let provider = Provider::<Http>::try_from("http://localhost:8545")?;
533+
/// let cast = Cast::new(provider);
534+
/// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc";
535+
/// let receipt = cast.receipt(tx_hash.to_string(), None, 1, false, false).await?;
536+
/// println!("{}", receipt);
537+
/// # Ok(())
538+
/// # }
539+
/// ```
540+
pub async fn receipt(
541+
&self,
542+
tx_hash: String,
543+
field: Option<String>,
544+
confs: usize,
545+
cast_async: bool,
546+
to_json: bool,
547+
) -> Result<String> {
548+
let tx_hash = H256::from_str(&tx_hash)?;
549+
550+
// try to get the receipt
551+
let receipt = self.provider.get_transaction_receipt(tx_hash).await?;
552+
553+
// if the async flag is provided, immediately exit if no tx is found,
554+
// otherwise try to poll for it
555+
let receipt = if cast_async {
556+
match receipt {
557+
Some(inner) => inner,
558+
None => return Ok("receipt not found".to_string()),
559+
}
560+
} else {
561+
match receipt {
562+
Some(inner) => inner,
563+
None => {
564+
let tx = PendingTransaction::new(tx_hash, self.provider.provider());
565+
match tx.confirmations(confs).await? {
566+
Some(inner) => inner,
567+
None => return Ok("receipt not found when polling pending tx. was the transaction dropped from the mempool?".to_string())
568+
}
569+
}
570+
}
571+
};
572+
573+
let receipt = if let Some(ref field) = field {
574+
serde_json::to_value(&receipt)?
575+
.get(field)
576+
.cloned()
577+
.ok_or_else(|| eyre::eyre!("field {} not found", field))?
578+
} else {
579+
serde_json::to_value(&receipt)?
580+
};
581+
582+
let receipt = if to_json { serde_json::to_string(&receipt)? } else { to_table(receipt) };
583+
Ok(receipt)
584+
}
525585
}
526586

527587
pub struct InterfaceSource {

cli/src/cast.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,15 @@ async fn main() -> eyre::Result<()> {
418418
let value = provider.get_storage_at(address, slot, block).await?;
419419
println!("{:?}", value);
420420
}
421+
Subcommands::Receipt { hash, field, to_json, rpc_url, cast_async, confirmations } => {
422+
let provider = Provider::try_from(rpc_url)?;
423+
println!(
424+
"{}",
425+
Cast::new(provider)
426+
.receipt(hash, field, confirmations, cast_async, to_json)
427+
.await?
428+
);
429+
}
421430
Subcommands::Nonce { block, who, rpc_url } => {
422431
let provider = Provider::try_from(rpc_url)?;
423432
println!("{}", Cast::new(provider).nonce(who, block).await?);

cli/src/opts/cast.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ pub enum Subcommands {
5959
ToUint256 { value: Option<String> },
6060
#[clap(name = "--to-unit")]
6161
#[clap(
62-
about = r#"convert an ETH amount into a specified unit: ether, gwei or wei (default: wei).
63-
Usage:
62+
about = r#"convert an ETH amount into a specified unit: ether, gwei or wei (default: wei).
63+
Usage:
6464
- 1ether wei | converts 1 ether to wei
6565
- "1 ether" wei | converts 1 ether to wei
6666
- 1ether | converts 1 ether to wei
@@ -151,6 +151,25 @@ pub enum Subcommands {
151151
#[clap(long, env = "ETH_RPC_URL")]
152152
rpc_url: String,
153153
},
154+
#[clap(name = "receipt")]
155+
#[clap(about = "Print information about the transaction receipt for <tx-hash>")]
156+
Receipt {
157+
hash: String,
158+
field: Option<String>,
159+
#[clap(
160+
short,
161+
long,
162+
help = "the number of confirmations until the receipt is fetched",
163+
default_value = "1"
164+
)]
165+
confirmations: usize,
166+
#[clap(long, env = "CAST_ASYNC")]
167+
cast_async: bool,
168+
#[clap(long = "json", short = 'j')]
169+
to_json: bool,
170+
#[clap(long, env = "ETH_RPC_URL")]
171+
rpc_url: String,
172+
},
154173
#[clap(name = "send")]
155174
#[clap(about = "Publish a transaction signed by <from> to call <to> with <data>")]
156175
SendTx {

0 commit comments

Comments
 (0)