From 1a33e96d5d48c8afaf416acfe3fea6d5b92d14b3 Mon Sep 17 00:00:00 2001 From: ara-stock <226676426+ara-stock@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:58:21 +0900 Subject: [PATCH] Serialize RemainderOptionBytes without a length prefix in the Rust client The data argument of write, initialize and set_data is a remainder option of bytes with no prefix, as the IDL, the JS client and the program define it. Write the raw bytes and read the rest of the input instead of using the Vec borsh encoding. Co-Authored-By: Claude Opus 5.5 --- clients/rust/src/hooked/remainder_options.rs | 69 ++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/clients/rust/src/hooked/remainder_options.rs b/clients/rust/src/hooked/remainder_options.rs index 41eba0f..114e6e7 100644 --- a/clients/rust/src/hooked/remainder_options.rs +++ b/clients/rust/src/hooked/remainder_options.rs @@ -126,7 +126,7 @@ impl BorshSerialize for RemainderOptionBytes { Self: Sized, { match &self { - Self::Some(item) => item.serialize(writer), + Self::Some(item) => writer.write_all(item), Self::None => Ok(()), } } @@ -137,9 +137,12 @@ impl BorshDeserialize for RemainderOptionBytes { where Self: Sized, { - match Vec::::deserialize_reader(reader) { - Ok(item) => Ok(RemainderOptionBytes::Some(item)), - Err(_) => Ok(RemainderOptionBytes::None), + let mut item = Vec::new(); + reader.read_to_end(&mut item)?; + if item.is_empty() { + Ok(RemainderOptionBytes::None) + } else { + Ok(RemainderOptionBytes::Some(item)) } } } @@ -152,3 +155,61 @@ impl From>> for RemainderOptionBytes { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::instructions::WriteInstructionArgs; + + #[test] + fn remainder_option_bytes_some_is_serialized_without_prefix() { + let bytes = borsh::to_vec(&RemainderOptionBytes::Some(vec![1, 2, 3])).unwrap(); + assert_eq!(bytes, vec![1, 2, 3]); + } + + #[test] + fn remainder_option_bytes_none_is_serialized_as_empty() { + let bytes = borsh::to_vec(&RemainderOptionBytes::None).unwrap(); + assert!(bytes.is_empty()); + } + + #[test] + fn remainder_option_bytes_deserializes_remaining_bytes() { + assert_eq!( + borsh::from_slice::(&[1, 2, 3]).unwrap(), + RemainderOptionBytes::Some(vec![1, 2, 3]) + ); + assert_eq!( + borsh::from_slice::(&[]).unwrap(), + RemainderOptionBytes::None + ); + } + + #[test] + fn remainder_option_bytes_round_trip() { + for value in [ + RemainderOptionBytes::Some(vec![3, 0, 0, 0, 255]), + RemainderOptionBytes::None, + ] { + let bytes = borsh::to_vec(&value).unwrap(); + assert_eq!( + borsh::from_slice::(&bytes).unwrap(), + value + ); + } + } + + #[test] + fn write_instruction_args_append_data_after_offset() { + let args = WriteInstructionArgs { + offset: 7, + data: RemainderOptionBytes::Some(vec![1, 2, 3]), + }; + let bytes = borsh::to_vec(&args).unwrap(); + assert_eq!(bytes, vec![7, 0, 0, 0, 1, 2, 3]); + assert_eq!( + borsh::from_slice::(&bytes).unwrap(), + args + ); + } +}