Skip to content

Commit

Permalink
Global seat eviction (#66)
Browse files Browse the repository at this point in the history
* evict helper start

* more impl

* evict

* evict

* test fix

* second tree implementation

* tests pass

* double sized global

* comments

* comment

* fmt

* scoping

* change sizes

* constant

* deflake tests

* log evictee balance

* test change

* test fix

* reduce max seats for test

* Do not crash when not found

* move check

* fmt
  • Loading branch information
brittcyr authored Sep 13, 2024
1 parent 9ed16a1 commit 4fd4152
Show file tree
Hide file tree
Showing 18 changed files with 913 additions and 92 deletions.
2 changes: 1 addition & 1 deletion client/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ mod test {
let global_account_info: AccountInfo = {
let mut global_value: DynamicAccount<GlobalFixed, Vec<u8>> = GlobalValue {
fixed: GlobalFixed::new_empty(&quote_mint_key),
dynamic: vec![0; GLOBAL_BLOCK_SIZE * 1],
dynamic: vec![0; GLOBAL_BLOCK_SIZE * 2],
};
global_value.global_expand().unwrap();
global_value.add_trader(&trader_key).unwrap();
Expand Down
7 changes: 5 additions & 2 deletions programs/manifest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use program::{
create_market::process_create_market, deposit::process_deposit,
expand_market::process_expand_market, global_add_trader::process_global_add_trader,
global_create::process_global_create, global_deposit::process_global_deposit,
global_withdraw::process_global_withdraw, process_swap, withdraw::process_withdraw,
ManifestInstruction,
global_evict::process_global_evict, global_withdraw::process_global_withdraw, process_swap,
withdraw::process_withdraw, ManifestInstruction,
};
use solana_program::{
account_info::AccountInfo, declare_id, entrypoint::ProgramResult, program_error::ProgramError,
Expand Down Expand Up @@ -89,6 +89,9 @@ pub fn process_instruction(
ManifestInstruction::GlobalWithdraw => {
process_global_withdraw(program_id, accounts, data)?;
}
ManifestInstruction::GlobalEvict => {
process_global_evict(program_id, accounts, data)?;
}
}

Ok(())
Expand Down
15 changes: 15 additions & 0 deletions programs/manifest/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,15 @@ pub struct GlobalWithdrawLog {
pub global_atoms: GlobalAtoms,
}

#[repr(C)]
#[derive(Clone, Copy, Zeroable, Pod, ShankAccount)]
pub struct GlobalEvictLog {
pub evictor: Pubkey,
pub evictee: Pubkey,
pub evictor_atoms: GlobalAtoms,
pub evictee_atoms: GlobalAtoms,
}

pub trait Discriminant {
fn discriminant() -> [u8; 8];
}
Expand Down Expand Up @@ -170,6 +179,7 @@ const GLOBAL_ADD_TRADER_LOG_DISCRIMINANT: [u8; 8] = [129, 246, 90, 94, 87, 186,
const GLOBAL_CLAIM_SEAT_LOG_DISCRIMINANT: [u8; 8] = [164, 46, 227, 175, 3, 143, 73, 86];
const GLOBAL_DEPOSIT_LOG_DISCRIMINANT: [u8; 8] = [16, 26, 72, 1, 145, 232, 182, 71];
const GLOBAL_WITHDRAW_LOG_DISCRIMINANT: [u8; 8] = [206, 118, 67, 64, 124, 109, 157, 201];
const GLOBAL_EVICT_LOG_DISCRIMINANT: [u8; 8] = [250, 180, 155, 38, 98, 223, 82, 223];

discriminant!(
CreateMarketLog,
Expand Down Expand Up @@ -219,3 +229,8 @@ discriminant!(
GLOBAL_WITHDRAW_LOG_DISCRIMINANT,
test_global_withdraw_log
);
discriminant!(
GlobalEvictLog,
GLOBAL_EVICT_LOG_DISCRIMINANT,
test_global_evict_log
);
4 changes: 4 additions & 0 deletions programs/manifest/src/program/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ pub enum ManifestError {
IncorrectAccount = 17,
#[error("Mint not allowed for market")]
InvalidMint = 18,
#[error("Cannot claim a new global seat, use evict")]
TooManyGlobalSeats = 19,
#[error("Can only evict the lowest depositor")]
InvalidEvict = 20,
}

impl From<ManifestError> for ProgramError {
Expand Down
13 changes: 11 additions & 2 deletions programs/manifest/src/program/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,21 @@ pub enum ManifestInstruction {
#[account(5, name = "token_program", desc = "Token program(22)")]
GlobalWithdraw = 10,

/// Evict another trader from the global account.
#[account(0, writable, signer, name = "payer", desc = "Payer")]
#[account(1, writable, name = "global", desc = "Global account")]
#[account(2, name = "mint", desc = "Mint for this global account")]
#[account(3, writable, name = "global_vault", desc = "Global vault")]
#[account(4, name = "trader_token", desc = "Trader token account")]
#[account(5, name = "evictee_token", desc = "Evictee token account")]
#[account(6, name = "token_program", desc = "Token program(22)")]
GlobalEvict = 11,

// TODO: Implement this. Users can clean another users unbacked or expired
// orders off the orderbook.
//#[account(0, writable, signer, name = "payer", desc = "Payer")]
// GlobalCleanOrder = 11,
// GlobalPurgeTrader = 12,

}

impl ManifestInstruction {
Expand All @@ -133,7 +142,7 @@ impl ManifestInstruction {

#[test]
fn test_instruction_serialization() {
let num_instructions: u8 = 10;
let num_instructions: u8 = 11;
for i in 0..=255 {
let instruction: ManifestInstruction = match ManifestInstruction::try_from(i) {
Ok(j) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::{
program::{global_deposit::GlobalDepositParams, ManifestInstruction},
validation::{get_global_address, get_global_vault_address},
};
use borsh::BorshSerialize;
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};

pub fn global_evict_instruction(
mint: &Pubkey,
payer: &Pubkey,
trader_token_account: &Pubkey,
evictee_token_account: &Pubkey,
token_program: &Pubkey,
num_atoms: u64,
) -> Instruction {
let (global, _global_bump) = get_global_address(mint);
let (global_vault, _global_vault_bump) = get_global_vault_address(mint);
Instruction {
program_id: crate::id(),
accounts: vec![
AccountMeta::new(*payer, true),
AccountMeta::new(global, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new(global_vault, false),
AccountMeta::new(*trader_token_account, false),
AccountMeta::new(*evictee_token_account, false),
AccountMeta::new_readonly(*token_program, false),
],
data: [
ManifestInstruction::GlobalEvict.to_vec(),
GlobalDepositParams::new(num_atoms).try_to_vec().unwrap(),
]
.concat(),
}
}
2 changes: 2 additions & 0 deletions programs/manifest/src/program/instruction_builders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod expand_market_instruction;
pub mod global_add_trader_instruction;
pub mod global_create_instruction;
pub mod global_deposit_instruction;
pub mod global_evict_instruction;
pub mod global_withdraw_instruction;
pub mod swap_instruction;
pub mod withdraw_instruction;
Expand All @@ -18,6 +19,7 @@ pub use expand_market_instruction::*;
pub use global_add_trader_instruction::*;
pub use global_create_instruction::*;
pub use global_deposit_instruction::*;
pub use global_evict_instruction::*;
pub use global_withdraw_instruction::*;
pub use swap_instruction::*;
pub use withdraw_instruction::*;
188 changes: 188 additions & 0 deletions programs/manifest/src/program/processor/global_evict.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
use std::cell::RefMut;

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, pubkey::Pubkey,
};

use crate::{
global_vault_seeds_with_bump,
logs::{emit_stack, GlobalDepositLog, GlobalEvictLog, GlobalWithdrawLog},
program::{get_mut_dynamic_account, ManifestError},
quantities::{GlobalAtoms, WrapperU64},
require,
state::GlobalRefMut,
validation::{get_global_vault_address, loaders::GlobalEvictContext},
};
use solana_program::program::invoke_signed;

#[derive(BorshDeserialize, BorshSerialize)]
pub struct GlobalEvictParams {
// Deposit amount that must be greater than the evictee deposit amount
amount_atoms: u64,
}

impl GlobalEvictParams {
pub fn new(amount_atoms: u64) -> Self {
GlobalEvictParams { amount_atoms }
}
}

pub(crate) fn process_global_evict(
_program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let global_evict_context: GlobalEvictContext = GlobalEvictContext::load(accounts)?;
let GlobalEvictParams { amount_atoms } = GlobalEvictParams::try_from_slice(data)?;

let GlobalEvictContext {
payer,
global,
mint,
global_vault,
trader_token,
evictee_token,
token_program,
} = global_evict_context;

// 1. Withdraw for the evictee
// 2. Evict the seat on the global account and claim
// 3. Deposit for the evictor
let global_data: &mut RefMut<&mut [u8]> = &mut global.try_borrow_mut_data()?;
let mut global_dynamic_account: GlobalRefMut = get_mut_dynamic_account(global_data);
let evictee_balance: GlobalAtoms =
global_dynamic_account.get_balance_atoms(&evictee_token.get_owner());

{
// Do verifications that this is a valid eviction.
require!(
evictee_balance < GlobalAtoms::new(amount_atoms),
ManifestError::InvalidEvict,
"Evictee balance {} is more than evictor wants to deposit",
evictee_balance.as_u64(),
)?;
global_dynamic_account.verify_min_balance(&evictee_token.get_owner())?;
}

// Withdraw
{
let evictee_balance: GlobalAtoms =
global_dynamic_account.get_balance_atoms(&evictee_token.get_owner());
global_dynamic_account.withdraw_global(&evictee_token.get_owner(), evictee_balance)?;

let (_, bump) = get_global_vault_address(mint.info.key);

// Do the token transfer
if *global_vault.owner == spl_token_2022::id() {
invoke_signed(
&spl_token_2022::instruction::transfer_checked(
token_program.key,
global_vault.key,
mint.info.key,
evictee_token.key,
global_vault.key,
&[],
evictee_balance.into(),
mint.mint.decimals,
)?,
&[
token_program.as_ref().clone(),
evictee_token.as_ref().clone(),
mint.as_ref().clone(),
global_vault.as_ref().clone(),
],
global_vault_seeds_with_bump!(mint.info.key, bump),
)?;
} else {
invoke_signed(
&spl_token::instruction::transfer(
token_program.key,
global_vault.key,
evictee_token.key,
global_vault.key,
&[],
evictee_balance.into(),
)?,
&[
token_program.as_ref().clone(),
global_vault.as_ref().clone(),
evictee_token.as_ref().clone(),
],
global_vault_seeds_with_bump!(mint.info.key, bump),
)?;
}

emit_stack(GlobalWithdrawLog {
global: *global.key,
trader: *payer.key,
global_atoms: GlobalAtoms::new(amount_atoms),
})?;
}

// Evict
{
global_dynamic_account
.evict_and_take_seat(&evictee_token.get_owner(), &trader_token.get_owner())?;

emit_stack(GlobalEvictLog {
evictee: evictee_token.get_owner(),
evictor: trader_token.get_owner(),
evictor_atoms: GlobalAtoms::new(amount_atoms),
evictee_atoms: evictee_balance,
})?;
}

// Deposit
{
global_dynamic_account.deposit_global(payer.key, GlobalAtoms::new(amount_atoms))?;

// Do the token transfer
if *global_vault.owner == spl_token_2022::id() {
invoke(
&spl_token_2022::instruction::transfer_checked(
token_program.key,
trader_token.key,
mint.info.key,
global_vault.key,
payer.key,
&[],
amount_atoms,
mint.mint.decimals,
)?,
&[
token_program.as_ref().clone(),
trader_token.as_ref().clone(),
mint.as_ref().clone(),
global_vault.as_ref().clone(),
payer.as_ref().clone(),
],
)?;
} else {
invoke(
&spl_token::instruction::transfer(
token_program.key,
trader_token.key,
global_vault.key,
payer.key,
&[],
amount_atoms,
)?,
&[
token_program.as_ref().clone(),
trader_token.as_ref().clone(),
global_vault.as_ref().clone(),
payer.as_ref().clone(),
],
)?;
}

emit_stack(GlobalDepositLog {
global: *global.key,
trader: *payer.key,
global_atoms: GlobalAtoms::new(amount_atoms),
})?;
}

Ok(())
}
10 changes: 5 additions & 5 deletions programs/manifest/src/program/processor/global_withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub(crate) fn process_global_withdraw(
global,
mint,
global_vault,
trader_token: trader_token_account,
trader_token,
token_program,
} = global_withdraw_context;

Expand All @@ -55,15 +55,15 @@ pub(crate) fn process_global_withdraw(
token_program.key,
global_vault.key,
mint.info.key,
trader_token_account.key,
trader_token.key,
global_vault.key,
&[],
amount_atoms,
mint.mint.decimals,
)?,
&[
token_program.as_ref().clone(),
trader_token_account.as_ref().clone(),
trader_token.as_ref().clone(),
mint.as_ref().clone(),
global_vault.as_ref().clone(),
],
Expand All @@ -74,15 +74,15 @@ pub(crate) fn process_global_withdraw(
&spl_token::instruction::transfer(
token_program.key,
global_vault.key,
trader_token_account.key,
trader_token.key,
global_vault.key,
&[],
amount_atoms,
)?,
&[
token_program.as_ref().clone(),
global_vault.as_ref().clone(),
trader_token_account.as_ref().clone(),
trader_token.as_ref().clone(),
],
global_vault_seeds_with_bump!(mint.info.key, bump),
)?;
Expand Down
Loading

0 comments on commit 4fd4152

Please sign in to comment.