cow_settlement_interface/instruction/
create_self_order.rs1use solana_instruction::{AccountMeta, Instruction};
4use solana_program_error::ProgramError;
5use solana_pubkey::Pubkey;
6
7pub use solana_system_interface::program::ID as SYSTEM_PROGRAM_ID;
8
9use super::InstructionInputParsing;
10use crate::{data::intent::EncodedOrderIntent, SettlementInstruction};
11
12pub struct CreateSelfOrder {
36 pub program_id: Pubkey,
37 pub authority: Pubkey,
38 pub created_by: Pubkey,
39 pub state_pda: Pubkey,
40 pub order_pda: Pubkey,
41 pub intent_bytes: [u8; EncodedOrderIntent::SIZE],
42}
43
44impl From<CreateSelfOrder> for Instruction {
45 fn from(builder: CreateSelfOrder) -> Self {
46 let mut data = Vec::with_capacity(1 + EncodedOrderIntent::SIZE);
47 data.push(SettlementInstruction::CreateSelfOrder.discriminator());
48 data.extend_from_slice(&builder.intent_bytes);
49
50 Instruction {
51 program_id: builder.program_id,
52 accounts: vec![
53 AccountMeta::new_readonly(builder.authority, true),
54 AccountMeta::new(builder.created_by, true),
55 AccountMeta::new_readonly(builder.state_pda, false),
56 AccountMeta::new(builder.order_pda, false),
57 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
58 ],
59 data,
60 }
61 }
62}
63
64pub struct CreateSelfOrderInput<'a, A> {
66 pub intent_bytes: [u8; EncodedOrderIntent::SIZE],
67 pub authority: &'a A,
68 pub created_by: &'a A,
69 pub state_pda: &'a A,
70 pub order_pda: &'a A,
71}
72
73impl<'a, A> InstructionInputParsing<'a, A> for CreateSelfOrderInput<'a, A> {
74 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::CreateSelfOrder;
75
76 fn parse_body(instruction_data: &'a [u8], accounts: &'a [A]) -> Result<Self, ProgramError> {
77 let intent_bytes: [u8; EncodedOrderIntent::SIZE] = instruction_data
79 .try_into()
80 .map_err(|_| ProgramError::InvalidInstructionData)?;
81
82 let [authority, created_by, state_pda, order_pda, _system_program, ..] = accounts else {
87 return Err(ProgramError::NotEnoughAccountKeys);
88 };
89
90 Ok(Self {
91 intent_bytes,
92 authority,
93 created_by,
94 state_pda,
95 order_pda,
96 })
97 }
98}
99
100#[cfg(any(test, feature = "test-fixtures"))]
104pub mod fixtures {
105 use solana_address::Address;
106
107 use super::{CreateSelfOrder, EncodedOrderIntent, Instruction};
108 use crate::data::intent::OrderIntent;
109
110 pub const NUM_ACCOUNTS: usize = 5;
113
114 pub fn self_order_data(intent: &OrderIntent) -> Vec<u8> {
117 let zero = Address::new_from_array([0; 32]);
118 Instruction::from(CreateSelfOrder {
119 program_id: zero,
120 authority: zero,
121 created_by: zero,
122 state_pda: zero,
123 order_pda: zero,
124 intent_bytes: (&EncodedOrderIntent::from(intent)).into(),
125 })
126 .data
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::fixtures::{self_order_data, NUM_ACCOUNTS};
133 use super::*;
134 use crate::data::intent::fixtures::sample_intent;
135 use crate::data::intent::OrderIntent;
136 use crate::fixtures::pubkey_from_seed;
137 use crate::instruction::fixtures::{fake_account, fake_sequential_accounts};
138 use crate::instruction::tests::{
139 assert_readonly_nonsigner, assert_readonly_signer, assert_writable_nonsigner,
140 assert_writable_signer,
141 };
142 use solana_account_view::AccountView;
143
144 fn intent() -> OrderIntent {
148 sample_intent(Default::default())
149 }
150
151 #[test]
152 fn create_self_order_input_parses_valid_input() {
153 let program_id = pubkey_from_seed("program id");
154 let authority = pubkey_from_seed("authority");
155 let created_by = pubkey_from_seed("created_by");
156 let state_pda = pubkey_from_seed("state pda");
157 let order_pda = pubkey_from_seed("order pda");
158 let intent_bytes: [u8; EncodedOrderIntent::SIZE] =
159 (&EncodedOrderIntent::from(&intent())).into();
160
161 let data = Instruction::from(CreateSelfOrder {
162 program_id,
163 authority,
164 created_by,
165 state_pda,
166 order_pda,
167 intent_bytes,
168 })
169 .data;
170 let accounts = [
171 fake_account(authority),
172 fake_account(created_by),
173 fake_account(state_pda),
174 fake_account(order_pda),
175 fake_account(pubkey_from_seed("system program")),
176 ];
177
178 let CreateSelfOrderInput {
179 intent_bytes: derived_intent_bytes,
180 authority: derived_authority,
181 created_by: derived_created_by,
182 state_pda: derived_state_pda,
183 order_pda: derived_order_pda,
184 } = CreateSelfOrderInput::parse(&data, &accounts).expect("parse should succeed");
185
186 assert_eq!(derived_intent_bytes, intent_bytes);
187 assert_eq!(*derived_authority.address(), authority);
188 assert_eq!(*derived_created_by.address(), created_by);
189 assert_eq!(*derived_state_pda.address(), state_pda);
190 assert_eq!(*derived_order_pda.address(), order_pda);
191 }
192
193 #[test]
194 fn create_self_order_input_rejects_short_data() {
195 let mut data = self_order_data(&intent());
196 data.pop();
197 let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
198 assert_eq!(
199 CreateSelfOrderInput::parse(&data, &accounts).err(),
200 Some(ProgramError::InvalidInstructionData),
201 );
202 }
203
204 #[test]
205 fn create_self_order_input_rejects_long_data() {
206 let mut data = self_order_data(&intent());
207 data.push(0); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
209 assert_eq!(
210 CreateSelfOrderInput::parse(&data, &accounts).err(),
211 Some(ProgramError::InvalidInstructionData),
212 );
213 }
214
215 #[test]
216 fn create_self_order_input_rejects_missing_accounts() {
217 let data = self_order_data(&intent());
218 let mut accounts: Vec<AccountView> = fake_sequential_accounts::<NUM_ACCOUNTS>().into();
219 accounts.pop();
220 assert_eq!(
221 CreateSelfOrderInput::parse(&data, &accounts).err(),
222 Some(ProgramError::NotEnoughAccountKeys),
223 );
224 }
225
226 #[test]
227 fn instruction_data_has_expected_layout() {
228 let intent_bytes = [0x42u8; EncodedOrderIntent::SIZE];
229
230 let Instruction { data, .. } = CreateSelfOrder {
231 program_id: pubkey_from_seed("program id"),
232 authority: pubkey_from_seed("authority"),
233 created_by: pubkey_from_seed("created_by"),
234 state_pda: pubkey_from_seed("state pda"),
235 order_pda: pubkey_from_seed("order pda"),
236 intent_bytes,
237 }
238 .into();
239
240 assert_eq!(data.len(), 1 + EncodedOrderIntent::SIZE);
241 assert_eq!(
242 data[0],
243 SettlementInstruction::CreateSelfOrder.discriminator()
244 );
245 assert_eq!(&data[1..], &intent_bytes);
246 }
247
248 #[test]
249 fn instruction_data_has_expected_accounts() {
250 let program_id = pubkey_from_seed("program id");
251 let authority = pubkey_from_seed("authority");
252 let created_by = pubkey_from_seed("created_by");
253 let state_pda = pubkey_from_seed("state pda");
254 let order_pda = pubkey_from_seed("order pda");
255 let intent_bytes = [0u8; EncodedOrderIntent::SIZE];
256
257 let Instruction { accounts, .. } = CreateSelfOrder {
258 program_id,
259 authority,
260 created_by,
261 state_pda,
262 order_pda,
263 intent_bytes,
264 }
265 .into();
266
267 assert_eq!(accounts.len(), 5);
268 assert_readonly_signer(&accounts[0], authority);
272 assert_writable_signer(&accounts[1], created_by);
273 assert_readonly_nonsigner(&accounts[2], state_pda);
274 assert_writable_nonsigner(&accounts[3], order_pda);
275 assert_readonly_nonsigner(&accounts[4], SYSTEM_PROGRAM_ID);
276 }
277}