pub use solana_instruction::{AccountMeta, Instruction};
use solana_program_error::ProgramError;
pub use solana_pubkey::Pubkey;
solana_pubkey::declare_id!("FYp8R5K4B3B1Kfr7QuWzMz4TwoT7wptjYtxgCrY5sRXb");
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,
ReclaimBuffer = 6,
TransferAuthority = 7,
AddSolver = 8,
}
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 = Role::unknown_role))]
pub enum Role {
Manager = 0,
ReclaimAuthority,
}
impl Role {
pub const ALL: [Self; 2] = [Role::Manager, Role::ReclaimAuthority];
pub fn discriminator(self) -> u8 {
self as u8
}
fn unknown_role(_: 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,
SellMintMismatch = 23,
LimitPriceViolated = 24,
PullAmountOverflow = 25,
FillExceedsOrderAmount = 26,
OrderNotExactlyFilled = 27,
AmountWithdrawnOverflow = 28,
AmountReceivedOverflow = 29,
OrderNotReclaimable = 30,
ReclaimRecipientMismatch = 31,
ReclaimAuthorityMismatch = 32,
ReclaimBufferNotCanonical = 33,
UnauthorizedAuthorityTransfer = 34,
UnauthorizedSolverManagement = 35,
SolverAlreadyExists = 36,
UnauthorizedSolver = 37,
OrderCreatedOnChainMismatch = 38,
}
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(any(test, feature = "test-fixtures"))]
pub mod fixtures {
use crate::Pubkey;
pub fn pubkey_from_seed(seed: &str) -> Pubkey {
Pubkey::new_from_array(solana_sha256_hasher::hash(seed.as_bytes()).to_bytes())
}
}
#[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(),
);
}
#[test]
fn role_try_from_partitions_all_bytes() {
for i in u8::MIN..=u8::MAX {
match Role::try_from(i) {
Ok(role) => assert_eq!(role as u8, i),
Err(err) => assert_eq!(err, ProgramError::InvalidInstructionData),
}
}
}
#[test]
fn role_try_from_matches_manager() {
assert_eq!(Role::try_from(0), Ok(Role::Manager));
}
#[test]
fn all_roles_lists_every_role_in_discriminator_order() {
let every_role: Vec<Role> = (u8::MIN..=u8::MAX)
.filter_map(|byte| Role::try_from(byte).ok())
.collect();
assert_eq!(Role::ALL.as_slice(), every_role.as_slice());
}
}