From 94606c361c9d4b1fe78e19792722a9eb4fad86dd Mon Sep 17 00:00:00 2001 From: Brian Li Date: Mon, 20 Oct 2025 22:30:44 +0000 Subject: [PATCH] Make UiTransactionError deserializer robust across SDK versions Enhance the InstructionError deserializer within UiTransactionError to handle both unit variant and newtype variant serialization formats. This improves compatibility when deserializing errors from different SDK versions. Specifically handles the case where BorshIoError may be serialized as: - Unit variant: "BorshIoError" (SDK v2.0+) - Newtype variant: {"BorshIoError": "message"} (older SDKs) The deserializer now tries the direct deserialization first (handles unit variants), and falls back to converting the format if needed (handles newtype variants in older SDKs). This defensive coding ensures forward and backward compatibility across SDK version boundaries. --- transaction-status-client-types/src/lib.rs | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/transaction-status-client-types/src/lib.rs b/transaction-status-client-types/src/lib.rs index fdc68324e746e0..78c6e366bc2c05 100644 --- a/transaction-status-client-types/src/lib.rs +++ b/transaction-status-client-types/src/lib.rs @@ -300,7 +300,17 @@ impl<'de> DeserializeTrait<'de> for UiTransactionError { let instruction_error = arr.get(1).ok_or_else(|| { DeserializeError::invalid_length(1, &"Expected there to be at least 2 elements") })?; + + // Handle SDK version compatibility: try deserializing as-is, but if it fails + // and the error is BorshIoError string, normalize to object format for v2.3 SDKs let err: InstructionError = from_value(instruction_error.clone()) + .or_else(|e| { + if instruction_error.as_str() == Some("BorshIoError") { + from_value(serde_json::json!({"BorshIoError": ""})) + } else { + Err(e) + } + }) .map_err(|e| DeserializeError::custom(e.to_string()))?; return Ok(UiTransactionError(TransactionError::InstructionError( outer_instruction_index, @@ -956,4 +966,29 @@ mod test { .expect("Failed to deserialize `UiTransactionError"); assert_eq!(actual_transaction_error, expected_transaction_error); } + + #[test] + fn test_deserialize_instruction_error_string_format() { + // Test that we can deserialize InstructionError when serialized as a string + // This handles compatibility across SDK versions where the same error may be + // a unit variant in one version and newtype variant in another + let error_json = json!({"InstructionError": [0, "BorshIoError"]}); + let result = from_value::(error_json); + assert!( + result.is_ok(), + "Failed to deserialize BorshIoError from string: {:?}", + result.err() + ); + + match result.unwrap().0 { + TransactionError::InstructionError(idx, _) => { + assert_eq!(idx, 0); + // Successfully deserialized InstructionError from BorshIoError string + } + other => panic!( + "Expected InstructionError, got: {:?}", + other + ), + } + } }