use solana_instruction::{AccountMeta, Instruction};
use solana_pubkey::Pubkey;
fn system_program_id() -> Pubkey {
solana_system_interface::program::ID
}
use crate::program::constants::{
instruction, ALT_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, MAX_MAKERS, TOKEN_2022_PROGRAM_ID,
TOKEN_PROGRAM_ID,
};
use crate::program::error::{SdkError, SdkResult};
use crate::program::orders::OrderPayload;
use crate::program::pda::{
get_alt_pda, get_conditional_mint_pda, get_exchange_pda, get_global_deposit_token_pda,
get_market_pda, get_mint_authority_pda, get_order_status_pda, get_orderbook_pda,
get_position_alt_pda, get_position_pda, get_user_global_deposit_pda, get_user_nonce_pda,
get_vault_pda,
};
use crate::program::types::{
ActivateMarketParams, AddDepositMintParams, BuildDepositParams, BuildMergeParams,
CreateMarketParams, CreateOrderbookParams, DepositAndSwapParams, DepositToGlobalParams,
ExtendPositionTokensParams, GlobalToMarketDepositParams, InitPositionTokensParams,
MatchOrdersMultiParams, RedeemWinningsParams, SetAuthorityParams, SettleMarketParams,
WhitelistDepositTokenParams, WithdrawFromGlobalParams, WithdrawFromPositionParams,
};
use crate::program::utils::{
get_conditional_token_ata, get_deposit_token_ata, serialize_outcome_metadata,
validate_outcome_count, OutcomeMetadataInput,
};
fn signer_mut(pubkey: Pubkey) -> AccountMeta {
AccountMeta::new(pubkey, true)
}
fn writable(pubkey: Pubkey) -> AccountMeta {
AccountMeta::new(pubkey, false)
}
fn readonly(pubkey: Pubkey) -> AccountMeta {
AccountMeta::new_readonly(pubkey, false)
}
pub fn build_initialize_ix(authority: &Pubkey, program_id: &Pubkey) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let keys = vec![
signer_mut(*authority),
writable(exchange),
readonly(system_program_id()),
];
let data = vec![instruction::INITIALIZE];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_create_market_ix(
params: &CreateMarketParams,
market_id: u64,
program_id: &Pubkey,
) -> SdkResult<Instruction> {
validate_outcome_count(params.num_outcomes)?;
let (exchange, _) = get_exchange_pda(program_id);
let (market, _) = get_market_pda(market_id, program_id);
let keys = vec![
signer_mut(params.authority),
writable(exchange),
writable(market),
readonly(system_program_id()),
];
let mut data = Vec::with_capacity(66);
data.push(instruction::CREATE_MARKET);
data.push(params.num_outcomes);
data.extend_from_slice(params.oracle.as_ref());
data.extend_from_slice(¶ms.question_id);
Ok(Instruction {
program_id: *program_id,
accounts: keys,
data,
})
}
pub fn build_add_deposit_mint_ix(
params: &AddDepositMintParams,
market: &Pubkey,
num_outcomes: u8,
program_id: &Pubkey,
) -> SdkResult<Instruction> {
if params.outcome_metadata.len() != num_outcomes as usize {
return Err(SdkError::InvalidOutcomeCount {
count: params.outcome_metadata.len() as u8,
});
}
let (exchange, _) = get_exchange_pda(program_id);
let (vault, _) = get_vault_pda(¶ms.deposit_mint, market, program_id);
let (mint_authority, _) = get_mint_authority_pda(market, program_id);
let mut keys = vec![
signer_mut(params.authority),
readonly(exchange),
writable(*market),
readonly(params.deposit_mint),
writable(vault),
readonly(mint_authority),
readonly(TOKEN_PROGRAM_ID),
readonly(TOKEN_2022_PROGRAM_ID),
readonly(system_program_id()),
];
for i in 0..num_outcomes {
let (mint, _) = get_conditional_mint_pda(market, ¶ms.deposit_mint, i, program_id);
keys.push(writable(mint));
}
let metadata_input: Vec<OutcomeMetadataInput> = params
.outcome_metadata
.iter()
.map(|m| OutcomeMetadataInput {
name: m.name.clone(),
symbol: m.symbol.clone(),
uri: m.uri.clone(),
})
.collect();
let mut data = vec![instruction::ADD_DEPOSIT_MINT];
data.extend(serialize_outcome_metadata(&metadata_input));
Ok(Instruction {
program_id: *program_id,
accounts: keys,
data,
})
}
pub fn build_deposit_ix(
params: &BuildDepositParams,
num_outcomes: u8,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
let position_collateral_ata = get_deposit_token_ata(&position, ¶ms.deposit_mint);
let mut keys = vec![
signer_mut(params.user),
readonly(exchange),
readonly(params.market),
readonly(params.deposit_mint),
writable(vault),
writable(user_deposit_ata),
writable(position),
writable(position_collateral_ata),
readonly(mint_authority),
readonly(TOKEN_PROGRAM_ID),
readonly(TOKEN_2022_PROGRAM_ID),
readonly(ASSOCIATED_TOKEN_PROGRAM_ID),
readonly(system_program_id()),
];
for i in 0..num_outcomes {
let (mint, _) =
get_conditional_mint_pda(¶ms.market, ¶ms.deposit_mint, i, program_id);
keys.push(writable(mint));
let position_ata = get_conditional_token_ata(&position, &mint);
keys.push(writable(position_ata));
}
let mut data = Vec::with_capacity(9);
data.push(instruction::MINT_COMPLETE_SET);
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_merge_ix(
params: &BuildMergeParams,
num_outcomes: u8,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
let mut keys = vec![
signer_mut(params.user),
readonly(exchange),
readonly(params.market),
readonly(params.deposit_mint),
writable(vault),
writable(position),
writable(user_deposit_ata),
readonly(mint_authority),
readonly(TOKEN_PROGRAM_ID),
readonly(TOKEN_2022_PROGRAM_ID),
];
for i in 0..num_outcomes {
let (mint, _) =
get_conditional_mint_pda(¶ms.market, ¶ms.deposit_mint, i, program_id);
keys.push(writable(mint));
let position_ata = get_conditional_token_ata(&position, &mint);
keys.push(writable(position_ata));
}
let mut data = Vec::with_capacity(9);
data.push(instruction::MERGE_COMPLETE_SET);
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_cancel_order_ix(
maker: &Pubkey,
market: &Pubkey,
order: &OrderPayload,
program_id: &Pubkey,
) -> Instruction {
let order_hash = order.hash();
let (order_status, _) = get_order_status_pda(&order_hash, program_id);
let keys = vec![
signer_mut(*maker),
readonly(*market),
writable(order_status),
readonly(system_program_id()),
];
let mut data = Vec::with_capacity(258);
data.push(instruction::CANCEL_ORDER);
data.extend_from_slice(&order_hash);
data.extend_from_slice(&order.serialize());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_increment_nonce_ix(user: &Pubkey, program_id: &Pubkey) -> Instruction {
let (user_nonce, _) = get_user_nonce_pda(user, program_id);
let keys = vec![
signer_mut(*user),
writable(user_nonce),
readonly(system_program_id()),
];
let data = vec![instruction::INCREMENT_NONCE];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_settle_market_ix(params: &SettleMarketParams, program_id: &Pubkey) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (market, _) = get_market_pda(params.market_id, program_id);
let keys = vec![
signer_mut(params.oracle),
readonly(exchange),
writable(market),
];
let data = vec![instruction::SETTLE_MARKET, params.winning_outcome];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_redeem_winnings_ix(
params: &RedeemWinningsParams,
winning_outcome: u8,
program_id: &Pubkey,
) -> Instruction {
let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let (winning_mint, _) = get_conditional_mint_pda(
¶ms.market,
¶ms.deposit_mint,
winning_outcome,
program_id,
);
let position_winning_ata = get_conditional_token_ata(&position, &winning_mint);
let user_deposit_ata = get_deposit_token_ata(¶ms.user, ¶ms.deposit_mint);
let keys = vec![
signer_mut(params.user),
readonly(params.market),
readonly(params.deposit_mint),
writable(vault),
writable(winning_mint),
writable(position),
writable(position_winning_ata),
writable(user_deposit_ata),
readonly(mint_authority),
readonly(TOKEN_PROGRAM_ID),
readonly(TOKEN_2022_PROGRAM_ID),
];
let mut data = Vec::with_capacity(9);
data.push(instruction::REDEEM_WINNINGS);
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_set_paused_ix(authority: &Pubkey, paused: bool, program_id: &Pubkey) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let keys = vec![signer_mut(*authority), writable(exchange)];
let data = vec![instruction::SET_PAUSED, if paused { 1 } else { 0 }];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_set_operator_ix(
authority: &Pubkey,
new_operator: &Pubkey,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let keys = vec![signer_mut(*authority), writable(exchange)];
let mut data = Vec::with_capacity(33);
data.push(instruction::SET_OPERATOR);
data.extend_from_slice(new_operator.as_ref());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_withdraw_from_position_ix(
params: &WithdrawFromPositionParams,
is_token_2022: bool,
program_id: &Pubkey,
) -> Instruction {
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let position_ata = if is_token_2022 {
get_conditional_token_ata(&position, ¶ms.mint)
} else {
get_deposit_token_ata(&position, ¶ms.mint)
};
let user_ata = if is_token_2022 {
get_conditional_token_ata(¶ms.user, ¶ms.mint)
} else {
get_deposit_token_ata(¶ms.user, ¶ms.mint)
};
let token_program = if is_token_2022 {
TOKEN_2022_PROGRAM_ID
} else {
TOKEN_PROGRAM_ID
};
let keys = vec![
signer_mut(params.user),
readonly(params.market),
writable(position),
readonly(params.mint),
writable(position_ata),
writable(user_ata),
readonly(token_program),
];
let mut data = Vec::with_capacity(10);
data.push(instruction::WITHDRAW_FROM_POSITION);
data.extend_from_slice(¶ms.amount.to_le_bytes());
data.push(params.outcome_index);
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_activate_market_ix(params: &ActivateMarketParams, program_id: &Pubkey) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (market, _) = get_market_pda(params.market_id, program_id);
let keys = vec![
signer_mut(params.authority),
readonly(exchange),
writable(market),
];
let data = vec![instruction::ACTIVATE_MARKET];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_match_orders_multi_ix(
params: &MatchOrdersMultiParams,
program_id: &Pubkey,
) -> SdkResult<Instruction> {
if params.maker_orders.is_empty() {
return Err(SdkError::MissingField("maker_orders".to_string()));
}
if params.maker_orders.len() > MAX_MAKERS {
return Err(SdkError::TooManyMakers {
count: params.maker_orders.len(),
});
}
if params.maker_orders.len() != params.maker_fill_amounts.len() {
return Err(SdkError::MissingField("maker_fill_amounts".to_string()));
}
if params.maker_orders.len() != params.taker_fill_amounts.len() {
return Err(SdkError::MissingField("taker_fill_amounts".to_string()));
}
let (exchange, _) = get_exchange_pda(program_id);
let taker_order_hash = params.taker_order.hash();
let (taker_nonce, _) = get_user_nonce_pda(¶ms.taker_order.maker, program_id);
let (taker_position, _) =
get_position_pda(¶ms.taker_order.maker, ¶ms.market, program_id);
let taker_base_ata = get_conditional_token_ata(&taker_position, ¶ms.base_mint);
let taker_quote_ata = get_conditional_token_ata(&taker_position, ¶ms.quote_mint);
let taker_full_fill = (params.full_fill_bitmask >> 7) & 1 == 1;
let mut keys = Vec::new();
keys.push(signer_mut(params.operator));
keys.push(readonly(exchange));
keys.push(readonly(params.market));
if !taker_full_fill {
let (taker_order_status, _) = get_order_status_pda(&taker_order_hash, program_id);
keys.push(writable(taker_order_status));
}
keys.push(readonly(taker_nonce));
keys.push(writable(taker_position));
keys.push(readonly(params.base_mint));
keys.push(readonly(params.quote_mint));
keys.push(writable(taker_base_ata));
keys.push(writable(taker_quote_ata));
keys.push(readonly(TOKEN_2022_PROGRAM_ID));
keys.push(readonly(system_program_id()));
for (i, maker_order) in params.maker_orders.iter().enumerate() {
let maker_full_fill = (params.full_fill_bitmask >> i) & 1 == 1;
if !maker_full_fill {
let maker_order_hash = maker_order.hash();
let (maker_order_status, _) = get_order_status_pda(&maker_order_hash, program_id);
keys.push(writable(maker_order_status));
}
let (maker_nonce, _) = get_user_nonce_pda(&maker_order.maker, program_id);
let (maker_position, _) = get_position_pda(&maker_order.maker, ¶ms.market, program_id);
let maker_base_ata = get_conditional_token_ata(&maker_position, ¶ms.base_mint);
let maker_quote_ata = get_conditional_token_ata(&maker_position, ¶ms.quote_mint);
keys.push(readonly(maker_nonce));
keys.push(writable(maker_position));
keys.push(writable(maker_base_ata));
keys.push(writable(maker_quote_ata));
}
let taker_compact = params.taker_order.to_order();
let num_makers = params.maker_orders.len() as u8;
let data_size = 1 + 29 + 64 + 1 + 1 + (params.maker_orders.len() * 109);
let mut data = Vec::with_capacity(data_size);
data.push(instruction::MATCH_ORDERS_MULTI);
data.extend_from_slice(&taker_compact.serialize());
data.extend_from_slice(¶ms.taker_order.signature);
data.push(num_makers);
data.push(params.full_fill_bitmask);
for (i, maker_order) in params.maker_orders.iter().enumerate() {
let maker_compact = maker_order.to_order();
data.extend_from_slice(&maker_compact.serialize());
data.extend_from_slice(&maker_order.signature);
data.extend_from_slice(¶ms.maker_fill_amounts[i].to_le_bytes());
data.extend_from_slice(¶ms.taker_fill_amounts[i].to_le_bytes());
}
Ok(Instruction {
program_id: *program_id,
accounts: keys,
data,
})
}
pub fn build_create_orderbook_ix(
params: &CreateOrderbookParams,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (orderbook, _) = get_orderbook_pda(¶ms.mint_a, ¶ms.mint_b, program_id);
let (lookup_table, _) = get_alt_pda(&orderbook, params.recent_slot);
let keys = vec![
signer_mut(params.authority),
readonly(params.market),
readonly(params.mint_a),
readonly(params.mint_b),
writable(orderbook),
writable(lookup_table),
readonly(exchange),
readonly(*ALT_PROGRAM_ID),
readonly(system_program_id()),
];
let mut data = Vec::with_capacity(10);
data.push(instruction::CREATE_ORDERBOOK);
data.extend_from_slice(¶ms.recent_slot.to_le_bytes());
data.push(params.base_index);
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_set_authority_ix(params: &SetAuthorityParams, program_id: &Pubkey) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let keys = vec![signer_mut(params.current_authority), writable(exchange)];
let mut data = Vec::with_capacity(33);
data.push(instruction::SET_AUTHORITY);
data.extend_from_slice(params.new_authority.as_ref());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_whitelist_deposit_token_ix(
params: &WhitelistDepositTokenParams,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (global_deposit_token, _) = get_global_deposit_token_pda(¶ms.mint, program_id);
let keys = vec![
signer_mut(params.authority),
readonly(exchange),
readonly(params.mint),
writable(global_deposit_token),
readonly(system_program_id()),
];
let data = vec![instruction::WHITELIST_DEPOSIT_TOKEN];
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_deposit_to_global_ix(
params: &DepositToGlobalParams,
program_id: &Pubkey,
) -> Instruction {
let (global_deposit_token, _) = get_global_deposit_token_pda(¶ms.mint, program_id);
let (user_global_deposit, _) =
get_user_global_deposit_pda(¶ms.user, ¶ms.mint, program_id);
let user_token_account = get_deposit_token_ata(¶ms.user, ¶ms.mint);
let keys = vec![
signer_mut(params.user),
readonly(global_deposit_token),
readonly(params.mint),
writable(user_global_deposit),
writable(user_token_account),
readonly(TOKEN_PROGRAM_ID),
readonly(system_program_id()),
];
let mut data = Vec::with_capacity(9);
data.push(instruction::DEPOSIT_TO_GLOBAL);
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_global_to_market_deposit_ix(
params: &GlobalToMarketDepositParams,
num_outcomes: u8,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (vault, _) = get_vault_pda(¶ms.deposit_mint, ¶ms.market, program_id);
let (global_deposit_token, _) = get_global_deposit_token_pda(¶ms.deposit_mint, program_id);
let (user_global_deposit, _) =
get_user_global_deposit_pda(¶ms.user, ¶ms.deposit_mint, program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let position_collateral_ata = get_deposit_token_ata(&position, ¶ms.deposit_mint);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let mut keys = vec![
signer_mut(params.user),
readonly(exchange),
readonly(params.market),
readonly(params.deposit_mint),
writable(vault),
readonly(global_deposit_token),
writable(user_global_deposit),
writable(position),
writable(position_collateral_ata),
readonly(mint_authority),
readonly(TOKEN_PROGRAM_ID),
readonly(TOKEN_2022_PROGRAM_ID),
readonly(ASSOCIATED_TOKEN_PROGRAM_ID),
readonly(system_program_id()),
];
for i in 0..num_outcomes {
let (mint, _) =
get_conditional_mint_pda(¶ms.market, ¶ms.deposit_mint, i, program_id);
keys.push(writable(mint));
let position_ata = get_conditional_token_ata(&position, &mint);
keys.push(writable(position_ata));
}
let mut data = Vec::with_capacity(9);
data.push(instruction::GLOBAL_TO_MARKET_DEPOSIT);
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_init_position_tokens_ix(
params: &InitPositionTokensParams,
num_outcomes: u8,
program_id: &Pubkey,
) -> Instruction {
let (exchange, _) = get_exchange_pda(program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let (lookup_table, _) = get_position_alt_pda(&position, params.recent_slot);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let mut keys = vec![
signer_mut(params.payer),
readonly(params.user),
readonly(exchange),
readonly(params.market),
writable(position),
writable(lookup_table),
readonly(mint_authority),
readonly(TOKEN_2022_PROGRAM_ID),
readonly(ASSOCIATED_TOKEN_PROGRAM_ID),
readonly(*ALT_PROGRAM_ID),
readonly(system_program_id()),
];
for deposit_mint in ¶ms.deposit_mints {
let (vault, _) = get_vault_pda(deposit_mint, ¶ms.market, program_id);
let (gdt, _) = get_global_deposit_token_pda(deposit_mint, program_id);
keys.push(readonly(*deposit_mint));
keys.push(readonly(vault));
keys.push(readonly(gdt));
for i in 0..num_outcomes {
let (mint, _) = get_conditional_mint_pda(¶ms.market, deposit_mint, i, program_id);
keys.push(readonly(mint));
let position_ata = get_conditional_token_ata(&position, &mint);
keys.push(writable(position_ata));
}
}
let mut data = Vec::with_capacity(10);
data.push(instruction::INIT_POSITION_TOKENS);
data.extend_from_slice(¶ms.recent_slot.to_le_bytes());
data.push(params.deposit_mints.len() as u8);
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
pub fn build_deposit_and_swap_ix(
params: &DepositAndSwapParams,
program_id: &Pubkey,
) -> SdkResult<Instruction> {
if params.makers.is_empty() {
return Err(SdkError::MissingField("makers".to_string()));
}
if params.makers.len() > MAX_MAKERS {
return Err(SdkError::TooManyMakers {
count: params.makers.len(),
});
}
let (exchange, _) = get_exchange_pda(program_id);
let (mint_authority, _) = get_mint_authority_pda(¶ms.market, program_id);
let (taker_position, _) =
get_position_pda(¶ms.taker_order.maker, ¶ms.market, program_id);
let (taker_nonce, _) = get_user_nonce_pda(¶ms.taker_order.maker, program_id);
let taker_side = params.taker_order.side as u8;
let (receive_mint, give_mint) = if taker_side == 0 {
(¶ms.base_mint, ¶ms.quote_mint)
} else {
(¶ms.quote_mint, ¶ms.base_mint)
};
let mut full_fill_bitmask: u8 = 0;
let mut deposit_bitmask: u8 = 0;
if params.taker_is_full_fill {
full_fill_bitmask |= 0x80;
}
if params.taker_is_deposit {
deposit_bitmask |= 0x80;
}
for (i, maker) in params.makers.iter().enumerate() {
if maker.is_full_fill {
full_fill_bitmask |= 1 << i;
}
if maker.is_deposit {
deposit_bitmask |= 1 << i;
}
}
let mut keys = Vec::new();
keys.push(signer_mut(params.operator));
keys.push(readonly(exchange));
keys.push(readonly(params.market));
keys.push(readonly(mint_authority));
keys.push(readonly(TOKEN_PROGRAM_ID));
if !params.taker_is_full_fill {
let taker_order_hash = params.taker_order.hash();
let (taker_order_status, _) = get_order_status_pda(&taker_order_hash, program_id);
keys.push(writable(taker_order_status));
}
let taker_receive_ata = get_conditional_token_ata(&taker_position, receive_mint);
let taker_give_ata = get_conditional_token_ata(&taker_position, give_mint);
keys.push(readonly(taker_nonce));
keys.push(writable(taker_position));
keys.push(readonly(params.base_mint));
keys.push(readonly(params.quote_mint));
keys.push(writable(taker_receive_ata));
keys.push(writable(taker_give_ata));
keys.push(readonly(TOKEN_2022_PROGRAM_ID));
keys.push(readonly(system_program_id()));
if params.taker_is_deposit {
let dm = ¶ms.taker_deposit_mint;
let (vault, _) = get_vault_pda(dm, ¶ms.market, program_id);
let (gdt, _) = get_global_deposit_token_pda(dm, program_id);
let (taker_global_deposit, _) =
get_user_global_deposit_pda(¶ms.taker_order.maker, dm, program_id);
keys.push(readonly(*dm));
keys.push(writable(vault));
keys.push(readonly(gdt));
keys.push(writable(taker_global_deposit));
for i in 0..params.num_outcomes {
let (cond_mint, _) = get_conditional_mint_pda(¶ms.market, dm, i, program_id);
let ata = get_conditional_token_ata(&taker_position, &cond_mint);
keys.push(writable(cond_mint));
keys.push(writable(ata));
}
}
for maker in ¶ms.makers {
let (maker_nonce, _) = get_user_nonce_pda(&maker.order.maker, program_id);
let (maker_position, _) = get_position_pda(&maker.order.maker, ¶ms.market, program_id);
if !maker.is_full_fill {
let maker_order_hash = maker.order.hash();
let (maker_order_status, _) = get_order_status_pda(&maker_order_hash, program_id);
keys.push(writable(maker_order_status));
}
keys.push(readonly(maker_nonce));
keys.push(writable(maker_position));
if maker.is_deposit {
let dm = &maker.deposit_mint;
let (vault, _) = get_vault_pda(dm, ¶ms.market, program_id);
let (gdt, _) = get_global_deposit_token_pda(dm, program_id);
let (maker_global_deposit, _) =
get_user_global_deposit_pda(&maker.order.maker, dm, program_id);
keys.push(readonly(*dm));
keys.push(writable(vault));
keys.push(readonly(gdt));
keys.push(writable(maker_global_deposit));
for j in 0..params.num_outcomes {
let (cond_mint, _) = get_conditional_mint_pda(¶ms.market, dm, j, program_id);
let maker_ata = get_conditional_token_ata(&maker_position, &cond_mint);
keys.push(writable(cond_mint));
keys.push(writable(maker_ata));
}
}
let maker_receive_ata = get_conditional_token_ata(&maker_position, receive_mint);
let maker_give_ata = get_conditional_token_ata(&maker_position, give_mint);
keys.push(writable(maker_receive_ata));
keys.push(writable(maker_give_ata));
}
let taker_compact = params.taker_order.to_order();
let num_makers = params.makers.len() as u8;
let data_size = 1 + 29 + 64 + 1 + 1 + 1 + (params.makers.len() * 109);
let mut data = Vec::with_capacity(data_size);
data.push(instruction::DEPOSIT_AND_SWAP);
data.extend_from_slice(&taker_compact.serialize());
data.extend_from_slice(¶ms.taker_order.signature);
data.push(num_makers);
data.push(full_fill_bitmask);
data.push(deposit_bitmask);
for maker in ¶ms.makers {
let maker_compact = maker.order.to_order();
data.extend_from_slice(&maker_compact.serialize());
data.extend_from_slice(&maker.order.signature);
data.extend_from_slice(&maker.maker_fill_amount.to_le_bytes());
data.extend_from_slice(&maker.taker_fill_amount.to_le_bytes());
}
Ok(Instruction {
program_id: *program_id,
accounts: keys,
data,
})
}
pub fn build_extend_position_tokens_ix(
params: &ExtendPositionTokensParams,
num_outcomes: u8,
program_id: &Pubkey,
) -> SdkResult<Instruction> {
if params.deposit_mints.is_empty() {
return Err(SdkError::MissingField("deposit_mints".to_string()));
}
let (exchange, _) = get_exchange_pda(program_id);
let (position, _) = get_position_pda(¶ms.user, ¶ms.market, program_id);
let mut keys = vec![
signer_mut(params.payer),
readonly(params.user),
readonly(exchange),
readonly(params.market),
readonly(position),
writable(params.lookup_table),
readonly(TOKEN_2022_PROGRAM_ID),
readonly(ASSOCIATED_TOKEN_PROGRAM_ID),
readonly(*ALT_PROGRAM_ID),
readonly(system_program_id()),
];
for deposit_mint in ¶ms.deposit_mints {
let (vault, _) = get_vault_pda(deposit_mint, ¶ms.market, program_id);
let (global_deposit_token, _) = get_global_deposit_token_pda(deposit_mint, program_id);
keys.push(readonly(*deposit_mint));
keys.push(readonly(vault));
keys.push(readonly(global_deposit_token));
for i in 0..num_outcomes {
let (cond_mint, _) =
get_conditional_mint_pda(¶ms.market, deposit_mint, i, program_id);
let position_ata = get_conditional_token_ata(&position, &cond_mint);
keys.push(readonly(cond_mint));
keys.push(writable(position_ata));
}
}
let data = vec![
instruction::EXTEND_POSITION_TOKENS,
params.deposit_mints.len() as u8,
];
Ok(Instruction {
program_id: *program_id,
accounts: keys,
data,
})
}
pub fn build_withdraw_from_global_ix(
params: &WithdrawFromGlobalParams,
program_id: &Pubkey,
) -> Instruction {
let (global_deposit_token, _) = get_global_deposit_token_pda(¶ms.mint, program_id);
let (user_global_deposit, _) =
get_user_global_deposit_pda(¶ms.user, ¶ms.mint, program_id);
let user_token_account = get_deposit_token_ata(¶ms.user, ¶ms.mint);
let keys = vec![
signer_mut(params.user),
readonly(global_deposit_token),
readonly(params.mint),
writable(user_global_deposit),
writable(user_token_account),
readonly(TOKEN_PROGRAM_ID),
];
let mut data = vec![instruction::WITHDRAW_FROM_GLOBAL];
data.extend_from_slice(¶ms.amount.to_le_bytes());
Instruction {
program_id: *program_id,
accounts: keys,
data,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::env::LightconeEnv;
use crate::program::types::{MakerFill, OrderSide};
fn test_program_id() -> Pubkey {
LightconeEnv::default().program_id()
}
#[test]
fn test_build_initialize_ix() {
let authority = Pubkey::new_unique();
let program_id = test_program_id();
let ix = build_initialize_ix(&authority, &program_id);
assert_eq!(ix.program_id, program_id);
assert_eq!(ix.accounts.len(), 3);
assert_eq!(ix.data, vec![instruction::INITIALIZE]);
}
#[test]
fn test_build_increment_nonce_ix() {
let user = Pubkey::new_unique();
let program_id = test_program_id();
let ix = build_increment_nonce_ix(&user, &program_id);
assert_eq!(ix.program_id, program_id);
assert_eq!(ix.accounts.len(), 3);
assert_eq!(ix.data, vec![instruction::INCREMENT_NONCE]);
}
#[test]
fn test_build_set_paused_ix() {
let authority = Pubkey::new_unique();
let program_id = test_program_id();
let ix_pause = build_set_paused_ix(&authority, true, &program_id);
assert_eq!(ix_pause.data, vec![instruction::SET_PAUSED, 1]);
let ix_unpause = build_set_paused_ix(&authority, false, &program_id);
assert_eq!(ix_unpause.data, vec![instruction::SET_PAUSED, 0]);
}
#[test]
fn test_build_set_operator_ix() {
let authority = Pubkey::new_unique();
let new_operator = Pubkey::new_unique();
let program_id = test_program_id();
let ix = build_set_operator_ix(&authority, &new_operator, &program_id);
assert_eq!(ix.data.len(), 33);
assert_eq!(ix.data[0], instruction::SET_OPERATOR);
assert_eq!(&ix.data[1..33], new_operator.as_ref());
}
#[test]
fn test_build_create_market_ix() {
let params = CreateMarketParams {
authority: Pubkey::new_unique(),
num_outcomes: 3,
oracle: Pubkey::new_unique(),
question_id: [42u8; 32],
};
let program_id = test_program_id();
let ix = build_create_market_ix(¶ms, 0, &program_id).unwrap();
assert_eq!(ix.accounts.len(), 4);
assert_eq!(ix.data.len(), 66); assert_eq!(ix.data[0], instruction::CREATE_MARKET);
assert_eq!(ix.data[1], 3);
}
#[test]
fn test_build_create_market_invalid_outcomes() {
let params = CreateMarketParams {
authority: Pubkey::new_unique(),
num_outcomes: 7, oracle: Pubkey::new_unique(),
question_id: [0u8; 32],
};
let program_id = test_program_id();
let result = build_create_market_ix(¶ms, 0, &program_id);
assert!(result.is_err());
}
#[test]
fn test_build_activate_market_ix() {
let params = ActivateMarketParams {
authority: Pubkey::new_unique(),
market_id: 5,
};
let program_id = test_program_id();
let ix = build_activate_market_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 3);
assert_eq!(ix.data, vec![instruction::ACTIVATE_MARKET]);
}
#[test]
fn test_build_settle_market_ix() {
let params = SettleMarketParams {
oracle: Pubkey::new_unique(),
market_id: 1,
winning_outcome: 2,
};
let program_id = test_program_id();
let ix = build_settle_market_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 3);
assert_eq!(ix.data.len(), 2);
assert_eq!(ix.data[0], instruction::SETTLE_MARKET);
assert_eq!(ix.data[1], 2);
}
#[test]
fn test_build_cancel_order_ix() {
let maker = Pubkey::new_unique();
let market = Pubkey::new_unique();
let program_id = test_program_id();
let order = OrderPayload {
nonce: 1,
salt: 0,
maker,
market,
base_mint: Pubkey::new_unique(),
quote_mint: Pubkey::new_unique(),
side: OrderSide::Bid,
amount_in: 100,
amount_out: 50,
expiration: 0,
signature: [0u8; 64],
};
let ix = build_cancel_order_ix(&maker, &market, &order, &program_id);
assert_eq!(ix.accounts.len(), 4);
assert_eq!(ix.data.len(), 266); assert_eq!(ix.data[0], instruction::CANCEL_ORDER);
}
#[test]
fn test_build_withdraw_from_position_ix() {
let program_id = test_program_id();
let params = WithdrawFromPositionParams {
user: Pubkey::new_unique(),
market: Pubkey::new_unique(),
mint: Pubkey::new_unique(),
amount: 1000,
outcome_index: 0,
};
let ix = build_withdraw_from_position_ix(¶ms, true, &program_id);
assert_eq!(ix.accounts.len(), 7);
assert_eq!(ix.data.len(), 10); assert_eq!(ix.data[0], instruction::WITHDRAW_FROM_POSITION);
assert_eq!(ix.data[9], 0); }
#[test]
fn test_build_create_orderbook_ix() {
let program_id = test_program_id();
let params = CreateOrderbookParams {
authority: Pubkey::new_unique(),
market: Pubkey::new_unique(),
mint_a: Pubkey::new_unique(),
mint_b: Pubkey::new_unique(),
recent_slot: 12345,
base_index: 0,
};
let ix = build_create_orderbook_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 9);
assert_eq!(ix.data.len(), 10); assert_eq!(ix.data[0], instruction::CREATE_ORDERBOOK);
assert_eq!(ix.data[9], 0); }
#[test]
fn test_build_set_authority_ix() {
let program_id = test_program_id();
let params = SetAuthorityParams {
current_authority: Pubkey::new_unique(),
new_authority: Pubkey::new_unique(),
};
let ix = build_set_authority_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 2);
assert_eq!(ix.data.len(), 33); assert_eq!(ix.data[0], instruction::SET_AUTHORITY);
assert_eq!(&ix.data[1..33], params.new_authority.as_ref());
}
#[test]
fn test_build_match_orders_multi_ix_data_format() {
let program_id = test_program_id();
let operator = Pubkey::new_unique();
let market = Pubkey::new_unique();
let base_mint = Pubkey::new_unique();
let quote_mint = Pubkey::new_unique();
let taker = OrderPayload {
nonce: 1,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Bid,
amount_in: 100,
amount_out: 50,
expiration: 0,
signature: [1u8; 64],
};
let maker = OrderPayload {
nonce: 2,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Ask,
amount_in: 50,
amount_out: 100,
expiration: 0,
signature: [2u8; 64],
};
let params = MatchOrdersMultiParams {
operator,
market,
base_mint,
quote_mint,
taker_order: taker,
maker_orders: vec![maker],
maker_fill_amounts: vec![50],
taker_fill_amounts: vec![100],
full_fill_bitmask: 0,
};
let ix = build_match_orders_multi_ix(¶ms, &program_id).unwrap();
assert_eq!(ix.data.len(), 221);
assert_eq!(ix.data[0], instruction::MATCH_ORDERS_MULTI);
assert_eq!(ix.accounts.len(), 17);
}
#[test]
fn test_build_match_orders_multi_ix_full_fill() {
let program_id = test_program_id();
let operator = Pubkey::new_unique();
let market = Pubkey::new_unique();
let base_mint = Pubkey::new_unique();
let quote_mint = Pubkey::new_unique();
let taker = OrderPayload {
nonce: 1,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Bid,
amount_in: 100,
amount_out: 50,
expiration: 0,
signature: [1u8; 64],
};
let maker = OrderPayload {
nonce: 2,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Ask,
amount_in: 50,
amount_out: 100,
expiration: 0,
signature: [2u8; 64],
};
let params = MatchOrdersMultiParams {
operator,
market,
base_mint,
quote_mint,
taker_order: taker,
maker_orders: vec![maker],
maker_fill_amounts: vec![50],
taker_fill_amounts: vec![100],
full_fill_bitmask: 0b10000001,
};
let ix = build_match_orders_multi_ix(¶ms, &program_id).unwrap();
assert_eq!(ix.accounts.len(), 15);
}
#[test]
fn test_build_whitelist_deposit_token_ix() {
let program_id = test_program_id();
let params = WhitelistDepositTokenParams {
authority: Pubkey::new_unique(),
mint: Pubkey::new_unique(),
};
let ix = build_whitelist_deposit_token_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 5);
assert_eq!(ix.data, vec![instruction::WHITELIST_DEPOSIT_TOKEN]);
}
#[test]
fn test_build_deposit_to_global_ix() {
let program_id = test_program_id();
let params = DepositToGlobalParams {
user: Pubkey::new_unique(),
mint: Pubkey::new_unique(),
amount: 1_000_000,
};
let ix = build_deposit_to_global_ix(¶ms, &program_id);
assert_eq!(ix.accounts.len(), 7);
assert_eq!(ix.data.len(), 9);
assert_eq!(ix.data[0], instruction::DEPOSIT_TO_GLOBAL);
}
#[test]
fn test_build_global_to_market_deposit_ix() {
let program_id = test_program_id();
let params = GlobalToMarketDepositParams {
user: Pubkey::new_unique(),
market: Pubkey::new_unique(),
deposit_mint: Pubkey::new_unique(),
amount: 500_000,
};
let ix = build_global_to_market_deposit_ix(¶ms, 3, &program_id);
assert_eq!(ix.accounts.len(), 20);
assert_eq!(ix.data.len(), 9);
assert_eq!(ix.data[0], instruction::GLOBAL_TO_MARKET_DEPOSIT);
}
#[test]
fn test_build_init_position_tokens_ix() {
let program_id = test_program_id();
let deposit_mint = Pubkey::new_unique();
let params = InitPositionTokensParams {
payer: Pubkey::new_unique(),
user: Pubkey::new_unique(),
market: Pubkey::new_unique(),
deposit_mints: vec![deposit_mint],
recent_slot: 12345,
};
let ix = build_init_position_tokens_ix(¶ms, 3, &program_id);
assert_eq!(ix.accounts.len(), 20);
assert_eq!(ix.data.len(), 10); assert_eq!(ix.data[0], instruction::INIT_POSITION_TOKENS);
assert_eq!(ix.data[9], 1); }
#[test]
fn test_build_deposit_and_swap_ix() {
let program_id = test_program_id();
let market = Pubkey::new_unique();
let deposit_mint = Pubkey::new_unique();
let base_mint = Pubkey::new_unique();
let quote_mint = Pubkey::new_unique();
let taker = OrderPayload {
nonce: 1,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Bid,
amount_in: 100,
amount_out: 50,
expiration: 0,
signature: [1u8; 64],
};
let maker_order = OrderPayload {
nonce: 2,
salt: 0,
maker: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
side: OrderSide::Ask,
amount_in: 50,
amount_out: 100,
expiration: 0,
signature: [2u8; 64],
};
let params = DepositAndSwapParams {
operator: Pubkey::new_unique(),
market,
base_mint,
quote_mint,
taker_order: taker,
taker_is_full_fill: false,
taker_is_deposit: true,
taker_deposit_mint: deposit_mint,
num_outcomes: 3,
makers: vec![MakerFill {
order: maker_order,
maker_fill_amount: 50,
taker_fill_amount: 100,
is_full_fill: false,
is_deposit: true,
deposit_mint,
}],
};
let ix = build_deposit_and_swap_ix(¶ms, &program_id).unwrap();
assert_eq!(ix.data.len(), 222);
assert_eq!(ix.data[0], instruction::DEPOSIT_AND_SWAP);
assert_eq!(ix.accounts.len(), 39);
}
}