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
}
}
#[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
}
}
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..]))
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum SettlementError {
FinalizeBeforeInitialize = 0,
BeginFinalizePairOverlap = 1,
MissingCounterpartInstruction = 2,
CounterpartIsExternal = 3,
InvalidCounterpartDiscriminator = 4,
InvalidCounterpartCounterpart = 5,
MismatchedCounterpartDiscriminator = 6,
OwnerMismatch = 7,
AccountNotDerivable = 8,
OrdersNotStrictlyIncreasing = 9,
SellTokenAccountMismatch = 10,
SellTokenAccountInvalid = 11,
SellTokenOwnerMismatch = 12,
AccountCountNotMatchingOrderCount = 13,
CalledViaCpi = 14,
OrderCancelled = 15,
OrderExpired = 16,
TransferCountMismatch = 17,
StateAccountMismatch = 18,
AccountCountNotMatchingPushCount = 19,
SettledOrderPushCountMismatch = 20,
PushDestinationMismatch = 21,
PushSourceNotBuffer = 22,
InvalidBuyTokenAccount = 23,
LimitPriceViolated = 24,
PullAmountOverflow = 25,
FillExceedsOrderAmount = 26,
OrderNotExactlyFilled = 27,
AmountWithdrawnOverflow = 28,
AmountReceivedOverflow = 29,
OrderNotExpired = 30,
ReclaimRecipientMismatch = 31,
}
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() {
assert_eq!(
recover_discriminator(&[42]),
Err(ProgramError::InvalidInstructionData),
);
}
#[test]
fn forwards_trailing_bytes() {
assert!(matches!(
recover_discriminator(&[
SettlementInstruction::BeginSettle.discriminator(),
42 ]),
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(),
);
}
}