1use core::mem::size_of;
7
8use arrayref::array_refs;
9use solana_instruction::{AccountMeta, Instruction};
10use solana_program_error::ProgramError;
11use solana_pubkey::Pubkey;
12
13pub use solana_system_interface::program::ID as SYSTEM_PROGRAM_ID;
14
15use super::InstructionInputParsing;
16use crate::SettlementInstruction;
17
18pub struct Initialize {
45 pub program_id: Pubkey,
46 pub payer: Pubkey,
47 pub state_pda: Pubkey,
48 pub native_sol_buffer: Pubkey,
49 pub manager: Pubkey,
50 pub solver_authority: Pubkey,
51 pub reclaim_authority: Pubkey,
52 pub settlement_owned_order_authority: Pubkey,
53}
54
55impl From<Initialize> for Instruction {
56 fn from(builder: Initialize) -> Self {
57 let mut data = vec![SettlementInstruction::Initialize.discriminator()];
58 data.extend_from_slice(&builder.manager.to_bytes());
59 data.extend_from_slice(&builder.solver_authority.to_bytes());
60 data.extend_from_slice(&builder.reclaim_authority.to_bytes());
61 data.extend_from_slice(&builder.settlement_owned_order_authority.to_bytes());
62 Instruction {
63 program_id: builder.program_id,
64 accounts: vec![
65 AccountMeta::new(builder.payer, true),
66 AccountMeta::new(builder.state_pda, false),
67 AccountMeta::new(builder.native_sol_buffer, false),
68 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
69 ],
70 data,
71 }
72 }
73}
74
75pub struct InitializeInput<'a, A> {
77 pub payer: &'a A,
78 pub state_pda: &'a A,
79 pub native_sol_buffer: &'a A,
80 pub manager: Pubkey,
81 pub solver_authority: Pubkey,
82 pub reclaim_authority: Pubkey,
83 pub settlement_owned_order_authority: Pubkey,
84}
85
86impl<'a, A> InstructionInputParsing<'a, A> for InitializeInput<'a, A> {
87 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::Initialize;
88
89 fn parse_body(instruction_data: &[u8], accounts: &'a [A]) -> Result<Self, ProgramError> {
90 let authorities: &[u8; 4 * size_of::<Pubkey>()] = instruction_data
91 .try_into()
92 .map_err(|_| ProgramError::InvalidInstructionData)?;
93 let (manager, solver_authority, reclaim_authority, settlement_owned_order_authority) = array_refs![
94 authorities,
95 size_of::<Pubkey>(),
96 size_of::<Pubkey>(),
97 size_of::<Pubkey>(),
98 size_of::<Pubkey>()
99 ];
100 let manager = Pubkey::new_from_array(*manager);
101 let solver_authority = Pubkey::new_from_array(*solver_authority);
102 let reclaim_authority = Pubkey::new_from_array(*reclaim_authority);
103 let settlement_owned_order_authority =
104 Pubkey::new_from_array(*settlement_owned_order_authority);
105
106 let [payer, state_pda, native_sol_buffer, _system, ..] = accounts else {
111 return Err(ProgramError::NotEnoughAccountKeys);
112 };
113
114 Ok(Self {
115 payer,
116 state_pda,
117 native_sol_buffer,
118 manager,
119 solver_authority,
120 reclaim_authority,
121 settlement_owned_order_authority,
122 })
123 }
124}
125
126#[cfg(any(test, feature = "test-fixtures"))]
129pub mod fixtures {
130 use solana_address::Address;
131
132 use super::{Initialize, Instruction};
133
134 pub const NUM_ACCOUNTS: usize = 4;
137
138 pub fn initialize_data() -> Vec<u8> {
141 let zero = Address::new_from_array([0; 32]);
142 Instruction::from(Initialize {
143 program_id: zero,
144 payer: zero,
145 state_pda: zero,
146 native_sol_buffer: zero,
147 manager: zero,
148 solver_authority: zero,
149 reclaim_authority: zero,
150 settlement_owned_order_authority: zero,
151 })
152 .data
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::fixtures::{initialize_data, NUM_ACCOUNTS};
159 use super::*;
160 use crate::fixtures::pubkey_from_seed;
161 use crate::instruction::fixtures::{fake_account, fake_sequential_accounts};
162 use crate::instruction::tests::{
163 assert_readonly_nonsigner, assert_writable_nonsigner, assert_writable_signer,
164 };
165 use solana_account_view::AccountView;
166 use solana_address::Address;
167
168 #[test]
169 fn initialize_input_parses_valid_input() {
170 let program_id = Address::new_unique();
171 let payer = fake_account(pubkey_from_seed("payer"));
172 let state_pda = fake_account(pubkey_from_seed("state pda"));
173 let native_sol_buffer = fake_account(pubkey_from_seed("native sol buffer"));
174 let manager = pubkey_from_seed("manager");
175 let solver_authority = pubkey_from_seed("solver authority");
176 let reclaim_authority = pubkey_from_seed("reclaim authority");
177 let settlement_owned_order_authority = pubkey_from_seed("settlement-owned-order authority");
178 let data = Instruction::from(Initialize {
179 program_id,
180 payer: *payer.address(),
181 state_pda: *state_pda.address(),
182 native_sol_buffer: *native_sol_buffer.address(),
183 manager,
184 solver_authority,
185 reclaim_authority,
186 settlement_owned_order_authority,
187 })
188 .data;
189
190 let system_program = fake_account(pubkey_from_seed("system program"));
191 let accounts = [payer, state_pda, native_sol_buffer, system_program];
192
193 let InitializeInput {
194 payer: parsed_payer,
195 state_pda: parsed_state_pda,
196 native_sol_buffer: parsed_native_sol_buffer,
197 manager: parsed_manager,
198 solver_authority: parsed_solver_authority,
199 reclaim_authority: parsed_reclaim_authority,
200 settlement_owned_order_authority: parsed_settlement_owned_order_authority,
201 } = InitializeInput::parse(&data, &accounts).expect("parse should succeed");
202
203 assert_eq!(parsed_payer.address(), payer.address());
204 assert_eq!(parsed_state_pda.address(), state_pda.address());
205 assert_eq!(
206 parsed_native_sol_buffer.address(),
207 native_sol_buffer.address()
208 );
209 assert_eq!(parsed_manager, manager);
210 assert_eq!(parsed_solver_authority, solver_authority);
211 assert_eq!(parsed_reclaim_authority, reclaim_authority);
212 assert_eq!(
213 parsed_settlement_owned_order_authority,
214 settlement_owned_order_authority
215 );
216 }
217
218 #[test]
219 fn initialize_input_rejects_long_data() {
220 let mut data = initialize_data();
221 data.push(0); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
223 assert_eq!(
224 InitializeInput::parse(&data, &accounts).err(),
225 Some(ProgramError::InvalidInstructionData),
226 );
227 }
228
229 #[test]
230 fn initialize_input_rejects_short_data() {
231 let mut data = initialize_data();
232 data.pop(); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
234 assert_eq!(
235 InitializeInput::parse(&data, &accounts).err(),
236 Some(ProgramError::InvalidInstructionData),
237 );
238 }
239
240 #[test]
241 fn initialize_input_rejects_missing_accounts() {
242 let data = initialize_data();
243 let mut accounts: Vec<AccountView> = fake_sequential_accounts::<NUM_ACCOUNTS>().into();
244 accounts.pop();
245 assert_eq!(
246 InitializeInput::parse(&data, &accounts).err(),
247 Some(ProgramError::NotEnoughAccountKeys),
248 );
249 }
250
251 #[test]
252 fn instruction_data_has_expected_layout() {
253 let program_id = pubkey_from_seed("program id");
254 let payer = pubkey_from_seed("payer");
255 let state_pda = pubkey_from_seed("state pda");
256 let native_sol_buffer = pubkey_from_seed("native sol buffer");
257 let manager = pubkey_from_seed("manager");
258 let solver_authority = pubkey_from_seed("solver authority");
259 let reclaim_authority = pubkey_from_seed("reclaim authority");
260 let settlement_owned_order_authority = pubkey_from_seed("settlement-owned-order authority");
261
262 let Instruction { data, .. } = Initialize {
263 program_id,
264 payer,
265 state_pda,
266 native_sol_buffer,
267 manager,
268 solver_authority,
269 reclaim_authority,
270 settlement_owned_order_authority,
271 }
272 .into();
273 assert_eq!(data.len(), 1 + 4 * core::mem::size_of::<Pubkey>());
274 assert_eq!(data[0], SettlementInstruction::Initialize.discriminator());
275 assert_eq!(&data[1..33], &manager.to_bytes());
276 assert_eq!(&data[33..65], &solver_authority.to_bytes());
277 assert_eq!(&data[65..97], &reclaim_authority.to_bytes());
278 assert_eq!(&data[97..], &settlement_owned_order_authority.to_bytes());
279 }
280
281 #[test]
282 fn instruction_data_regression() {
283 let manager = Pubkey::new_from_array([0x11; 32]);
284 let solver_authority = Pubkey::new_from_array([0x22; 32]);
285 let reclaim_authority = Pubkey::new_from_array([0x33; 32]);
286 let settlement_owned_order_authority = Pubkey::new_from_array([0x44; 32]);
287
288 let Instruction { data, .. } = Initialize {
289 program_id: pubkey_from_seed("program id"),
290 payer: pubkey_from_seed("payer"),
291 state_pda: pubkey_from_seed("state pda"),
292 native_sol_buffer: pubkey_from_seed("native sol buffer"),
293 manager,
294 solver_authority,
295 reclaim_authority,
296 settlement_owned_order_authority,
297 }
298 .into();
299
300 #[rustfmt::skip]
301 let expected: [u8; 1 + 4 * core::mem::size_of::<Pubkey>()] = [
302 0x03,
304 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
306 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
307 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
308 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
309 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
311 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
312 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
313 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
314 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
316 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
317 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
318 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
319 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
321 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
322 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
323 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
324 ];
325 assert_eq!(data, expected);
326 }
327
328 #[test]
329 fn instruction_has_expected_accounts() {
330 let program_id = pubkey_from_seed("program id");
331 let payer = pubkey_from_seed("payer");
332 let state_pda = pubkey_from_seed("state pda");
333 let native_sol_buffer = pubkey_from_seed("native sol buffer");
334 let manager = pubkey_from_seed("manager");
335 let solver_authority = pubkey_from_seed("solver authority");
336 let reclaim_authority = pubkey_from_seed("reclaim authority");
337 let settlement_owned_order_authority = pubkey_from_seed("settlement-owned-order authority");
338
339 let Instruction { accounts, .. } = Initialize {
340 program_id,
341 payer,
342 state_pda,
343 native_sol_buffer,
344 manager,
345 solver_authority,
346 reclaim_authority,
347 settlement_owned_order_authority,
348 }
349 .into();
350
351 assert_eq!(accounts.len(), 4);
352 assert_writable_signer(&accounts[0], payer);
356 assert_writable_nonsigner(&accounts[1], state_pda);
357 assert_writable_nonsigner(&accounts[2], native_sol_buffer);
358 assert_readonly_nonsigner(&accounts[3], SYSTEM_PROGRAM_ID);
359 }
360}