@@ -85,7 +85,7 @@ pub enum Error {
8585 CannotAccessAliasConfigFile ,
8686 #[ error( "cannot parse contract ID {0}: {1}" ) ]
8787 CannotParseContractId ( String , DecodeError ) ,
88- #[ error( "contract not found: {0}" ) ]
88+ #[ error( "contract not found: {0}{hint}" , hint = wasm_hash_hint ( . 0 ) ) ]
8989 ContractNotFound ( String ) ,
9090 #[ error( "Failed to read upgrade check file: {path}: {error}" ) ]
9191 UpgradeCheckReadFailed { path : PathBuf , error : io:: Error } ,
@@ -109,6 +109,14 @@ pub enum Error {
109109 InvalidSigningKey ,
110110}
111111
112+ fn wasm_hash_hint ( value : & str ) -> & ' static str {
113+ if value. len ( ) == 64 && value. bytes ( ) . all ( |b| b. is_ascii_hexdigit ( ) ) {
114+ "; expected a contract address (C...), got a hash"
115+ } else {
116+ ""
117+ }
118+ }
119+
112120#[ derive( Debug , clap:: Args , Default , Clone ) ]
113121#[ group( skip) ]
114122pub struct Args {
@@ -928,6 +936,60 @@ pub fn cli_config_file() -> Result<PathBuf, Error> {
928936 Ok ( global_config_path ( ) ?. join ( "config.toml" ) )
929937}
930938
939+ #[ cfg( test) ]
940+ mod error_message_tests {
941+ use super :: * ;
942+
943+ #[ test]
944+ fn contract_not_found_plain_alias_has_no_hint ( ) {
945+ let err = Error :: ContractNotFound ( "alice" . to_string ( ) ) ;
946+ assert_eq ! ( err. to_string( ) , "contract not found: alice" ) ;
947+ }
948+
949+ #[ test]
950+ fn contract_not_found_64_char_lowercase_hex_includes_wasm_hash_hint ( ) {
951+ let hash = "5ea0f3d6c880148c8da088809e851732127fc36b7b42bbdde6052fcc6f6253f3" ;
952+ let err = Error :: ContractNotFound ( hash. to_string ( ) ) ;
953+ assert_eq ! (
954+ err. to_string( ) ,
955+ format!( "contract not found: {hash}; expected a contract address (C...), got a hash" ) ,
956+ ) ;
957+ }
958+
959+ #[ test]
960+ fn contract_not_found_64_char_uppercase_hex_includes_wasm_hash_hint ( ) {
961+ let hash = "5EA0F3D6C880148C8DA088809E851732127FC36B7B42BBDDE6052FCC6F6253F3" ;
962+ let err = Error :: ContractNotFound ( hash. to_string ( ) ) ;
963+ assert ! (
964+ err. to_string( ) . contains( "got a hash" ) ,
965+ "expected wasm-hash hint for uppercase hex, got: {err}" ,
966+ ) ;
967+ }
968+
969+ #[ test]
970+ fn contract_not_found_64_char_mixed_case_hex_includes_wasm_hash_hint ( ) {
971+ let hash = "5ea0F3d6C880148c8DA088809e851732127fc36b7b42BBDDE6052fcc6F6253F3" ;
972+ let err = Error :: ContractNotFound ( hash. to_string ( ) ) ;
973+ assert ! (
974+ err. to_string( ) . contains( "got a hash" ) ,
975+ "expected wasm-hash hint for mixed-case hex, got: {err}" ,
976+ ) ;
977+ }
978+
979+ #[ test]
980+ fn contract_not_found_short_hex_string_has_no_hint ( ) {
981+ let err = Error :: ContractNotFound ( "deadbeef" . to_string ( ) ) ;
982+ assert_eq ! ( err. to_string( ) , "contract not found: deadbeef" ) ;
983+ }
984+
985+ #[ test]
986+ fn contract_not_found_64_char_non_hex_has_no_hint ( ) {
987+ let value = "z" . repeat ( 64 ) ;
988+ let err = Error :: ContractNotFound ( value. clone ( ) ) ;
989+ assert_eq ! ( err. to_string( ) , format!( "contract not found: {value}" ) ) ;
990+ }
991+ }
992+
931993#[ cfg( all( test, unix) ) ]
932994mod tests {
933995 use super :: * ;
0 commit comments