diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a7332b9e..ea013cf6dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The minor version will be incremented upon a breaking change and the patch versi - cli: Allow force `init` and `new` ([#2698](https://github.com/coral-xyz/anchor/pull/2698)). - cli: Add verifiable option when `deploy` ([#2705](https://github.com/coral-xyz/anchor/pull/2705)). - cli: Add support for passing arguments to the underlying `solana program deploy` command with `anchor deploy` ([#2709](https://github.com/coral-xyz/anchor/pull/2709)). +- lang: Add `InstructionData::write_to` implementation ([#2733](https://github.com/coral-xyz/anchor/pull/2733)). ### Fixes diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 02fdfe10e4..c428f894db 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -271,6 +271,16 @@ pub trait InstructionData: Discriminator + AnchorSerialize { self.serialize(&mut data).unwrap(); data } + + /// Clears `data` and writes instruction data to it. + /// + /// We use a `Vec`` here because of the additional flexibility of re-allocation (only if + /// necessary), and because the data field in `Instruction` expects a `Vec`. + fn write_to(&self, mut data: &mut Vec) { + data.clear(); + data.extend_from_slice(&Self::DISCRIMINATOR); + self.serialize(&mut data).unwrap() + } } /// An event that can be emitted via a Solana log. See [`emit!`](crate::prelude::emit) for an example. diff --git a/lang/tests/serialization.rs b/lang/tests/serialization.rs new file mode 100644 index 0000000000..9a93e4f4e2 --- /dev/null +++ b/lang/tests/serialization.rs @@ -0,0 +1,36 @@ +use anchor_lang::{AnchorDeserialize, AnchorSerialize, Discriminator, InstructionData}; + +#[test] +fn test_instruction_data() { + // Define some test type and implement ser/de, discriminator, and ix data + #[derive(Default, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)] + struct MyType { + foo: [u8; 8], + bar: String, + } + impl Discriminator for MyType { + const DISCRIMINATOR: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + } + impl InstructionData for MyType {} + + // Initialize some instance of the type + let instance = MyType { + foo: [0, 2, 4, 6, 8, 10, 12, 14], + bar: "sharding sucks".into(), + }; + + // Serialize using both methods + let data = instance.data(); + let mut write = vec![]; + instance.write_to(&mut write); + + // Check that one is correct and that they are equal (implies other is correct) + let correct_disc = data[0..8] == MyType::DISCRIMINATOR; + let correct_data = MyType::deserialize(&mut &data[8..]).is_ok_and(|result| result == instance); + let correct_serialization = correct_disc & correct_data; + assert!(correct_serialization, "serialization was not correct"); + assert_eq!( + &data, &write, + "the different methods produced different serialized representations" + ); +}