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