Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 14 additions & 188 deletions solana/programs/matching-engine/src/fallback/processor/execute_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use solana_program::{instruction::Instruction, program::invoke_signed_unchecked}

use crate::{
error::MatchingEngineError,
processor::ExecuteOrderInternalAccounting,
state::{Auction, AuctionStatus, Custodian, MessageProtocol},
utils::{self, auction::DepositPenalty},
ID,
};

Expand Down Expand Up @@ -346,193 +346,25 @@ pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> {
// TODO: Do we have to verify the CCTP message transmitter program is passed
// in?

////////////////////////////////////////////////////////////////////////////
//
// TODO: This execute order logic has been taken from the original execute
// order instructions. We plan on using a helper method instead of copy-
// pasting the same logic here.
//
////////////////////////////////////////////////////////////////////////////

// Prepare the execute order (get the user amount, fill, and order executed event)
let current_slot = Clock::get().unwrap().slot;

// We extend the grace period for locally executed orders. Reserving a sequence number for
// the fast fill will most likely require an additional transaction, so this buffer allows
// the best offer participant to perform his duty without the risk of getting slashed by
// another executor.
let additional_grace_period = match active_auction.target_protocol {
MessageProtocol::Local { .. } => {
crate::EXECUTE_FAST_ORDER_LOCAL_ADDITIONAL_GRACE_PERIOD.into()
}
_ => None,
};

let DepositPenalty {
penalty,
user_reward,
} = utils::auction::compute_deposit_penalty(
&auction_config,
active_auction_inner_info,
current_slot,
additional_grace_period,
);

let init_auction_fee = fast_market_order.init_auction_fee;

let user_amount = active_auction_inner_info
.amount_in
.saturating_sub(active_auction_inner_info.offer_price)
.saturating_sub(init_auction_fee)
.saturating_add(user_reward);

// Keep track of the remaining amount in the custody token account. Whatever remains will go
// to the executor.

let custody_token =
let auction_custody =
TokenAccount::try_deserialize(&mut &auction_custody_info.data.borrow()[..])?;
let mut remaining_custodied_amount = custody_token.amount.saturating_sub(user_amount);

// Offer price + security deposit was checked in placing the initial offer.
let mut deposit_and_fee = active_auction_inner_info
.offer_price
.saturating_add(active_auction_inner_info.security_deposit)
.saturating_sub(user_reward);

let penalized = penalty > 0;

if penalized && auction_best_offer_token_info.key != executor_token_info.key {
deposit_and_fee = deposit_and_fee.saturating_sub(penalty);
let (user_amount, new_status, _penalized) = ExecuteOrderInternalAccounting {
active_auction_key: active_auction_info.key,
active_auction: &active_auction,
auction_custody_key: auction_custody_info.key,
auction_custody: &auction_custody,
best_offer_token_info: auction_best_offer_token_info,
executor_token_key: executor_token_info.key,
initial_offer_token_info: auction_initial_offer_token_info,
custodian_key: custodian_info.key,
auction_config: &auction_config,
}

// Need these seeds in order to transfer tokens and then set authority of auction custody token account to the custodian
let auction_signer_seeds = &[
Auction::SEED_PREFIX,
active_auction.vaa_hash.as_ref(),
&[active_auction.bump],
];

// If the initial offer token account doesn't exist anymore, we have nowhere to send the
// init auction fee. The executor will get these funds instead.
//
// We check that this is a legitimate token account.
if utils::checked_deserialize_token_account(
auction_initial_offer_token_info,
&common::USDC_MINT,
)
.is_some()
{
if auction_best_offer_token_info.key() != auction_initial_offer_token_info.key() {
// Pay the auction initiator their fee.
let transfer_ix = spl_token::instruction::transfer(
&spl_token::ID,
&auction_custody_info.key(),
&auction_initial_offer_token_info.key(),
&active_auction_info.key(),
&[],
init_auction_fee,
)
.unwrap();

invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?;
// Because the initial offer token was paid this fee, we account for it here.
remaining_custodied_amount =
remaining_custodied_amount.saturating_sub(init_auction_fee);
} else {
// Add it to the reimbursement.
deposit_and_fee = deposit_and_fee
.checked_add(init_auction_fee)
.ok_or_else(|| MatchingEngineError::U64Overflow)?;
}
}

// Return the security deposit and the fee to the highest bidder.
if auction_best_offer_token_info.key == executor_token_info.key {
// If the best offer token is equal to the executor token, just send whatever remains in
// the custody token account.
//
// NOTE: This will revert if the best offer token does not exist. But this will present
// an opportunity for another executor to execute this order and take what the best
// offer token would have received.
let transfer_ix = spl_token::instruction::transfer(
&spl_token::ID,
&auction_custody_info.key(),
&auction_best_offer_token_info.key(),
&active_auction_info.key(),
&[],
deposit_and_fee,
)
.unwrap();
msg!(
"Sending deposit and fee amount {} to best offer token account",
deposit_and_fee
);
invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?;
} else {
// Otherwise, send the deposit and fee to the best offer token. If the best offer token
// doesn't exist at this point (which would be unusual), we will reserve these funds
// for the executor token.
if utils::checked_deserialize_token_account(
auction_best_offer_token_info,
&common::USDC_MINT,
)
.is_some()
{
let transfer_ix = spl_token::instruction::transfer(
&spl_token::ID,
&auction_custody_info.key(),
&auction_best_offer_token_info.key(),
&active_auction_info.key(),
&[],
deposit_and_fee,
)
.unwrap();
msg!(
"Sending deposit and fee {} to best offer token account",
deposit_and_fee
);
invoke_signed_unchecked(&transfer_ix, accounts, &[auction_signer_seeds])?;
remaining_custodied_amount = remaining_custodied_amount.saturating_sub(deposit_and_fee);
}

// And pay the executor whatever remains in the auction custody token account.
if remaining_custodied_amount > 0 {
let instruction = spl_token::instruction::transfer(
&spl_token::ID,
auction_custody_info.key,
executor_token_info.key,
&active_auction_info.key(),
&[],
remaining_custodied_amount,
)
.unwrap();
msg!(
"Sending remaining custodied amount {} to executor token account",
remaining_custodied_amount
);
invoke_signed_unchecked(&instruction, accounts, &[auction_signer_seeds])?;
}
}

// Set the authority of the custody token account to the custodian. He will take over from
// here.
let set_authority_ix = spl_token::instruction::set_authority(
&spl_token::ID,
auction_custody_info.key,
Some(custodian_info.key),
spl_token::instruction::AuthorityType::AccountOwner,
active_auction_info.key,
&[],
)
.unwrap();

invoke_signed_unchecked(&set_authority_ix, accounts, &[auction_signer_seeds])?;
.into_calculate_and_transfer(fast_market_order.init_auction_fee, accounts)?;

// Set the active auction status
active_auction.status = AuctionStatus::Completed {
slot: current_slot,
execute_penalty: if penalized { penalty.into() } else { None },
};
active_auction.status = new_status;

let active_auction_info_data: &mut [u8] = &mut active_auction_info.data.borrow_mut();
let mut active_auction_cursor = std::io::Cursor::new(active_auction_info_data);
Expand All @@ -549,12 +381,6 @@ pub(super) fn process(accounts: &[AccountInfo]) -> Result<()> {
.unwrap(),
};

////////////////////////////////////////////////////////////////////////////
//
// TODO: See above TODO. This is the end of the copy-pasted logic.
//
////////////////////////////////////////////////////////////////////////////

// TODO: Write test that passes in random keypair for CCTP message account
// to show that not having to check the PDA address is safe.
let (_, new_cctp_message_bump) = Pubkey::find_program_address(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use solana_program::{instruction::Instruction, program::invoke_signed_unchecked}

use crate::{
error::MatchingEngineError,
processor::calculate_security_deposit,
state::{Auction, AuctionInfo, AuctionStatus, MessageProtocol},
ID,
};
Expand Down Expand Up @@ -261,11 +262,10 @@ pub(super) fn process(

// The total amount being transferred to the auction's custody token account
// is the order's amount and auction participant's security deposit.
let security_deposit = fast_market_order.max_fee.saturating_add(
crate::utils::auction::compute_notional_security_deposit(
&auction_config,
fast_market_order.amount_in,
),
let security_deposit = calculate_security_deposit(
fast_market_order.max_fee,
fast_market_order.amount_in,
&auction_config,
);

let transfer_ix = spl_token::instruction::transfer(
Expand Down
Loading