use solana_pubkey::Pubkey;
use crate::ix::constants::{PHOENIX_PROGRAM_ID, SYSTEM_PROGRAM_ID};
use crate::ix::error::PhoenixIxError;
use crate::ix::flight::constants::{
FLIGHT_PROGRAM_ID, flight_register_builder_discriminant, get_flight_builder_state_address,
get_flight_global_state_address,
};
use crate::ix::types::{AccountMeta, Instruction};
#[derive(Debug, Clone)]
pub struct RegisterBuilderParams {
trader_authority: Pubkey,
trader_account: Pubkey,
trader_pda_index: u8,
subaccount_index: u8,
fee_bps: u64,
}
impl RegisterBuilderParams {
pub fn builder() -> RegisterBuilderParamsBuilder {
RegisterBuilderParamsBuilder::new()
}
pub fn trader_authority(&self) -> Pubkey {
self.trader_authority
}
pub fn trader_account(&self) -> Pubkey {
self.trader_account
}
pub fn trader_pda_index(&self) -> u8 {
self.trader_pda_index
}
pub fn subaccount_index(&self) -> u8 {
self.subaccount_index
}
pub fn fee_bps(&self) -> u64 {
self.fee_bps
}
}
#[derive(Default)]
pub struct RegisterBuilderParamsBuilder {
trader_authority: Option<Pubkey>,
trader_account: Option<Pubkey>,
trader_pda_index: Option<u8>,
subaccount_index: Option<u8>,
fee_bps: Option<u64>,
}
impl RegisterBuilderParamsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn trader_authority(mut self, trader_authority: Pubkey) -> Self {
self.trader_authority = Some(trader_authority);
self
}
pub fn trader_account(mut self, trader_account: Pubkey) -> Self {
self.trader_account = Some(trader_account);
self
}
pub fn trader_pda_index(mut self, trader_pda_index: u8) -> Self {
self.trader_pda_index = Some(trader_pda_index);
self
}
pub fn subaccount_index(mut self, subaccount_index: u8) -> Self {
self.subaccount_index = Some(subaccount_index);
self
}
pub fn fee_bps(mut self, fee_bps: u64) -> Self {
self.fee_bps = Some(fee_bps);
self
}
pub fn build(self) -> Result<RegisterBuilderParams, PhoenixIxError> {
Ok(RegisterBuilderParams {
trader_authority: self
.trader_authority
.ok_or(PhoenixIxError::MissingField("trader_authority"))?,
trader_account: self
.trader_account
.ok_or(PhoenixIxError::MissingField("trader_account"))?,
trader_pda_index: self.trader_pda_index.unwrap_or(0),
subaccount_index: self.subaccount_index.unwrap_or(0),
fee_bps: self
.fee_bps
.ok_or(PhoenixIxError::MissingField("fee_bps"))?,
})
}
}
pub fn create_register_builder_ix(
params: RegisterBuilderParams,
) -> Result<Instruction, PhoenixIxError> {
let data = encode_register_builder(¶ms);
let accounts = build_accounts(¶ms);
Ok(Instruction {
program_id: FLIGHT_PROGRAM_ID,
accounts,
data,
})
}
fn encode_register_builder(params: &RegisterBuilderParams) -> Vec<u8> {
let mut data = Vec::with_capacity(18);
data.extend_from_slice(&flight_register_builder_discriminant());
data.push(params.trader_pda_index());
data.push(params.subaccount_index());
data.extend_from_slice(¶ms.fee_bps().to_le_bytes());
data
}
fn build_accounts(params: &RegisterBuilderParams) -> Vec<AccountMeta> {
vec![
AccountMeta::readonly(get_flight_global_state_address()),
AccountMeta::readonly(PHOENIX_PROGRAM_ID),
AccountMeta::writable_signer(params.trader_authority()),
AccountMeta::writable(params.trader_account()),
AccountMeta::writable(get_flight_builder_state_address(¶ms.trader_authority())),
AccountMeta::readonly(SYSTEM_PROGRAM_ID),
]
}
#[cfg(test)]
mod tests {
use super::*;
fn build_params() -> RegisterBuilderParams {
RegisterBuilderParams::builder()
.trader_authority(Pubkey::new_unique())
.trader_account(Pubkey::new_unique())
.fee_bps(250)
.build()
.unwrap()
}
#[test]
fn test_defaults() {
let params = build_params();
assert_eq!(params.trader_pda_index(), 0);
assert_eq!(params.subaccount_index(), 0);
assert_eq!(params.fee_bps(), 250);
}
#[test]
fn test_missing_authority() {
let result = RegisterBuilderParams::builder().fee_bps(1).build();
assert!(matches!(
result,
Err(PhoenixIxError::MissingField("trader_authority"))
));
}
#[test]
fn test_missing_fee_bps() {
let result = RegisterBuilderParams::builder()
.trader_authority(Pubkey::new_unique())
.trader_account(Pubkey::new_unique())
.build();
assert!(matches!(
result,
Err(PhoenixIxError::MissingField("fee_bps"))
));
}
#[test]
fn test_data_encoding() {
let authority = Pubkey::new_unique();
let params = RegisterBuilderParams::builder()
.trader_authority(authority)
.trader_account(Pubkey::new_unique())
.trader_pda_index(3)
.subaccount_index(7)
.fee_bps(1000)
.build()
.unwrap();
let ix = create_register_builder_ix(params).unwrap();
assert_eq!(ix.program_id, FLIGHT_PROGRAM_ID);
assert_eq!(ix.data.len(), 18);
assert_eq!(&ix.data[..8], &flight_register_builder_discriminant());
assert_eq!(ix.data[8], 3);
assert_eq!(ix.data[9], 7);
assert_eq!(&ix.data[10..18], &1000u64.to_le_bytes());
}
#[test]
fn test_account_layout() {
let authority = Pubkey::new_unique();
let trader_account = Pubkey::new_unique();
let params = RegisterBuilderParams::builder()
.trader_authority(authority)
.trader_account(trader_account)
.fee_bps(1)
.build()
.unwrap();
let ix = create_register_builder_ix(params).unwrap();
assert_eq!(ix.accounts.len(), 6);
assert_eq!(ix.accounts[0].pubkey, get_flight_global_state_address());
assert!(!ix.accounts[0].is_signer);
assert!(!ix.accounts[0].is_writable);
assert_eq!(ix.accounts[1].pubkey, PHOENIX_PROGRAM_ID);
assert!(!ix.accounts[1].is_writable);
assert_eq!(ix.accounts[2].pubkey, authority);
assert!(ix.accounts[2].is_signer);
assert!(ix.accounts[2].is_writable);
assert_eq!(ix.accounts[3].pubkey, trader_account);
assert!(ix.accounts[3].is_writable);
assert!(!ix.accounts[3].is_signer);
assert_eq!(
ix.accounts[4].pubkey,
get_flight_builder_state_address(&authority)
);
assert!(ix.accounts[4].is_writable);
assert_eq!(ix.accounts[5].pubkey, SYSTEM_PROGRAM_ID);
assert!(!ix.accounts[5].is_writable);
}
}