ddx-python 1.0.4__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. ddx/.gitignore +1 -0
  2. ddx/__init__.py +58 -0
  3. ddx/_rust/__init__.pyi +2685 -0
  4. ddx/_rust/common/__init__.pyi +17 -0
  5. ddx/_rust/common/accounting.pyi +6 -0
  6. ddx/_rust/common/enums.pyi +3 -0
  7. ddx/_rust/common/requests/__init__.pyi +23 -0
  8. ddx/_rust/common/requests/intents.pyi +19 -0
  9. ddx/_rust/common/specs.pyi +17 -0
  10. ddx/_rust/common/state/__init__.pyi +41 -0
  11. ddx/_rust/common/state/keys.pyi +29 -0
  12. ddx/_rust/common/transactions.pyi +7 -0
  13. ddx/_rust/decimal.pyi +3 -0
  14. ddx/_rust/h256.pyi +3 -0
  15. ddx/_rust.abi3.so +0 -0
  16. ddx/app_config/ethereum/addresses.json +526 -0
  17. ddx/auditor/README.md +32 -0
  18. ddx/auditor/__init__.py +0 -0
  19. ddx/auditor/auditor_driver.py +1043 -0
  20. ddx/auditor/websocket_message.py +54 -0
  21. ddx/common/__init__.py +0 -0
  22. ddx/common/epoch_params.py +28 -0
  23. ddx/common/fill_context.py +141 -0
  24. ddx/common/logging.py +184 -0
  25. ddx/common/market_aware_account.py +259 -0
  26. ddx/common/market_specs.py +64 -0
  27. ddx/common/trade_mining_params.py +19 -0
  28. ddx/common/transaction_utils.py +85 -0
  29. ddx/common/transactions/__init__.py +0 -0
  30. ddx/common/transactions/advance_epoch.py +91 -0
  31. ddx/common/transactions/advance_settlement_epoch.py +63 -0
  32. ddx/common/transactions/all_price_checkpoints.py +84 -0
  33. ddx/common/transactions/cancel.py +76 -0
  34. ddx/common/transactions/cancel_all.py +88 -0
  35. ddx/common/transactions/complete_fill.py +103 -0
  36. ddx/common/transactions/disaster_recovery.py +96 -0
  37. ddx/common/transactions/event.py +48 -0
  38. ddx/common/transactions/fee_distribution.py +119 -0
  39. ddx/common/transactions/funding.py +292 -0
  40. ddx/common/transactions/futures_expiry.py +123 -0
  41. ddx/common/transactions/genesis.py +108 -0
  42. ddx/common/transactions/inner/__init__.py +0 -0
  43. ddx/common/transactions/inner/adl_outcome.py +25 -0
  44. ddx/common/transactions/inner/fill.py +232 -0
  45. ddx/common/transactions/inner/liquidated_position.py +41 -0
  46. ddx/common/transactions/inner/liquidation_entry.py +41 -0
  47. ddx/common/transactions/inner/liquidation_fill.py +118 -0
  48. ddx/common/transactions/inner/outcome.py +32 -0
  49. ddx/common/transactions/inner/trade_fill.py +292 -0
  50. ddx/common/transactions/insurance_fund_update.py +138 -0
  51. ddx/common/transactions/insurance_fund_withdraw.py +100 -0
  52. ddx/common/transactions/liquidation.py +353 -0
  53. ddx/common/transactions/partial_fill.py +125 -0
  54. ddx/common/transactions/pnl_realization.py +120 -0
  55. ddx/common/transactions/post.py +72 -0
  56. ddx/common/transactions/post_order.py +95 -0
  57. ddx/common/transactions/price_checkpoint.py +97 -0
  58. ddx/common/transactions/signer_registered.py +62 -0
  59. ddx/common/transactions/specs_update.py +61 -0
  60. ddx/common/transactions/strategy_update.py +158 -0
  61. ddx/common/transactions/tradable_product_update.py +98 -0
  62. ddx/common/transactions/trade_mining.py +147 -0
  63. ddx/common/transactions/trader_update.py +131 -0
  64. ddx/common/transactions/withdraw.py +90 -0
  65. ddx/common/transactions/withdraw_ddx.py +74 -0
  66. ddx/common/utils.py +176 -0
  67. ddx/config.py +17 -0
  68. ddx/derivadex_client.py +270 -0
  69. ddx/models/__init__.py +0 -0
  70. ddx/models/base.py +132 -0
  71. ddx/py.typed +0 -0
  72. ddx/realtime_client/__init__.py +2 -0
  73. ddx/realtime_client/config.py +2 -0
  74. ddx/realtime_client/models/__init__.py +611 -0
  75. ddx/realtime_client/realtime_client.py +646 -0
  76. ddx/rest_client/__init__.py +0 -0
  77. ddx/rest_client/clients/__init__.py +0 -0
  78. ddx/rest_client/clients/base_client.py +60 -0
  79. ddx/rest_client/clients/market_client.py +1243 -0
  80. ddx/rest_client/clients/on_chain_client.py +439 -0
  81. ddx/rest_client/clients/signed_client.py +292 -0
  82. ddx/rest_client/clients/system_client.py +843 -0
  83. ddx/rest_client/clients/trade_client.py +357 -0
  84. ddx/rest_client/constants/__init__.py +0 -0
  85. ddx/rest_client/constants/endpoints.py +66 -0
  86. ddx/rest_client/contracts/__init__.py +0 -0
  87. ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
  88. ddx/rest_client/contracts/ddx/__init__.py +1949 -0
  89. ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
  90. ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
  91. ddx/rest_client/contracts/i_stake/__init__.py +696 -0
  92. ddx/rest_client/exceptions/__init__.py +0 -0
  93. ddx/rest_client/exceptions/exceptions.py +32 -0
  94. ddx/rest_client/http/__init__.py +0 -0
  95. ddx/rest_client/http/http_client.py +336 -0
  96. ddx/rest_client/models/__init__.py +0 -0
  97. ddx/rest_client/models/market.py +693 -0
  98. ddx/rest_client/models/signed.py +61 -0
  99. ddx/rest_client/models/system.py +311 -0
  100. ddx/rest_client/models/trade.py +185 -0
  101. ddx/rest_client/utils/__init__.py +0 -0
  102. ddx/rest_client/utils/encryption_utils.py +26 -0
  103. ddx/utils/__init__.py +0 -0
  104. ddx_python-1.0.4.dist-info/METADATA +63 -0
  105. ddx_python-1.0.4.dist-info/RECORD +106 -0
  106. ddx_python-1.0.4.dist-info/WHEEL +5 -0
