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