Skip to content

Commit 1bc493d

Browse files
committed
wip: eviction authorized delegate
1 parent 31ad32a commit 1bc493d

File tree

5 files changed

+176
-15
lines changed

5 files changed

+176
-15
lines changed

src/instruction.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,44 @@ pub enum SeatManagerInstruction {
116116
#[account(0, writable, name = "seat_manager", desc = "This account holds the seat manager state")]
117117
#[account(1, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to renounce the seat manager authority")]
118118
ConfirmRenounceSeatManagerAuthority = 11,
119+
120+
/// AddApprovedEvictor
121+
#[account(0, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to add an authorized delegate for eviction")]
122+
#[account(1, name = "authorized_delegate")]
123+
#[account(2, name = "authorized_delegate_pda")]
124+
#[account(3, name = "system_program", desc = "System program")]
125+
AddApprovedEvictor = 12,
126+
127+
/// RemoveApprovedEvictor
128+
#[account(0, signer, name = "seat_manager_authority", desc = "The seat manager authority must sign to remove an authorized delegate for eviction")]
129+
#[account(1, name = "authorized_delegate")]
130+
#[account(2, name = "authorized_delegate_pda")]
131+
#[account(3, name = "system_program", desc = "System program")]
132+
RemoveApprovedEvictor = 13,
133+
134+
/// Evict Seat
135+
#[account(0, name = "phoenix_program", desc = "Phoenix program")]
136+
#[account(1, name = "log_authority", desc = "Phoenix log authority")]
137+
#[account(2, writable, name = "market", desc = "This account holds the market state")]
138+
#[account(3, writable, name = "seat_manager", desc = "The seat manager account must sign to evict a seat")]
139+
#[account(4, writable, name = "seat_deposit_collector", desc = "Collects deposits for claiming new seats and refunds for evicting seats")]
140+
#[account(5, name = "base_mint")]
141+
#[account(6, name = "quote_mint")]
142+
#[account(7, writable, name = "base_vault")]
143+
#[account(8, writable, name = "quote_vault")]
144+
#[account(9, name = "associated_token_account_program", desc = "Associated token account program")]
145+
#[account(10, name = "token_program", desc = "Token program")]
146+
#[account(11, name = "system program", desc = "System program to handle refund transfers")]
147+
#[account(12, signer, name = "signer")]
148+
#[account(13, name = "authorized_delegate_pda")]
149+
// There can be multiple traders, so the following pattern can be repeated indefinitely
150+
#[account(14, writable, name = "trader")]
151+
#[account(15, name = "seat", desc = "The trader's PDA seat account, seeds are [b'seat', market_address, trader_address]")]
152+
#[account(16, writable, name = "base_account", desc = "The trader's associated token account for the base mint")]
153+
#[account(17, writable, name = "quote_account", desc = "The trader's associated token account for the quote mint")]
154+
#[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")]
155+
#[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")]
156+
EvictSeatWithAuthorizedDelegate = 14,
119157
}
120158

