1use crate::cmd::{
2 access_list::AccessListArgs, artifact::ArtifactArgs, bind::BindArgs, call::CallArgs,
3 constructor_args::ConstructorArgsArgs, create2::Create2Args, creation_code::CreationCodeArgs,
4 estimate::EstimateArgs, find_block::FindBlockArgs, interface::InterfaceArgs, logs::LogsArgs,
5 mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs, send::SendTxArgs, storage::StorageArgs,
6 txpool::TxPoolSubcommands, wallet::WalletSubcommands,
7};
8use alloy_primitives::{Address, B256, U256};
9use alloy_rpc_types::BlockId;
10use clap::{Parser, Subcommand, ValueHint};
11use eyre::Result;
12use foundry_cli::opts::{EtherscanOpts, GlobalArgs, RpcOpts};
13use foundry_common::{
14 ens::NameOrAddress,
15 version::{LONG_VERSION, SHORT_VERSION},
16};
17use std::{path::PathBuf, str::FromStr};
18
19#[derive(Parser)]
21#[command(
22 name = "cast",
23 version = SHORT_VERSION,
24 long_version = LONG_VERSION,
25 after_help = "Find more information in the book: http://book.getfoundry.sh/reference/cast/cast.html",
26 next_display_order = None,
27)]
28pub struct Cast {
29 #[command(flatten)]
31 pub global: GlobalArgs,
32
33 #[command(subcommand)]
34 pub cmd: CastSubcommand,
35}
36
37#[derive(Subcommand)]
38pub enum CastSubcommand {
39 #[command(visible_aliases = &["--max-int", "maxi"])]
41 MaxInt {
42 #[arg(default_value = "int256")]
44 r#type: String,
45 },
46
47 #[command(visible_aliases = &["--min-int", "mini"])]
49 MinInt {
50 #[arg(default_value = "int256")]
52 r#type: String,
53 },
54
55 #[command(visible_aliases = &["--max-uint", "maxu"])]
57 MaxUint {
58 #[arg(default_value = "uint256")]
60 r#type: String,
61 },
62
63 #[command(visible_aliases = &["--address-zero", "az"])]
65 AddressZero,
66
67 #[command(visible_aliases = &["--hash-zero", "hz"])]
69 HashZero,
70
71 #[command(
73 visible_aliases = &[
74 "--from-ascii",
75 "--from-utf8",
76 "from-ascii",
77 "fu",
78 "fa"]
79 )]
80 FromUtf8 {
81 text: Option<String>,
83 },
84
85 #[command(visible_aliases = &["--concat-hex", "ch"])]
87 ConcatHex {
88 data: Vec<String>,
90 },
91
92 #[command(visible_aliases = &["--from-bin", "from-binx", "fb"])]
94 FromBin,
95
96 #[command(visible_aliases = &["--to-hexdata", "thd", "2hd"])]
104 ToHexdata {
105 input: Option<String>,
107 },
108
109 #[command(
111 visible_aliases = &["--to-checksum-address",
112 "--to-checksum",
113 "to-checksum",
114 "ta",
115 "2a"]
116 )]
117 ToCheckSumAddress {
118 address: Option<Address>,
120 },
121
122 #[command(visible_aliases = &["--to-ascii", "tas", "2as"])]
124 ToAscii {
125 hexdata: Option<String>,
127 },
128
129 #[command(visible_aliases = &["--to-utf8", "tu8", "2u8"])]
131 ToUtf8 {
132 hexdata: Option<String>,
134 },
135
136 #[command(visible_aliases = &["--from-fix", "ff"])]
138 FromFixedPoint {
139 decimals: Option<String>,
141
142 #[arg(allow_hyphen_values = true)]
144 value: Option<String>,
145 },
146
147 #[command(visible_aliases = &["--to-bytes32", "tb", "2b"])]
149 ToBytes32 {
150 bytes: Option<String>,
152 },
153
154 #[command(visible_aliases = &["--to-fix", "tf", "2f"])]
156 ToFixedPoint {
157 decimals: Option<String>,
159
160 #[arg(allow_hyphen_values = true)]
162 value: Option<String>,
163 },
164
165 #[command(name = "to-uint256", visible_aliases = &["--to-uint256", "tu", "2u"])]
167 ToUint256 {
168 value: Option<String>,
170 },
171
172 #[command(name = "to-int256", visible_aliases = &["--to-int256", "ti", "2i"])]
174 ToInt256 {
175 value: Option<String>,
177 },
178
179 #[command(name = "shl")]
181 LeftShift {
182 value: String,
184
185 bits: String,
187
188 #[arg(long)]
190 base_in: Option<String>,
191
192 #[arg(long, default_value = "16")]
194 base_out: String,
195 },
196
197 #[command(name = "shr")]
199 RightShift {
200 value: String,
202
203 bits: String,
205
206 #[arg(long)]
208 base_in: Option<String>,
209
210 #[arg(long, default_value = "16")]
212 base_out: String,
213 },
214
215 #[command(visible_aliases = &["--to-unit", "tun", "2un"])]
224 ToUnit {
225 value: Option<String>,
227
228 #[arg(default_value = "wei")]
230 unit: String,
231 },
232
233 #[command(visible_aliases = &["--parse-units", "pun"])]
240 ParseUnits {
241 value: Option<String>,
243
244 #[arg(default_value = "18")]
246 unit: u8,
247 },
248
249 #[command(visible_aliases = &["--format-units", "fun"])]
256 FormatUnits {
257 value: Option<String>,
259
260 #[arg(default_value = "18")]
262 unit: u8,
263 },
264
265 #[command(visible_aliases = &["--to-wei", "tw", "2w"])]
269 ToWei {
270 #[arg(allow_hyphen_values = true)]
272 value: Option<String>,
273
274 #[arg(default_value = "eth")]
276 unit: String,
277 },
278
279 #[command(visible_aliases = &["--from-wei", "fw"])]
283 FromWei {
284 #[arg(allow_hyphen_values = true)]
286 value: Option<String>,
287
288 #[arg(default_value = "eth")]
290 unit: String,
291 },
292
293 #[command(visible_aliases = &["--to-rlp"])]
304 ToRlp {
305 value: Option<String>,
310 },
311
312 #[command(visible_aliases = &["--from-rlp"])]
314 FromRlp {
315 value: Option<String>,
317
318 #[arg(long, alias = "int")]
320 as_int: bool,
321 },
322
323 #[command(visible_aliases = &["--to-hex", "th", "2h"])]
325 ToHex(ToBaseArgs),
326
327 #[command(visible_aliases = &["--to-dec", "td", "2d"])]
329 ToDec(ToBaseArgs),
330
331 #[command(
333 visible_aliases = &["--to-base",
334 "--to-radix",
335 "to-radix",
336 "tr",
337 "2r"]
338 )]
339 ToBase {
340 #[command(flatten)]
341 base: ToBaseArgs,
342
343 #[arg(value_name = "BASE")]
345 base_out: Option<String>,
346 },
347 #[command(visible_aliases = &["ac", "acl"])]
349 AccessList(AccessListArgs),
350 #[command(visible_alias = "l")]
352 Logs(LogsArgs),
353 #[command(visible_alias = "bl")]
355 Block {
356 block: Option<BlockId>,
360
361 #[arg(long, short)]
363 field: Option<String>,
364
365 #[arg(long, env = "CAST_FULL_BLOCK")]
366 full: bool,
367
368 #[command(flatten)]
369 rpc: RpcOpts,
370 },
371
372 #[command(visible_alias = "bn")]
374 BlockNumber {
375 block: Option<BlockId>,
377 #[command(flatten)]
378 rpc: RpcOpts,
379 },
380
381 #[command(visible_alias = "c")]
383 Call(CallArgs),
384
385 #[command(name = "calldata", visible_alias = "cd")]
387 CalldataEncode {
388 sig: String,
390
391 #[arg(allow_hyphen_values = true)]
393 args: Vec<String>,
394 },
395
396 Chain {
398 #[command(flatten)]
399 rpc: RpcOpts,
400 },
401
402 #[command(visible_aliases = &["ci", "cid"])]
404 ChainId {
405 #[command(flatten)]
406 rpc: RpcOpts,
407 },
408
409 #[command(visible_alias = "cl")]
411 Client {
412 #[command(flatten)]
413 rpc: RpcOpts,
414 },
415
416 #[command(visible_alias = "ca")]
418 ComputeAddress {
419 address: Option<Address>,
421
422 #[arg(long)]
424 nonce: Option<u64>,
425
426 #[command(flatten)]
427 rpc: RpcOpts,
428 },
429
430 #[command(visible_alias = "da")]
432 Disassemble {
433 bytecode: Option<String>,
435 },
436
437 #[command(name = "mktx", visible_alias = "m")]
439 MakeTx(MakeTxArgs),
440
441 #[command(visible_aliases = &["na", "nh"])]
443 Namehash { name: Option<String> },
444
445 #[command(visible_alias = "t")]
447 Tx {
448 tx_hash: String,
450
451 field: Option<String>,
454
455 #[arg(long, conflicts_with = "field")]
457 raw: bool,
458
459 #[command(flatten)]
460 rpc: RpcOpts,
461 },
462
463 #[command(visible_alias = "re")]
465 Receipt {
466 tx_hash: String,
468
469 field: Option<String>,
471
472 #[arg(long, default_value = "1")]
474 confirmations: u64,
475
476 #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")]
478 cast_async: bool,
479
480 #[command(flatten)]
481 rpc: RpcOpts,
482 },
483
484 #[command(name = "send", visible_alias = "s")]
486 SendTx(SendTxArgs),
487
488 #[command(name = "publish", visible_alias = "p")]
490 PublishTx {
491 raw_tx: String,
493
494 #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")]
496 cast_async: bool,
497
498 #[command(flatten)]
499 rpc: RpcOpts,
500 },
501
502 #[command(visible_alias = "e")]
504 Estimate(EstimateArgs),
505
506 #[command(visible_aliases = &["calldata-decode", "--calldata-decode", "cdd"])]
511 DecodeCalldata {
512 sig: String,
514
515 calldata: String,
517 },
518
519 #[command(visible_aliases = &["string-decode", "--string-decode", "sd"])]
523 DecodeString {
524 data: String,
526 },
527
528 #[command(visible_aliases = &["event-decode", "--event-decode", "ed"])]
530 DecodeEvent {
531 #[arg(long, visible_alias = "event-sig")]
533 sig: Option<String>,
534 data: String,
536 },
537
538 #[command(visible_aliases = &["error-decode", "--error-decode", "erd"])]
540 DecodeError {
541 #[arg(long, visible_alias = "error-sig")]
543 sig: Option<String>,
544 data: String,
546 },
547
548 #[command(name = "decode-abi", visible_aliases = &["abi-decode", "--abi-decode", "ad"])]
554 DecodeAbi {
555 sig: String,
557
558 calldata: String,
560
561 #[arg(long, short, help_heading = "Decode input data instead of output data")]
563 input: bool,
564 },
565
566 #[command(visible_alias = "ae")]
568 AbiEncode {
569 sig: String,
571
572 #[arg(long)]
574 packed: bool,
575
576 #[arg(allow_hyphen_values = true)]
578 args: Vec<String>,
579 },
580
581 #[command(visible_alias = "in")]
583 Index {
584 key_type: String,
586
587 key: String,
589
590 slot_number: String,
592 },
593
594 #[command(name = "index-erc7201", alias = "index-erc-7201", visible_aliases = &["index7201", "in7201"])]
596 IndexErc7201 {
597 id: Option<String>,
599 #[arg(long, default_value = "erc7201")]
601 formula_id: String,
602 },
603
604 #[command(visible_alias = "impl")]
607 Implementation {
608 #[arg(long, short = 'B')]
612 block: Option<BlockId>,
613
614 #[arg(long)]
618 beacon: bool,
619
620 #[arg(value_parser = NameOrAddress::from_str)]
622 who: NameOrAddress,
623
624 #[command(flatten)]
625 rpc: RpcOpts,
626 },
627
628 #[command(visible_alias = "adm")]
630 Admin {
631 #[arg(long, short = 'B')]
635 block: Option<BlockId>,
636
637 #[arg(value_parser = NameOrAddress::from_str)]
639 who: NameOrAddress,
640
641 #[command(flatten)]
642 rpc: RpcOpts,
643 },
644
645 #[command(name = "4byte", visible_aliases = &["4", "4b"])]
647 FourByte {
648 selector: Option<String>,
650 },
651
652 #[command(name = "4byte-calldata", aliases = &["4byte-decode", "4d", "4bd"], visible_aliases = &["4c", "4bc"])]
654 FourByteCalldata {
655 calldata: Option<String>,
657 },
658
659 #[command(name = "4byte-event", visible_aliases = &["4e", "4be", "topic0-event", "t0e"])]
661 FourByteEvent {
662 #[arg(value_name = "TOPIC_0")]
664 topic: Option<String>,
665 },
666
667 #[command(visible_aliases = &["ups"])]
675 UploadSignature {
676 signatures: Vec<String>,
681 },
682
683 #[command(visible_alias = "pc")]
687 PrettyCalldata {
688 calldata: Option<String>,
690
691 #[arg(long, short)]
693 offline: bool,
694 },
695
696 #[command(visible_alias = "a")]
698 Age {
699 block: Option<BlockId>,
703
704 #[command(flatten)]
705 rpc: RpcOpts,
706 },
707
708 #[command(visible_alias = "b")]
710 Balance {
711 #[arg(long, short = 'B')]
715 block: Option<BlockId>,
716
717 #[arg(value_parser = NameOrAddress::from_str)]
719 who: NameOrAddress,
720
721 #[arg(long, short)]
723 ether: bool,
724
725 #[command(flatten)]
726 rpc: RpcOpts,
727
728 #[arg(long, alias = "erc721")]
731 erc20: Option<Address>,
732 },
733
734 #[command(visible_aliases = &["ba", "fee", "basefee"])]
736 BaseFee {
737 block: Option<BlockId>,
741
742 #[command(flatten)]
743 rpc: RpcOpts,
744 },
745
746 #[command(visible_alias = "co")]
748 Code {
749 #[arg(long, short = 'B')]
753 block: Option<BlockId>,
754
755 #[arg(value_parser = NameOrAddress::from_str)]
757 who: NameOrAddress,
758
759 #[arg(long, short)]
761 disassemble: bool,
762
763 #[command(flatten)]
764 rpc: RpcOpts,
765 },
766
767 #[command(visible_alias = "cs")]
769 Codesize {
770 #[arg(long, short = 'B')]
774 block: Option<BlockId>,
775
776 #[arg(value_parser = NameOrAddress::from_str)]
778 who: NameOrAddress,
779
780 #[command(flatten)]
781 rpc: RpcOpts,
782 },
783
784 #[command(visible_alias = "g")]
786 GasPrice {
787 #[command(flatten)]
788 rpc: RpcOpts,
789 },
790
791 #[command(visible_alias = "se")]
793 SigEvent {
794 event_string: Option<String>,
796 },
797
798 #[command(visible_aliases = &["k", "keccak256"])]
800 Keccak {
801 data: Option<String>,
803 },
804
805 #[command(visible_aliases = &["--hash-message", "hm"])]
807 HashMessage {
808 message: Option<String>,
810 },
811
812 #[command(visible_alias = "rn")]
814 ResolveName {
815 who: Option<String>,
817
818 #[arg(long)]
820 verify: bool,
821
822 #[command(flatten)]
823 rpc: RpcOpts,
824 },
825
826 #[command(visible_alias = "la")]
828 LookupAddress {
829 who: Option<Address>,
831
832 #[arg(long)]
834 verify: bool,
835
836 #[command(flatten)]
837 rpc: RpcOpts,
838 },
839
840 #[command(visible_alias = "st")]
842 Storage(StorageArgs),
843
844 #[command(visible_alias = "pr")]
846 Proof {
847 #[arg(value_parser = NameOrAddress::from_str)]
849 address: NameOrAddress,
850
851 #[arg(value_parser = parse_slot)]
853 slots: Vec<B256>,
854
855 #[arg(long, short = 'B')]
859 block: Option<BlockId>,
860
861 #[command(flatten)]
862 rpc: RpcOpts,
863 },
864
865 #[command(visible_alias = "n")]
867 Nonce {
868 #[arg(long, short = 'B')]
872 block: Option<BlockId>,
873
874 #[arg(value_parser = NameOrAddress::from_str)]
876 who: NameOrAddress,
877
878 #[command(flatten)]
879 rpc: RpcOpts,
880 },
881
882 #[command()]
884 Codehash {
885 #[arg(long, short = 'B')]
889 block: Option<BlockId>,
890
891 #[arg(value_parser = NameOrAddress::from_str)]
893 who: NameOrAddress,
894
895 #[arg(value_parser = parse_slot)]
897 slots: Vec<B256>,
898
899 #[command(flatten)]
900 rpc: RpcOpts,
901 },
902
903 #[command(visible_alias = "sr")]
905 StorageRoot {
906 #[arg(long, short = 'B')]
910 block: Option<BlockId>,
911
912 #[arg(value_parser = NameOrAddress::from_str)]
914 who: NameOrAddress,
915
916 #[arg(value_parser = parse_slot)]
918 slots: Vec<B256>,
919
920 #[command(flatten)]
921 rpc: RpcOpts,
922 },
923
924 #[command(visible_aliases = &["et", "src"])]
926 Source {
927 address: String,
929
930 #[arg(long, short)]
932 flatten: bool,
933
934 #[arg(short, value_hint = ValueHint::DirPath, alias = "path")]
936 directory: Option<PathBuf>,
937
938 #[command(flatten)]
939 etherscan: EtherscanOpts,
940
941 #[arg(long, env = "EXPLORER_API_URL")]
944 explorer_api_url: Option<String>,
945
946 #[arg(long, env = "EXPLORER_URL")]
948 explorer_url: Option<String>,
949 },
950
951 #[command(visible_alias = "w")]
953 Wallet {
954 #[command(subcommand)]
955 command: WalletSubcommands,
956 },
957
958 #[command(visible_alias = "cc")]
960 CreationCode(CreationCodeArgs),
961
962 #[command(visible_alias = "ar")]
964 Artifact(ArtifactArgs),
965
966 #[command(visible_alias = "cra")]
968 ConstructorArgs(ConstructorArgsArgs),
969
970 #[command(visible_alias = "i")]
974 Interface(InterfaceArgs),
975
976 #[command(visible_alias = "bi")]
978 Bind(BindArgs),
979
980 #[command(visible_alias = "si")]
982 Sig {
983 sig: Option<String>,
985
986 optimize: Option<usize>,
988 },
989
990 #[command(visible_alias = "c2")]
992 Create2(Create2Args),
993
994 #[command(visible_alias = "f")]
996 FindBlock(FindBlockArgs),
997
998 #[command(visible_alias = "com")]
1000 Completions {
1001 #[arg(value_enum)]
1002 shell: clap_complete::Shell,
1003 },
1004
1005 #[command(visible_alias = "fig")]
1007 GenerateFigSpec,
1008
1009 #[command(visible_alias = "r")]
1011 Run(RunArgs),
1012
1013 #[command(visible_alias = "rp")]
1015 Rpc(RpcArgs),
1016
1017 #[command(name = "format-bytes32-string", visible_aliases = &["--format-bytes32-string"])]
1019 FormatBytes32String {
1020 string: Option<String>,
1022 },
1023
1024 #[command(name = "parse-bytes32-string", visible_aliases = &["--parse-bytes32-string"])]
1026 ParseBytes32String {
1027 bytes: Option<String>,
1029 },
1030 #[command(name = "parse-bytes32-address", visible_aliases = &["--parse-bytes32-address"])]
1031 #[command(about = "Parses a checksummed address from bytes32 encoding.")]
1032 ParseBytes32Address {
1033 #[arg(value_name = "BYTES")]
1034 bytes: Option<String>,
1035 },
1036
1037 #[command(visible_aliases = &["dt", "decode-tx"])]
1039 DecodeTransaction { tx: Option<String> },
1040
1041 #[command(visible_alias = "sel")]
1043 Selectors {
1044 bytecode: Option<String>,
1046
1047 #[arg(long, short)]
1049 resolve: bool,
1050 },
1051
1052 #[command()]
1054 DecodeEof { eof: Option<String> },
1055
1056 #[command(visible_alias = "tp")]
1058 TxPool {
1059 #[command(subcommand)]
1060 command: TxPoolSubcommands,
1061 },
1062}
1063
1064#[derive(Debug, Parser)]
1066pub struct ToBaseArgs {
1067 #[arg(allow_hyphen_values = true)]
1069 pub value: Option<String>,
1070
1071 #[arg(long, short = 'i')]
1073 pub base_in: Option<String>,
1074}
1075
1076pub fn parse_slot(s: &str) -> Result<B256> {
1077 let slot = U256::from_str(s).map_err(|e| eyre::eyre!("Could not parse slot number: {e}"))?;
1078 Ok(B256::from(slot))
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use super::*;
1084 use crate::SimpleCast;
1085 use alloy_rpc_types::{BlockNumberOrTag, RpcBlockHash};
1086 use clap::CommandFactory;
1087
1088 #[test]
1089 fn verify_cli() {
1090 Cast::command().debug_assert();
1091 }
1092
1093 #[test]
1094 fn parse_proof_slot() {
1095 let args: Cast = Cast::parse_from([
1096 "foundry-cli",
1097 "proof",
1098 "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
1099 "0",
1100 "1",
1101 "0x0000000000000000000000000000000000000000000000000000000000000000",
1102 "0x1",
1103 "0x01",
1104 ]);
1105 match args.cmd {
1106 CastSubcommand::Proof { slots, .. } => {
1107 assert_eq!(
1108 slots,
1109 vec![
1110 B256::ZERO,
1111 U256::from(1).into(),
1112 B256::ZERO,
1113 U256::from(1).into(),
1114 U256::from(1).into()
1115 ]
1116 );
1117 }
1118 _ => unreachable!(),
1119 };
1120 }
1121
1122 #[test]
1123 fn parse_call_data() {
1124 let args: Cast = Cast::parse_from([
1125 "foundry-cli",
1126 "calldata",
1127 "f()",
1128 "5c9d55b78febcc2061715ba4f57ecf8ea2711f2c",
1129 "2",
1130 ]);
1131 match args.cmd {
1132 CastSubcommand::CalldataEncode { args, .. } => {
1133 assert_eq!(
1134 args,
1135 vec!["5c9d55b78febcc2061715ba4f57ecf8ea2711f2c".to_string(), "2".to_string()]
1136 )
1137 }
1138 _ => unreachable!(),
1139 };
1140 }
1141
1142 #[test]
1144 fn parse_signature() {
1145 let args: Cast = Cast::parse_from([
1146 "foundry-cli",
1147 "sig",
1148 "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)",
1149 ]);
1150 match args.cmd {
1151 CastSubcommand::Sig { sig, .. } => {
1152 let sig = sig.unwrap();
1153 assert_eq!(
1154 sig,
1155 "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)".to_string()
1156 );
1157
1158 let selector = SimpleCast::get_selector(&sig, 0).unwrap();
1159 assert_eq!(selector.0, "0x23b872dd".to_string());
1160 }
1161 _ => unreachable!(),
1162 };
1163 }
1164
1165 #[test]
1166 fn parse_block_ids() {
1167 struct TestCase {
1168 input: String,
1169 expect: BlockId,
1170 }
1171
1172 let test_cases = [
1173 TestCase {
1174 input: "0".to_string(),
1175 expect: BlockId::Number(BlockNumberOrTag::Number(0u64)),
1176 },
1177 TestCase {
1178 input: "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a"
1179 .to_string(),
1180 expect: BlockId::Hash(RpcBlockHash::from_hash(
1181 "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a"
1182 .parse()
1183 .unwrap(),
1184 None,
1185 )),
1186 },
1187 TestCase {
1188 input: "latest".to_string(),
1189 expect: BlockId::Number(BlockNumberOrTag::Latest),
1190 },
1191 TestCase {
1192 input: "earliest".to_string(),
1193 expect: BlockId::Number(BlockNumberOrTag::Earliest),
1194 },
1195 TestCase {
1196 input: "pending".to_string(),
1197 expect: BlockId::Number(BlockNumberOrTag::Pending),
1198 },
1199 TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumberOrTag::Safe) },
1200 TestCase {
1201 input: "finalized".to_string(),
1202 expect: BlockId::Number(BlockNumberOrTag::Finalized),
1203 },
1204 ];
1205
1206 for test in test_cases {
1207 let result: BlockId = test.input.parse().unwrap();
1208 assert_eq!(result, test.expect);
1209 }
1210 }
1211}