SerumPlaceOrder: Only pass the payer bank/vault
This commit is contained in:
parent
36723792a1
commit
e0437305ee
|
@ -605,6 +605,10 @@ impl MangoClient {
|
|||
let rates = get_fee_rates(fee_tier);
|
||||
(s3.market.pc_lot_size as f64 * (1f64 + rates.0)) as u64 * (limit_price * max_base_qty)
|
||||
};
|
||||
let payer_mint_info = match side {
|
||||
Serum3Side::Bid => s3.quote.mint_info,
|
||||
Serum3Side::Ask => s3.base.mint_info,
|
||||
};
|
||||
|
||||
self.program()
|
||||
.request()
|
||||
|
@ -616,10 +620,8 @@ impl MangoClient {
|
|||
group: self.group(),
|
||||
account: self.mango_account_address,
|
||||
open_orders,
|
||||
quote_bank: s3.quote.mint_info.first_bank(),
|
||||
quote_vault: s3.quote.mint_info.first_vault(),
|
||||
base_bank: s3.base.mint_info.first_bank(),
|
||||
base_vault: s3.base.mint_info.first_vault(),
|
||||
payer_bank: payer_mint_info.first_bank(),
|
||||
payer_vault: payer_mint_info.first_vault(),
|
||||
serum_market: s3.market.address,
|
||||
serum_program: s3.market.market.serum_program,
|
||||
serum_market_external: s3.market.market.serum_market_external,
|
||||
|
|
|
@ -169,6 +169,10 @@ pub fn serum3_liq_force_cancel_orders(
|
|||
&mut base_bank,
|
||||
after_base_vault,
|
||||
before_base_vault,
|
||||
)?;
|
||||
apply_vault_difference(
|
||||
&mut account.borrow_mut(),
|
||||
serum_market.market_index,
|
||||
&mut quote_bank,
|
||||
after_quote_vault,
|
||||
before_quote_vault,
|
||||
|
|
|
@ -9,7 +9,6 @@ use fixed::types::I80F48;
|
|||
use num_enum::IntoPrimitive;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use serum_dex::instruction::NewOrderInstructionV3;
|
||||
use serum_dex::matching::Side;
|
||||
use serum_dex::state::OpenOrders;
|
||||
|
||||
/// For loan origination fees bookkeeping purposes
|
||||
|
@ -169,19 +168,13 @@ pub struct Serum3PlaceOrder<'info> {
|
|||
/// CHECK: Validated by the serum cpi call
|
||||
pub market_vault_signer: UncheckedAccount<'info>,
|
||||
|
||||
// TODO: do we need to pass both, or just payer?
|
||||
// TODO: Can we reduce the number of accounts by requiring the banks
|
||||
// to be in the remainingAccounts (where they need to be anyway, for
|
||||
// health checks - but they need to be mut)
|
||||
// token_index and bank.vault == vault is validated inline at #3
|
||||
/// The bank that pays for the order, if necessary
|
||||
// token_index and payer_bank.vault == payer_vault is validated inline at #3
|
||||
#[account(mut, has_one = group)]
|
||||
pub quote_bank: AccountLoader<'info, Bank>,
|
||||
pub payer_bank: AccountLoader<'info, Bank>,
|
||||
/// The bank vault that pays for the order, if necessary
|
||||
#[account(mut)]
|
||||
pub quote_vault: Box<Account<'info, TokenAccount>>,
|
||||
#[account(mut, has_one = group)]
|
||||
pub base_bank: AccountLoader<'info, Bank>,
|
||||
#[account(mut)]
|
||||
pub base_vault: Box<Account<'info, TokenAccount>>,
|
||||
pub payer_vault: Box<Account<'info, TokenAccount>>,
|
||||
|
||||
pub token_program: Program<'info, Token>,
|
||||
}
|
||||
|
@ -221,25 +214,14 @@ pub fn serum3_place_order(
|
|||
MangoError::SomeError
|
||||
);
|
||||
|
||||
// Validate banks and vaults #3
|
||||
let quote_bank = ctx.accounts.quote_bank.load()?;
|
||||
require!(
|
||||
quote_bank.vault == ctx.accounts.quote_vault.key(),
|
||||
MangoError::SomeError
|
||||
);
|
||||
require!(
|
||||
quote_bank.token_index == serum_market.quote_token_index,
|
||||
MangoError::SomeError
|
||||
);
|
||||
let base_bank = ctx.accounts.base_bank.load()?;
|
||||
require!(
|
||||
base_bank.vault == ctx.accounts.base_vault.key(),
|
||||
MangoError::SomeError
|
||||
);
|
||||
require!(
|
||||
base_bank.token_index == serum_market.base_token_index,
|
||||
MangoError::SomeError
|
||||
);
|
||||
// Validate bank and vault #3
|
||||
let payer_bank = ctx.accounts.payer_bank.load()?;
|
||||
require_keys_eq!(payer_bank.vault, ctx.accounts.payer_vault.key());
|
||||
let payer_token_index = match side {
|
||||
Serum3Side::Bid => serum_market.quote_token_index,
|
||||
Serum3Side::Ask => serum_market.base_token_index,
|
||||
};
|
||||
require_eq!(payer_bank.token_index, payer_token_index);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -261,8 +243,7 @@ pub fn serum3_place_order(
|
|||
// Before-order tracking
|
||||
//
|
||||
|
||||
let before_base_vault = ctx.accounts.base_vault.amount;
|
||||
let before_quote_vault = ctx.accounts.quote_vault.amount;
|
||||
let before_vault = ctx.accounts.payer_vault.amount;
|
||||
|
||||
let before_oo = {
|
||||
let oo_ai = &ctx.accounts.open_orders.as_ref();
|
||||
|
@ -271,21 +252,17 @@ pub fn serum3_place_order(
|
|||
};
|
||||
|
||||
// Provide a readable error message in case the vault doesn't have enough tokens
|
||||
let (vault_amount, needed_amount) = match side {
|
||||
Serum3Side::Ask => (
|
||||
before_base_vault,
|
||||
max_base_qty.saturating_sub(before_oo.native_base_free()),
|
||||
),
|
||||
Serum3Side::Bid => (
|
||||
before_quote_vault,
|
||||
max_native_quote_qty_including_fees.saturating_sub(before_oo.native_quote_free()),
|
||||
),
|
||||
let needed_amount = match side {
|
||||
Serum3Side::Ask => max_base_qty.saturating_sub(before_oo.native_base_free()),
|
||||
Serum3Side::Bid => {
|
||||
max_native_quote_qty_including_fees.saturating_sub(before_oo.native_quote_free())
|
||||
}
|
||||
};
|
||||
if vault_amount < needed_amount {
|
||||
if before_vault < needed_amount {
|
||||
return err!(MangoError::InsufficentBankVaultFunds).with_context(|| {
|
||||
format!(
|
||||
"bank vault does not have enough tokens, need {} but have {}",
|
||||
needed_amount, vault_amount
|
||||
needed_amount, before_vault
|
||||
)
|
||||
});
|
||||
}
|
||||
|
@ -318,29 +295,21 @@ pub fn serum3_place_order(
|
|||
//
|
||||
// After-order tracking
|
||||
//
|
||||
ctx.accounts.base_vault.reload()?;
|
||||
ctx.accounts.quote_vault.reload()?;
|
||||
let after_base_vault = ctx.accounts.base_vault.amount;
|
||||
let after_quote_vault = ctx.accounts.quote_vault.amount;
|
||||
ctx.accounts.payer_vault.reload()?;
|
||||
let after_vault = ctx.accounts.payer_vault.amount;
|
||||
|
||||
// Placing an order cannot increase vault balances
|
||||
require_gte!(before_base_vault, after_base_vault);
|
||||
require_gte!(before_quote_vault, after_quote_vault);
|
||||
// Placing an order cannot increase vault balance
|
||||
require_gte!(before_vault, after_vault);
|
||||
|
||||
// Charge the difference in vault balances to the user's account
|
||||
// Charge the difference in vault balance to the user's account
|
||||
let vault_difference = {
|
||||
let mut base_bank = ctx.accounts.base_bank.load_mut()?;
|
||||
let mut quote_bank = ctx.accounts.quote_bank.load_mut()?;
|
||||
|
||||
let mut payer_bank = ctx.accounts.payer_bank.load_mut()?;
|
||||
apply_vault_difference(
|
||||
&mut account.borrow_mut(),
|
||||
serum_market.market_index,
|
||||
&mut base_bank,
|
||||
after_base_vault,
|
||||
before_base_vault,
|
||||
&mut quote_bank,
|
||||
after_quote_vault,
|
||||
before_quote_vault,
|
||||
&mut payer_bank,
|
||||
after_vault,
|
||||
before_vault,
|
||||
)?
|
||||
};
|
||||
|
||||
|
@ -395,16 +364,13 @@ impl OODifference {
|
|||
}
|
||||
|
||||
pub struct VaultDifference {
|
||||
base_index: TokenIndex,
|
||||
quote_index: TokenIndex,
|
||||
base_native_change: I80F48,
|
||||
quote_native_change: I80F48,
|
||||
token_index: TokenIndex,
|
||||
native_change: I80F48,
|
||||
}
|
||||
|
||||
impl VaultDifference {
|
||||
pub fn adjust_health_cache(&self, health_cache: &mut HealthCache) -> Result<()> {
|
||||
health_cache.adjust_token_balance(self.base_index, self.base_native_change)?;
|
||||
health_cache.adjust_token_balance(self.quote_index, self.quote_native_change)?;
|
||||
health_cache.adjust_token_balance(self.token_index, self.native_change)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -414,74 +380,52 @@ impl VaultDifference {
|
|||
pub fn apply_vault_difference(
|
||||
account: &mut MangoAccountRefMut,
|
||||
serum_market_index: Serum3MarketIndex,
|
||||
base_bank: &mut Bank,
|
||||
after_base_vault: u64,
|
||||
before_base_vault: u64,
|
||||
quote_bank: &mut Bank,
|
||||
after_quote_vault: u64,
|
||||
before_quote_vault: u64,
|
||||
bank: &mut Bank,
|
||||
vault_after: u64,
|
||||
vault_before: u64,
|
||||
) -> Result<VaultDifference> {
|
||||
let base_needed_change = cm!(I80F48::from(after_base_vault) - I80F48::from(before_base_vault));
|
||||
let needed_change = cm!(I80F48::from(vault_after) - I80F48::from(vault_before));
|
||||
|
||||
let (base_position, _) = account.token_position_mut(base_bank.token_index)?;
|
||||
let base_native_before = base_position.native(&base_bank);
|
||||
base_bank.change_without_fee(base_position, base_needed_change)?;
|
||||
let base_native_after = base_position.native(&base_bank);
|
||||
let base_native_change = cm!(base_native_after - base_native_before);
|
||||
let base_borrows = base_native_change
|
||||
.max(base_native_after)
|
||||
.min(I80F48::ZERO)
|
||||
.abs()
|
||||
.to_num::<u64>();
|
||||
|
||||
let quote_needed_change =
|
||||
cm!(I80F48::from(after_quote_vault) - I80F48::from(before_quote_vault));
|
||||
|
||||
let (quote_position, _) = account.token_position_mut(quote_bank.token_index)?;
|
||||
let quote_native_before = quote_position.native("e_bank);
|
||||
quote_bank.change_without_fee(quote_position, quote_needed_change)?;
|
||||
let quote_native_after = quote_position.native("e_bank);
|
||||
let quote_native_change = cm!(quote_native_after - quote_native_before);
|
||||
let quote_borrows = quote_native_change
|
||||
.max(quote_native_after)
|
||||
let (position, _) = account.token_position_mut(bank.token_index)?;
|
||||
let native_before = position.native(&bank);
|
||||
bank.change_without_fee(position, needed_change)?;
|
||||
let native_after = position.native(&bank);
|
||||
let native_change = cm!(native_after - native_before);
|
||||
let new_borrows = native_change
|
||||
.max(native_after)
|
||||
.min(I80F48::ZERO)
|
||||
.abs()
|
||||
.to_num::<u64>();
|
||||
|
||||
let market = account.serum3_orders_mut(serum_market_index).unwrap();
|
||||
let borrows_without_fee = if bank.token_index == market.base_token_index {
|
||||
&mut market.base_borrows_without_fee
|
||||
} else if bank.token_index == market.quote_token_index {
|
||||
&mut market.quote_borrows_without_fee
|
||||
} else {
|
||||
return Err(error_msg!(
|
||||
"assert failed: apply_vault_difference called with bad token index"
|
||||
));
|
||||
};
|
||||
|
||||
// Only for place: Add to potential borrow amounts
|
||||
market.base_borrows_without_fee = cm!(market.base_borrows_without_fee + base_borrows);
|
||||
market.quote_borrows_without_fee = cm!(market.quote_borrows_without_fee + quote_borrows);
|
||||
// Only for place: Add to potential borrow amount
|
||||
let old_value = *borrows_without_fee;
|
||||
*borrows_without_fee = cm!(old_value + new_borrows);
|
||||
|
||||
// Only for settle/liq_force_cancel: Reduce the potential borrow amounts
|
||||
if base_needed_change > 0 {
|
||||
market.base_borrows_without_fee = market
|
||||
.base_borrows_without_fee
|
||||
.saturating_sub(base_needed_change.to_num::<u64>());
|
||||
}
|
||||
if quote_needed_change > 0 {
|
||||
market.quote_borrows_without_fee = market
|
||||
.quote_borrows_without_fee
|
||||
.saturating_sub(quote_needed_change.to_num::<u64>());
|
||||
if needed_change > 0 {
|
||||
*borrows_without_fee = (*borrows_without_fee).saturating_sub(needed_change.to_num::<u64>());
|
||||
}
|
||||
|
||||
Ok(VaultDifference {
|
||||
base_index: base_bank.token_index,
|
||||
quote_index: quote_bank.token_index,
|
||||
base_native_change,
|
||||
quote_native_change,
|
||||
token_index: bank.token_index,
|
||||
native_change,
|
||||
})
|
||||
}
|
||||
|
||||
fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Result<()> {
|
||||
use crate::serum3_cpi;
|
||||
|
||||
let order_payer_token_account = match order.side {
|
||||
Side::Bid => &ctx.quote_vault,
|
||||
Side::Ask => &ctx.base_vault,
|
||||
};
|
||||
|
||||
let group = ctx.group.load()?;
|
||||
serum3_cpi::PlaceOrder {
|
||||
program: ctx.serum_program.to_account_info(),
|
||||
|
@ -495,7 +439,7 @@ fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Resu
|
|||
token_program: ctx.token_program.to_account_info(),
|
||||
|
||||
open_orders: ctx.open_orders.to_account_info(),
|
||||
order_payer_token_account: order_payer_token_account.to_account_info(),
|
||||
order_payer_token_account: ctx.payer_vault.to_account_info(),
|
||||
user_authority: ctx.group.to_account_info(),
|
||||
}
|
||||
.call(&group, order)
|
||||
|
|
|
@ -164,6 +164,10 @@ pub fn serum3_settle_funds(ctx: Context<Serum3SettleFunds>) -> Result<()> {
|
|||
&mut base_bank,
|
||||
after_base_vault,
|
||||
before_base_vault,
|
||||
)?;
|
||||
apply_vault_difference(
|
||||
&mut account.borrow_mut(),
|
||||
serum_market.market_index,
|
||||
&mut quote_bank,
|
||||
after_quote_vault,
|
||||
before_quote_vault,
|
||||
|
|
|
@ -1600,14 +1600,17 @@ impl<'keypair> ClientInstruction for Serum3PlaceOrderInstruction<'keypair> {
|
|||
)
|
||||
.await;
|
||||
|
||||
let (payer_bank, payer_vault) = match self.side {
|
||||
Serum3Side::Bid => (quote_info.first_bank(), quote_info.first_vault()),
|
||||
Serum3Side::Ask => (base_info.first_bank(), base_info.first_vault()),
|
||||
};
|
||||
|
||||
let accounts = Self::Accounts {
|
||||
group: account.fixed.group,
|
||||
account: self.account,
|
||||
open_orders,
|
||||
quote_bank: quote_info.first_bank(),
|
||||
quote_vault: quote_info.first_vault(),
|
||||
base_bank: base_info.first_bank(),
|
||||
base_vault: base_info.first_vault(),
|
||||
payer_bank,
|
||||
payer_vault,
|
||||
serum_market: self.serum_market,
|
||||
serum_program: serum_market.serum_program,
|
||||
serum_market_external: serum_market.serum_market_external,
|
||||
|
|
|
@ -1079,6 +1079,13 @@ export class MangoClient {
|
|||
.baseSizeNumberToLots(size)
|
||||
.mul(serum3MarketExternal.priceNumberToLots(price)),
|
||||
);
|
||||
const payerTokenIndex = (() => {
|
||||
if (side == Serum3Side.bid) {
|
||||
return serum3Market.quoteTokenIndex;
|
||||
} else {
|
||||
return serum3Market.baseTokenIndex;
|
||||
}
|
||||
})();
|
||||
|
||||
return await this.program.methods
|
||||
.serum3PlaceOrder(
|
||||
|
@ -1107,14 +1114,8 @@ export class MangoClient {
|
|||
marketBaseVault: serum3MarketExternal.decoded.baseVault,
|
||||
marketQuoteVault: serum3MarketExternal.decoded.quoteVault,
|
||||
marketVaultSigner: serum3MarketExternalVaultSigner,
|
||||
quoteBank: group.getFirstBankByTokenIndex(serum3Market.quoteTokenIndex)
|
||||
.publicKey,
|
||||
quoteVault: group.getFirstBankByTokenIndex(serum3Market.quoteTokenIndex)
|
||||
.vault,
|
||||
baseBank: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
||||
.publicKey,
|
||||
baseVault: group.getFirstBankByTokenIndex(serum3Market.baseTokenIndex)
|
||||
.vault,
|
||||
payerBank: group.getFirstBankByTokenIndex(payerTokenIndex).publicKey,
|
||||
payerVault: group.getFirstBankByTokenIndex(payerTokenIndex).vault,
|
||||
})
|
||||
.remainingAccounts(
|
||||
healthRemainingAccounts.map(
|
||||
|
|
|
@ -1605,24 +1605,20 @@ export type MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "quoteBank",
|
||||
"name": "payerBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
"isSigner": false,
|
||||
"docs": [
|
||||
"The bank that pays for the order, if necessary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "quoteVault",
|
||||
"name": "payerVault",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "baseBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "baseVault",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
"isSigner": false,
|
||||
"docs": [
|
||||
"The bank vault that pays for the order, if necessary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tokenProgram",
|
||||
|
@ -4015,11 +4011,17 @@ export type MangoV4 = {
|
|||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "previousNativeCoinReserved",
|
||||
"name": "baseBorrowsWithoutFee",
|
||||
"docs": [
|
||||
"Tracks the amount of borrows that have flowed into the serum open orders account.",
|
||||
"These borrows did not have the loan origination fee applied, and that may happen",
|
||||
"later (in serum3_settle_funds) if we can guarantee that the funds were used.",
|
||||
"In particular a place-on-book, cancel, settle should not cost fees."
|
||||
],
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "previousNativePcReserved",
|
||||
"name": "quoteBorrowsWithoutFee",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
|
@ -6887,24 +6889,20 @@ export const IDL: MangoV4 = {
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "quoteBank",
|
||||
"name": "payerBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
"isSigner": false,
|
||||
"docs": [
|
||||
"The bank that pays for the order, if necessary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "quoteVault",
|
||||
"name": "payerVault",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "baseBank",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
},
|
||||
{
|
||||
"name": "baseVault",
|
||||
"isMut": true,
|
||||
"isSigner": false
|
||||
"isSigner": false,
|
||||
"docs": [
|
||||
"The bank vault that pays for the order, if necessary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tokenProgram",
|
||||
|
@ -9297,11 +9295,17 @@ export const IDL: MangoV4 = {
|
|||
"type": "publicKey"
|
||||
},
|
||||
{
|
||||
"name": "previousNativeCoinReserved",
|
||||
"name": "baseBorrowsWithoutFee",
|
||||
"docs": [
|
||||
"Tracks the amount of borrows that have flowed into the serum open orders account.",
|
||||
"These borrows did not have the loan origination fee applied, and that may happen",
|
||||
"later (in serum3_settle_funds) if we can guarantee that the funds were used.",
|
||||
"In particular a place-on-book, cancel, settle should not cost fees."
|
||||
],
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
"name": "previousNativePcReserved",
|
||||
"name": "quoteBorrowsWithoutFee",
|
||||
"type": "u64"
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue