cow_settlement_interface/instruction/
remove_solver.rs1use core::mem::size_of;
9
10use solana_instruction::{AccountMeta, Instruction};
11use solana_program_error::ProgramError;
12use solana_pubkey::Pubkey;
13
14use crate::instruction::InstructionInputParsing;
15use crate::SettlementInstruction;
16
17pub struct RemoveSolver {
27 pub program_id: Pubkey,
28 pub authority: Pubkey,
29 pub rent_recipient: Pubkey,
30 pub state_pda: Pubkey,
31 pub solver: Pubkey,
32}
33
34impl From<RemoveSolver> for Instruction {
35 fn from(builder: RemoveSolver) -> Self {
36 let mut data = vec![SettlementInstruction::RemoveSolver.discriminator()];
37 data.extend_from_slice(&builder.solver.to_bytes());
38 Instruction {
39 program_id: builder.program_id,
40 accounts: vec![
41 AccountMeta::new_readonly(builder.authority, true),
42 AccountMeta::new(builder.rent_recipient, false),
43 AccountMeta::new(builder.state_pda, false),
44 ],
45 data,
46 }
47 }
48}
49
50pub struct RemoveSolverInput<'a, A> {
52 pub authority: &'a A,
53 pub rent_recipient: &'a A,
54 pub state_pda: &'a A,
55 pub solver: Pubkey,
56}
57
58impl<'a, A> InstructionInputParsing<'a, A> for RemoveSolverInput<'a, A> {
59 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::RemoveSolver;
60
61 fn parse_body(instruction_data: &[u8], accounts: &'a [A]) -> Result<Self, ProgramError> {
62 let solver: &[u8; size_of::<Pubkey>()] = instruction_data
63 .try_into()
64 .map_err(|_| ProgramError::InvalidInstructionData)?;
65 let solver = Pubkey::new_from_array(*solver);
66
67 let [authority, rent_recipient, state_pda, ..] = accounts else {
69 return Err(ProgramError::NotEnoughAccountKeys);
70 };
71
72 Ok(Self {
73 authority,
74 rent_recipient,
75 state_pda,
76 solver,
77 })
78 }
79}
80
81#[cfg(any(test, feature = "test-fixtures"))]
84pub mod fixtures {
85 use solana_address::Address;
86
87 use super::{Instruction, RemoveSolver};
88
89 pub const NUM_ACCOUNTS: usize = 3;
92
93 pub fn remove_solver_data() -> Vec<u8> {
96 let zero = Address::new_from_array([0; 32]);
97 Instruction::from(RemoveSolver {
98 program_id: zero,
99 authority: zero,
100 rent_recipient: zero,
101 state_pda: zero,
102 solver: zero,
103 })
104 .data
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::fixtures::{remove_solver_data, NUM_ACCOUNTS};
111 use super::*;
112 use crate::fixtures::pubkey_from_seed;
113 use crate::instruction::fixtures::{fake_account, fake_sequential_accounts};
114 use crate::instruction::tests::{assert_readonly_signer, assert_writable_nonsigner};
115 use solana_account_view::AccountView;
116
117 #[test]
118 fn remove_solver_input_parses_valid_input() {
119 let program_id = pubkey_from_seed("program id");
120 let authority = fake_account(pubkey_from_seed("solver authority"));
121 let rent_recipient = fake_account(pubkey_from_seed("rent recipient"));
122 let state_pda = fake_account(pubkey_from_seed("state pda"));
123 let solver = pubkey_from_seed("solver");
124
125 let data = Instruction::from(RemoveSolver {
126 program_id,
127 authority: *authority.address(),
128 rent_recipient: *rent_recipient.address(),
129 state_pda: *state_pda.address(),
130 solver,
131 })
132 .data;
133 let accounts = [authority, rent_recipient, state_pda];
134
135 let RemoveSolverInput {
136 authority: parsed_authority,
137 rent_recipient: parsed_rent_recipient,
138 state_pda: parsed_state_pda,
139 solver: parsed_solver,
140 } = RemoveSolverInput::parse(&data, &accounts).expect("parse should succeed");
141
142 assert_eq!(parsed_authority.address(), accounts[0].address());
143 assert_eq!(parsed_rent_recipient.address(), accounts[1].address());
144 assert_eq!(parsed_state_pda.address(), accounts[2].address());
145 assert_eq!(parsed_solver, solver);
146 }
147
148 #[test]
149 fn remove_solver_input_rejects_long_data() {
150 let mut data = remove_solver_data();
151 data.push(0); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
153 assert_eq!(
154 RemoveSolverInput::parse(&data, &accounts).err(),
155 Some(ProgramError::InvalidInstructionData),
156 );
157 }
158
159 #[test]
160 fn remove_solver_input_rejects_short_data() {
161 let mut data = remove_solver_data();
162 data.pop(); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
164 assert_eq!(
165 RemoveSolverInput::parse(&data, &accounts).err(),
166 Some(ProgramError::InvalidInstructionData),
167 );
168 }
169
170 #[test]
171 fn remove_solver_input_rejects_missing_accounts() {
172 let data = remove_solver_data();
173 let mut accounts: Vec<AccountView> = fake_sequential_accounts::<NUM_ACCOUNTS>().into();
174 accounts.pop();
175 assert_eq!(
176 RemoveSolverInput::parse(&data, &accounts).err(),
177 Some(ProgramError::NotEnoughAccountKeys),
178 );
179 }
180
181 #[test]
182 fn instruction_data_has_expected_layout() {
183 let solver = pubkey_from_seed("solver");
184 let Instruction { data, .. } = RemoveSolver {
185 program_id: pubkey_from_seed("program id"),
186 authority: pubkey_from_seed("solver authority"),
187 rent_recipient: pubkey_from_seed("rent recipient"),
188 state_pda: pubkey_from_seed("state pda"),
189 solver,
190 }
191 .into();
192
193 assert_eq!(data.len(), 1 + size_of::<Pubkey>());
194 assert_eq!(data[0], SettlementInstruction::RemoveSolver.discriminator());
195 assert_eq!(&data[1..], &solver.to_bytes());
196 }
197
198 #[test]
199 fn instruction_data_regression() {
200 let solver = Pubkey::new_from_array([0x11; 32]);
201 let Instruction { data, .. } = RemoveSolver {
202 program_id: pubkey_from_seed("program id"),
203 authority: pubkey_from_seed("solver authority"),
204 rent_recipient: pubkey_from_seed("rent recipient"),
205 state_pda: pubkey_from_seed("state pda"),
206 solver,
207 }
208 .into();
209
210 #[rustfmt::skip]
211 let expected: [u8; 1 + size_of::<Pubkey>()] = [
212 0x09,
214 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
216 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
217 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
218 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
219 ];
220 assert_eq!(data, expected);
221 }
222
223 #[test]
224 fn instruction_has_expected_accounts() {
225 let authority = pubkey_from_seed("solver authority");
226 let rent_recipient = pubkey_from_seed("rent recipient");
227 let state_pda = pubkey_from_seed("state pda");
228 let Instruction { accounts, .. } = RemoveSolver {
229 program_id: pubkey_from_seed("program id"),
230 authority,
231 rent_recipient,
232 state_pda,
233 solver: pubkey_from_seed("solver"),
234 }
235 .into();
236
237 assert_eq!(accounts.len(), 3);
238 assert_readonly_signer(&accounts[0], authority);
241 assert_writable_nonsigner(&accounts[1], rent_recipient);
242 assert_writable_nonsigner(&accounts[2], state_pda);
243 }
244}