Skip to content

Commit

Permalink
wip: eviction authorized delegate
Browse files Browse the repository at this point in the history
  • Loading branch information
joebuild committed Jun 3, 2024
1 parent cb7784a commit 00d9918
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 15 deletions.
38 changes: 38 additions & 0 deletions src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,44 @@ pub enum SeatManagerInstruction {
#[account(0, writable, name = "seat_manager", desc = "This account holds the seat manager state")]
#[account(1, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to renounce the seat manager authority")]
ConfirmRenounceSeatManagerAuthority = 11,

/// AddApprovedEvictor
#[account(0, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to add an authorized delegate for eviction")]
#[account(1, name = "authorized_delegate")]
#[account(2, name = "authorized_delegate_pda")]
#[account(3, name = "system_program", desc = "System program")]
AddApprovedEvictor = 12,

/// RemoveApprovedEvictor
#[account(0, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to remove an authorized delegate for eviction")]
#[account(1, name = "authorized_delegate")]
#[account(2, name = "authorized_delegate_pda")]
#[account(3, name = "system_program", desc = "System program")]
RemoveApprovedEvictor = 13,

/// Evict Seat
#[account(0, name = "phoenix_program", desc = "Phoenix program")]
#[account(1, name = "log_authority", desc = "Phoenix log authority")]
#[account(2, writable, name = "market", desc = "This account holds the market state")]
#[account(3, writable, name = "seat_manager", desc = "The seat manager account must sign to evict a seat")]
#[account(4, writable, name = "seat_deposit_collector", desc = "Collects deposits for claiming new seats and refunds for evicting seats")]
#[account(5, name = "base_mint")]
#[account(6, name = "quote_mint")]
#[account(7, writable, name = "base_vault")]
#[account(8, writable, name = "quote_vault")]
#[account(9, name = "associated_token_account_program", desc = "Associated token account program")]
#[account(10, name = "token_program", desc = "Token program")]
#[account(11, name = "system program", desc = "System program to handle refund transfers")]
#[account(12, signer, name = "signer")]
#[account(13, name = "authorized_delegate_pda")]
// There can be multiple traders, so the following pattern can be repeated indefinitely
#[account(14, writable, name = "trader")]
#[account(15, name = "seat", desc = "The trader's PDA seat account, seeds are [b'seat', market_address, trader_address]")]
#[account(16, writable, name = "base_account", desc = "The trader's associated token account for the base mint")]
#[account(17, writable, name = "quote_account", desc = "The trader's associated token account for the quote mint")]
#[account(18, writable, name = "base_account_backup", desc = "Non-ATA token account for the base mint, in case the ATA owner is no longer the trader")]
#[account(19, writable, name = "quote_account_backup", desc = "Non-ATA token account for the quote mint, in case the ATA owner is no longer the trader")]
EvictSeatWithAuthorizedDelegate = 14,
}

