light_compressed_token/
batch_compress.rs

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    // Some if one amount per pubkey.
9    pub amounts: Option<Vec<u64>>,
10    pub lamports: Option<u64>,
11    // Some if one amount across all pubkeys.
12    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}