Skip to content

Commit

Permalink
Merge pull request #863 from galacticcouncil/otc_fee
Browse files Browse the repository at this point in the history
feat: OTC fees
  • Loading branch information
Roznovjak authored Jul 16, 2024
2 parents 29f7568 + c8efdcc commit d86308e
Show file tree
Hide file tree
Showing 21 changed files with 276 additions and 149 deletions.
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pallets/otc-settlements/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = 'pallet-otc-settlements'
version = '1.0.0'
version = '1.0.1'
description = 'A pallet with offchain worker closing OTC arbs'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
2 changes: 1 addition & 1 deletion pallets/otc-settlements/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ benchmarks! {
<T as crate::Config>::Currency::mint_into(DAI.into(), &account, 1_000_000_000 * ONE)?;

assert_ok!(
pallet_otc::Pallet::<T>::place_order(RawOrigin::Signed(account).into(), HDX.into(), DAI.into(), 100_000_000 * ONE, 200_000_001 * ONE, true)
pallet_otc::Pallet::<T>::place_order(RawOrigin::Signed(account).into(), HDX.into(), DAI.into(), 100_000_000 * ONE, 202_020_001 * ONE, true)
);

let route = <T as crate::Config>::Router::get_route(AssetPair {
Expand Down
19 changes: 16 additions & 3 deletions pallets/otc-settlements/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ impl<T: Config> Pallet<T> {
/// - `amount`: Amount necessary to close the arb.
/// - `route`: The route we trade against. Required for the fee calculation.
pub fn settle_otc(otc_id: OrderId, amount: Balance, route: Vec<Trade<AssetIdOf<T>>>) -> DispatchResult {
log::debug!(
target: "offchain_worker::settle_otc",
"calling settle_otc(): otc_id: {:?} amount: {:?} route: {:?}", otc_id, amount, route);

let pallet_acc = Self::account_id();

let otc = <pallet_otc::Orders<T>>::get(otc_id).ok_or(Error::<T>::OrderNotFound)?;
Expand All @@ -315,8 +319,7 @@ impl<T: Config> Pallet<T> {
<T as Config>::Currency::mint_into(asset_a, &pallet_acc, amount)?;

// get initial otc price
let otc_price =
FixedU128::checked_from_rational(otc.amount_out, otc.amount_in).ok_or(ArithmeticError::Overflow)?;
let otc_price = Self::otc_price(&otc)?;

// Router trade is disabled in the benchmarks, so disable this one as well.
// Without disabling it, the requirements for the extrinsic cannot be met (e.g. profit).
Expand Down Expand Up @@ -456,7 +459,7 @@ impl<T: Config> Pallet<T> {

let mut list = vec![];
for (otc_id, otc) in <pallet_otc::Orders<T>>::iter() {
let otc_price = FixedU128::checked_from_rational(otc.amount_out, otc.amount_in);
let otc_price = Self::otc_price(&otc).ok();

let route = T::Router::get_route(AssetPair {
// To get the correct price, we need to switch the assets, otherwise
Expand Down Expand Up @@ -594,4 +597,14 @@ impl<T: Config> Pallet<T> {
}
None
}

/// Calculates the price (asset_out/asset_in) after subtracting the OTC fee from the amount_out.
fn otc_price(otc: &Order<T::AccountId, T::AssetId>) -> Result<FixedU128, DispatchError> {
let fee = pallet_otc::Pallet::<T>::calculate_fee(otc.amount_out);
Ok(FixedU128::checked_from_rational(
otc.amount_out.checked_sub(fee).ok_or(ArithmeticError::Overflow)?,
otc.amount_in,
)
.ok_or(ArithmeticError::Overflow)?)
}
}
3 changes: 3 additions & 0 deletions pallets/otc-settlements/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ parameter_types! {
pub MinProfitLimit: Balance = 10_000_000_000_000;
pub PricePrecision: FixedU128 = FixedU128::from_rational(1, 1_000_000);
pub MinProfitPercentage: Perbill = Perbill::from_rational(1u32, 100_000_u32); // 0.001%
pub OtcFee: Permill = Permill::from_percent(1u32);
}

parameter_type_with_key! {
Expand Down Expand Up @@ -107,6 +108,8 @@ impl pallet_otc::Config for Test {
type RuntimeEvent = RuntimeEvent;
type ExistentialDeposits = ExistentialDeposits;
type ExistentialDepositMultiplier = ExistentialDepositMultiplier;
type Fee = OtcFee;
type FeeReceiver = TreasuryAccount;
type WeightInfo = ();
}

Expand Down
40 changes: 19 additions & 21 deletions pallets/otc-settlements/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ fn profit_should_be_transferred_to_treasury_when_zero_initial_pallet_balance() {
HDX,
DAI,
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));

Expand Down Expand Up @@ -226,7 +226,7 @@ fn profit_should_be_transferred_to_treasury_when_nonzero_initial_pallet_balance(
HDX,
DAI,
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));

Expand Down Expand Up @@ -258,7 +258,7 @@ fn existing_arb_opportunity_should_trigger_trade() {
HDX, // otc asset_in
DAI, // otc asset_out
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));

Expand Down Expand Up @@ -294,7 +294,7 @@ fn existing_arb_opportunity_should_trigger_trade() {

expect_last_events(vec![Event::Executed {
asset_id: HDX,
profit: 2_067_802_207_347,
profit: 17_736_110_470_326,
}
.into()]);
});
Expand All @@ -308,10 +308,8 @@ fn existing_arb_opportunity_should_trigger_trade_when_otc_is_not_partially_filla
RuntimeOrigin::signed(ALICE),
HDX, // otc asset_in
DAI, // otc asset_out
// we got following 2 values from the logs of
// existing_arb_opportunity_should_trigger_trade test
828_170_776_368_178,
1_664_623_260_500_037,
100_000 * ONE,
300_000 * ONE,
false, // not partially fillable
));

Expand Down Expand Up @@ -347,7 +345,7 @@ fn existing_arb_opportunity_should_trigger_trade_when_otc_is_not_partially_filla

expect_last_events(vec![Event::Executed {
asset_id: HDX,
profit: 2_067_802_207_347,
profit: 2_732_618_471_117_260,
}
.into()]);
});
Expand All @@ -365,7 +363,7 @@ fn existing_arb_opportunity_of_insufficient_asset_should_trigger_trade() {
BTC, // otc asset_in
HDX, // otc asset_out
200_000 * ONE,
101_000 * ONE,
105_000 * ONE,
true,
));

