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