cow_settlement_interface/instruction/
initialize.rs1use solana_instruction::{AccountMeta, Instruction};
6use solana_program_error::ProgramError;
7use solana_pubkey::Pubkey;
8
9pub use solana_system_interface::program::ID as SYSTEM_PROGRAM_ID;
10
11use super::InstructionInputParsing;
12use crate::SettlementInstruction;
13
14pub struct Initialize {
38 pub program_id: Pubkey,
39 pub payer: Pubkey,
40 pub state_pda: Pubkey,
41 pub reclaim_authority: Pubkey,
42}
43
44impl From<Initialize> for Instruction {
45 fn from(builder: Initialize) -> Self {
46 let mut data = vec![SettlementInstruction::Initialize.discriminator()];
47 data.extend_from_slice(&builder.reclaim_authority.to_bytes());
48 Instruction {
49 program_id: builder.program_id,
50 accounts: vec![
51 AccountMeta::new(builder.payer, true),
52 AccountMeta::new(builder.state_pda, false),
53 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
54 ],
55 data,
56 }
57 }
58}
59
60pub struct InitializeInput<'a, A> {
62 pub payer: &'a A,
63 pub state_pda: &'a mut A,
64 pub reclaim_authority: Pubkey,
65}
66
67impl<'a, A> InstructionInputParsing<'a, A> for InitializeInput<'a, A> {
68 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::Initialize;
69
70 fn parse_body(instruction_data: &[u8], accounts: &'a mut [A]) -> Result<Self, ProgramError> {
71 let reclaim_authority: [u8; 32] = instruction_data
72 .try_into()
73 .map_err(|_| ProgramError::InvalidInstructionData)?;
74 let reclaim_authority = Pubkey::new_from_array(reclaim_authority);
75
76 let [payer, state_pda, _system, ..] = accounts else {
80 return Err(ProgramError::NotEnoughAccountKeys);
81 };
82
83 Ok(Self {
84 payer,
85 state_pda,
86 reclaim_authority,
87 })
88 }
89}
90
91#[cfg(any(test, feature = "test-fixtures"))]
94pub mod fixtures {
95 use solana_address::Address;
96
97 use super::{Initialize, Instruction};
98
99 pub const NUM_ACCOUNTS: usize = 3;
101
102 pub fn initialize_data() -> Vec<u8> {
105 let zero = Address::new_from_array([0; 32]);
106 Instruction::from(Initialize {
107 program_id: zero,
108 payer: zero,
109 state_pda: zero,
110 reclaim_authority: zero,
111 })
112 .data
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::fixtures::{initialize_data, NUM_ACCOUNTS};
119 use super::*;
120 use crate::data::state::EncodedStateAccount;
121 use crate::instruction::fixtures::{fake_account_from_array, fake_sequential_accounts};
122 use crate::instruction::tests::{
123 assert_readonly_nonsigner, assert_writable_nonsigner, assert_writable_signer,
124 };
125 use solana_account_view::AccountView;
126 use solana_address::Address;
127
128 #[test]
129 fn initialize_input_parses_valid_input() {
130 let program_id = Address::new_unique();
131 let payer = fake_account_from_array([1; 32]);
132 let state_pda = fake_account_from_array([2; 32]);
133 let reclaim_authority = Address::new_from_array([4; 32]);
134 let data = Instruction::from(Initialize {
135 program_id,
136 payer: *payer.address(),
137 state_pda: *state_pda.address(),
138 reclaim_authority,
139 })
140 .data;
141
142 let system_program = fake_account_from_array([3; 32]);
143 let mut accounts = [payer, state_pda, system_program];
144
145 let InitializeInput {
146 payer: parsed_payer,
147 state_pda: parsed_state_pda,
148 reclaim_authority: parsed_reclaim_authority,
149 } = InitializeInput::parse(&data, &mut accounts).expect("parse should succeed");
150
151 assert_eq!(parsed_payer.address(), payer.address());
152 assert_eq!(parsed_state_pda.address(), state_pda.address());
153 assert_eq!(parsed_reclaim_authority, reclaim_authority);
154 }
155
156 #[test]
157 fn initialize_input_rejects_long_data() {
158 let mut data = initialize_data();
159 data.push(0); let mut accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
161 assert_eq!(
162 InitializeInput::parse(&data, &mut accounts).err(),
163 Some(ProgramError::InvalidInstructionData),
164 );
165 }
166
167 #[test]
168 fn initialize_input_rejects_short_data() {
169 let mut data = initialize_data();
170 data.pop(); let mut accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
172 assert_eq!(
173 InitializeInput::parse(&data, &mut accounts).err(),
174 Some(ProgramError::InvalidInstructionData),
175 );
176 }
177
178 #[test]
179 fn initialize_input_rejects_missing_accounts() {
180 let data = initialize_data();
181 let mut accounts: Vec<AccountView> = fake_sequential_accounts::<NUM_ACCOUNTS>().into();
182 accounts.pop();
183 assert_eq!(
184 InitializeInput::parse(&data, &mut accounts).err(),
185 Some(ProgramError::NotEnoughAccountKeys),
186 );
187 }
188
189 #[test]
190 fn instruction_data_has_expected_layout() {
191 let program_id = Pubkey::new_from_array([1; 32]);
192 let payer = Pubkey::new_from_array([2; 32]);
193 let state_pda = Pubkey::new_from_array([3; 32]);
194 let reclaim_authority = Pubkey::new_from_array([4; 32]);
195
196 let Instruction { data, .. } = Initialize {
197 program_id,
198 payer,
199 state_pda,
200 reclaim_authority,
201 }
202 .into();
203 assert_eq!(data.len(), EncodedStateAccount::SIZE);
204 assert_eq!(data[0], SettlementInstruction::Initialize.discriminator());
205 assert_eq!(&data[1..], &reclaim_authority.to_bytes());
206 }
207
208 #[test]
209 fn instruction_has_expected_accounts() {
210 let program_id = Pubkey::new_from_array([1; 32]);
211 let payer = Pubkey::new_from_array([2; 32]);
212 let state_pda = Pubkey::new_from_array([3; 32]);
213 let reclaim_authority = Pubkey::new_from_array([4; 32]);
214
215 let Instruction { accounts, .. } = Initialize {
216 program_id,
217 payer,
218 state_pda,
219 reclaim_authority,
220 }
221 .into();
222
223 assert_eq!(accounts.len(), 3);
224 assert_writable_signer(&accounts[0], payer);
227 assert_writable_nonsigner(&accounts[1], state_pda);
228 assert_readonly_nonsigner(&accounts[2], SYSTEM_PROGRAM_ID);
229 }
230}