1use light_compressed_account::instruction_data::with_account_info::CompressedAccountInfo;
2
3use crate::error::{LightSdkError, Result};
4
5pub fn transfer_compressed_sol(
11 from: &mut CompressedAccountInfo,
12 to: &mut CompressedAccountInfo,
13 lamports: u64,
14) -> Result<()> {
15 if let Some(output) = from.output.as_mut() {
16 output.lamports = output
17 .lamports
18 .checked_sub(lamports)
19 .ok_or(LightSdkError::TransferFromInsufficientLamports)?;
20 }
21 else {
35 return Err(LightSdkError::TransferFromNoLamports);
36 };
37
38 if let Some(output) = to.output.as_mut() {
39 output.lamports = output
40 .lamports
41 .checked_add(lamports)
42 .ok_or(LightSdkError::TransferIntegerOverflow)?;
43 } else {
44 return Err(LightSdkError::TransferFromNoLamports);
45 }
46
47 Ok(())
48}
49
50#[cfg(test)]
51mod tests {
52 use light_compressed_account::{
53 compressed_account::PackedMerkleContext,
54 instruction_data::with_account_info::{
55 CompressedAccountInfo, InAccountInfo, OutAccountInfo,
56 },
57 };
58
59 use super::*;
60 use crate::Pubkey;
61
62 fn mock_account(_owner: &Pubkey, lamports: Option<u64>) -> CompressedAccountInfo {
64 let input_lamports = lamports.unwrap_or(0);
65 CompressedAccountInfo {
66 input: Some(InAccountInfo {
67 lamports: input_lamports,
68 data_hash: [0; 32],
70 merkle_context: PackedMerkleContext {
71 merkle_tree_pubkey_index: 0,
72 queue_pubkey_index: 0,
73 leaf_index: 0,
74 prove_by_index: false,
75 },
76 discriminator: [0; 8],
78 root_index: 0,
79 }),
80 output: Some(OutAccountInfo {
81 lamports: input_lamports,
82 data_hash: [0; 32],
84 data: Vec::new(),
85 output_merkle_tree_index: 0,
86 discriminator: [0; 8],
88 }),
89 address: Some([1; 32]),
90 }
91 }
92
93 fn mock_account_without_input(_owner: &Pubkey) -> CompressedAccountInfo {
95 CompressedAccountInfo {
96 input: None,
97 output: Some(OutAccountInfo {
98 lamports: 0,
99 data_hash: [0; 32],
101 data: Vec::new(),
102 output_merkle_tree_index: 0,
103 discriminator: [0; 8],
105 }),
106 address: Some([1; 32]),
107 }
108 }
109
110 #[test]
111 fn test_transfer_success() {
112 let from_pubkey = Pubkey::new_unique();
113 let mut from = mock_account(&from_pubkey, Some(1000));
114 let to_pubkey = Pubkey::new_unique();
115 let mut to = mock_account(&to_pubkey, Some(500));
116
117 let result = transfer_compressed_sol(&mut from, &mut to, 300);
118 assert!(result.is_ok());
119 assert_eq!(from.output.as_ref().unwrap().lamports, 700);
120 assert_eq!(to.output.as_ref().unwrap().lamports, 800);
121 }
122
123 #[test]
124 fn test_transfer_from_no_input() {
125 let from_pubkey = Pubkey::new_unique();
126 let mut from = mock_account_without_input(&from_pubkey);
127 let to_pubkey = Pubkey::new_unique();
128 let mut to = mock_account(&to_pubkey, Some(500));
129
130 let result = transfer_compressed_sol(&mut from, &mut to, 300);
131 assert_eq!(result, Err(LightSdkError::TransferFromInsufficientLamports));
132 }
133
134 #[test]
135 fn test_transfer_from_no_lamports() {
136 let from_pubkey = Pubkey::new_unique();
137 let mut from = mock_account(&from_pubkey, None);
138 let to_pubkey = Pubkey::new_unique();
139 let mut to = mock_account(&to_pubkey, Some(500));
140
141 let result = transfer_compressed_sol(&mut from, &mut to, 300);
142 assert_eq!(result, Err(LightSdkError::TransferFromInsufficientLamports));
143 }
144
145 #[test]
146 fn test_transfer_insufficient_lamports() {
147 let from_pubkey = Pubkey::new_unique();
148 let mut from = mock_account(&from_pubkey, Some(200));
149 let to_pubkey = Pubkey::new_unique();
150 let mut to = mock_account(&to_pubkey, Some(500));
151
152 let result = transfer_compressed_sol(&mut from, &mut to, 300);
153 assert_eq!(result, Err(LightSdkError::TransferFromInsufficientLamports));
154 }
155
156 #[test]
157 fn test_transfer_integer_overflow() {
158 let from_pubkey = Pubkey::new_unique();
159 let mut from = mock_account(&from_pubkey, Some(1000));
160 let to_pubkey = Pubkey::new_unique();
161 let mut to = mock_account(&to_pubkey, Some(u64::MAX - 500));
162
163 let result = transfer_compressed_sol(&mut from, &mut to, 600);
164 assert_eq!(result, Err(LightSdkError::TransferIntegerOverflow));
165 }
166
167 #[test]
168 fn test_transfer_to_no_lamports() {
169 let from_pubkey = Pubkey::new_unique();
170 let mut from = mock_account(&from_pubkey, Some(1000));
171 let to_pubkey = Pubkey::new_unique();
172 let mut to = mock_account(&to_pubkey, Some(0));
173 to.output.as_mut().unwrap().lamports = 0;
174
175 let result = transfer_compressed_sol(&mut from, &mut to, 500);
176 assert!(result.is_ok());
177 assert_eq!(from.output.as_ref().unwrap().lamports, 500);
178 assert_eq!(to.output.as_ref().unwrap().lamports, 500);
179 }
180}