impl SeatManagerInstruction {
Expand Down
21 changes: 17 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use ellipsis_macros::declare_id;
use instruction::SeatManagerInstruction;
use processor::{
process_change_market_status, process_claim_market_authority, process_claim_seat,
process_claim_seat_manager_authority, process_confirm_renounce_seat_manager_authority,
process_designated_market_maker, process_evict_seat, process_name_successor,
process_authorized_evictor, process_change_market_status, process_claim_market_authority,
process_claim_seat, process_claim_seat_manager_authority,
process_confirm_renounce_seat_manager_authority, process_designated_market_maker,
process_evict_seat, process_name_successor,
};
use solana_program::instruction::Instruction;
use solana_program::msg;
Expand Down Expand Up @@ -163,7 +164,7 @@ pub fn process_instruction(
}
SeatManagerInstruction::EvictSeat => {
msg!("SeatManagerInstruction::EvictSeat");
process_evict_seat(program_id, accounts)
process_evict_seat(program_id, accounts, false)
}
SeatManagerInstruction::AddDesignatedMarketMaker => {
msg!("SeatManagerInstruction::AddDesignatedMarketMaker");
Expand Down Expand Up @@ -197,5 +198,17 @@ pub fn process_instruction(
msg!("SeatManagerInstruction::ConfirmRenounceSeatManagerAuthority");
process_confirm_renounce_seat_manager_authority(program_id, accounts)
}
SeatManagerInstruction::AddApprovedEvictor => {
msg!("SeatManagerInstruction::AddApprovedEvictor");
process_authorized_evictor(program_id, accounts, false)
}
SeatManagerInstruction::RemoveApprovedEvictor => {
msg!("SeatManagerInstruction::RemoveApprovedEvictor");
process_authorized_evictor(program_id, accounts, true)
}
SeatManagerInstruction::EvictSeatWithAuthorizedDelegate => {
msg!("SeatManagerInstruction::EvictSeat");
process_evict_seat(program_id, accounts, true)
}
}
}
78 changes: 78 additions & 0 deletions src/processor/authorized_evictor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use phoenix::program::{
checkers::{Program, Signer, PDA},
system_utils::create_account,
};
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError,
pubkey::Pubkey, rent::Rent, system_program, sysvar::Sysvar,
};

pub fn process_authorized_evictor(
program_id: &Pubkey,
accounts: &[AccountInfo],
remove: bool,
) -> ProgramResult {
let seat_manager_authority = Signer::new(&accounts[0])?;
let authorized_delegate = &accounts[1];
let authorized_delegate_pda = &accounts[2];

// Get pubkey for PDA derived from: seat_manager_auth and authorized_delegate
let authorized_delegate_pda_seeds = get_authorized_delegate_seeds_and_validate(
&seat_manager_authority.key,
&authorized_delegate.key,
&authorized_delegate_pda.key,
&program_id,
)?;

let system_program = Program::new(&accounts[3], &system_program::id())?;

if !remove {
create_account(
&seat_manager_authority,
&authorized_delegate_pda,
&system_program,
program_id,
&Rent::get()?,
1,
authorized_delegate_pda_seeds.clone(),
)?;
} else {
// TODO
}

Ok(())
}

pub fn get_authorized_delegate_seeds_and_validate(
seat_manager_authority: &Pubkey,
authorized_delegate: &Pubkey,
authorized_delegate_pda: &Pubkey,
program_id: &Pubkey,
) -> Result<Vec<Vec<u8>>, ProgramError> {
let mut seeds = vec![
seat_manager_authority.to_bytes().to_vec(),
authorized_delegate.to_bytes().to_vec(),
];
let (derived_pda, bump) = Pubkey::find_program_address(
seeds
.iter()
.map(|seed| seed.as_slice())
.collect::<Vec<&[u8]>>()
.as_slice(),
&program_id,
);
seeds.push(vec![bump]);

if derived_pda == *authorized_delegate_pda {
Ok(seeds)
} else {
let caller = std::panic::Location::caller();
msg!(
"Invalid authorized delegate key, expected: {} found {}.\n{}",
authorized_delegate_pda,
derived_pda,
caller
);
return Err(ProgramError::InvalidInstructionData.into());
}
}
52 changes: 41 additions & 11 deletions src/processor/evict_seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use solana_program::{
};
use spl_associated_token_account::instruction::create_associated_token_account;

use super::get_authorized_delegate_seeds_and_validate;

struct TraderAccountsContext<'a, 'info> {
trader: &'a AccountInfo<'info>,
_seat: &'a AccountInfo<'info>,
Expand Down Expand Up @@ -81,7 +83,11 @@ impl<'a, 'info> TraderAccountsContext<'a, 'info> {
}
}

pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
pub fn process_evict_seat(
program_id: &Pubkey,
accounts: &[AccountInfo],
evict_with_delegate: bool,
) -> ProgramResult {
let market_ai = MarketAccount::new(&accounts[2])?;
let seat_manager = SeatManagerAccount::new_with_market(&accounts[3], market_ai.key)?;
let seat_deposit_collector = PDA::new(
Expand All @@ -98,8 +104,25 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
// Retrieve seat manager seeds and check if signer is authorized
let is_fully_authorized = *signer.key == seat_manager.load()?.authority;

// If we are doing eviction with an authorized delegate, we need to validate the signer
let mut is_authorized_delegate = false;
if evict_with_delegate {
let authorized_delegate_pda = &accounts[13];

// First just check that the PDA passed in corresponds to the seat manager auth and the signer
let _seeds = get_authorized_delegate_seeds_and_validate(
&seat_manager.load()?.authority,
&signer.key,
&authorized_delegate_pda.key,
program_id,
)?;

// If the PDA exists, then the signer is an authorized delegate
is_authorized_delegate = authorized_delegate_pda.lamports() > 0;
}

// Get market parameters to perform checks
let (base_mint, quote_mint, market_size_params, has_eviction_privileges) = {
let (base_mint, quote_mint, market_size_params, seats_are_full) = {
let market_bytes = market_ai.data.borrow();
let (header_bytes, market_bytes) = market_bytes.split_at(size_of::<MarketHeader>());
let market_header =
Expand All @@ -118,8 +141,7 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
let registered_traders = market.get_registered_traders();

// When this boolean is true, signer has the privilege to evict any seat with 0 locked base lots and 0 locked quote lots
let has_eviction_privileges =
registered_traders.capacity() == registered_traders.len();
let seats_are_full = registered_traders.capacity() == registered_traders.len();

assert_with_msg(
base_mint_ai.info.key == &base_mint,
Expand All @@ -135,12 +157,17 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
base_mint,
quote_mint,
market_header.market_size_params,
has_eviction_privileges,
seats_are_full,
)
};

let mut accounts_starting_index = 13;
if evict_with_delegate {
accounts_starting_index += 1;
}

// Perform eviction for trader(s)
for trader_accounts in &accounts[13..].iter().chunks(6) {
for trader_accounts in &accounts[accounts_starting_index..].iter().chunks(6) {
let TraderAccountsContext {
trader: trader_ai,
_seat,
Expand All @@ -165,11 +192,14 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
&& trader_state.base_lots_free == 0
&& trader_state.quote_lots_free == 0;

let can_evict_trader = if has_eviction_privileges || is_fully_authorized {
trader_state.base_lots_locked == 0 && trader_state.quote_lots_locked == 0
} else {
seat_is_empty
};
// If a trader's seat has 0 locked base lots, 0 locked quote lots, then an approved deligate can remove it
let seat_is_unlocked =
trader_state.base_lots_locked == 0 && trader_state.quote_lots_locked == 0;

let can_evict_trader = seat_is_empty // anyone can permissionlessly evict an empty seat
|| (seats_are_full && seat_is_unlocked) // anyone can permissionlessly evict an unlocked seat when seats are full
|| (is_fully_authorized && seat_is_unlocked) // the fully authorized seat manager can evict an unlocked seat
|| (is_authorized_delegate && seat_is_unlocked); // the authorized delegate can evict an unlocked seat

if can_evict_trader {
// Change seat status
Expand Down
2 changes: 2 additions & 0 deletions src/processor/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod authorized_evictor;
pub mod change_market_fee_recipient;
pub mod change_market_status;
pub mod change_seat_manager_authority;
Expand All @@ -8,6 +9,7 @@ pub mod designated_market_maker;
pub mod evict_seat;
pub mod name_market_authority_successor;

pub use authorized_evictor::*;
pub use change_market_fee_recipient::*;
pub use change_market_status::*;
pub use change_seat_manager_authority::*;
Expand Down

0 comments on commit 00d9918

Please sign in to comment.