@@ -0,0 +1,108 @@
1
+ """
2
+ Genesis module
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+ from attrs import define, field
8
+ from ddx.common.logging import auditor_logger
9
+ from ddx.common.transactions.event import Event
10
+ from ddx._rust.common.specs import SpecsKind
11
+ from ddx._rust.common.state import Balance, DerivadexSMT
12
+ from ddx._rust.common.state.keys import InsuranceFundKey, SpecsKey
13
+ from ddx._rust.decimal import Decimal
14
+
15
+ logger = auditor_logger(__name__)
16
+
17
+
18
+ @define
19
+ class Genesis(Event):
20
+ """
21
+ Defines a Genesis
22
+
23
+ A Genesis is a non-transitioning transaction that indicates
24
+ the start of the first epoch.
25
+
26
+ Attributes:
27
+ state_root_hash (str): State root hash at time of marker
28
+ request_index (int): Sequenced request index of transaction
29
+ """
30
+
31
+ state_root_hash: Optional[str] = field(eq=str.lower)
32
+ request_index: int = field(default=-1, eq=False)
33
+
34
+ @classmethod
35
+ def decode_value_into_cls(cls, raw_tx_log_event: dict):
36
+ """
37
+ Decode a raw transaction log event (dict) into a Genesis
38
+ instance.
39
+
40
+ Parameters
41
+ ----------
42
+ raw_tx_log_event : dict
43
+ Raw transaction log event being processed
44
+ """
45
+
46
+ genesis_tx_event = raw_tx_log_event["event"]["c"]
47
+
48
+ return cls(
49
+ genesis_tx_event.get("stateRootHash"),
50
+ raw_tx_log_event["requestIndex"],
51
+ )
52
+
53
+ def process_tx(
54
+ self,
55
+ **kwargs,
56
+ ):
57
+ """
58
+ Process an EpochMarker transaction of type Genesis. This
59
+ indicates the very first event in the transaction log, although
60
+ it is not state-transitioning in the way typical transactions
61
+ are.
62
+
63
+ Parameters
64
+ ----------
65
+ **kwargs
66
+ Additional args specific to Genesis transactions
67
+ """
68
+
69
+ insurance_fund_balance = Balance.default()
70
+ ddx_fee_pool = Decimal("0")
71
+
72
+ specs = {}
73
+ for key, val in kwargs["genesis_params"]["specs"].items():
74
+ if key.startswith("SINGLENAMEPERP"):
75
+ kind = SpecsKind.SingleNamePerpetual
76
+ elif key.startswith("INDEXFUNDPERP"):
77
+ kind = SpecsKind.IndexFundPerpetual
78
+ elif key.startswith("BINARYFUTURE"):
79
+ kind = SpecsKind.BinaryPredictionFuture
80
+ elif key.startswith("QUARTERLYFUTURE"):
81
+ kind = SpecsKind.QuarterlyExpiryFuture
82
+ elif key.startswith("SPOTGATEWAY"):
83
+ kind = SpecsKind.SpotGateway
84
+ elif key.startswith("SPOTINDEX"):
85
+ kind = SpecsKind.SpotIndex
86
+ elif key.startswith("BINARYGATEWAY"):
87
+ kind = SpecsKind.BinaryPredictionGateway
88
+ elif key.startswith("BINARYINDEX"):
89
+ kind = SpecsKind.BinaryIndex
90
+ else:
91
+ raise Exception("Invalid spec in Genesis params")
92
+ # only split once to avoid splitting on the second dash
93
+ specs[SpecsKey(kind, key.split("-", 1)[1])] = val
94
+
95
+ logger.info(
96
+ f"Initializing SMT from genesis:\n\tInsurance fund: {insurance_fund_balance}\n\tDDX fee pool: {ddx_fee_pool}\n\tSpecs: {specs}\n\tCurrent datetime: {kwargs['current_time']}"
97
+ )
98
+ smt = DerivadexSMT.from_genesis(
99
+ insurance_fund_balance, ddx_fee_pool, specs, kwargs["current_time"]
100
+ )
101
+ kwargs["smt"](kwargs["auditor_instance"], smt)
102
+
103
+ # Set the expected epoch ID to be 1 and the expected tx ordinal
104
+ # to be -1, because we immediately increment this by 1, thus
105
+ # setting it to 0, which will be the first tx ordinal of the
106
+ # next epoch
107
+ kwargs["expected_epoch_id"](kwargs["auditor_instance"], 1)
108
+ kwargs["expected_tx_ordinal"](kwargs["auditor_instance"], -1)
File without changes
@@ -0,0 +1,25 @@
1
+ """
2
+ AdlOutcome module
3
+ """
4
+
5
+ from attrs import define, field
6
+ from ddx._rust.decimal import Decimal
7
+
8
+
9
+ @define(hash=True)
10
+ class AdlOutcome:
11
+ """
12
+ Defines an AdlOutcome
13
+
14
+ An AdlOutcome is a scenario where a strategy has been auto-deleveraged
15
+ due to a liquidation.
16
+
17
+ Attributes:
18
+ trader_address (str): Auto-deleveraged trader's ethereum address
19
+ strategy_id_hash (str): Auto-deleveraged strategy ID hash
20
+ request_index (int): Sequenced request index of transaction
21
+ """
22
+
23
+ trader_address: str = field(eq=str.lower)
24
+ strategy_id_hash: str = field(eq=str.lower)
25
+ request_index: int = field(default=-1, eq=False, hash=False)
@@ -0,0 +1,232 @@
1
+ """
2
+ Fill module
3
+ """
4
+
5
+ import logging
6
+ from enum import Enum
7
+ from typing import Optional
8
+
9
+ from attrs import define, field
10
+ from ddx.common.fill_context import (MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS,
11
+ FillContext, apply_trade)
12
+ from ddx.common.transaction_utils import get_most_recent_price
13
+ from ddx.common.transactions.event import Event
14
+ from ddx.common.transactions.inner.outcome import Outcome
15
+ from ddx._rust.common import ProductSymbol, TokenSymbol
16
+ from ddx._rust.common.enums import OrderSide, TradeSide
17
+ from ddx._rust.common.state import (DerivadexSMT, InsuranceFund, Position,
18
+ Price, Stats)
19
+ from ddx._rust.common.state.keys import (EpochMetadataKey, InsuranceFundKey,
20
+ PositionKey, PriceKey, StatsKey,
21
+ StrategyKey, TraderKey)
22
+ from ddx._rust.decimal import Decimal
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ @define(hash=True)
28
+ class Fill(Event):
29
+ """
30
+ Defines a Fill
31
+ """
32
+
33
+ symbol: ProductSymbol
34
+ amount: Decimal = field(hash=False)
35
+ price: Decimal = field(hash=False)
36
+ taker_side: OrderSide = field(hash=False)
37
+ time_value: int
38
+ request_index: int = field(eq=False, hash=False)
39
+
40
+ def adjust_for_maker_taker(
41
+ self,
42
+ smt: DerivadexSMT,
43
+ epoch_id: int,
44
+ is_maker: bool,
45
+ outcome: Outcome,
46
+ trade_mining_active: bool = False,
47
+ maker_book_order_time_value: Optional[int] = None,
48
+ reconcile_fees: bool = True,
49
+ ):
50
+ """
51
+ Make some adjustments to the SMT based on whether we are
52
+ considering the maker or the taker component of the Trade.
53
+ In this method, we will be adjusting the the Strategy,
54
+ Position, and Stats leaves.
55
+
56
+ Parameters
57
+ ----------
58
+ smt: DerivadexSMT
59
+ DerivaDEX Sparse Merkle Tree
60
+ epoch_id: int
61
+ Epoch ID used for recording fees if any
62
+ is_maker : bool
63
+ Whether outcome is for the maker or taker
64
+ outcome: Outcome
65
+ Outcome to adjust
66
+ trade_mining_active : bool
67
+ Whether trade mining is active
68
+ maker_book_order_time_value: Optional[int]
69
+ Maker book order time value
70
+ """
71
+
72
+ context = FillContext(outcome)
73
+
74
+ position_key: PositionKey = PositionKey(
75
+ outcome.trader,
76
+ outcome.strategy_id_hash,
77
+ self.symbol,
78
+ )
79
+
80
+ position: Optional[Position] = smt.position(position_key)
81
+
82
+ position = context.apply_fill(
83
+ position,
84
+ (
85
+ (OrderSide.Ask if self.taker_side == OrderSide.Bid else OrderSide.Bid)
86
+ if is_maker
87
+ else self.taker_side
88
+ ),
89
+ (TradeSide.Maker if is_maker else TradeSide.Taker),
90
+ self.amount,
91
+ self.price,
92
+ )
93
+ smt.store_position(
94
+ position_key,
95
+ position,
96
+ )
97
+
98
+ trader_key: TraderKey = TraderKey(outcome.trader)
99
+ trader = smt.trader(trader_key)
100
+
101
+ if outcome.fee > Decimal("0") and trader.pay_fees_in_ddx:
102
+ # Gets the most recent DDX price within MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS
103
+ _, recent_ddx_price = get_most_recent_price(
104
+ smt, ProductSymbol("DDXP"), self.time_value
105
+ )
106
+ if (
107
+ recent_ddx_price.time_value
108
+ >= self.time_value - MAX_DDX_PRICE_CHECKPOINT_AGE_IN_TICKS
109
+ ):
110
+ if context.apply_ddx_fee_and_mutate_trader(
111
+ trader, recent_ddx_price.index_price
112
+ ):
113
+ smt.store_trader(trader_key, trader)
114
+ else:
115
+ raise Exception(
116
+ "DDX fee election enabled but no DDX price checkpoint within the max age was found, should have been caught by the operator"
117
+ )
118
+
119
+ strategy_key: StrategyKey = position_key.as_strategy_key()
120
+ strategy = smt.strategy(strategy_key)
121
+ context.realize_trade_and_mutate_strategy(strategy)
122
+ smt.store_strategy(strategy_key, strategy)
123
+
124
+ if reconcile_fees and outcome.fee > Decimal("0"):
125
+ if outcome.pay_fee_in_ddx:
126
+ logger.info(f"Fee of {outcome.fee} DDX to be paid")
127
+ epoch_metadata_key: EpochMetadataKey = EpochMetadataKey(epoch_id)
128
+ epoch_metadata = smt.epoch_metadata(epoch_metadata_key)
129
+ epoch_metadata.ddx_fee_pool = (
130
+ epoch_metadata.ddx_fee_pool + outcome.fee
131
+ ).recorded_amount()
132
+ smt.store_epoch_metadata(epoch_metadata_key, epoch_metadata)
133
+ else:
134
+ logger.info(f"Fee of {outcome.fee} USDC to be paid")
135
+ insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
136
+ insurance_fund = smt.insurance_fund(insurance_fund_key)
137
+ insurance_fund[TokenSymbol.USDC] += outcome.fee
138
+ smt.store_insurance_fund(insurance_fund_key, insurance_fund)
139
+
140
+ if (
141
+ trade_mining_active
142
+ and maker_book_order_time_value
143
+ and self.time_value > maker_book_order_time_value + 1
144
+ ):
145
+ stats_key: StatsKey = StatsKey(outcome.trader)
146
+ stats = smt.stats(stats_key)
147
+ notional_amount = self.amount * self.price
148
+ if stats is None:
149
+ # If Stats leaf doesn't exist in the tree, we need
150
+ # to create/add a new one
151
+ if is_maker:
152
+ # Initialize the trader's maker volume
153
+ stats = Stats(notional_amount, Decimal("0"))
154
+ else:
155
+ # Initialize the trader's taker volume
156
+ stats = Stats(Decimal("0"), notional_amount)
157
+ else:
158
+ # If Stats leaf does exist, we update the existing leaf
159
+ if is_maker:
160
+ # Increment the trader's maker volume
161
+ stats.maker_volume += notional_amount
162
+ else:
163
+ # Increment the trader's taker volume
164
+ stats.taker_volume += notional_amount
165
+
166
+ smt.store_stats(stats_key, stats)
167
+
168
+ def adjust_for_maker(
169
+ self,
170
+ smt: DerivadexSMT,
171
+ epoch_id: int,
172
+ trade_mining_active: bool = False,
173
+ maker_book_order_time_value: Optional[int] = None,
174
+ reconcile_fees: bool = True,
175
+ ):
176
+ """
177
+ Make some adjustments to the SMT based on the maker side
178
+ of the Trade.
179
+
180
+ Parameters
181
+ ----------
182
+ smt: DerivadexSMT
183
+ DerivaDEX Sparse Merkle Tree
184
+ epoch_id: int
185
+ Epoch ID used for recording fees if any
186
+ trade_mining_active : bool
187
+ Whether trade mining is active
188
+ maker_book_order_time_value: Optional[int]
189
+ Maker book order time value
190
+ """
191
+ self.adjust_for_maker_taker(
192
+ smt,
193
+ epoch_id,
194
+ True,
195
+ self.maker_outcome,
196
+ trade_mining_active,
197
+ maker_book_order_time_value,
198
+ reconcile_fees,
199
+ )
200
+
201
+ def adjust_for_taker(
202
+ self,
203
+ smt: DerivadexSMT,
204
+ epoch_id: int,
205
+ trade_mining_active: bool = False,
206
+ maker_book_order_time_value: Optional[int] = None,
207
+ reconcile_fees: bool = True,
208
+ ):
209
+ """
210
+ Make some adjustments to the SMT based on the taker side
211
+ of the Trade.
212
+
213
+ Parameters
214
+ ----------
215
+ smt: DerivadexSMT
216
+ DerivaDEX Sparse Merkle Tree
217
+ epoch_id: int
218
+ Epoch ID used for recording fees if any
219
+ maker_book_order_time_value: int
220
+ Maker book order time value
221
+ trade_mining_active : bool
222
+ Whether trade mining is active
223
+ """
224
+ self.adjust_for_maker_taker(
225
+ smt,
226
+ epoch_id,
227
+ False,
228
+ self.taker_outcome,
229
+ trade_mining_active,
230
+ maker_book_order_time_value,
231
+ reconcile_fees,
232
+ )
@@ -0,0 +1,41 @@
1
+ """
2
+ LiquidatedPosition module
3
+ """
4
+
5
+ from attrs import define, field
6
+ from ddx.common.transactions.cancel import Cancel
7
+ from ddx.common.transactions.inner.adl_outcome import AdlOutcome
8
+ from ddx.common.transactions.inner.liquidation_fill import LiquidationFill
9
+ from ddx._rust.decimal import Decimal
10
+
11
+
12
+ @define
13
+ class LiquidatedPosition:
14
+ """
15
+ Defines a LiquidatedPosition
16
+
17
+ A LiquidatedPosition has data pertaining to a liquidated position.
18
+
19
+ Attributes:
20
+ amount (Decimal): Liquidated balance amount
21
+ trade_outcomes (list[LiquidationFill | Cancel]): A list of trade outcome objects
22
+ adl_outcomes (list[AdlOutcome]): Positions that were ADL'd as a result of the liquidation
23
+ new_insurance_fund_cap (Decimal): Insurance fund capitalization after the liquidation
24
+ request_index (int): Sequenced request index of transaction
25
+ """
26
+
27
+ amount: Decimal
28
+ trade_outcomes: list[LiquidationFill | Cancel] = field(eq=set)
29
+ adl_outcomes: list[AdlOutcome] = field(eq=set)
30
+ new_insurance_fund_cap: Decimal
31
+ request_index: int = field(default=-1, eq=False)
32
+
33
+ def __hash__(self):
34
+ return hash(
35
+ (
36
+ self.amount,
37
+ frozenset(self.trade_outcomes),
38
+ frozenset(self.adl_outcomes),
39
+ self.new_insurance_fund_cap,
40
+ )
41
+ )
@@ -0,0 +1,41 @@
1
+ """
2
+ LiquidationEntry module
3
+ """
4
+
5
+ from attrs import define, field
6
+ from ddx.common.transactions.cancel import Cancel
7
+ from ddx.common.transactions.inner.liquidated_position import LiquidatedPosition
8
+ from ddx._rust.common import ProductSymbol
9
+
10
+
11
+ @define
12
+ class LiquidationEntry:
13
+ """
14
+ Defines a LiquidationEntry
15
+
16
+ A LiquidationEntry contains data pertaining to individual trader and
17
+ strategy liquidations.
18
+
19
+ Attributes:
20
+ trader_address (str): Liquidated trader's Ethereum address
21
+ strategy_id_hash (str): Liquidated strategy ID hash
22
+ canceled_orders (list[Cancel]): Canceled orders for liquidated trader
23
+ positions (list[tuple[str, LiquidatedPosition]]): Contains information pertaining to individual liquidated positions by symbol
24
+ request_index (int): Sequenced request index of transaction
25
+ """
26
+
27
+ trader_address: str = field(eq=str.lower)
28
+ strategy_id_hash: str = field(eq=str.lower)
29
+ canceled_orders: list[Cancel] = field(eq=set)
30
+ positions: list[tuple[ProductSymbol, LiquidatedPosition]] = field(eq=set)
31
+ request_index: int = field(default=-1, eq=False)
32
+
33
+ def __hash__(self):
34
+ return hash(
35
+ (
36
+ self.trader_address,
37
+ self.strategy_id_hash,
38
+ frozenset(self.canceled_orders),
39
+ frozenset(self.positions),
40
+ )
41
+ )
@@ -0,0 +1,118 @@
1
+ """
2
+ LiquidationFill module
3
+ """
4
+
5
+ import logging
6
+
7
+ from attrs import define, field
8
+ from ddx.common.transactions.inner.fill import Fill
9
+ from ddx.common.transactions.inner.outcome import Outcome
10
+ from ddx._rust.common import ProductSymbol
11
+ from ddx._rust.common.enums import OrderSide
12
+ from ddx._rust.common.state import DerivadexSMT
13
+ from ddx._rust.common.state.keys import BookOrderKey
14
+ from ddx._rust.decimal import Decimal
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @define(hash=True)
20
+ class LiquidationFill(Fill):
21
+ """
22
+ Defines a LiquidationFill
23
+ """
24
+
25
+ maker_order_hash: str = field(eq=str.lower)
26
+ maker_outcome: Outcome = field(hash=False)
27
+ maker_order_remaining_amount: Decimal = field(hash=False)
28
+ index_price_hash: str = field(eq=str.lower)
29
+ request_index: int = field(default=-1, eq=False, hash=False)
30
+
31
+ def __init__(
32
+ self,
33
+ symbol: ProductSymbol,
34
+ index_price_hash: str,
35
+ maker_order_hash: str,
36
+ maker_order_remaining_amount: Decimal,
37
+ amount: Decimal,
38
+ price: Decimal,
39
+ taker_side: OrderSide,
40
+ maker_outcome: Outcome,
41
+ time_value: int,
42
+ request_index: int = -1,
43
+ ):
44
+ """
45
+ Initialize a LiquidationFill instance
46
+ Parameters
47
+ ----------
48
+ symbol : ProductSymbol
49
+ Product symbol
50
+ index_price_hash : str
51
+ Index price hash
52
+ maker_order_hash : str
53
+ Maker order hash
54
+ maker_order_remaining_amount : Decimal
55
+ Maker order remaining amount
56
+ amount : Decimal
57
+ Amount
58
+ price : Decimal
59
+ Price
60
+ taker_side : OrderSide
61
+ Taker side
62
+ maker_outcome : Outcome
63
+ Maker outcome
64
+ time_value : int
65
+ Time value
66
+ request_index : int
67
+ Request index
68
+ """
69
+ super().__init__(
70
+ symbol,
71
+ amount,
72
+ price,
73
+ taker_side,
74
+ time_value,
75
+ request_index,
76
+ )
77
+ self.maker_order_hash = maker_order_hash
78
+ self.maker_outcome = maker_outcome
79
+ self.maker_order_remaining_amount = maker_order_remaining_amount
80
+ self.index_price_hash = index_price_hash
81
+
82
+ def process_tx(
83
+ self,
84
+ smt: DerivadexSMT,
85
+ **kwargs,
86
+ ):
87
+ """
88
+ Process a LiquidationFill transaction. These are Fill
89
+ transactions that have risen from either a Liquidation.
90
+
91
+ Parameters
92
+ ----------
93
+ smt: DerivadexSMT
94
+ DerivaDEX Sparse Merkle Tree
95
+ **kwargs
96
+ Additional args specific to Liquidation transactions
97
+ """
98
+
99
+ maker_book_order_key: BookOrderKey = BookOrderKey(
100
+ self.symbol, self.maker_order_hash
101
+ )
102
+
103
+ maker_book_order = smt.book_order(maker_book_order_key)
104
+ maker_book_order_time_value = maker_book_order.time_value
105
+
106
+ maker_book_order.amount = self.maker_order_remaining_amount
107
+ smt.store_book_order(maker_book_order_key, maker_book_order)
108
+
109
+ # Adjust the maker-related position. Take note that
110
+ # in a liquidation, there is no taker component, so
111
+ # unlike its counterpart (TradeFill), a
112
+ # LiquidationFill only considers the maker
113
+ self.adjust_for_maker(
114
+ smt,
115
+ kwargs["epoch_id"],
116
+ kwargs["trade_mining_active"],
117
+ maker_book_order_time_value,
118
+ )
@@ -0,0 +1,32 @@
1
+ """
2
+ Outcome module
3
+ """
4
+
5
+ from typing import Optional
6
+
7
+ from attrs import define, field
8
+ from ddx._rust.decimal import Decimal
9
+
10
+
11
+ @define(hash=True)
12
+ class Outcome:
13
+ """
14
+ Defines a Outcome
15
+
16
+ An Outcome is a part of a Fill transaction. It holds information
17
+ including the fees paid, the strategy ID and trader address, and
18
+ whether fees are being paid in DDX or not (i.e. USDC).
19
+
20
+ Attributes:
21
+ trader (str): Trader's Ethereum address
22
+ strategy_id_hash (str): Strategy ID hash
23
+ fee (Decimal): Fees paid for filled trade
24
+ pay_fee_in_ddx (bool): Whether the trader has elected to pay the fee in DDX
25
+ """
26
+
27
+ trader: str = field(eq=str.lower, default="")
28
+ strategy_id_hash: str = field(eq=str.lower, default="")
29
+ # The below fields do not exist in the txlog (and thus are not checked in transaction
30
+ # equality), and are only used when executing/processing transactions.
31
+ fee: Optional[Decimal] = field(eq=False, default=None)
32
+ pay_fee_in_ddx: Optional[bool] = field(eq=False, default=None)