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