-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
244 lines (223 loc) · 8.68 KB
/
Copy pathlib.rs
File metadata and controls
244 lines (223 loc) · 8.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//! Shared types and instruction builders for the CoW Protocol settlement program.
pub use solana_instruction::{AccountMeta, Instruction};
use solana_program_error::ProgramError;
pub use solana_pubkey::Pubkey;
solana_pubkey::declare_id!("MooohhPEAAHwAwEozL7JPEmnDvaahuUpccYN4Yb8ccK");
pub mod data;
pub mod instruction;
pub mod pda;
#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
#[repr(u8)]
#[num_enum(error_type(
name = ProgramError,
constructor = SettlementInstruction::unknown_discriminator,
))]
pub enum SettlementInstruction {
BeginSettle = 0,
FinalizeSettle = 1,
CreateOrder = 2,
Initialize = 3,
CreateBuffer = 4,
ReclaimOrder = 5,
}
impl SettlementInstruction {
pub fn discriminator(self) -> u8 {
self as u8
}
fn unknown_discriminator(_: u8) -> ProgramError {
ProgramError::InvalidInstructionData
}
}
/// Identifies the account type a given account's data belongs to, via the
/// single discriminator byte stored at its front. Starts at 128 to keep
/// account discriminators visually distinct from instruction discriminators.
#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
#[repr(u8)]
#[num_enum(error_type(
name = ProgramError,
constructor = SettlementAccount::unknown_discriminator,
))]
pub enum SettlementAccount {
OrderAccount = 128,
SettlementState = 129,
}
impl SettlementAccount {
pub const fn discriminator(self) -> u8 {
self as u8
}
fn unknown_discriminator(_: u8) -> ProgramError {
ProgramError::InvalidAccountData
}
}
/// Recover the discriminator from the first byte of the payload and the
/// remaining bytes to parse.
/// Returns `InvalidInstructionData` for an insufficient length or an
/// unknown discriminator.
pub fn recover_discriminator(
instruction_data: &[u8],
) -> Result<(SettlementInstruction, &[u8]), ProgramError> {
let discriminator = instruction_data
.first()
.copied()
.ok_or(ProgramError::InvalidInstructionData)
.and_then(SettlementInstruction::try_from)?;
Ok((discriminator, &instruction_data[1..]))
}
/// Program-side errors surfaced by the settlement program.
/// The discriminant value is the on-chain `ProgramError::Custom` code.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum SettlementError {
/// The `FinalizeSettle` included as input to `BeginSettle` isn't before
/// the actual `BeginSettle` index.
FinalizeBeforeInitialize = 0,
/// Another `BeginSettle`/`FinalizeSettle` of this program appears strictly
/// between this pair's bounds, nesting or overlapping two settlements.
BeginFinalizePairOverlap = 1,
/// The counterpart index points past the end of the transaction's
/// instruction list, so no instruction sits there.
MissingCounterpartInstruction = 2,
/// The instruction at the counterpart index belongs to a different program.
CounterpartIsExternal = 3,
/// The counterpart instruction's discriminator byte couldn't be recovered
/// from its data.
InvalidCounterpartDiscriminator = 4,
/// The counterpart instruction's own counterpart index couldn't be
/// recovered from its data.
InvalidCounterpartCounterpart = 5,
/// The counterpart's discriminator isn't the expected
/// `BeginSettle`/`FinalizeSettle` kind, or its counterpart index doesn't
/// point back at this instruction.
MismatchedCounterpartDiscriminator = 6,
/// `CreateOrder` instruction wasn't signed by the created `OrderIntent`
/// owner.
OwnerMismatch = 7,
/// An account was provided that cannot be derived from the seeds recognized by the program
AccountNotDerivable = 8,
/// `BeginSettle`'s order accounts aren't passed strictly increasing by
/// address.
OrdersNotStrictlyIncreasing = 9,
/// A `BeginSettle` sell token account doesn't match the
/// `sell_token_account` recorded in the order's intent.
SellTokenAccountMismatch = 10,
/// A `BeginSettle` sell token account isn't a valid SPL token account
/// (wrong data length or not owned by the token program).
SellTokenAccountInvalid = 11,
/// A `BeginSettle` sell token account's SPL owner isn't the order's intent
/// owner.
SellTokenOwnerMismatch = 12,
/// `BeginSettle`'s order-account count doesn't match the structure its
/// instruction data expects: `n` orders each contribute an order PDA and a
/// sell token account, plus one destination account per transfer.
AccountCountNotMatchingOrderCount = 13,
/// `BeginSettle` or `FinalizeSettle` was invoked via CPI rather than as a
/// top-level transaction instruction.
CalledViaCpi = 14,
/// A `BeginSettle` order has been cancelled by its owner and can no longer
/// be settled.
OrderCancelled = 15,
/// A `BeginSettle` order's `valid_to` lies in the past: the order has
/// expired and can no longer be settled.
OrderExpired = 16,
/// The transfer counts in `BeginSettle` don't sum to the number of transfer
/// amounts, so destinations and amounts can't be paired up exactly.
TransferCountMismatch = 17,
/// `BeginSettle`'s state account isn't the canonical settlement state PDA,
/// which must sign the pulls as the user's token delegate.
StateAccountMismatch = 18,
/// `FinalizeSettle`'s push-account count doesn't match its instruction
/// data: each push contributes a source buffer and a destination account,
/// so the count must be twice the number of push amounts.
AccountCountNotMatchingPushCount = 19,
/// `BeginSettle`: the number of pushes carried by the paired `FinalizeSettle`
/// doesn't equal the number of settled orders. Each order must be paid by
/// exactly one push.
SettledOrderPushCountMismatch = 20,
/// `BeginSettle`: a paired `FinalizeSettle` push doesn't send its proceeds
/// to the order's buy token account; its destination differs from the
/// `buy_token_account` in the order's intent.
PushDestinationMismatch = 21,
/// `FinalizeSettle`: a push doesn't draw funds from the canonical buffer
/// for its destination's mint.
PushSourceNotBuffer = 22,
/// `FinalizeSettle`: a push's destination isn't a valid SPL token account
/// (wrong data length or not owned by the token program), so its mint can't
/// be read to derive the buffer.
InvalidBuyTokenAccount = 23,
/// `ReclaimOrder` was called before the order's `valid_to` has elapsed.
OrderNotExpired = 24,
/// `ReclaimOrder`'s `reclaim_recipient` account doesn't match the
/// `created_by` address recorded in the order.
ReclaimRecipientMismatch = 25,
}
impl From<SettlementError> for u32 {
fn from(e: SettlementError) -> Self {
e as u32
}
}
impl From<SettlementError> for solana_program_error::ProgramError {
fn from(e: SettlementError) -> Self {
Self::Custom(e.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_empty_payload() {
assert_eq!(
recover_discriminator(&[]),
Err(ProgramError::InvalidInstructionData),
);
}
#[test]
fn rejects_unknown_discriminator() {
// 42 is outside the set of valid discriminators.
assert_eq!(
recover_discriminator(&[42]),
Err(ProgramError::InvalidInstructionData),
);
}
#[test]
fn forwards_trailing_bytes() {
assert!(matches!(
recover_discriminator(&[
SettlementInstruction::BeginSettle.discriminator(),
42 // unused
]),
Ok((SettlementInstruction::BeginSettle, [42])),
));
}
#[test]
fn settlement_instruction_try_from_partitions_all_bytes() {
for i in u8::MIN..=u8::MAX {
match SettlementInstruction::try_from(i) {
Ok(ix) => assert_eq!(ix as u8, i),
Err(err) => assert_eq!(err, ProgramError::InvalidInstructionData),
}
}
}
#[test]
fn settlement_instruction_try_from_matches_begin_settle() {
assert_eq!(
SettlementInstruction::try_from(0),
Ok(SettlementInstruction::BeginSettle)
);
}
#[test]
fn settlement_account_try_from_partitions_all_bytes() {
for i in u8::MIN..=u8::MAX {
match SettlementAccount::try_from(i) {
Ok(account) => assert_eq!(account as u8, i),
Err(err) => assert_eq!(err, ProgramError::InvalidAccountData),
}
}
}
#[test]
fn settlement_account_discriminators_are_distinct() {
assert_ne!(
SettlementAccount::OrderAccount.discriminator(),
SettlementAccount::SettlementState.discriminator(),
);
}
}