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