Expand Down Expand Up @@ -401,7 +399,7 @@ fn existing_arb_opportunity_of_insufficient_asset_should_trigger_trade() {

expect_last_events(vec![Event::Executed {
asset_id: BTC,
profit: 16_419_654_005_878,
profit: 245_338_363_920_576,
}
.into()]);
});
Expand All @@ -416,15 +414,15 @@ fn multiple_arb_opportunities_should_trigger_trades() {
HDX, // otc asset_in
DAI, // otc asset_out
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));
assert_ok!(OTC::place_order(
RuntimeOrigin::signed(ALICE),
DOT, // otc asset_in
KSM, // otc asset_out
100_000 * ONE,
101_000 * ONE,
102_000 * ONE,
true,
));

Expand All @@ -433,12 +431,12 @@ fn multiple_arb_opportunities_should_trigger_trades() {
expect_events(vec![
Event::Executed {
asset_id: HDX,
profit: 2_067_802_207_347,
profit: 17736110470326,
}
.into(),
Event::Executed {
asset_id: DOT,
profit: 12_345_450_557_916,
profit: 11860096179879,
}
.into(),
]);
Expand Down Expand Up @@ -476,7 +474,7 @@ fn trade_should_be_triggered_when_arb_opportunity_appears() {
<OtcSettlements as Hooks<BlockNumberFor<Test>>>::offchain_worker(System::block_number());

// make a trade to move the price and create an arb opportunity
assert_ok!(Omnipool::sell(RuntimeOrigin::signed(ALICE), HDX, DAI, 1_000 * ONE, ONE,));
assert_ok!(Omnipool::sell(RuntimeOrigin::signed(ALICE), HDX, DAI, 3_000 * ONE, ONE,));

System::set_block_number(System::block_number() + 1);

Expand Down Expand Up @@ -510,7 +508,7 @@ fn trade_should_be_triggered_when_arb_opportunity_appears() {

expect_last_events(vec![Event::Executed {
asset_id: HDX,
profit: 2_981_065_139_674,
profit: 5_173_145_606_735,
}
.into()]);
});
Expand Down Expand Up @@ -619,7 +617,7 @@ fn test_offchain_worker_unsigned_transaction_submission() {
HDX, // otc asset_in
DAI, // otc asset_out
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));

Expand All @@ -641,7 +639,7 @@ fn test_offchain_worker_unsigned_transaction_submission() {
tx.call,
crate::mock::RuntimeCall::OtcSettlements(crate::Call::settle_otc_order {
otc_id: 0,
amount: 828_170_776_368_178,
amount: 2_413_749_694_825_193,
route,
})
);
Expand All @@ -657,7 +655,7 @@ fn test_offchain_worker_signed_transaction_submission() {
HDX, // otc asset_in
DAI, // otc asset_out
100_000 * ONE,
201_000 * ONE,
205_000 * ONE,
true,
));

Expand All @@ -671,7 +669,7 @@ fn test_offchain_worker_signed_transaction_submission() {
assert_ok!(OtcSettlements::settle_otc_order(
RuntimeOrigin::signed(ALICE),
otc_id,
828_170_776_368_178,
2_413_749_694_825_193,
route,
));
})
Expand Down
9 changes: 6 additions & 3 deletions pallets/otc-settlements/src/weights.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// This file is part of HydraDX.

// Copyright (C) 2020-2023 Intergalactic, Limited (GIB).
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -15,7 +18,7 @@
//! Autogenerated weights for `pallet_otc_settlements`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2024-06-20, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2024-07-12, STEPS: `10`, REPEAT: `30`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `bench-bot`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
Expand Down Expand Up @@ -61,8 +64,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `747`
// Estimated: `6196`
// Minimum execution time: 100_657_000 picoseconds.
Weight::from_parts(101_597_000, 6196)
// Minimum execution time: 97_577_000 picoseconds.
Weight::from_parts(98_741_000, 6196)
.saturating_add(RocksDbWeight::get().reads(4_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
Expand Down
2 changes: 1 addition & 1 deletion pallets/otc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = 'pallet-otc'
version = '1.1.5'
version = '2.0.0'
description = 'A pallet for trustless over-the-counter trading'
authors = ['GalacticCouncil']
edition = '2021'
Expand Down
3 changes: 2 additions & 1 deletion pallets/otc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
## General description
This pallet provides basic over-the-counter (OTC) trading functionality.
It allows anyone to `place_order` by specifying a pair of assets (in and out), their respective amounts, and
whether the order is partially fillable. The order price is static and calculated as `amount_out / amount_in`.
whether the order is partially fillable. Fee is applied to all trades and is deducted from the `amount_out`.
Because of the fee, the order price is static and calculated as `(amount_out - fee) / amount_in`.

## Notes
The pallet implements a minimum order size as an alternative to storage fees. The amounts of an open order cannot
Expand Down
Loading

0 comments on commit d86308e

Please sign in to comment.