Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c79a653
initial idea
tonylee08 Nov 20, 2025
f2f2fab
market order
tonylee08 Nov 20, 2025
49afe0b
order
tonylee08 Nov 20, 2025
f5caf67
clenaup
tonylee08 Nov 20, 2025
1c21e13
concept
tonylee08 Nov 21, 2025
449bd58
modify structs
tonylee08 Nov 24, 2025
817d664
wip
tonylee08 Nov 25, 2025
6015938
wip
tonylee08 Nov 26, 2025
483b046
wip, restructure tpsl
tonylee08 Nov 26, 2025
d27e59e
can place
tonylee08 Nov 26, 2025
9ac5544
basic without price
tonylee08 Nov 26, 2025
86325a3
tpsl
tonylee08 Nov 26, 2025
ea27b10
formatting
tonylee08 Nov 26, 2025
20c2c73
refactor
tonylee08 Nov 26, 2025
b0950c8
tpsl
tonylee08 Nov 26, 2025
35e802b
wip tests
tonylee08 Nov 26, 2025
5f30e03
wip tests
tonylee08 Nov 26, 2025
b6e1c64
tpsl
tonylee08 Dec 2, 2025
1232c1c
basic tests
tonylee08 Dec 2, 2025
dc748ac
limit order tests
tonylee08 Dec 2, 2025
b9501f3
formatting
tonylee08 Dec 2, 2025
ee05124
cleanup
tonylee08 Dec 2, 2025
3364f76
bug fix
tonylee08 Dec 2, 2025
13038ac
more tests
tonylee08 Dec 2, 2025
affcae0
remove deprecated event
tonylee08 Dec 2, 2025
cfd1343
invalid price denied
tonylee08 Dec 2, 2025
e7cebf9
cleanup
tonylee08 Dec 2, 2025
f0bdebd
clenaup
tonylee08 Dec 2, 2025
d44c504
update to vecmap
tonylee08 Dec 2, 2025
f15fbc8
cleanup
tonylee08 Dec 2, 2025
83facd5
cancel all conditional order identifiers
tonylee08 Dec 2, 2025
8ea2b10
comments
tonylee08 Dec 2, 2025
705fdd9
validate owner
tonylee08 Dec 2, 2025
4d172b8
expired order
tonylee08 Dec 2, 2025
99f25d0
update tests
tonylee08 Dec 2, 2025
1a04f9f
assert prices
tonylee08 Dec 2, 2025
86516c3
expired order
tonylee08 Dec 2, 2025
808f527
cancel conditional order
tonylee08 Dec 2, 2025
cceca5e
events
tonylee08 Dec 2, 2025
4f0b94d
insufficient funds
tonylee08 Dec 2, 2025
2446e26
pool
tonylee08 Dec 2, 2025
e9ec707
Merge branch 'main' into tlee/tpsl
tonylee08 Dec 2, 2025
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
130 changes: 130 additions & 0 deletions packages/deepbook/sources/pool.move
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,137 @@ public fun locked_balance<BaseAsset, QuoteAsset>(
(base_quantity, quote_quantity, deep_quantity)
}

