1use crate::cmd::{
2 access_list::AccessListArgs, artifact::ArtifactArgs, bind::BindArgs, call::CallArgs,
3 constructor_args::ConstructorArgsArgs, create2::Create2Args, creation_code::CreationCodeArgs,
4 da_estimate::DAEstimateArgs, estimate::EstimateArgs, find_block::FindBlockArgs,
5 interface::InterfaceArgs, logs::LogsArgs, mktx::MakeTxArgs, rpc::RpcArgs, run::RunArgs,
6 send::SendTxArgs, storage::StorageArgs, txpool::TxPoolSubcommands, wallet::WalletSubcommands,
7};
8use alloy_ens::NameOrAddress;
9use alloy_primitives::{Address, B256, Selector, U256};
10use alloy_rpc_types::BlockId;
11use clap::{Parser, Subcommand, ValueHint};
12use eyre::Result;
13use foundry_cli::opts::{EtherscanOpts, GlobalArgs, RpcOpts};
14use foundry_common::version::{LONG_VERSION, SHORT_VERSION};
15use std::{path::PathBuf, str::FromStr};
16
17#[derive(Parser)]
19#[command(
20 name = "cast",
21 version = SHORT_VERSION,
22 long_version = LONG_VERSION,
23 after_help = "Find more information in the book: https://getfoundry.sh/cast/overview",
24 next_display_order = None,
25)]
26pub struct Cast {
27 #[command(flatten)]
29 pub global: GlobalArgs,
30
31 #[command(subcommand)]
32 pub cmd: CastSubcommand,
33}
34
35#[derive(Subcommand)]
36pub enum CastSubcommand {
37 #[command(visible_aliases = &["--max-int", "maxi"])]
39 MaxInt {
40 #[arg(default_value = "int256")]
42 r#type: String,
43 },
44
45 #[command(visible_aliases = &["--min-int", "mini"])]
47 MinInt {
48 #[arg(default_value = "int256")]
50 r#type: String,
51 },
52
53 #[command(visible_aliases = &["--max-uint", "maxu"])]
55 MaxUint {
56 #[arg(default_value = "uint256")]
58 r#type: String,
59 },
60
61 #[command(visible_aliases = &["--address-zero", "az"])]
63 AddressZero,
64
65 #[command(visible_aliases = &["--hash-zero", "hz"])]
67 HashZero,
68
69 #[command(
71 visible_aliases = &[
72 "--from-ascii",
73 "--from-utf8",
74 "from-ascii",
75 "fu",
76 "fa"]
77 )]
78 FromUtf8 {
79 text: Option<String>,
81 },
82
83 #[command(visible_aliases = &["--concat-hex", "ch"])]
85 ConcatHex {
86 data: Vec<String>,
88 },
89
90 #[command(visible_aliases = &["--from-bin", "from-binx", "fb"])]
92 FromBin,
93
94 #[command(visible_aliases = &["--to-hexdata", "thd", "2hd"])]
102 ToHexdata {
103 input: Option<String>,
105 },
106
107 #[command(
109 visible_aliases = &["--to-checksum-address",
110 "--to-checksum",
111 "to-checksum",
112 "ta",
113 "2a"]
114 )]
115 ToCheckSumAddress {
116 address: Option<Address>,
118 },
119
120 #[command(visible_aliases = &["--to-ascii", "tas", "2as"])]
122 ToAscii {
123 hexdata: Option<String>,
125 },
126
127 #[command(visible_aliases = &["--to-utf8", "tu8", "2u8"])]
129 ToUtf8 {
130 hexdata: Option<String>,
132 },
133
134 #[command(visible_aliases = &["--from-fix", "ff"])]
136 FromFixedPoint {
137 decimals: Option<String>,
139
140 #[arg(allow_hyphen_values = true)]
142 value: Option<String>,
143 },
144
145 #[command(visible_aliases = &["--to-bytes32", "tb", "2b"])]
147 ToBytes32 {
148 bytes: Option<String>,
150 },
151
152 #[command(visible_aliases = &["--to-fix", "tf", "2f"])]
154 ToFixedPoint {
155 decimals: Option<String>,
157
158 #[arg(allow_hyphen_values = true)]
160 value: Option<String>,
161 },
162
163 #[command(name = "to-uint256", visible_aliases = &["--to-uint256", "tu", "2u"])]
165 ToUint256 {
166 value: Option<String>,
168 },
169
170 #[command(name = "to-int256", visible_aliases = &["--to-int256", "ti", "2i"])]
172 ToInt256 {
173 value: Option<String>,
175 },
176
177 #[command(name = "shl")]
179 LeftShift {
180 value: String,
182
183 bits: String,
185
186 #[arg(long)]
188 base_in: Option<String>,
189
190 #[arg(long, default_value = "16")]
192 base_out: String,
193 },
194
195 #[command(name = "shr")]
197 RightShift {
198 value: String,
200
201 bits: String,
203
204 #[arg(long)]
206 base_in: Option<String>,
207
208 #[arg(long, default_value = "16")]
210 base_out: String,
211 },
212
213 #[command(visible_aliases = &["--to-unit", "tun", "2un"])]
222 ToUnit {
223 value: Option<String>,
225
226 #[arg(default_value = "wei")]
228 unit: String,
229 },
230
231 #[command(visible_aliases = &["--parse-units", "pun"])]
238 ParseUnits {
239 value: Option<String>,
241
242 #[arg(default_value = "18")]
244 unit: u8,
245 },
246
247 #[command(visible_aliases = &["--format-units", "fun"])]
254 FormatUnits {
255 value: Option<String>,
257
258 #[arg(default_value = "18")]
260 unit: u8,
261 },
262
263 #[command(visible_aliases = &["--to-wei", "tw", "2w"])]
267 ToWei {
268 #[arg(allow_hyphen_values = true)]
270 value: Option<String>,
271
272 #[arg(default_value = "eth")]
274 unit: String,
275 },
276
277 #[command(visible_aliases = &["--from-wei", "fw"])]
281 FromWei {
282 #[arg(allow_hyphen_values = true)]
284 value: Option<String>,
285
286 #[arg(default_value = "eth")]
288 unit: String,
289 },
290
291 #[command(visible_aliases = &["--to-rlp"])]
302 ToRlp {
303 value: Option<String>,
308 },
309
310 #[command(visible_aliases = &["--from-rlp"])]
312 FromRlp {
313 value: Option<String>,
315
316 #[arg(long, alias = "int")]
318 as_int: bool,
319 },
320
321 #[command(visible_aliases = &["--to-hex", "th", "2h"])]
323 ToHex(ToBaseArgs),
324
325 #[command(visible_aliases = &["--to-dec", "td", "2d"])]
327 ToDec(ToBaseArgs),
328
329 #[command(
331 visible_aliases = &["--to-base",
332 "--to-radix",
333 "to-radix",
334 "tr",
335 "2r"]
336 )]
337 ToBase {
338 #[command(flatten)]
339 base: ToBaseArgs,
340
341 #[arg(value_name = "BASE")]
343 base_out: Option<String>,
344 },
345 #[command(visible_aliases = &["ac", "acl"])]
347 AccessList(AccessListArgs),
348 #[command(visible_alias = "l")]
350 Logs(LogsArgs),
351 #[command(visible_alias = "bl")]
353 Block {
354 block: Option<BlockId>,
358
359 #[arg(long, short)]
361 field: Option<String>,
362
363 #[arg(long, conflicts_with = "field")]
365 raw: bool,
366
367 #[arg(long, env = "CAST_FULL_BLOCK")]
368 full: bool,
369
370 #[command(flatten)]
371 rpc: RpcOpts,
372 },
373
374 #[command(visible_alias = "bn")]
376 BlockNumber {
377 block: Option<BlockId>,
379 #[command(flatten)]
380 rpc: RpcOpts,
381 },
382
383 #[command(visible_alias = "c")]
385 Call(CallArgs),
386
387 #[command(name = "calldata", visible_alias = "cd")]
389 CalldataEncode {
390 sig: String,
392
393 #[arg(allow_hyphen_values = true)]
395 args: Vec<String>,
396
397 #[arg(long, value_name = "PATH")]
399 file: Option<PathBuf>,
400 },
401
402 Chain {
404 #[command(flatten)]
405 rpc: RpcOpts,
406 },
407
408 #[command(visible_aliases = &["ci", "cid"])]
410 ChainId {
411 #[command(flatten)]
412 rpc: RpcOpts,
413 },
414
415 #[command(visible_alias = "cl")]
417 Client {
418 #[command(flatten)]
419 rpc: RpcOpts,
420 },
421
422 #[command(visible_alias = "ca")]
424 ComputeAddress {
425 address: Option<Address>,
427
428 #[arg(
430 long,
431 conflicts_with = "salt",
432 conflicts_with = "init_code",
433 conflicts_with = "init_code_hash"
434 )]
435 nonce: Option<u64>,
436
437 #[arg(long, conflicts_with = "nonce")]
439 salt: Option<B256>,
440
441 #[arg(
443 long,
444 requires = "salt",
445 conflicts_with = "init_code_hash",
446 conflicts_with = "nonce"
447 )]
448 init_code: Option<String>,
449
450 #[arg(long, requires = "salt", conflicts_with = "init_code", conflicts_with = "nonce")]
452 init_code_hash: Option<B256>,
453
454 #[command(flatten)]
455 rpc: RpcOpts,
456 },
457
458 #[command(visible_alias = "da")]
460 Disassemble {
461 bytecode: Option<String>,
463 },
464
465 #[command(name = "mktx", visible_alias = "m")]
467 MakeTx(MakeTxArgs),
468
469 #[command(visible_aliases = &["na", "nh"])]
471 Namehash { name: Option<String> },
472
473 #[command(visible_alias = "t")]
475 Tx {
476 tx_hash: Option<String>,
478
479 #[arg(long, value_parser = NameOrAddress::from_str)]
481 from: Option<NameOrAddress>,
482
483 #[arg(long)]
485 nonce: Option<u64>,
486
487 field: Option<String>,
490
491 #[arg(long, conflicts_with = "field")]
493 raw: bool,
494
495 #[command(flatten)]
496 rpc: RpcOpts,
497 },
498
499 #[command(visible_alias = "re")]
501 Receipt {
502 tx_hash: String,
504
505 field: Option<String>,
507
508 #[arg(long, default_value = "1")]
510 confirmations: u64,
511
512 #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")]
514 cast_async: bool,
515
516 #[command(flatten)]
517 rpc: RpcOpts,
518 },
519
520 #[command(name = "send", visible_alias = "s")]
522 SendTx(SendTxArgs),
523
524 #[command(name = "publish", visible_alias = "p")]
526 PublishTx {
527 raw_tx: String,
529
530 #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")]
532 cast_async: bool,
533
534 #[command(flatten)]
535 rpc: RpcOpts,
536 },
537
538 #[command(visible_alias = "e")]
540 Estimate(EstimateArgs),
541
542 #[command(visible_aliases = &["calldata-decode", "--calldata-decode", "cdd"])]
547 DecodeCalldata {
548 sig: String,
550
551 calldata: String,
553 },
554
555 #[command(visible_aliases = &["string-decode", "--string-decode", "sd"])]
559 DecodeString {
560 data: String,
562 },
563
564 #[command(visible_aliases = &["event-decode", "--event-decode", "ed"])]
566 DecodeEvent {
567 #[arg(long, visible_alias = "event-sig")]
569 sig: Option<String>,
570 data: String,
572 },
573
574 #[command(visible_aliases = &["error-decode", "--error-decode", "erd"])]
576 DecodeError {
577 #[arg(long, visible_alias = "error-sig")]
579 sig: Option<String>,
580 data: String,
582 },
583
584 #[command(name = "decode-abi", visible_aliases = &["abi-decode", "--abi-decode", "ad"])]
590 DecodeAbi {
591 sig: String,
593
594 calldata: String,
596
597 #[arg(long, short, help_heading = "Decode input data instead of output data")]
599 input: bool,
600 },
601
602 #[command(visible_alias = "ae")]
604 AbiEncode {
605 sig: String,
607
608 #[arg(long)]
610 packed: bool,
611
612 #[arg(allow_hyphen_values = true)]
614 args: Vec<String>,
615 },
616
617 #[command(visible_alias = "in")]
619 Index {
620 key_type: String,
622
623 key: String,
625
626 slot_number: String,
628 },
629
630 #[command(name = "index-erc7201", alias = "index-erc-7201", visible_aliases = &["index7201", "in7201"])]
632 IndexErc7201 {
633 id: Option<String>,
635 #[arg(long, default_value = "erc7201")]
637 formula_id: String,
638 },
639
640 #[command(visible_alias = "impl")]
643 Implementation {
644 #[arg(long, short = 'B')]
648 block: Option<BlockId>,
649
650 #[arg(long)]
654 beacon: bool,
655
656 #[arg(value_parser = NameOrAddress::from_str)]
658 who: NameOrAddress,
659
660 #[command(flatten)]
661 rpc: RpcOpts,
662 },
663
664 #[command(visible_alias = "adm")]
666 Admin {
667 #[arg(long, short = 'B')]
671 block: Option<BlockId>,
672
673 #[arg(value_parser = NameOrAddress::from_str)]
675 who: NameOrAddress,
676
677 #[command(flatten)]
678 rpc: RpcOpts,
679 },
680
681 #[command(name = "4byte", visible_aliases = &["4", "4b"])]
683 FourByte {
684 selector: Option<Selector>,
686 },
687
688 #[command(name = "4byte-calldata", aliases = &["4byte-decode", "4d", "4bd"], visible_aliases = &["4c", "4bc"])]
690 FourByteCalldata {
691 calldata: Option<String>,
693 },
694
695 #[command(name = "4byte-event", visible_aliases = &["4e", "4be", "topic0-event", "t0e"])]
697 FourByteEvent {
698 #[arg(value_name = "TOPIC_0")]
700 topic: Option<B256>,
701 },
702
703 #[command(visible_aliases = &["ups"])]
711 UploadSignature {
712 signatures: Vec<String>,
717 },
718
719 #[command(visible_alias = "pc")]
723 PrettyCalldata {
724 calldata: Option<String>,
726
727 #[arg(long, short)]
729 offline: bool,
730 },
731
732 #[command(visible_alias = "a")]
734 Age {
735 block: Option<BlockId>,
739
740 #[command(flatten)]
741 rpc: RpcOpts,
742 },
743
744 #[command(visible_alias = "b")]
746 Balance {
747 #[arg(long, short = 'B')]
751 block: Option<BlockId>,
752
753 #[arg(value_parser = NameOrAddress::from_str)]
755 who: NameOrAddress,
756
757 #[arg(long, short)]
759 ether: bool,
760
761 #[command(flatten)]
762 rpc: RpcOpts,
763
764 #[arg(long, alias = "erc721")]
767 erc20: Option<Address>,
768 },
769
770 #[command(visible_aliases = &["ba", "fee", "basefee"])]
772 BaseFee {
773 block: Option<BlockId>,
777
778 #[command(flatten)]
779 rpc: RpcOpts,
780 },
781
782 #[command(visible_alias = "co")]
784 Code {
785 #[arg(long, short = 'B')]
789 block: Option<BlockId>,
790
791 #[arg(value_parser = NameOrAddress::from_str)]
793 who: NameOrAddress,
794
795 #[arg(long, short)]
797 disassemble: bool,
798
799 #[command(flatten)]
800 rpc: RpcOpts,
801 },
802
803 #[command(visible_alias = "cs")]
805 Codesize {
806 #[arg(long, short = 'B')]
810 block: Option<BlockId>,
811
812 #[arg(value_parser = NameOrAddress::from_str)]
814 who: NameOrAddress,
815
816 #[command(flatten)]
817 rpc: RpcOpts,
818 },
819
820 #[command(visible_alias = "g")]
822 GasPrice {
823 #[command(flatten)]
824 rpc: RpcOpts,
825 },
826
827 #[command(visible_alias = "se")]
829 SigEvent {
830 event_string: Option<String>,
832 },
833
834 #[command(visible_aliases = &["k", "keccak256"])]
836 Keccak {
837 data: Option<String>,
839 },
840
841 #[command(visible_aliases = &["--hash-message", "hm"])]
843 HashMessage {
844 message: Option<String>,
846 },
847
848 #[command(visible_alias = "rn")]
850 ResolveName {
851 who: Option<String>,
853
854 #[arg(long)]
856 verify: bool,
857
858 #[command(flatten)]
859 rpc: RpcOpts,
860 },
861
862 #[command(visible_alias = "la")]
864 LookupAddress {
865 who: Option<Address>,
867
868 #[arg(long)]
870 verify: bool,
871
872 #[command(flatten)]
873 rpc: RpcOpts,
874 },
875
876 #[command(visible_alias = "st")]
878 Storage(StorageArgs),
879
880 #[command(visible_alias = "pr")]
882 Proof {
883 #[arg(value_parser = NameOrAddress::from_str)]
885 address: NameOrAddress,
886
887 #[arg(value_parser = parse_slot)]
889 slots: Vec<B256>,
890
891 #[arg(long, short = 'B')]
895 block: Option<BlockId>,
896
897 #[command(flatten)]
898 rpc: RpcOpts,
899 },
900
901 #[command(visible_alias = "n")]
903 Nonce {
904 #[arg(long, short = 'B')]
908 block: Option<BlockId>,
909
910 #[arg(value_parser = NameOrAddress::from_str)]
912 who: NameOrAddress,
913
914 #[command(flatten)]
915 rpc: RpcOpts,
916 },
917
918 #[command()]
920 Codehash {
921 #[arg(long, short = 'B')]
925 block: Option<BlockId>,
926
927 #[arg(value_parser = NameOrAddress::from_str)]
929 who: NameOrAddress,
930
931 #[arg(value_parser = parse_slot)]
933 slots: Vec<B256>,
934
935 #[command(flatten)]
936 rpc: RpcOpts,
937 },
938
939 #[command(visible_alias = "sr")]
941 StorageRoot {
942 #[arg(long, short = 'B')]
946 block: Option<BlockId>,
947
948 #[arg(value_parser = NameOrAddress::from_str)]
950 who: NameOrAddress,
951
952 #[arg(value_parser = parse_slot)]
954 slots: Vec<B256>,
955
956 #[command(flatten)]
957 rpc: RpcOpts,
958 },
959
960 #[command(visible_aliases = &["et", "src"])]
962 Source {
963 address: String,
965
966 #[arg(long, short)]
968 flatten: bool,
969
970 #[arg(short, value_hint = ValueHint::DirPath, alias = "path")]
972 directory: Option<PathBuf>,
973
974 #[command(flatten)]
975 etherscan: EtherscanOpts,
976
977 #[arg(long, env = "EXPLORER_API_URL")]
980 explorer_api_url: Option<String>,
981
982 #[arg(long, env = "EXPLORER_URL")]
984 explorer_url: Option<String>,
985 },
986
987 #[command(visible_alias = "w")]
989 Wallet {
990 #[command(subcommand)]
991 command: WalletSubcommands,
992 },
993
994 #[command(visible_alias = "cc")]
996 CreationCode(CreationCodeArgs),
997
998 #[command(visible_alias = "ar")]
1000 Artifact(ArtifactArgs),
1001
1002 #[command(visible_alias = "cra")]
1004 ConstructorArgs(ConstructorArgsArgs),
1005
1006 #[command(visible_alias = "i")]
1010 Interface(InterfaceArgs),
1011
1012 #[command(visible_alias = "bi")]
1014 Bind(BindArgs),
1015
1016 #[command(visible_alias = "si")]
1018 Sig {
1019 sig: Option<String>,
1021
1022 optimize: Option<usize>,
1024 },
1025
1026 #[command(visible_alias = "c2")]
1028 Create2(Create2Args),
1029
1030 #[command(visible_alias = "f")]
1032 FindBlock(FindBlockArgs),
1033
1034 #[command(visible_alias = "com")]
1036 Completions {
1037 #[arg(value_enum)]
1038 shell: clap_complete::Shell,
1039 },
1040
1041 #[command(visible_alias = "fig")]
1043 GenerateFigSpec,
1044
1045 #[command(visible_alias = "r")]
1047 Run(RunArgs),
1048
1049 #[command(visible_alias = "rp")]
1051 Rpc(RpcArgs),
1052
1053 #[command(name = "format-bytes32-string", visible_aliases = &["--format-bytes32-string"])]
1055 FormatBytes32String {
1056 string: Option<String>,
1058 },
1059
1060 #[command(name = "parse-bytes32-string", visible_aliases = &["--parse-bytes32-string"])]
1062 ParseBytes32String {
1063 bytes: Option<String>,
1065 },
1066 #[command(name = "parse-bytes32-address", visible_aliases = &["--parse-bytes32-address"])]
1067 #[command(about = "Parses a checksummed address from bytes32 encoding.")]
1068 ParseBytes32Address {
1069 #[arg(value_name = "BYTES")]
1070 bytes: Option<String>,
1071 },
1072
1073 #[command(visible_aliases = &["dt", "decode-tx"])]
1075 DecodeTransaction { tx: Option<String> },
1076
1077 #[command(visible_aliases = &["decode-auth"])]
1079 RecoverAuthority { auth: String },
1080
1081 #[command(visible_alias = "sel")]
1083 Selectors {
1084 bytecode: Option<String>,
1086
1087 #[arg(long, short)]
1089 resolve: bool,
1090 },
1091
1092 #[command(visible_alias = "tp")]
1094 TxPool {
1095 #[command(subcommand)]
1096 command: TxPoolSubcommands,
1097 },
1098 #[command(name = "da-estimate")]
1100 DAEstimate(DAEstimateArgs),
1101}
1102
1103#[derive(Debug, Parser)]
1105pub struct ToBaseArgs {
1106 #[arg(allow_hyphen_values = true)]
1108 pub value: Option<String>,
1109
1110 #[arg(long, short = 'i')]
1112 pub base_in: Option<String>,
1113}
1114
1115pub fn parse_slot(s: &str) -> Result<B256> {
1116 let slot = U256::from_str(s).map_err(|e| eyre::eyre!("Could not parse slot number: {e}"))?;
1117 Ok(B256::from(slot))
1118}
1119
1120#[cfg(test)]
1121mod tests {
1122 use super::*;
1123 use crate::SimpleCast;
1124 use alloy_rpc_types::{BlockNumberOrTag, RpcBlockHash};
1125 use clap::CommandFactory;
1126
1127 #[test]
1128 fn verify_cli() {
1129 Cast::command().debug_assert();
1130 }
1131
1132 #[test]
1133 fn parse_proof_slot() {
1134 let args: Cast = Cast::parse_from([
1135 "foundry-cli",
1136 "proof",
1137 "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
1138 "0",
1139 "1",
1140 "0x0000000000000000000000000000000000000000000000000000000000000000",
1141 "0x1",
1142 "0x01",
1143 ]);
1144 match args.cmd {
1145 CastSubcommand::Proof { slots, .. } => {
1146 assert_eq!(
1147 slots,
1148 vec![
1149 B256::ZERO,
1150 U256::from(1).into(),
1151 B256::ZERO,
1152 U256::from(1).into(),
1153 U256::from(1).into()
1154 ]
1155 );
1156 }
1157 _ => unreachable!(),
1158 };
1159 }
1160
1161 #[test]
1162 fn parse_call_data() {
1163 let args: Cast = Cast::parse_from([
1164 "foundry-cli",
1165 "calldata",
1166 "f()",
1167 "5c9d55b78febcc2061715ba4f57ecf8ea2711f2c",
1168 "2",
1169 ]);
1170 match args.cmd {
1171 CastSubcommand::CalldataEncode { args, .. } => {
1172 assert_eq!(
1173 args,
1174 vec!["5c9d55b78febcc2061715ba4f57ecf8ea2711f2c".to_string(), "2".to_string()]
1175 )
1176 }
1177 _ => unreachable!(),
1178 };
1179 }
1180
1181 #[test]
1182 fn parse_call_data_with_file() {
1183 let args: Cast = Cast::parse_from(["foundry-cli", "calldata", "f()", "--file", "test.txt"]);
1184 match args.cmd {
1185 CastSubcommand::CalldataEncode { sig, file, args } => {
1186 assert_eq!(sig, "f()".to_string());
1187 assert_eq!(file, Some(PathBuf::from("test.txt")));
1188 assert!(args.is_empty());
1189 }
1190 _ => unreachable!(),
1191 };
1192 }
1193
1194 #[test]
1196 fn parse_signature() {
1197 let args: Cast = Cast::parse_from([
1198 "foundry-cli",
1199 "sig",
1200 "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)",
1201 ]);
1202 match args.cmd {
1203 CastSubcommand::Sig { sig, .. } => {
1204 let sig = sig.unwrap();
1205 assert_eq!(
1206 sig,
1207 "__$_$__$$$$$__$$_$$$_$$__$$___$$(address,address,uint256)".to_string()
1208 );
1209
1210 let selector = SimpleCast::get_selector(&sig, 0).unwrap();
1211 assert_eq!(selector.0, "0x23b872dd".to_string());
1212 }
1213 _ => unreachable!(),
1214 };
1215 }
1216
1217 #[test]
1218 fn parse_block_ids() {
1219 struct TestCase {
1220 input: String,
1221 expect: BlockId,
1222 }
1223
1224 let test_cases = [
1225 TestCase {
1226 input: "0".to_string(),
1227 expect: BlockId::Number(BlockNumberOrTag::Number(0u64)),
1228 },
1229 TestCase {
1230 input: "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a"
1231 .to_string(),
1232 expect: BlockId::Hash(RpcBlockHash::from_hash(
1233 "0x56462c47c03df160f66819f0a79ea07def1569f8aac0fe91bb3a081159b61b4a"
1234 .parse()
1235 .unwrap(),
1236 None,
1237 )),
1238 },
1239 TestCase {
1240 input: "latest".to_string(),
1241 expect: BlockId::Number(BlockNumberOrTag::Latest),
1242 },
1243 TestCase {
1244 input: "earliest".to_string(),
1245 expect: BlockId::Number(BlockNumberOrTag::Earliest),
1246 },
1247 TestCase {
1248 input: "pending".to_string(),
1249 expect: BlockId::Number(BlockNumberOrTag::Pending),
1250 },
1251 TestCase { input: "safe".to_string(), expect: BlockId::Number(BlockNumberOrTag::Safe) },
1252 TestCase {
1253 input: "finalized".to_string(),
1254 expect: BlockId::Number(BlockNumberOrTag::Finalized),
1255 },
1256 ];
1257
1258 for test in test_cases {
1259 let result: BlockId = test.input.parse().unwrap();
1260 assert_eq!(result, test.expect);
1261 }
1262 }
1263}