121159
impl SeatManagerInstruction {

src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use ellipsis_macros::declare_id;
22
use instruction::SeatManagerInstruction;
33
use processor::{
4-
process_change_market_status, process_claim_market_authority, process_claim_seat,
5-
process_claim_seat_manager_authority, process_confirm_renounce_seat_manager_authority,
6-
process_designated_market_maker, process_evict_seat, process_name_successor,
4+
process_authorized_evictor, process_change_market_status, process_claim_market_authority,
5+
process_claim_seat, process_claim_seat_manager_authority,
6+
process_confirm_renounce_seat_manager_authority, process_designated_market_maker,
7+
process_evict_seat, process_name_successor,
78
};
89
use solana_program::instruction::Instruction;
910
use solana_program::msg;
@@ -163,7 +164,7 @@ pub fn process_instruction(
163164
}
164165
SeatManagerInstruction::EvictSeat => {
165166
msg!("SeatManagerInstruction::EvictSeat");
166-
process_evict_seat(program_id, accounts)
167+
process_evict_seat(program_id, accounts, false)
167168
}
168169
SeatManagerInstruction::AddDesignatedMarketMaker => {
169170
msg!("SeatManagerInstruction::AddDesignatedMarketMaker");
@@ -197,5 +198,17 @@ pub fn process_instruction(
197198
msg!("SeatManagerInstruction::ConfirmRenounceSeatManagerAuthority");
198199
process_confirm_renounce_seat_manager_authority(program_id, accounts)
199200
}
201+
SeatManagerInstruction::AddApprovedEvictor => {
202+
msg!("SeatManagerInstruction::AddApprovedEvictor");
203+
process_authorized_evictor(program_id, accounts, false)
204+
}
205+
SeatManagerInstruction::RemoveApprovedEvictor => {
206+
msg!("SeatManagerInstruction::RemoveApprovedEvictor");
207+
process_authorized_evictor(program_id, accounts, true)
208+
}
209+
SeatManagerInstruction::EvictSeatWithAuthorizedDelegate => {
210+
msg!("SeatManagerInstruction::EvictSeat");
211+
process_evict_seat(program_id, accounts, true)
212+
}
200213
}
201214
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use phoenix::program::{
2+
checkers::{Program, Signer, PDA},
3+
system_utils::create_account,
4+
};
5+
use solana_program::{
6+
account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError,
7+
pubkey::Pubkey, rent::Rent, system_program, sysvar::Sysvar,
8+
};
9+
10+
pub fn process_authorized_evictor(
11+
program_id: &Pubkey,
12+
accounts: &[AccountInfo],
13+
remove: bool,
14+
) -> ProgramResult {
15+
let seat_manager_authority = Signer::new(&accounts[0])?;
16+
let authorized_delegate = &accounts[1];
17+
let authorized_delegate_pda = &accounts[2];
18+
19+
// Get pubkey for PDA derived from: seat_manager_auth and authorized_delegate
20+
let authorized_delegate_pda_seeds = get_authorized_delegate_seeds_and_validate(
21+
&seat_manager_authority.key,
22+
&authorized_delegate.key,
23+
&authorized_delegate_pda.key,
24+
&program_id,
25+
)?;
26+
27+
let system_program = Program::new(&accounts[3], &system_program::id())?;
28+
29+
if !remove {
30+
create_account(
31+
&seat_manager_authority,
32+
&authorized_delegate_pda,
33+
&system_program,
34+
program_id,
35+
&Rent::get()?,
36+
1,
37+
authorized_delegate_pda_seeds.clone(),
38+
)?;
39+
} else {
40+
// TODO
41+
}
42+
43+
Ok(())
44+
}
45+
46+
pub fn get_authorized_delegate_seeds_and_validate(
47+
seat_manager_authority: &Pubkey,
48+
authorized_delegate: &Pubkey,
49+
authorized_delegate_pda: &Pubkey,
50+
program_id: &Pubkey,
51+
) -> Result<Vec<Vec<u8>>, ProgramError> {
52+
let mut seeds = vec![
53+
seat_manager_authority.to_bytes().to_vec(),
54+
authorized_delegate.to_bytes().to_vec(),
55+
];
56+
let (derived_pda, bump) = Pubkey::find_program_address(
57+
seeds
58+
.iter()
59+
.map(|seed| seed.as_slice())
60+
.collect::<Vec<&[u8]>>()
61+
.as_slice(),
62+
&program_id,
63+
);
64+
seeds.push(vec![bump]);
65+
66+
if derived_pda == *authorized_delegate_pda {
67+
Ok(seeds)
68+
} else {
69+
let caller = std::panic::Location::caller();
70+
msg!(
71+
"Invalid authorized delegate key, expected: {} found {}.\n{}",
72+
authorized_delegate_pda,
73+
derived_pda,
74+
caller
75+
);
76+
return Err(ProgramError::InvalidInstructionData.into());
77+
}
78+
}

src/processor/evict_seat.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use solana_program::{
2323
};
2424
use spl_associated_token_account::instruction::create_associated_token_account;
2525

26+
use super::get_authorized_delegate_seeds_and_validate;
27+
2628
struct TraderAccountsContext<'a, 'info> {
2729
trader: &'a AccountInfo<'info>,
2830
_seat: &'a AccountInfo<'info>,
@@ -81,7 +83,11 @@ impl<'a, 'info> TraderAccountsContext<'a, 'info> {
8183
}
8284
}
8385

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

107+
// If we are doing eviction with an authorized delegate, we need to validate the signer
108+
let mut is_authorized_delegate = false;
109+
if evict_with_delegate {
110+
let authorized_delegate_pda = &accounts[13];
111+
112+
// First just check that the PDA passed in corresponds to the seat manager auth and the signer
113+
let _seeds = get_authorized_delegate_seeds_and_validate(
114+
&seat_manager.load()?.authority,
115+
&signer.key,
116+
&authorized_delegate_pda.key,
117+
program_id,
118+
)?;
119+
120+
// If the PDA exists, then the signer is an authorized delegate
121+
is_authorized_delegate = authorized_delegate_pda.lamports() > 0;
122+
}
123+
101124
// Get market parameters to perform checks
102-
let (base_mint, quote_mint, market_size_params, has_eviction_privileges) = {
125+
let (base_mint, quote_mint, market_size_params, seats_are_full) = {
103126
let market_bytes = market_ai.data.borrow();
104127
let (header_bytes, market_bytes) = market_bytes.split_at(size_of::<MarketHeader>());
105128
let market_header =
@@ -118,8 +141,7 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
118141
let registered_traders = market.get_registered_traders();
119142

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

124146
assert_with_msg(
125147
base_mint_ai.info.key == &base_mint,
@@ -135,12 +157,17 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
135157
base_mint,
136158
quote_mint,
137159
market_header.market_size_params,
138-
has_eviction_privileges,
160+
seats_are_full,
139161
)
140162
};
141163

164+
let mut accounts_starting_index = 13;
165+
if evict_with_delegate {
166+
accounts_starting_index += 1;
167+
}
168+
142169
// Perform eviction for trader(s)
143-
for trader_accounts in &accounts[13..].iter().chunks(6) {
170+
for trader_accounts in &accounts[accounts_starting_index..].iter().chunks(6) {
144171
let TraderAccountsContext {
145172
trader: trader_ai,
146173
_seat,
@@ -165,11 +192,14 @@ pub fn process_evict_seat(program_id: &Pubkey, accounts: &[AccountInfo]) -> Prog
165192
&& trader_state.base_lots_free == 0
166193
&& trader_state.quote_lots_free == 0;
167194

168-
let can_evict_trader = if has_eviction_privileges || is_fully_authorized {
169-
trader_state.base_lots_locked == 0 && trader_state.quote_lots_locked == 0
170-
} else {
171-
seat_is_empty
172-
};
195+
// If a trader's seat has 0 locked base lots, 0 locked quote lots, then an approved deligate can remove it
196+
let seat_is_unlocked =
197+
trader_state.base_lots_locked == 0 && trader_state.quote_lots_locked == 0;
198+
199+
let can_evict_trader = seat_is_empty // anyone can permissionlessly evict an empty seat
200+
|| (seats_are_full && seat_is_unlocked) // anyone can permissionlessly evict an unlocked seat when seats are full
201+
|| (is_fully_authorized && seat_is_unlocked) // the fully authorized seat manager can evict an unlocked seat
202+
|| (is_authorized_delegate && seat_is_unlocked); // the authorized delegate can evict an unlocked seat
173203

174204
if can_evict_trader {
175205
// Change seat status

src/processor/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod authorized_evictor;
12
pub mod change_market_fee_recipient;
23
pub mod change_market_status;
34
pub mod change_seat_manager_authority;
@@ -8,6 +9,7 @@ pub mod designated_market_maker;
89
pub mod evict_seat;
910
pub mod name_market_authority_successor;
1011

12+
pub use authorized_evictor::*;
1113
pub use change_market_fee_recipient::*;
1214
pub use change_market_status::*;
1315
pub use change_seat_manager_authority::*;

0 commit comments

Comments
 (0)