cow_settlement_interface/instruction/settle/
mod.rs1use solana_account_view::AccountView;
5use solana_address::Address;
6use solana_program_error::ProgramError;
7
8pub use crate::token_program::TokenProgram;
9pub use solana_sdk_ids::sysvar::instructions::ID as INSTRUCTIONS_SYSVAR_ID;
10
11pub const MINT_PLACEHOLDER: solana_pubkey::Pubkey = INSTRUCTIONS_SYSVAR_ID;
16
17pub trait Keyed {
21 fn key(&self) -> &Address;
22}
23
24impl Keyed for AccountView {
25 fn key(&self) -> &Address {
26 self.address()
27 }
28}
29
30impl Keyed for Address {
31 fn key(&self) -> &Address {
32 self
33 }
34}
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct MaybeMint<'a, A>(&'a A);
43
44impl<'a, A> MaybeMint<'a, A> {
45 pub fn new(slot: &'a A) -> Self {
48 Self(slot)
49 }
50}
51
52impl<'a, A: Keyed> MaybeMint<'a, A> {
53 pub fn get(&self) -> Option<&'a A> {
56 if self.0.key() == &MINT_PLACEHOLDER {
57 None
58 } else {
59 Some(self.0)
60 }
61 }
62}
63
64mod begin;
65mod finalize;
66
67pub use begin::{BeginSettle, BeginSettleInput, Pull, SettledOrder, SettledOrders};
68pub use finalize::{
69 finalize_push_data, FinalizeSettle, FinalizeSettleInput, Push, Pushes, FINALIZE_FIXED_ACCOUNTS,
70 FINALIZE_PUSH_ACCOUNTS,
71};
72
73pub fn recover_counterpart(instruction_data: &[u8]) -> Result<(u16, &[u8]), ProgramError> {
82 match instruction_data {
83 [b1, b2, rest @ ..] => Ok((u16::from_le_bytes([*b1, *b2]), rest)),
84 _ => Err(ProgramError::InvalidInstructionData),
85 }
86}
87
88#[cfg(any(test, feature = "test-fixtures"))]
89pub mod fixtures {
90 use proptest::prelude::*;
91 use solana_pubkey::Pubkey;
92
93 #[derive(Debug)]
96 pub struct ArbPushes {
97 pub source_buffers: Vec<Pubkey>,
98 pub destinations: Vec<Pubkey>,
99 pub mints: Vec<Option<Pubkey>>,
100 pub bumps: Vec<u8>,
101 pub amounts: Vec<u64>,
102 }
103
104 pub fn arb_pushes(
106 count: impl Into<prop::collection::SizeRange>,
107 ) -> impl Strategy<Value = ArbPushes> {
108 prop::collection::vec(
109 (
110 any::<[u8; 32]>().prop_map(Pubkey::new_from_array),
111 any::<[u8; 32]>().prop_map(Pubkey::new_from_array),
112 any::<Option<[u8; 32]>>().prop_map(|mint| mint.map(Pubkey::new_from_array)),
113 any::<u8>(),
114 any::<u64>(),
115 ),
116 count,
117 )
118 .prop_map(|pushes| ArbPushes {
119 source_buffers: pushes.iter().map(|push| push.0).collect(),
120 destinations: pushes.iter().map(|push| push.1).collect(),
121 mints: pushes.iter().map(|push| push.2).collect(),
122 bumps: pushes.iter().map(|push| push.3).collect(),
123 amounts: pushes.iter().map(|push| push.4).collect(),
124 })
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::fixtures::pubkey_from_seed;
132 use crate::instruction::fixtures::fake_account;
133 use hex_literal::hex;
134 use solana_address::Address;
135
136 macro_rules! ix_data {
141 ($($chunk:expr),* $(,)?) => {
142 [$(&$chunk[..]),*].concat()
143 };
144 }
145 pub(crate) use ix_data;
146
147 pub(crate) fn mint_address(mint: MaybeMint<'_, AccountView>) -> &Address {
151 mint.get().expect("a real mint").address()
152 }
153
154 #[test]
155 fn rejects_empty_payload() {
156 assert_eq!(
157 recover_counterpart(&[]),
158 Err(ProgramError::InvalidInstructionData),
159 );
160 }
161
162 #[test]
163 fn rejects_too_short_payload() {
164 assert_eq!(
165 recover_counterpart(&[42]),
166 Err(ProgramError::InvalidInstructionData),
167 );
168 }
169
170 #[test]
171 fn returns_trailing_bytes() {
172 assert_eq!(
173 recover_counterpart(
174 &[
175 &hex!("3713")[..], &[42][..], ]
178 .concat()
179 ),
180 Ok((0x1337, [42].as_slice())),
181 );
182 }
183
184 #[test]
185 fn maybe_mint_get_resolves_an_account_slot() {
186 let mint = pubkey_from_seed("a real mint");
187 let account = fake_account(mint);
188 assert_eq!(mint_address(MaybeMint::new(&account)), &mint);
189
190 let placeholder = fake_account(MINT_PLACEHOLDER);
191 assert!(MaybeMint::new(&placeholder).get().is_none());
192 }
193
194 #[test]
195 fn maybe_mint_get_resolves_an_address_slot() {
196 let mint = pubkey_from_seed("a real mint");
197 assert_eq!(MaybeMint::new(&mint).get(), Some(&mint));
198
199 assert_eq!(MaybeMint::new(&MINT_PLACEHOLDER).get(), None);
200 }
201}