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