@@ -585,6 +585,11 @@ impl Spec {
585585 ( val, ScType :: Result ( inner) ) => self . xdr_to_json ( val, & inner. ok_type ) ?,
586586
587587 ( val, ScType :: Option ( inner) ) => self . xdr_to_json ( val, & inner. value_type ) ?,
588+
589+ // ScType::Val is the generic Soroban value type that can hold any ScVal.
590+ // Delegate to to_json which handles all ScVal variants without type info.
591+ ( val, ScType :: Val ) => to_json ( val) ?,
592+
588593 ( ScVal :: Map ( Some ( _) ) | ScVal :: Vec ( Some ( _) ) | ScVal :: U32 ( _) , type_) => {
589594 self . sc_object_to_json ( val, type_) ?
590595 }
@@ -2450,4 +2455,63 @@ mod tests {
24502455 let json = spec. sc_map_to_json ( & sc_map, & map_type) . unwrap ( ) ;
24512456 assert_eq ! ( json. to_string( ) , r#"{"foo":2}"# ) ;
24522457 }
2458+
2459+ #[ test]
2460+ fn test_xdr_to_json_bytes_with_val_type ( ) {
2461+ // Regression test for https://github.com/stellar/stellar-cli/issues/2469
2462+ // When a contract function returns ScType::Val and the runtime value is
2463+ // ScVal::Bytes (e.g. BytesN<32>), xdr_to_json should succeed instead of
2464+ // panicking with "doesn't have a matching Val".
2465+ let spec = Spec ( None ) ;
2466+ let bytes_val = ScVal :: Bytes ( ScBytes (
2467+ vec ! [
2468+ 0x05 , 0x5e , 0xf8 , 0x16 , 0x22 , 0x3e , 0xe5 , 0x21 , 0x6b , 0x18 , 0xc2 , 0xdf , 0x00 , 0xd6 ,
2469+ 0x15 , 0xee , 0x08 , 0x9a , 0x8e , 0xf1 , 0x1b , 0x92 , 0x7a , 0x76 , 0x4e , 0x4f , 0x5d , 0x6c ,
2470+ 0x0c , 0xb4 , 0xf4 , 0xc7 ,
2471+ ]
2472+ . try_into ( )
2473+ . unwrap ( ) ,
2474+ ) ) ;
2475+ let result = spec. xdr_to_json ( & bytes_val, & ScType :: Val ) ;
2476+ assert_eq ! (
2477+ result. unwrap( ) ,
2478+ Value :: String (
2479+ "055ef816223ee5216b18c2df00d615ee089a8ef11b927a764e4f5d6c0cb4f4c7" . to_string( )
2480+ )
2481+ ) ;
2482+ }
2483+
2484+ #[ test]
2485+ fn test_xdr_to_json_map_with_val_type ( ) {
2486+ // ScVal::Map with ScType::Val should delegate to to_json, not sc_object_to_json.
2487+ let spec = Spec ( None ) ;
2488+ let map_val = ScVal :: Map ( Some (
2489+ ScMap :: sorted_from ( vec ! [ ScMapEntry {
2490+ key: ScVal :: Symbol ( ScSymbol ( "key" . try_into( ) . unwrap( ) ) ) ,
2491+ val: ScVal :: U32 ( 42 ) ,
2492+ } ] )
2493+ . unwrap ( ) ,
2494+ ) ) ;
2495+ let result = spec. xdr_to_json ( & map_val, & ScType :: Val ) ;
2496+ assert ! ( result. is_ok( ) , "Map with ScType::Val should not error" ) ;
2497+ }
2498+
2499+ #[ test]
2500+ fn test_xdr_to_json_vec_with_val_type ( ) {
2501+ // ScVal::Vec with ScType::Val should delegate to to_json, not sc_object_to_json.
2502+ let spec = Spec ( None ) ;
2503+ let vec_val = ScVal :: Vec ( Some (
2504+ ScVec :: try_from ( vec ! [ ScVal :: U32 ( 1 ) , ScVal :: U32 ( 2 ) ] ) . unwrap ( ) ,
2505+ ) ) ;
2506+ let result = spec. xdr_to_json ( & vec_val, & ScType :: Val ) ;
2507+ assert ! ( result. is_ok( ) , "Vec with ScType::Val should not error" ) ;
2508+ }
2509+
2510+ #[ test]
2511+ fn test_xdr_to_json_u32_with_val_type ( ) {
2512+ // ScVal::U32 with ScType::Val should delegate to to_json, not sc_object_to_json.
2513+ let spec = Spec ( None ) ;
2514+ let result = spec. xdr_to_json ( & ScVal :: U32 ( 100 ) , & ScType :: Val ) ;
2515+ assert_eq ! ( result. unwrap( ) , Value :: Number ( 100 . into( ) ) ) ;
2516+ }
24532517}
0 commit comments