cow_settlement_interface/instruction/
withdraw_native_sol.rs1use core::mem::size_of;
18
19use solana_instruction::{AccountMeta, Instruction};
20use solana_program_error::ProgramError;
21use solana_pubkey::Pubkey;
22
23use super::InstructionInputParsing;
24use crate::SettlementInstruction;
25
26pub struct WithdrawNativeSol {
35 pub program_id: Pubkey,
36 pub state_pda: Pubkey,
37 pub authority: Pubkey,
38 pub native_sol_buffer: Pubkey,
39 pub recipient: Pubkey,
40 pub amount: u64,
41}
42
43impl From<WithdrawNativeSol> for Instruction {
44 fn from(builder: WithdrawNativeSol) -> Self {
45 let mut data = vec![SettlementInstruction::WithdrawNativeSol.discriminator()];
46 data.extend_from_slice(&builder.amount.to_le_bytes());
47 Instruction {
48 program_id: builder.program_id,
49 accounts: vec![
50 AccountMeta::new_readonly(builder.state_pda, false),
51 AccountMeta::new_readonly(builder.authority, true),
52 AccountMeta::new(builder.native_sol_buffer, false),
53 AccountMeta::new(builder.recipient, false),
54 ],
55 data,
56 }
57 }
58}
59
60pub struct WithdrawNativeSolInput<'a, A> {
62 pub state_pda: &'a A,
63 pub authority: &'a A,
64 pub native_sol_buffer: &'a A,
65 pub recipient: &'a A,
66 pub amount: u64,
67}
68
69impl<'a, A> InstructionInputParsing<'a, A> for WithdrawNativeSolInput<'a, A> {
70 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::WithdrawNativeSol;
71
72 fn parse_body(instruction_data: &[u8], accounts: &'a [A]) -> Result<Self, ProgramError> {
73 let amount: &[u8; size_of::<u64>()] = instruction_data
74 .try_into()
75 .map_err(|_| ProgramError::InvalidInstructionData)?;
76 let amount = u64::from_le_bytes(*amount);
77
78 let [state_pda, authority, native_sol_buffer, recipient, ..] = accounts else {
81 return Err(ProgramError::NotEnoughAccountKeys);
82 };
83
84 Ok(Self {
85 state_pda,
86 authority,
87 native_sol_buffer,
88 recipient,
89 amount,
90 })
91 }
92}
93
94#[cfg(any(test, feature = "test-fixtures"))]
98pub mod fixtures {
99 use solana_address::Address;
100
101 use super::{Instruction, WithdrawNativeSol};
102
103 pub const NUM_ACCOUNTS: usize = 4;
106
107 pub fn withdraw_native_sol_data(amount: u64) -> Vec<u8> {
110 let zero = Address::new_from_array([0; 32]);
111 Instruction::from(WithdrawNativeSol {
112 program_id: zero,
113 state_pda: zero,
114 authority: zero,
115 native_sol_buffer: zero,
116 recipient: zero,
117 amount,
118 })
119 .data
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::fixtures::{withdraw_native_sol_data, NUM_ACCOUNTS};
126 use super::*;
127 use crate::fixtures::pubkey_from_seed;
128 use crate::instruction::fixtures::{fake_account, fake_sequential_accounts};
129 use crate::instruction::tests::{
130 assert_readonly_nonsigner, assert_readonly_signer, assert_writable_nonsigner,
131 };
132
133 fn sample() -> WithdrawNativeSol {
134 WithdrawNativeSol {
135 program_id: pubkey_from_seed("program id"),
136 state_pda: pubkey_from_seed("state pda"),
137 authority: pubkey_from_seed("authority"),
138 native_sol_buffer: pubkey_from_seed("native sol buffer"),
139 recipient: pubkey_from_seed("recipient"),
140 amount: 0x0102_0304_0506_0708,
141 }
142 }
143
144 #[test]
145 fn withdraw_native_sol_input_parses_valid_input() {
146 let builder = sample();
147 let accounts = [
148 fake_account(builder.state_pda),
149 fake_account(builder.authority),
150 fake_account(builder.native_sol_buffer),
151 fake_account(builder.recipient),
152 ];
153 let (state_pda, authority, native_sol_buffer, recipient, amount) = (
154 builder.state_pda,
155 builder.authority,
156 builder.native_sol_buffer,
157 builder.recipient,
158 builder.amount,
159 );
160 let data = Instruction::from(builder).data;
161
162 let WithdrawNativeSolInput {
163 state_pda: parsed_state_pda,
164 authority: parsed_authority,
165 native_sol_buffer: parsed_native_sol_buffer,
166 recipient: parsed_recipient,
167 amount: parsed_amount,
168 } = WithdrawNativeSolInput::parse(&data, &accounts).expect("parse should succeed");
169
170 assert_eq!(*parsed_state_pda.address(), state_pda);
171 assert_eq!(*parsed_authority.address(), authority);
172 assert_eq!(*parsed_native_sol_buffer.address(), native_sol_buffer);
173 assert_eq!(*parsed_recipient.address(), recipient);
174 assert_eq!(parsed_amount, amount);
175 }
176
177 #[test]
178 fn withdraw_native_sol_input_rejects_short_data() {
179 let mut data = withdraw_native_sol_data(1);
180 data.pop();
181 let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
182 assert_eq!(
183 WithdrawNativeSolInput::parse(&data, &accounts).err(),
184 Some(ProgramError::InvalidInstructionData),
185 );
186 }
187
188 #[test]
189 fn withdraw_native_sol_input_rejects_long_data() {
190 let mut data = withdraw_native_sol_data(1);
191 data.push(0);
192 let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
193 assert_eq!(
194 WithdrawNativeSolInput::parse(&data, &accounts).err(),
195 Some(ProgramError::InvalidInstructionData),
196 );
197 }
198
199 #[test]
200 fn withdraw_native_sol_input_rejects_missing_accounts() {
201 let data = withdraw_native_sol_data(1);
202 let accounts = fake_sequential_accounts::<{ NUM_ACCOUNTS - 1 }>();
203 assert_eq!(
204 WithdrawNativeSolInput::parse(&data, &accounts).err(),
205 Some(ProgramError::NotEnoughAccountKeys),
206 );
207 }
208
209 #[test]
210 fn instruction_data_has_expected_layout() {
211 let Instruction { data, .. } = WithdrawNativeSol {
212 amount: 0x0102_0304_0506_0708,
213 ..sample()
214 }
215 .into();
216 assert_eq!(
217 data,
218 [
219 SettlementInstruction::WithdrawNativeSol.discriminator(),
220 8,
221 7,
222 6,
223 5,
224 4,
225 3,
226 2,
227 1,
228 ]
229 );
230 }
231
232 #[test]
233 fn instruction_has_expected_accounts() {
234 let builder = sample();
235 let ix = Instruction::from(sample());
236
237 assert_eq!(ix.program_id, builder.program_id);
238 assert_eq!(ix.accounts.len(), NUM_ACCOUNTS);
239 assert_readonly_nonsigner(&ix.accounts[0], builder.state_pda);
240 assert_readonly_signer(&ix.accounts[1], builder.authority);
241 assert_writable_nonsigner(&ix.accounts[2], builder.native_sol_buffer);
242 assert_writable_nonsigner(&ix.accounts[3], builder.recipient);
243 }
244}