Skip to content

Commit

Permalink
Pay trading fees with input token (#319)
Browse files Browse the repository at this point in the history
* emit volumes event on epoch change

* emit volumes event on epoch change

* pay trading fee with input token

* gitignore

* remove pnpm lock

* undo gitignore
  • Loading branch information
0xaslan authored Jan 30, 2025
1 parent 01c6ff3 commit 870f4aa
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 102 deletions.
14 changes: 10 additions & 4 deletions packages/deepbook/sources/book/book.move
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,21 @@ public(package) fun get_quantity_out(
else book_side.prev_slice(ref, offset);
};

let quantity_in_deep = if (is_bid) {
deep_price.deep_quantity(
let fee_quantity = if (is_bid) {
deep_price.fee_quantity(
quantity_out,
quote_quantity - quantity_in_left,
is_bid,
)
} else {
deep_price.deep_quantity(base_quantity - quantity_in_left, quantity_out)
deep_price.fee_quantity(
base_quantity - quantity_in_left,
quantity_out,
is_bid,
)
};
let deep_fee = math::mul(taker_fee, quantity_in_deep);

let deep_fee = math::mul(taker_fee, fee_quantity.deep());

if (is_bid) {
(quantity_out, quantity_in_left, deep_fee)
Expand Down
20 changes: 16 additions & 4 deletions packages/deepbook/sources/book/fill.move
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,22 @@ public(package) fun get_settled_maker_quantities(self: &Fill): Balances {
balances::new(base, quote, 0)
}

public(package) fun set_fill_maker_fee(self: &mut Fill, fee: u64) {
self.maker_fee = fee;
public(package) fun set_fill_maker_fee(self: &mut Fill, fee: &Balances) {
if (fee.deep() > 0) {
self.maker_fee_is_deep = true;
} else {
self.maker_fee_is_deep = false;
};

self.maker_fee = fee.non_zero_value();
}

public(package) fun set_fill_taker_fee(self: &mut Fill, fee: u64) {
self.taker_fee = fee;
public(package) fun set_fill_taker_fee(self: &mut Fill, fee: &Balances) {
if (fee.deep() > 0) {
self.taker_fee_is_deep = true;
} else {
self.taker_fee_is_deep = false;
};

self.taker_fee = fee.non_zero_value();
}
56 changes: 30 additions & 26 deletions packages/deepbook/sources/book/order.move
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,14 @@ public(package) fun calculate_cancel_refund(
let cancel_quantity = cancel_quantity.get_with_default(
self.quantity - self.filled_quantity,
);
let deep_out = math::mul(
maker_fee,
self
.order_deep_price()
.deep_quantity(
cancel_quantity,
math::mul(cancel_quantity, self.price()),
),
);
let mut fee_quantity = self
.order_deep_price
.fee_quantity(
cancel_quantity,
math::mul(cancel_quantity, self.price()),
self.is_bid(),
);
fee_quantity.mul(maker_fee);

let mut base_out = 0;
let mut quote_out = 0;
Expand All @@ -231,35 +230,40 @@ public(package) fun calculate_cancel_refund(
base_out = cancel_quantity;
};

balances::new(base_out, quote_out, deep_out)
let mut refund = balances::new(base_out, quote_out, 0);
refund.add_balances(fee_quantity);

refund
}

public(package) fun locked_balance(
self: &Order,
maker_fee: u64,
): (u64, u64, u64) {
public(package) fun locked_balance(self: &Order, maker_fee: u64): Balances {
let (is_bid, order_price, _) = utils::decode_order_id(self.order_id());
let mut base_quantity = 0;
let mut quote_quantity = 0;
let remaining_base_quantity = self.quantity() - self.filled_quantity();
let remaining_quote_quantity = math::mul(remaining_base_quantity, order_price);
let remaining_quote_quantity = math::mul(
remaining_base_quantity,
order_price,
);

if (is_bid) {
quote_quantity = quote_quantity + remaining_quote_quantity;
} else {
base_quantity = base_quantity + remaining_base_quantity;
};
let deep_quantity = math::mul(
maker_fee,
self
.order_deep_price()
.deep_quantity(
remaining_base_quantity,
remaining_quote_quantity,
),
);

(base_quantity, quote_quantity, deep_quantity)
let mut fee_quantity = self
.order_deep_price()
.fee_quantity(
remaining_base_quantity,
remaining_quote_quantity,
is_bid,
);
fee_quantity.mul(maker_fee);

let mut locked_balance = balances::new(base_quantity, quote_quantity, 0);
locked_balance.add_balances(fee_quantity);

locked_balance
}

public(package) fun emit_order_canceled(
Expand Down
82 changes: 44 additions & 38 deletions packages/deepbook/sources/book/order_info.move
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,16 @@ public(package) fun fills_ref(self: &mut OrderInfo): &mut vector<Fill> {
&mut self.fills
}

public(package) fun paid_fees_balances(self: &OrderInfo): Balances {
if (self.fee_is_deep) {
balances::new(0, 0, self.paid_fees)
} else if (self.is_bid) {
balances::new(0, self.paid_fees, 0)
} else {
balances::new(self.paid_fees, 0, 0)
}
}

/// Given a partially filled `OrderInfo`, the taker fee and maker fee, for the user
/// placing the order, calculate all of the balances that need to be settled and
/// the balances that are owed. The executed quantity is multiplied by the taker_fee
Expand All @@ -292,44 +302,40 @@ public(package) fun calculate_partial_fill_balances(
taker_fee: u64,
maker_fee: u64,
): (Balances, Balances) {
let taker_deep_in = math::mul(
taker_fee,
self
.order_deep_price
.deep_quantity(
self.executed_quantity,
self.cumulative_quote_quantity,
),
);
self.paid_fees = taker_deep_in;
let fills = &mut self.fills;
let mut taker_fee_quantity = self
.order_deep_price
.fee_quantity(
self.executed_quantity,
self.cumulative_quote_quantity,
self.is_bid,
);
taker_fee_quantity.mul(taker_fee);
self.paid_fees = taker_fee_quantity.non_zero_value();

let fills = &mut self.fills;
let mut i = 0;
while (i < fills.length()) {
let fill = &mut fills[i];
if (!fill.expired()) {
let base_quantity = fill.base_quantity();
let quote_quantity = fill.quote_quantity();
let fill_taker_fee = math::mul(
taker_fee,
self
.order_deep_price
.deep_quantity(
base_quantity,
quote_quantity,
),
);
if (fill_taker_fee > 0) {
fill.set_fill_taker_fee(fill_taker_fee);
};
let mut fill_taker_fee_quantity = self
.order_deep_price
.fee_quantity(
base_quantity,
quote_quantity,
self.is_bid,
);
fill_taker_fee_quantity.mul(taker_fee);
fill.set_fill_taker_fee(&fill_taker_fee_quantity);
};

i = i + 1;
};

let mut settled_balances = balances::new(0, 0, 0);
let mut owed_balances = balances::new(0, 0, 0);
owed_balances.add_deep(taker_deep_in);
owed_balances.add_balances(taker_fee_quantity);

if (self.is_bid) {
settled_balances.add_base(self.executed_quantity);
Expand All @@ -341,17 +347,16 @@ public(package) fun calculate_partial_fill_balances(

let remaining_quantity = self.remaining_quantity();
if (self.order_inserted()) {
let maker_deep_in = math::mul(
maker_fee,
self
.order_deep_price
.deep_quantity(
remaining_quantity,
math::mul(remaining_quantity, self.price()),
),
);
self.maker_fees = maker_deep_in;
owed_balances.add_deep(maker_deep_in);
let mut maker_fee_quantity = self
.order_deep_price
.fee_quantity(
remaining_quantity,
math::mul(remaining_quantity, self.price()),
self.is_bid,
);
maker_fee_quantity.mul(maker_fee);
self.maker_fees = maker_fee_quantity.non_zero_value();
owed_balances.add_balances(maker_fee_quantity);
if (self.is_bid) {
owed_balances.add_quote(
math::mul(remaining_quantity, self.price()),
Expand Down Expand Up @@ -512,7 +517,8 @@ public(package) fun emit_orders_filled(self: &OrderInfo, timestamp: u64) {
if (!fill.expired()) {
event::emit(self.order_filled_from_fill(fill, timestamp));
} else {
let cancel_maker = self.balance_manager_id() == fill.balance_manager_id();
let cancel_maker =
self.balance_manager_id() == fill.balance_manager_id();
if (cancel_maker) {
self.emit_order_canceled_maker_from_fill(fill, timestamp);
} else {
Expand Down Expand Up @@ -591,7 +597,7 @@ fun order_expired_from_fill(
is_bid: !self.is_bid(),
original_quantity: fill.original_maker_quantity(),
base_asset_quantity_canceled: fill.base_quantity(),
timestamp
timestamp,
}
}

Expand All @@ -610,6 +616,6 @@ fun emit_order_canceled_maker_from_fill(
!self.is_bid(),
fill.original_maker_quantity(),
fill.base_quantity(),
timestamp
timestamp,
)
}
5 changes: 5 additions & 0 deletions packages/deepbook/sources/helper/constants.move
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const MAX_PRICE: u64 = ((1u128 << 63) - 1) as u64;
const DEFAULT_STAKE_REQUIRED: u64 = 100_000_000; // 100 DEEP
const HALF: u64 = 500_000_000;
const DEEP_UNIT: u64 = 1_000_000;
const FEE_PENALTY_MULTIPLIER: u64 = 250_000_000; // 25%

// Restrictions on limit orders.
// No restriction on the order.
Expand Down Expand Up @@ -214,6 +215,10 @@ public fun max_fan_out(): u64 {
MAX_FAN_OUT
}

public fun fee_penalty_multiplier(): u64 {
FEE_PENALTY_MULTIPLIER
}

#[test_only]
public fun maker_fee(): u64 {
MAKER_FEE
Expand Down
31 changes: 19 additions & 12 deletions packages/deepbook/sources/pool.move
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const EInvalidLotSize: u64 = 4;
const EInvalidMinSize: u64 = 5;
const EInvalidQuantityIn: u64 = 6;
const EIneligibleReferencePool: u64 = 7;
const EFeeTypeNotSupported: u64 = 8;
const EInvalidOrderBalanceManager: u64 = 9;
const EIneligibleTargetPool: u64 = 10;
const EPackageVersionDisabled: u64 = 11;
Expand Down Expand Up @@ -972,10 +971,13 @@ public fun get_order_deep_required<BaseAsset, QuoteAsset>(
let self = self.load_inner();
let maker_fee = self.state.governance().trade_params().maker_fee();
let taker_fee = self.state.governance().trade_params().taker_fee();
let deep_quantity = order_deep_price.deep_quantity(
base_quantity,
math::mul(base_quantity, price),
);
let deep_quantity = order_deep_price
.fee_quantity(
base_quantity,
math::mul(base_quantity, price),
true,
)
.deep();

(math::mul(taker_fee, deep_quantity), math::mul(maker_fee, deep_quantity))
}
Expand All @@ -998,10 +1000,10 @@ public fun locked_balance<BaseAsset, QuoteAsset>(

account_orders.do_ref!(|order| {
let maker_fee = self.state.history().historic_maker_fee(order.epoch());
let (base, quote, deep) = order.locked_balance(maker_fee);
base_quantity = base_quantity + base;
quote_quantity = quote_quantity + quote;
deep_quantity = deep_quantity + deep;
let locked_balance = order.locked_balance(maker_fee);
base_quantity = base_quantity + locked_balance.base();
quote_quantity = quote_quantity + locked_balance.quote();
deep_quantity = deep_quantity + locked_balance.deep();
});

let settled_balances = self
Expand Down Expand Up @@ -1195,9 +1197,14 @@ fun place_order_int<BaseAsset, QuoteAsset>(
ctx: &TxContext,
): OrderInfo {
let whitelist = self.whitelisted();
assert!(pay_with_deep || whitelist, EFeeTypeNotSupported);

let self = self.load_inner_mut();

let order_deep_price = if (pay_with_deep) {
self.deep_price.get_order_deep_price(whitelist)
} else {
self.deep_price.empty_deep_price()
};

let mut order_info = order_info::new(
self.pool_id,
balance_manager.id(),
Expand All @@ -1211,7 +1218,7 @@ fun place_order_int<BaseAsset, QuoteAsset>(
pay_with_deep,
ctx.epoch(),
expire_timestamp,
self.deep_price.get_order_deep_price(whitelist),
order_deep_price,
market_order,
clock.timestamp_ms(),
);
Expand Down
18 changes: 18 additions & 0 deletions packages/deepbook/sources/state/balances.move
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/// Whenever funds are moved, they are moved in the form of `Balances`.
module deepbook::balances;

use deepbook::math;

// === Structs ===
public struct Balances has store, copy, drop {
base: u64,
Expand Down Expand Up @@ -59,3 +61,19 @@ public(package) fun quote(balances: &Balances): u64 {
public(package) fun deep(balances: &Balances): u64 {
balances.deep
}

public(package) fun mul(balances: &mut Balances, factor: u64) {
balances.base = math::mul(balances.base, factor);
balances.quote = math::mul(balances.quote, factor);
balances.deep = math::mul(balances.deep, factor);
}

public(package) fun non_zero_value(balances: &Balances): u64 {
if (balances.base > 0) {
balances.base
} else if (balances.quote > 0) {
balances.quote
} else {
balances.deep
}
}
Loading

0 comments on commit 870f4aa

Please sign in to comment.