cow_settlement_interface/instruction/
create_order.rs1use solana_instruction::{AccountMeta, Instruction};
8use solana_program_error::ProgramError;
9use solana_pubkey::Pubkey;
10
11pub use solana_system_interface::program::ID as SYSTEM_PROGRAM_ID;
12
13use super::InstructionInputParsing;
14use crate::{data::intent::EncodedOrderIntent, SettlementInstruction};
15
16pub struct CreateOrder {
50 pub program_id: Pubkey,
51 pub owner: Pubkey,
52 pub created_by: Pubkey,
53 pub order_pda: Pubkey,
54 pub intent_bytes: [u8; EncodedOrderIntent::SIZE],
55}
56
57impl From<CreateOrder> for Instruction {
58 fn from(builder: CreateOrder) -> Self {
59 let mut data = Vec::with_capacity(1 + EncodedOrderIntent::SIZE);
60 data.push(SettlementInstruction::CreateOrder.discriminator());
61 data.extend_from_slice(&builder.intent_bytes);
62
63 Instruction {
64 program_id: builder.program_id,
65 accounts: vec![
66 AccountMeta::new_readonly(builder.owner, true),
67 AccountMeta::new(builder.created_by, true),
68 AccountMeta::new(builder.order_pda, false),
69 AccountMeta::new_readonly(SYSTEM_PROGRAM_ID, false),
70 ],
71 data,
72 }
73 }
74}
75
76pub struct CreateOrderInput<'a, A> {
78 pub intent_bytes: [u8; EncodedOrderIntent::SIZE],
79 pub owner: &'a A,
80 pub created_by: &'a A,
81 pub order_pda: &'a A,
82}
83
84impl<'a, A> InstructionInputParsing<'a, A> for CreateOrderInput<'a, A> {
85 const DISCRIMINATOR: SettlementInstruction = SettlementInstruction::CreateOrder;
86
87 fn parse_body(instruction_data: &'a [u8], accounts: &'a [A]) -> Result<Self, ProgramError> {
88 if instruction_data.len() != EncodedOrderIntent::SIZE {
91 return Err(ProgramError::InvalidInstructionData);
92 }
93 let [owner, created_by, order_pda, _, ..] = accounts else {
98 return Err(ProgramError::NotEnoughAccountKeys);
99 };
100
101 let intent_bytes: [u8; EncodedOrderIntent::SIZE] =
102 instruction_data.try_into().expect("length checked above");
103
104 Ok(Self {
105 intent_bytes,
106 owner,
107 created_by,
108 order_pda,
109 })
110 }
111}
112
113#[cfg(any(test, feature = "test-fixtures"))]
116pub mod fixtures {
117 use solana_address::Address;
118
119 use super::{CreateOrder, Instruction};
120 use crate::data::intent::{fixtures::sample_intent, EncodedOrderIntent};
121
122 pub const NUM_ACCOUNTS: usize = 4;
125
126 pub fn valid_intent_bytes() -> [u8; EncodedOrderIntent::SIZE] {
130 (&EncodedOrderIntent::from(&sample_intent(Default::default()))).into()
131 }
132
133 pub fn default_order_data(intent_bytes: &[u8; EncodedOrderIntent::SIZE]) -> Vec<u8> {
136 let zero = Address::new_from_array([0; 32]);
137 Instruction::from(CreateOrder {
138 program_id: zero,
139 owner: zero,
140 created_by: zero,
141 order_pda: zero,
142 intent_bytes: *intent_bytes,
143 })
144 .data
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::fixtures::{default_order_data, valid_intent_bytes, NUM_ACCOUNTS};
151 use super::*;
152 use crate::instruction::fixtures::{
153 fake_account, fake_account_from_array, fake_sequential_accounts,
154 };
155 use crate::instruction::tests::{
156 assert_readonly_nonsigner, assert_readonly_signer, assert_writable_nonsigner,
157 assert_writable_signer,
158 };
159 use solana_account_view::AccountView;
160 use solana_address::Address;
161
162 #[test]
163 fn create_order_input_parses_valid_input() {
164 let program_id = Address::new_from_array([21; 32]);
165 let owner = Address::new_from_array([22; 32]);
166 let created_by = Address::new_from_array([24; 32]);
167 let order_pda = Address::new_from_array([23; 32]);
168 let intent_bytes = valid_intent_bytes();
169
170 let data = Instruction::from(CreateOrder {
171 program_id,
172 owner,
173 created_by,
174 order_pda,
175 intent_bytes,
176 })
177 .data;
178 let accounts = [
179 fake_account(owner),
180 fake_account(created_by),
181 fake_account(order_pda),
182 fake_account_from_array([4; 32]),
183 ];
184
185 let CreateOrderInput {
186 intent_bytes: derived_intent_bytes,
187 owner: derived_owner,
188 created_by: derived_created_by,
189 order_pda: derived_order_pda,
190 } = CreateOrderInput::parse(&data, &accounts).expect("parse should succeed");
191
192 assert_eq!(derived_intent_bytes, intent_bytes);
193 assert_eq!(*derived_order_pda.address(), order_pda);
194 assert_eq!(*derived_owner.address(), owner);
195 assert_eq!(*derived_created_by.address(), created_by);
196 }
197
198 #[test]
199 fn create_order_input_rejects_short_data() {
200 let intent_bytes = valid_intent_bytes();
201 let mut data = default_order_data(&intent_bytes);
202 data.pop();
203 let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
204 assert_eq!(
205 CreateOrderInput::parse(&data, &accounts).err(),
206 Some(ProgramError::InvalidInstructionData),
207 );
208 }
209
210 #[test]
211 fn create_order_input_rejects_long_data() {
212 let intent_bytes = valid_intent_bytes();
213 let mut data = default_order_data(&intent_bytes);
214 data.push(0); let accounts = fake_sequential_accounts::<NUM_ACCOUNTS>();
216 assert_eq!(
217 CreateOrderInput::parse(&data, &accounts).err(),
218 Some(ProgramError::InvalidInstructionData),
219 );
220 }
221
222 #[test]
223 fn create_order_input_rejects_missing_accounts() {
224 let intent_bytes = valid_intent_bytes();
225 let data = default_order_data(&intent_bytes);
226 let mut accounts: Vec<AccountView> = fake_sequential_accounts::<NUM_ACCOUNTS>().into();
227 accounts.pop();
228 assert_eq!(
229 CreateOrderInput::parse(&data, &accounts).err(),
230 Some(ProgramError::NotEnoughAccountKeys),
231 );
232 }
233
234 #[test]
235 fn instruction_data_has_expected_layout() {
236 let program_id = Pubkey::new_from_array([1; 32]);
237 let owner = Pubkey::new_from_array([2; 32]);
238 let created_by = Pubkey::new_from_array([4; 32]);
239 let order_pda = Pubkey::new_from_array([3; 32]);
240 let intent_bytes = [0x42u8; EncodedOrderIntent::SIZE];
241
242 let Instruction { data, .. } = CreateOrder {
243 program_id,
244 owner,
245 created_by,
246 order_pda,
247 intent_bytes,
248 }
249 .into();
250
251 assert_eq!(data.len(), 1 + EncodedOrderIntent::SIZE);
252 assert_eq!(data[0], SettlementInstruction::CreateOrder.discriminator());
253 assert_eq!(&data[1..], &intent_bytes);
254 }
255
256 #[test]
257 fn instruction_data_has_expected_accounts() {
258 let program_id = Pubkey::new_from_array([1; 32]);
259 let owner = Pubkey::new_from_array([2; 32]);
260 let created_by = Pubkey::new_from_array([4; 32]);
261 let order_pda = Pubkey::new_from_array([3; 32]);
262 let intent_bytes = [0u8; EncodedOrderIntent::SIZE];
263
264 let Instruction { accounts, .. } = CreateOrder {
265 program_id,
266 owner,
267 created_by,
268 order_pda,
269 intent_bytes,
270 }
271 .into();
272
273 assert_eq!(accounts.len(), 4);
274 assert_readonly_signer(&accounts[0], owner);
278 assert_writable_signer(&accounts[1], created_by);
279 assert_writable_nonsigner(&accounts[2], order_pda);
280 assert_readonly_nonsigner(&accounts[3], SYSTEM_PROGRAM_ID);
281 }
282}