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