1use anchor_lang::{prelude::*, AnchorDeserialize, AnchorSerialize};
2use light_zero_copy::{borsh::Deserialize, errors::ZeroCopyError, slice::ZeroCopySliceBorsh};
3use zerocopy::{little_endian::U64, Ref};
4
5#[derive(Debug, Default, Clone, PartialEq, AnchorSerialize, AnchorDeserialize)]
6pub struct BatchCompressInstructionDataBorsh {
7 pub pubkeys: Vec<Pubkey>,
8 pub amounts: Option<Vec<u64>>,
10 pub lamports: Option<u64>,
11 pub amount: Option<u64>,
13 pub index: u8,
14 pub bump: u8,
15}
16
17pub struct BatchCompressInstructionData<'a> {
18 pub pubkeys: ZeroCopySliceBorsh<'a, light_compressed_account::pubkey::Pubkey>,
19 pub amounts: Option<ZeroCopySliceBorsh<'a, U64>>,
20 pub lamports: Option<Ref<&'a [u8], U64>>,
21 pub amount: Option<Ref<&'a [u8], U64>>,
22 pub index: u8,
23 pub bump: u8,
24}
25
26impl<'a> Deserialize<'a> for BatchCompressInstructionData<'a> {
27 type Output = Self;
28
29 fn zero_copy_at(bytes: &'a [u8]) -> std::result::Result<(Self, &'a [u8]), ZeroCopyError> {
30 let (pubkeys, bytes) = ZeroCopySliceBorsh::from_bytes_at(bytes)?;
31 let (amounts, bytes) = Option::<ZeroCopySliceBorsh<U64>>::zero_copy_at(bytes)?;
32 let (lamports, bytes) = Option::<U64>::zero_copy_at(bytes)?;
33 let (amount, bytes) = Option::<U64>::zero_copy_at(bytes)?;
34 let (index, bytes) = u8::zero_copy_at(bytes)?;
35 let (bump, bytes) = u8::zero_copy_at(bytes)?;
36 Ok((
37 Self {
38 pubkeys,
39 amounts,
40 lamports,
41 amount,
42 index,
43 bump,
44 },
45 bytes,
46 ))
47 }
48}
49
50#[cfg(test)]
51mod test {
52 use super::*;
53 #[test]
54 fn test_batch_compress_instruction_data() {
55 let data = super::BatchCompressInstructionDataBorsh {
56 pubkeys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
57 amounts: Some(vec![1, 2]),
58 lamports: Some(3),
59 amount: Some(1),
60 index: 1,
61 bump: 2,
62 };
63 let mut vec = Vec::new();
64 data.serialize(&mut vec).unwrap();
65 let (decoded_data, _) = super::BatchCompressInstructionData::zero_copy_at(&vec).unwrap();
66 assert_eq!(decoded_data.pubkeys.len(), 2);
67 assert_eq!(decoded_data.amounts.as_ref().unwrap().len(), 2);
68 assert_eq!(*decoded_data.lamports.unwrap(), U64::from(3));
69 for (i, pubkey) in decoded_data.pubkeys.iter().enumerate() {
70 assert_eq!(data.pubkeys[i], pubkey.into(),);
71 }
72 for (i, amount) in decoded_data.amounts.as_ref().unwrap().iter().enumerate() {
73 assert_eq!(amount.get(), data.amounts.as_ref().unwrap()[i]);
74 }
75 assert_eq!(decoded_data.index, 1);
76 assert_eq!(*decoded_data.amount.unwrap(), data.amount.unwrap());
77 assert_eq!(decoded_data.bump, data.bump);
78 }
79
80 #[test]
81 fn test_batch_compress_instruction_data_none() {
82 let data = super::BatchCompressInstructionDataBorsh {
83 pubkeys: vec![Pubkey::new_unique(), Pubkey::new_unique()],
84 amounts: Some(vec![1, 2]),
85 amount: None,
86 lamports: None,
87 index: 0,
88 bump: 0,
89 };
90 let mut vec = Vec::new();
91 data.serialize(&mut vec).unwrap();
92 let (decoded_data, _) = super::BatchCompressInstructionData::zero_copy_at(&vec).unwrap();
93 assert_eq!(decoded_data.pubkeys.len(), 2);
94 assert_eq!(decoded_data.amounts.as_ref().unwrap().len(), 2);
95 assert!(decoded_data.lamports.is_none());
96 for (i, pubkey) in decoded_data.pubkeys.iter().enumerate() {
97 assert_eq!(data.pubkeys[i], (*pubkey).into(),);
98 }
99 for (i, amount) in decoded_data.amounts.as_ref().unwrap().iter().enumerate() {
100 assert_eq!(amount.get(), data.amounts.as_ref().unwrap()[i]);
101 }
102 assert_eq!(decoded_data.index, 0);
103 assert_eq!(decoded_data.amount, None);
104 assert_eq!(decoded_data.bump, data.bump);
105 }
106
107 #[test]
108 fn test_batch_compress_instruction_data_randomized() {
109 use rand::Rng;
110
111 for _ in 0..100000 {
112 let mut rng = rand::thread_rng();
113
114 let pubkeys_count = rng.gen_range(1..10);
115 let pubkeys: Vec<Pubkey> = (0..pubkeys_count).map(|_| Pubkey::new_unique()).collect();
116
117 let amounts = if rng.gen_bool(0.5) {
118 Some((0..pubkeys_count).map(|_| rng.gen_range(1..1000)).collect())
119 } else {
120 None
121 };
122
123 let lamports = if rng.gen_bool(0.5) {
124 Some(rng.gen_range(1..1000))
125 } else {
126 None
127 };
128
129 let amount = if rng.gen_bool(0.5) {
130 Some(rng.gen_range(1..1000))
131 } else {
132 None
133 };
134
135 let index = rng.gen_range(0..=u8::MAX);
136 let bump = rng.gen_range(0..=u8::MAX);
137
138 let data = super::BatchCompressInstructionDataBorsh {
139 pubkeys,
140 amounts,
141 lamports,
142 amount,
143 index,
144 bump,
145 };
146
147 let mut vec = Vec::new();
148 data.serialize(&mut vec).unwrap();
149 let (decoded_data, _) =
150 super::BatchCompressInstructionData::zero_copy_at(&vec).unwrap();
151
152 assert_eq!(decoded_data.pubkeys.len(), data.pubkeys.len());
153 if let Some(amounts) = &data.amounts {
154 assert_eq!(decoded_data.amounts.as_ref().unwrap().len(), amounts.len());
155 for (i, amount) in decoded_data.amounts.as_ref().unwrap().iter().enumerate() {
156 assert_eq!(amount.get(), amounts[i]);
157 }
158 } else {
159 assert!(decoded_data.amounts.is_none());
160 }
161
162 if let Some(lamports) = data.lamports {
163 assert_eq!(*decoded_data.lamports.unwrap(), U64::from(lamports));
164 } else {
165 assert!(decoded_data.lamports.is_none());
166 }
167
168 if let Some(amount) = data.amount {
169 assert_eq!(*decoded_data.amount.unwrap(), U64::from(amount));
170 } else {
171 assert!(decoded_data.amount.is_none());
172 }
173
174 for (i, pubkey) in decoded_data.pubkeys.iter().enumerate() {
175 assert_eq!(data.pubkeys[i], (*pubkey).into());
176 }
177
178 assert_eq!(decoded_data.index, data.index);
179 assert_eq!(decoded_data.bump, data.bump);
180 }
181 }
182}