Skip to main content

cow_settlement_interface/
error.rs

1//! Program-side errors surfaced by the settlement program.
2
3/// Program-side errors surfaced by the settlement program.
4/// The discriminant value is the on-chain `ProgramError::Custom` code.
5#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
6#[repr(u32)]
7pub enum SettlementError {
8    /// The `FinalizeSettle` included as input to `BeginSettle` isn't before
9    /// the actual `BeginSettle` index.
10    FinalizeBeforeInitialize = 0,
11    /// Another `BeginSettle`/`FinalizeSettle` of this program appears strictly
12    /// between this pair's bounds, nesting or overlapping two settlements.
13    BeginFinalizePairOverlap = 1,
14    /// The counterpart index points past the end of the transaction's
15    /// instruction list, so no instruction sits there.
16    MissingCounterpartInstruction = 2,
17    /// The instruction at the counterpart index belongs to a different program.
18    CounterpartIsExternal = 3,
19    /// The counterpart instruction's discriminator byte couldn't be recovered
20    /// from its data.
21    InvalidCounterpartDiscriminator = 4,
22    /// The counterpart instruction's own counterpart index couldn't be
23    /// recovered from its data.
24    InvalidCounterpartCounterpart = 5,
25    /// The counterpart's discriminator isn't the expected
26    /// `BeginSettle`/`FinalizeSettle` kind, or its counterpart index doesn't
27    /// point back at this instruction.
28    MismatchedCounterpartDiscriminator = 6,
29    /// An order-creation instruction's intent owner isn't the owner it must
30    /// have: the signer for `CreateOrder`, the settlement state PDA for
31    /// `CreateSettlementOwnedOrder`.
32    OwnerMismatch = 7,
33    /// An account was provided that cannot be derived from the seeds recognized by the program
34    AccountNotDerivable = 8,
35    /// `BeginSettle`'s order accounts aren't passed strictly increasing by
36    /// address.
37    OrdersNotStrictlyIncreasing = 9,
38    /// A `BeginSettle` sell token account doesn't match the
39    /// `sell_token_account` recorded in the order's intent.
40    SellTokenAccountMismatch = 10,
41    /// A `BeginSettle` sell token account isn't a valid SPL token account
42    /// (wrong data length or not owned by the token program).
43    SellTokenAccountInvalid = 11,
44    /// A `BeginSettle` sell token account's SPL owner isn't the order's intent
45    /// owner.
46    SellTokenOwnerMismatch = 12,
47    /// `BeginSettle`'s order-account count doesn't match the structure its
48    /// instruction data expects: `n` orders each contribute an order PDA, a
49    /// sell token account, a sell mint, plus one destination account per
50    /// transfer.
51    AccountCountNotMatchingOrderCount = 13,
52    /// `BeginSettle` or `FinalizeSettle` was invoked via CPI rather than as a
53    /// top-level transaction instruction.
54    CalledViaCpi = 14,
55    /// A `BeginSettle` order has been cancelled by its owner and can no longer
56    /// be settled.
57    OrderCancelled = 15,
58    /// A `BeginSettle` order's `valid_to` lies in the past: the order has
59    /// expired and can no longer be settled.
60    OrderExpired = 16,
61    /// The transfer counts in `BeginSettle` don't sum to the number of transfer
62    /// amounts, so destinations and amounts can't be paired up exactly.
63    TransferCountMismatch = 17,
64    /// `BeginSettle`'s state account isn't the canonical settlement state PDA,
65    /// which must sign the pulls as the user's token delegate.
66    StateAccountMismatch = 18,
67    /// `FinalizeSettle`'s push-account count doesn't match its instruction
68    /// data: each push contributes a source buffer, a destination, and a mint,
69    /// so the count must be three times the number of push amounts.
70    AccountCountNotMatchingPushCount = 19,
71    /// `BeginSettle`: the number of pushes carried by the paired `FinalizeSettle`
72    /// doesn't equal the number of settled orders. Each order must be paid by
73    /// exactly one push.
74    SettledOrderPushCountMismatch = 20,
75    /// `BeginSettle`: a paired `FinalizeSettle` push doesn't send its proceeds
76    /// to the order's buy token account; its destination differs from the
77    /// `buy_token_account` in the order's intent.
78    PushDestinationMismatch = 21,
79    /// `BeginSettle`: a paired `FinalizeSettle` push doesn't draw funds from the
80    /// canonical buffer for the order's `buy_mint`.
81    PushSourceNotBuffer = 22,
82    /// `BeginSettle`: the OrderIntent `sell_token_account` holds a different
83    /// mint than the declared `sell_mint`.
84    SellMintMismatch = 23,
85    /// `BeginSettle`: a settled order's executed price (`amount_out/amount_in`)
86    /// is worse than the order's limit price (`buy_amount/sell_amount`).
87    LimitPriceViolated = 24,
88    /// `BeginSettle`: an order's pull amounts sum to more than `u64::MAX`.
89    PullAmountOverflow = 25,
90    /// `BeginSettle`: filling this order would consume more tokens than the
91    /// maximum the user is willing to trade on this intent.
92    /// Sell: `amount_in > sell_amount`; buy: `amount_out > buy_amount`.
93    FillExceedsOrderAmount = 26,
94    /// `BeginSettle`: a non-`partially_fillable` order isn't filled exactly to
95    /// its amount (either under- or over-filled).
96    /// Sell: `amount_in != sell_amount`; buy: total `amount_out != buy_amount`.
97    OrderNotExactlyFilled = 27,
98    /// `BeginSettle`: the order's cumulative `amount_withdrawn` would exceed
99    /// `u64::MAX` once this settlement's pulls are added.
100    AmountWithdrawnOverflow = 28,
101    /// `BeginSettle`: the order's cumulative `amount_received` would exceed
102    /// `u64::MAX` once this settlement's push is added.
103    AmountReceivedOverflow = 29,
104    /// `ReclaimOrder` was called on an order that has not yet become eligible
105    /// for reclaim.
106    OrderNotReclaimable = 30,
107    /// `ReclaimOrder`'s `reclaim_recipient` account doesn't match the
108    /// `created_by` address recorded in the order.
109    ReclaimRecipientMismatch = 31,
110    /// `ReclaimBuffer`'s `reclaim_authority` account isn't a signer, or doesn't
111    /// match the `reclaim_authority` address recorded in the settlement state
112    /// PDA.
113    ReclaimAuthorityMismatch = 32,
114    /// A `ReclaimBuffer` `buffer_pda` doesn't sit at the canonical buffer PDA
115    /// derived from its paired `mint`.
116    ReclaimBufferNotCanonical = 33,
117    /// `TransferAuthority`'s signer is neither the manager nor the current
118    /// holder of the role being transferred, so it may not transfer it.
119    UnauthorizedAuthorityTransfer = 34,
120    /// `AddSolver`/`RemoveSolver`'s solver-authority account isn't a signer, or
121    /// doesn't match the `solver_authority` recorded in the settlement state
122    /// PDA, so it may not change the solver list.
123    UnauthorizedSolverManagement = 35,
124    /// `AddSolver`'s solver is already in the state PDA's solver list.
125    SolverAlreadyExists = 36,
126    /// `BeginSettle`'s solver account isn't a signer or isn't in the state PDA's
127    /// solver list, so it may not settle.
128    UnauthorizedSolver = 37,
129    /// `RemoveSolver`'s solver isn't in the state PDA's solver list.
130    SolverNotFound = 38,
131    /// `CreateBuffer` asked the token program how long a token account for a
132    /// mint has to be and couldn't read the answer, so it can't size the
133    /// buffer.
134    BufferSizeUnavailable = 39,
135    /// The token program for a given token or mint is not supported.
136    InvalidTokenProgram = 40,
137    /// `CreateSettlementOwnedOrder`'s settlement-owned-order-authority account
138    /// isn't a signer, or doesn't match the `settlement_owned_order_authority`
139    /// recorded in the settlement state PDA.
140    UnauthorizedSettlementOwnedOrder = 41,
141    /// An intent has a zero sell or buy amount, which is not supported.
142    ZeroOrderAmount = 42,
143    /// A mint account supplied to `BeginSettle` or `FinalizeSettle`
144    /// was not able to be parsed as such.
145    InvalidMint = 43,
146    /// `BeginSettle`: a paired `FinalizeSettle` push's destination is the very
147    /// buffer the push draws from, so settling it would move no funds yet still
148    /// record the declared amount as received.
149    PushSourceIsDestination = 44,
150    /// Moving lamports out of the native SOL buffer would empty it, which the
151    /// runtime would then delete.
152    NativeSolBufferEmptied = 45,
153    /// A buffer to be reclaimed holds more tokens than the burn limit supplied
154    /// for it, so its balance can't be cleared for closing.
155    ReclaimBufferBurnLimitExceeded = 46,
156    /// `WithdrawNativeSol`'s authority account isn't a signer, or doesn't
157    /// match the `settlement_owned_order_authority` recorded in the settlement
158    /// state PDA.
159    UnauthorizedNativeSolWithdrawal = 47,
160    /// `WithdrawNativeSol`'s native SOL buffer account isn't the canonical
161    /// native SOL buffer PDA.
162    NativeSolBufferMismatch = 48,
163}
164
165impl From<SettlementError> for u32 {
166    fn from(e: SettlementError) -> Self {
167        e as u32
168    }
169}
170
171impl From<SettlementError> for solana_program_error::ProgramError {
172    fn from(e: SettlementError) -> Self {
173        Self::Custom(e.into())
174    }
175}
176
177impl From<SettlementError> for solana_instruction_error::InstructionError {
178    fn from(e: SettlementError) -> Self {
179        Self::Custom(e.into())
180    }
181}
182
183/// Decodes the settlement error from an instruction that failed in the
184/// settlement program, handing back the original error if it isn't a known
185/// settlement error code.
186pub fn parse_settlement_error(
187    e: solana_instruction_error::InstructionError,
188) -> Result<SettlementError, solana_instruction_error::InstructionError> {
189    match e {
190        solana_instruction_error::InstructionError::Custom(code) => {
191            SettlementError::try_from(code).map_err(|_| e)
192        }
193        _ => Err(e),
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use solana_instruction_error::InstructionError;
200
201    use super::*;
202
203    #[test]
204    fn parse_settlement_error_decodes_custom_codes() {
205        assert_eq!(
206            parse_settlement_error(InstructionError::from(SettlementError::OrderNotReclaimable)),
207            Ok(SettlementError::OrderNotReclaimable)
208        );
209        assert_eq!(
210            parse_settlement_error(InstructionError::Custom(u32::MAX)),
211            Err(InstructionError::Custom(u32::MAX))
212        );
213        assert_eq!(
214            parse_settlement_error(InstructionError::InvalidAccountData),
215            Err(InstructionError::InvalidAccountData)
216        );
217    }
218}