use solana_pubkey::Pubkey;
use crate::domain::market::Market;
use crate::program::constants::{MAX_OUTCOMES, MIN_OUTCOMES};
use crate::program::error::{SdkError, SdkResult};
use crate::program::orders::OrderPayload;
use crate::shared::DepositSource;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum MarketStatus {
Pending = 0,
Active = 1,
Resolved = 2,
Cancelled = 3,
}
impl TryFrom<u8> for MarketStatus {
type Error = SdkError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(MarketStatus::Pending),
1 => Ok(MarketStatus::Active),
2 => Ok(MarketStatus::Resolved),
3 => Ok(MarketStatus::Cancelled),
_ => Err(SdkError::InvalidMarketStatus(value)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum OrderSide {
Bid = 0,
Ask = 1,
}
impl TryFrom<u8> for OrderSide {
type Error = SdkError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(OrderSide::Bid),
1 => Ok(OrderSide::Ask),
_ => Err(SdkError::InvalidSide(value)),
}
}
}
#[derive(Debug, Clone)]
pub struct CreateMarketParams {
pub manager: Pubkey,
pub num_outcomes: u8,
pub oracle: Pubkey,
pub question_id: [u8; 32],
pub maker_fee_bps: i16,
pub taker_fee_bps: i16,
}
#[derive(Debug, Clone)]
pub struct AddDepositMintParams {
pub manager: Pubkey,
pub deposit_mint: Pubkey,
}
#[derive(Debug, Clone)]
pub struct BuildDepositParams {
pub user: Pubkey,
pub market: Pubkey,
pub deposit_mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct BuildMergeParams {
pub user: Pubkey,
pub market: Pubkey,
pub deposit_mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct SettleMarketParams {
pub oracle: Pubkey,
pub market_id: u64,
pub payout_numerators: Vec<u32>,
}
impl SettleMarketParams {
pub fn new(oracle: Pubkey, market_id: u64, payout_numerators: Vec<u32>) -> Self {
Self {
oracle,
market_id,
payout_numerators,
}
}
pub fn winner_takes_all(
oracle: Pubkey,
market_id: u64,
winning_outcome: u8,
num_outcomes: u8,
) -> SdkResult<Self> {
let mut payout_numerators = vec![0; validate_num_outcomes(num_outcomes)? as usize];
let max_index = num_outcomes.saturating_sub(1);
if winning_outcome >= num_outcomes {
return Err(SdkError::InvalidOutcomeIndex {
index: winning_outcome,
max: max_index,
});
}
payout_numerators[winning_outcome as usize] = 1;
Ok(Self::new(oracle, market_id, payout_numerators))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScalarResolutionParams {
pub min_value: i128,
pub max_value: i128,
pub resolved_value: i128,
pub lower_outcome_index: u8,
pub upper_outcome_index: u8,
pub num_outcomes: u8,
}
pub fn scalar_to_payout_numerators(params: ScalarResolutionParams) -> SdkResult<Vec<u32>> {
validate_num_outcomes(params.num_outcomes)?;
validate_scalar_outcome_index(params.lower_outcome_index, params.num_outcomes)?;
validate_scalar_outcome_index(params.upper_outcome_index, params.num_outcomes)?;
if params.lower_outcome_index == params.upper_outcome_index {
return Err(SdkError::DuplicateScalarOutcomes);
}
let range = params
.max_value
.checked_sub(params.min_value)
.ok_or(SdkError::Overflow)?;
if range <= 0 {
return Err(SdkError::InvalidScalarRange);
}
let clamped = params
.resolved_value
.clamp(params.min_value, params.max_value);
let lower_numerator = params
.max_value
.checked_sub(clamped)
.ok_or(SdkError::Overflow)?;
let upper_numerator = clamped
.checked_sub(params.min_value)
.ok_or(SdkError::Overflow)?;
let mut numerators = vec![0u128; params.num_outcomes as usize];
numerators[params.lower_outcome_index as usize] = lower_numerator as u128;
numerators[params.upper_outcome_index as usize] = upper_numerator as u128;
reduce_and_fit_payout_numerators(&numerators)
}
fn validate_num_outcomes(num_outcomes: u8) -> SdkResult<u8> {
if !(MIN_OUTCOMES..=MAX_OUTCOMES).contains(&num_outcomes) {
return Err(SdkError::InvalidOutcomeCount {
count: num_outcomes,
});
}
Ok(num_outcomes)
}
fn validate_scalar_outcome_index(index: u8, num_outcomes: u8) -> SdkResult<()> {
if index >= num_outcomes {
return Err(SdkError::InvalidOutcomeIndex {
index,
max: num_outcomes.saturating_sub(1),
});
}
Ok(())
}
fn reduce_and_fit_payout_numerators(numerators: &[u128]) -> SdkResult<Vec<u32>> {
let gcd = numerators
.iter()
.copied()
.filter(|n| *n > 0)
.reduce(gcd_u128)
.ok_or(SdkError::InvalidPayoutNumerators)?;
let mut reduced = Vec::with_capacity(numerators.len());
let mut sum = 0u128;
for numerator in numerators {
let value = if *numerator == 0 { 0 } else { numerator / gcd };
if value > u32::MAX as u128 {
return Err(SdkError::PayoutVectorExceedsU32);
}
sum = sum.checked_add(value).ok_or(SdkError::Overflow)?;
reduced.push(value as u32);
}
if sum == 0 {
return Err(SdkError::InvalidPayoutNumerators);
}
if sum > u32::MAX as u128 {
return Err(SdkError::PayoutVectorExceedsU32);
}
Ok(reduced)
}
fn gcd_u128(mut a: u128, mut b: u128) -> u128 {
while b != 0 {
let remainder = a % b;
a = b;
b = remainder;
}
a
}
#[derive(Debug, Clone)]
pub struct RedeemWinningsParams {
pub user: Pubkey,
pub market: Pubkey,
pub deposit_mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct WithdrawFromPositionParams {
pub user: Pubkey,
pub market: Pubkey,
pub mint: Pubkey,
pub amount: u64,
pub outcome_index: u8,
}
#[derive(Debug, Clone)]
pub struct ActivateMarketParams {
pub manager: Pubkey,
pub market_id: u64,
}
#[derive(Debug, Clone)]
pub struct BidOrderParams {
pub nonce: u64,
pub salt: u64,
pub maker: Pubkey,
pub market: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub amount_in: u64,
pub amount_out: u64,
pub expiration: i64,
}
#[derive(Debug, Clone)]
pub struct AskOrderParams {
pub nonce: u64,
pub salt: u64,
pub maker: Pubkey,
pub market: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub amount_in: u64,
pub amount_out: u64,
pub expiration: i64,
}
#[derive(Debug, Clone)]
pub struct MatchOrdersMultiParams {
pub operator: Pubkey,
pub market: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub fee_receiver: Pubkey,
pub taker_order: OrderPayload,
pub maker_orders: Vec<OrderPayload>,
pub maker_fill_amounts: Vec<u64>,
pub taker_fill_amounts: Vec<u64>,
pub full_fill_bitmask: u8,
}
#[derive(Debug, Clone)]
pub struct CreateOrderbookParams {
pub manager: Pubkey,
pub market: Pubkey,
pub mint_a: Pubkey,
pub mint_b: Pubkey,
pub fee_receiver: Pubkey,
pub mint_a_deposit_mint: Pubkey,
pub mint_b_deposit_mint: Pubkey,
pub recent_slot: u64,
pub base_index: u8,
pub mint_a_outcome_index: u8,
pub mint_b_outcome_index: u8,
}
#[derive(Debug, Clone)]
pub struct SetAuthorityParams {
pub current_authority: Pubkey,
pub new_authority: Pubkey,
}
#[derive(Debug, Clone)]
pub struct SetManagerParams {
pub authority: Pubkey,
pub new_manager: Pubkey,
}
#[derive(Debug, Clone)]
pub struct MarketFeeUpdate {
pub market: Pubkey,
pub maker_fee_bps: i16,
pub taker_fee_bps: i16,
}
#[derive(Debug, Clone)]
pub struct SetMarketFeesParams {
pub manager: Pubkey,
pub updates: Vec<MarketFeeUpdate>,
}
#[derive(Debug, Clone)]
pub struct SetFeeReceiverParams {
pub authority: Pubkey,
pub new_fee_receiver: Pubkey,
}
#[derive(Debug, Clone)]
pub struct ConditionalMetadataParams {
pub manager: Pubkey,
pub market: Pubkey,
pub deposit_mint: Pubkey,
pub outcome_index: u8,
pub name: String,
pub symbol: String,
pub uri: String,
}
#[derive(Debug, Clone)]
pub struct WhitelistDepositTokenParams {
pub authority: Pubkey,
pub mint: Pubkey,
}
#[derive(Debug, Clone)]
pub struct DepositToGlobalParams {
pub user: Pubkey,
pub mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone, Copy)]
pub enum DepositToGlobalAltContext {
Create {
recent_slot: u64,
},
Extend {
lookup_table: Pubkey,
},
}
#[derive(Debug, Clone)]
pub struct GlobalToMarketDepositParams {
pub user: Pubkey,
pub market: Pubkey,
pub deposit_mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct InitPositionTokensParams {
pub payer: Pubkey,
pub user: Pubkey,
pub market: Pubkey,
pub deposit_mints: Vec<Pubkey>,
pub recent_slot: u64,
}
#[derive(Debug, Clone)]
pub struct MakerFill {
pub order: OrderPayload,
pub maker_fill_amount: u64,
pub taker_fill_amount: u64,
pub is_full_fill: bool,
pub is_deposit: bool,
pub deposit_mint: Pubkey,
}
#[derive(Debug, Clone)]
pub struct DepositAndSwapParams {
pub operator: Pubkey,
pub market: Pubkey,
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub fee_receiver: Pubkey,
pub taker_order: OrderPayload,
pub taker_is_full_fill: bool,
pub taker_is_deposit: bool,
pub taker_deposit_mint: Pubkey,
pub num_outcomes: u8,
pub makers: Vec<MakerFill>,
}
#[derive(Debug, Clone)]
pub struct ExtendPositionTokensParams {
pub operator: Pubkey,
pub user: Pubkey,
pub market: Pubkey,
pub lookup_table: Pubkey,
pub deposit_mints: Vec<Pubkey>,
}
#[derive(Debug, Clone)]
pub struct WithdrawFromGlobalParams {
pub user: Pubkey,
pub mint: Pubkey,
pub amount: u64,
}
#[derive(Debug, Clone)]
pub struct ClosePositionAltParams {
pub operator: Pubkey,
pub position: Pubkey,
pub market: Pubkey,
pub lookup_table: Pubkey,
}
#[derive(Debug, Clone)]
pub struct CloseOrderStatusParams {
pub operator: Pubkey,
pub order_hash: [u8; 32],
}
#[derive(Debug, Clone)]
pub struct ClosePositionTokenAccountsParams {
pub operator: Pubkey,
pub market: Pubkey,
pub position: Pubkey,
pub deposit_mints: Vec<Pubkey>,
}
#[derive(Debug, Clone)]
pub struct CloseOrderbookAltParams {
pub operator: Pubkey,
pub orderbook: Pubkey,
pub market: Pubkey,
pub lookup_table: Pubkey,
}
#[derive(Debug, Clone)]
pub struct CloseOrderbookParams {
pub operator: Pubkey,
pub orderbook: Pubkey,
pub market: Pubkey,
pub lookup_table: Pubkey,
}
#[derive(Debug)]
pub struct DepositParams<'a> {
pub user: Pubkey,
pub mint: Pubkey,
pub amount: u64,
pub market: Option<&'a Market>,
pub deposit_source: Option<DepositSource>,
}
#[derive(Debug)]
pub struct MarketWithdrawContext<'a> {
pub market: &'a Market,
pub outcome_index: u8,
}
#[derive(Debug)]
pub struct WithdrawParams<'a> {
pub user: Pubkey,
pub mint: Pubkey,
pub amount: u64,
pub market_context: Option<MarketWithdrawContext<'a>>,
pub deposit_source: Option<DepositSource>,
}