/// Check if a limit order can be placed based on balance manager balances.
/// Returns true if the balance manager has sufficient balance (accounting for fees) to place the order, false otherwise.
/// Assumes the limit order is a taker order as a worst case scenario.
public fun can_place_limit_order<BaseAsset, QuoteAsset>(
self: &Pool<BaseAsset, QuoteAsset>,
balance_manager: &BalanceManager,
price: u64,
quantity: u64,
is_bid: bool,
pay_with_deep: bool,
expire_timestamp: u64,
clock: &Clock,
): bool {
let whitelist = self.whitelisted();
let pool_inner = self.load_inner();
if (expire_timestamp < clock.timestamp_ms()) {
return false
};

// Get order deep price for fee calculation
let order_deep_price = if (pay_with_deep) {
pool_inner.deep_price.get_order_deep_price(whitelist)
} else {
pool_inner.deep_price.empty_deep_price()
};

let quote_quantity = math::mul(quantity, price);

// Calculate fee quantity using taker fee (worst case for limit orders)
let taker_fee = pool_inner.state.governance().trade_params().taker_fee();
let fee_balances = order_deep_price.fee_quantity(quantity, quote_quantity, is_bid);

// Calculate required balances
let mut required_base = 0;
let mut required_quote = 0;
let mut required_deep = 0;

if (is_bid) {
required_quote = quote_quantity;
if (pay_with_deep) {
required_deep = fee_balances.deep();
} else {
let fee_quote = math::mul(fee_balances.quote(), taker_fee);
required_quote = required_quote + fee_quote;
};
} else {
required_base = quantity;
if (pay_with_deep) {
required_deep = fee_balances.deep();
} else {
let fee_base = math::mul(fee_balances.base(), taker_fee);
required_base = required_base + fee_base;
};
};

// Get current balances from balance manager
let available_base = balance_manager.balance<BaseAsset>();
let available_quote = balance_manager.balance<QuoteAsset>();
let available_deep = balance_manager.balance<DEEP>();
Comment on lines +1452 to +1454
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add settled balances in the pool to these values?


// Check if available balances are sufficient
(available_base >= required_base) && (available_quote >= required_quote) && (available_deep >= required_deep)
}

/// Check if a market order can be placed based on balance manager balances.
/// Returns true if the balance manager has sufficient balance (accounting for fees) to place the order, false otherwise.
/// Does not account for discounted taker fees
public fun can_place_market_order<BaseAsset, QuoteAsset>(
self: &Pool<BaseAsset, QuoteAsset>,
balance_manager: &BalanceManager,
quantity: u64,
is_bid: bool,
pay_with_deep: bool,
clock: &Clock,
): bool {
let mut required_base = 0;
let mut required_deep = 0;

// Get current balances from balance manager
let available_base = balance_manager.balance<BaseAsset>();
let available_quote = balance_manager.balance<QuoteAsset>();
let available_deep = balance_manager.balance<DEEP>();

if (is_bid) {
// For bid orders: check if available quote can return desired base quantity
// get_quantity_out_input_fee already accounts for fees being deducted from quote
let (base_out, _, deep_required) = if (pay_with_deep) {
self.get_quantity_out(0, available_quote, clock)
} else {
self.get_quantity_out_input_fee(0, available_quote, clock)
};

// Not enough quote balance for the base quantity
if (base_out < quantity) {
return false
};

if (pay_with_deep) {
required_deep = deep_required;
};
} else {
// For ask orders: if paying fees in input token (base), need quantity + fees
// get_quantity_out_input_fee accounts for fees, so we need to check if we have enough base
// including fees that will be deducted
let (_, _, deep_required) = if (pay_with_deep) {
self.get_quantity_out(quantity, 0, clock)
} else {
self.get_quantity_out_input_fee(quantity, 0, clock)
};

// If paying fees in base asset, need quantity + fees
required_base = if (pay_with_deep) {
quantity
} else {
// Fees are deducted from base, so need more base to account for fees
let (taker_fee, _, _) = self.pool_trade_params();
let input_fee_rate = math::mul(taker_fee, constants::fee_penalty_multiplier());
math::mul(quantity, constants::float_scaling() + input_fee_rate)
};

if (pay_with_deep) {
required_deep = deep_required;
};
};

// Check if available balances are sufficient
(available_base >= required_base) && (available_deep >= required_deep)
}

/// Returns the trade params for the pool.
/// Returns (taker_fee, maker_fee, stake_required)
public fun pool_trade_params<BaseAsset, QuoteAsset>(
self: &Pool<BaseAsset, QuoteAsset>,
): (u64, u64, u64) {
Expand Down
Loading