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,292 @@
1
+ """
2
+ TradeFill module
3
+ """
4
+
5
+ import asyncio
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, TokenSymbol
11
+ from ddx._rust.common.enums import OrderSide
12
+ from ddx._rust.common.state import DerivadexSMT, Price
13
+ from ddx._rust.common.state.keys import (
14
+ BookOrderKey,
15
+ EpochMetadataKey,
16
+ InsuranceFundKey,
17
+ PriceKey,
18
+ StrategyKey,
19
+ TraderKey,
20
+ )
21
+ from ddx._rust.decimal import Decimal
22
+
23
+
24
+ AFFILIATE_STANDARD_L1 = Decimal("0.10")
25
+ AFFILIATE_STANDARD_L2 = Decimal("0.05")
26
+ AFFILIATE_STANDARD_REBATE = Decimal("0.10")
27
+ AFFILIATE_PREMIUM_L1 = Decimal("0.20")
28
+ AFFILIATE_PREMIUM_L2 = Decimal("0.10")
29
+ AFFILIATE_PREMIUM_REBATE = Decimal("0.20")
30
+ AFFILIATE_STANDARD_MIN_DDX = Decimal("1_000")
31
+ AFFILIATE_PREMIUM_MIN_DDX = Decimal("10_000")
32
+
33
+
34
+ def _affiliate_schedule_for(trader):
35
+ if trader is None:
36
+ return None
37
+ stake = trader.avail_ddx_balance + trader.locked_ddx_balance
38
+ if stake >= AFFILIATE_PREMIUM_MIN_DDX:
39
+ return (AFFILIATE_PREMIUM_L1, AFFILIATE_PREMIUM_L2, AFFILIATE_PREMIUM_REBATE)
40
+ if stake >= AFFILIATE_STANDARD_MIN_DDX:
41
+ return (
42
+ AFFILIATE_STANDARD_L1,
43
+ AFFILIATE_STANDARD_L2,
44
+ AFFILIATE_STANDARD_REBATE,
45
+ )
46
+ return None
47
+
48
+
49
+ def _resolve_affiliate_chain(smt: DerivadexSMT, taker_address: str):
50
+ taker = smt.trader(TraderKey(taker_address))
51
+ if taker is None:
52
+ return (None, None, None, None)
53
+ l1 = taker.referral_address
54
+ l1_trader = smt.trader(TraderKey(l1)) if l1 is not None else None
55
+ l2 = None
56
+ l2_trader = None
57
+ if l1_trader is not None and l1_trader.referral_address is not None:
58
+ l2 = l1_trader.referral_address
59
+ l2_trader = smt.trader(TraderKey(l2))
60
+ return (l1, l1_trader, l2, l2_trader)
61
+
62
+
63
+ def _compute_affiliate_split(
64
+ total_fee: Decimal,
65
+ l1_schedule,
66
+ l2_schedule,
67
+ l1_exists: bool,
68
+ l2_exists: bool,
69
+ taker_schedule,
70
+ ):
71
+ if total_fee == Decimal("0"):
72
+ return {
73
+ "platform": Decimal("0"),
74
+ "l1": Decimal("0"),
75
+ "l2": Decimal("0"),
76
+ "rebate": Decimal("0"),
77
+ }
78
+ l1 = total_fee * l1_schedule[0] if l1_exists and l1_schedule else Decimal("0")
79
+ l2 = total_fee * l2_schedule[1] if l2_exists and l2_schedule else Decimal("0")
80
+ # TODO: in alpha1 where there are 100 traders, they always get the taker rebate
81
+ if l1_exists and l1_schedule:
82
+ rebate = total_fee * l1_schedule[2]
83
+ elif taker_schedule:
84
+ rebate = total_fee * taker_schedule[2]
85
+ else:
86
+ rebate = Decimal("0")
87
+ platform = total_fee - (l1 + l2 + rebate)
88
+ return {"platform": platform, "l1": l1, "l2": l2, "rebate": rebate}
89
+
90
+
91
+ def _credit_affiliate_usdc(smt: DerivadexSMT, trader_address: str, amount: Decimal):
92
+ if amount == Decimal("0"):
93
+ return
94
+ strategy_key = StrategyKey(
95
+ trader_address, StrategyKey.generate_strategy_id_hash("main")
96
+ )
97
+ strategy = smt.strategy(strategy_key)
98
+ if strategy is None:
99
+ return
100
+ strategy.set_avail_collateral(
101
+ TokenSymbol.USDC,
102
+ (strategy.avail_collateral[TokenSymbol.USDC] + amount).recorded_amount(),
103
+ )
104
+ smt.store_strategy(strategy_key, strategy)
105
+
106
+
107
+ def _credit_affiliate_ddx(smt: DerivadexSMT, trader_address: str, amount: Decimal):
108
+ if amount == Decimal("0"):
109
+ return
110
+ trader = smt.trader(TraderKey(trader_address))
111
+ if trader is None:
112
+ return
113
+ trader.avail_ddx_balance = (
114
+ trader.avail_ddx_balance + amount
115
+ ).recorded_amount()
116
+ smt.store_trader(TraderKey(trader_address), trader)
117
+
118
+
119
+ @define(hash=True)
120
+ class TradeFill(Fill):
121
+ """
122
+ Defines a TradeFill
123
+ """
124
+
125
+ maker_order_hash: str = field(eq=str.lower)
126
+ maker_outcome: Outcome = field(hash=False)
127
+ maker_order_remaining_amount: Decimal = field(hash=False)
128
+ taker_order_hash: str = field(eq=str.lower)
129
+ taker_outcome: Outcome = field(hash=False)
130
+ request_index: int = field(default=-1, eq=False, hash=False)
131
+
132
+ def __init__(
133
+ self,
134
+ symbol: ProductSymbol,
135
+ taker_order_hash: str,
136
+ maker_order_hash: str,
137
+ maker_order_remaining_amount: Decimal,
138
+ amount: Decimal,
139
+ price: Decimal,
140
+ taker_side: OrderSide,
141
+ maker_outcome: Outcome,
142
+ taker_outcome: Outcome,
143
+ time_value: int,
144
+ request_index: int = -1,
145
+ ):
146
+ """
147
+ Initialize a TradeFill instance
148
+ Parameters
149
+ ----------
150
+ symbol : ProductSymbol
151
+ Product symbol
152
+ taker_order_hash : str
153
+ Taker order hash
154
+ maker_order_hash : str
155
+ Maker order hash
156
+ maker_order_remaining_amount : Decimal
157
+ Maker order remaining amount
158
+ amount : Decimal
159
+ Amount
160
+ price : Decimal
161
+ Price
162
+ taker_side : OrderSide
163
+ Taker side
164
+ maker_outcome : Outcome
165
+ Maker outcome
166
+ taker_outcome : Outcome
167
+ Taker outcome
168
+ time_value : int
169
+ Time value
170
+ request_index : int
171
+ Request index
172
+ """
173
+ super().__init__(
174
+ symbol,
175
+ amount,
176
+ price,
177
+ taker_side,
178
+ time_value,
179
+ request_index,
180
+ )
181
+ self.maker_order_hash = maker_order_hash
182
+ self.maker_outcome = maker_outcome
183
+ self.maker_order_remaining_amount = maker_order_remaining_amount
184
+ self.taker_order_hash = taker_order_hash
185
+ self.taker_outcome = taker_outcome
186
+
187
+ def process_tx(
188
+ self,
189
+ smt: DerivadexSMT,
190
+ **kwargs,
191
+ ):
192
+ """
193
+ Process a TradeFill transaction. These are Fill transactions
194
+ that have risen from either a CompleteFill or a PartialFill
195
+ transaction.
196
+
197
+ Parameters
198
+ ----------
199
+ smt: DerivadexSMT
200
+ DerivaDEX Sparse Merkle Tree
201
+ **kwargs
202
+ Additional args specific to CompleteFill/PartialFill transactions
203
+ """
204
+
205
+ maker_book_order_key: BookOrderKey = BookOrderKey(
206
+ self.symbol, self.maker_order_hash
207
+ )
208
+ maker_book_order = smt.book_order(maker_book_order_key)
209
+ maker_book_order_time_value = maker_book_order.time_value
210
+
211
+ maker_book_order.amount = self.maker_order_remaining_amount
212
+ smt.store_book_order(maker_book_order_key, maker_book_order)
213
+
214
+ # Make the appropriate adjustments for both the maker and taker
215
+ # components of the Trade
216
+ self.adjust_for_maker(
217
+ smt,
218
+ kwargs["epoch_id"],
219
+ kwargs["trade_mining_active"],
220
+ maker_book_order_time_value,
221
+ reconcile_fees=False,
222
+ )
223
+ self.adjust_for_taker(
224
+ smt,
225
+ kwargs["epoch_id"],
226
+ kwargs["trade_mining_active"],
227
+ maker_book_order_time_value,
228
+ reconcile_fees=False,
229
+ )
230
+ self._reconcile_fees(
231
+ smt,
232
+ kwargs["epoch_id"],
233
+ )
234
+
235
+ def _reconcile_fees(self, smt: DerivadexSMT, epoch_id: int):
236
+ fees_default, fees_ddx = Decimal("0"), Decimal("0")
237
+ for outcome in (self.maker_outcome, self.taker_outcome):
238
+ if outcome.fee > Decimal("0"):
239
+ if outcome.pay_fee_in_ddx:
240
+ fees_ddx += outcome.fee
241
+ else:
242
+ fees_default += outcome.fee
243
+
244
+ l1 = l2 = l1_trader = l2_trader = None
245
+ if self.taker_outcome is not None:
246
+ l1, l1_trader, l2, l2_trader = _resolve_affiliate_chain(
247
+ smt, self.taker_outcome.trader
248
+ )
249
+ l1_schedule = _affiliate_schedule_for(l1_trader)
250
+ l2_schedule = _affiliate_schedule_for(l2_trader)
251
+ taker = smt.trader(TraderKey(self.taker_outcome.trader))
252
+ taker_schedule = _affiliate_schedule_for(taker)
253
+
254
+ if fees_default > Decimal("0"):
255
+ split = _compute_affiliate_split(
256
+ fees_default, l1_schedule, l2_schedule, l1 is not None, l2 is not None, taker_schedule
257
+ )
258
+ if l1 is not None:
259
+ _credit_affiliate_usdc(smt, l1, split["l1"])
260
+ if l2 is not None:
261
+ _credit_affiliate_usdc(smt, l2, split["l2"])
262
+ if self.taker_outcome is not None:
263
+ _credit_affiliate_usdc(
264
+ smt, self.taker_outcome.trader, split["rebate"]
265
+ )
266
+
267
+ insurance_fund_key = InsuranceFundKey()
268
+ insurance_fund = smt.insurance_fund(insurance_fund_key)
269
+ insurance_fund[TokenSymbol.USDC] = (
270
+ insurance_fund[TokenSymbol.USDC] + split["platform"]
271
+ ).recorded_amount()
272
+ smt.store_insurance_fund(insurance_fund_key, insurance_fund)
273
+
274
+ if fees_ddx > Decimal("0"):
275
+ split = _compute_affiliate_split(
276
+ fees_ddx, l1_schedule, l2_schedule, l1 is not None, l2 is not None, taker_schedule
277
+ )
278
+ if l1 is not None:
279
+ _credit_affiliate_ddx(smt, l1, split["l1"])
280
+ if l2 is not None:
281
+ _credit_affiliate_ddx(smt, l2, split["l2"])
282
+ if self.taker_outcome is not None:
283
+ _credit_affiliate_ddx(
284
+ smt, self.taker_outcome.trader, split["rebate"]
285
+ )
286
+
287
+ epoch_metadata_key = EpochMetadataKey(epoch_id)
288
+ epoch_metadata = smt.epoch_metadata(epoch_metadata_key)
289
+ epoch_metadata.ddx_fee_pool = (
290
+ epoch_metadata.ddx_fee_pool + split["platform"]
291
+ ).recorded_amount()
292
+ smt.store_epoch_metadata(epoch_metadata_key, epoch_metadata)
@@ -0,0 +1,138 @@
1
+ """
2
+ InsuranceFundUpdate
3
+ """
4
+
5
+ from attrs import define, field
6
+ from ddx.common.transactions.event import Event
7
+ from ddx._rust.common import TokenSymbol
8
+ from ddx._rust.common.state import DerivadexSMT, InsuranceFundContribution
9
+ from ddx._rust.common.state.keys import InsuranceFundContributionKey, InsuranceFundKey
10
+ from ddx._rust.common.transactions import InsuranceFundUpdateKind
11
+ from ddx._rust.decimal import Decimal
12
+
13
+
14
+ @define
15
+ class InsuranceFundUpdate(Event):
16
+ """
17
+ Defines an InsuranceFundUpdate
18
+
19
+ An InsuranceFundUpdate is an update to a trader's insurance fund
20
+ contribution (such as depositing or withdrawing
21
+ collateral).
22
+
23
+ Attributes:
24
+ address (str): Trader's Ethereum address this insurance fund update belongs to
25
+ collateral_address (str): Collateral's Ethereum address a deposit/withdrawal has been made with
26
+ amount (Decimal): The amount of collateral deposited or withdrawn
27
+ update_kind (InsuranceFundUpdateKind): Update kind (Deposit=0, Withdraw=1)
28
+ tx_hash (str): The Ethereum transaction's hash
29
+ request_index (int): Sequenced request index of transaction
30
+ """
31
+
32
+ address: str = field(eq=str.lower)
33
+ collateral_address: str = field(eq=str.lower)
34
+ amount: Decimal
35
+ update_kind: InsuranceFundUpdateKind
36
+ tx_hash: str = field(eq=str.lower)
37
+ request_index: int = field(default=-1, eq=False)
38
+
39
+ @classmethod
40
+ def decode_value_into_cls(cls, raw_tx_log_event: dict):
41
+ """
42
+ Decode a raw transaction log event (dict) into an
43
+ InsuranceFundUpdate instance.
44
+
45
+ Parameters
46
+ ----------
47
+ raw_tx_log_event : dict
48
+ Raw transaction log event being processed
49
+ """
50
+
51
+ insurance_fund_update_tx_event = raw_tx_log_event["event"]["c"]
52
+
53
+ return cls(
54
+ insurance_fund_update_tx_event["address"],
55
+ insurance_fund_update_tx_event["collateralAddress"],
56
+ Decimal(insurance_fund_update_tx_event["amount"]),
57
+ InsuranceFundUpdateKind(insurance_fund_update_tx_event["updateKind"]),
58
+ insurance_fund_update_tx_event["txHash"],
59
+ raw_tx_log_event["requestIndex"],
60
+ )
61
+
62
+ def process_tx(
63
+ self,
64
+ smt: DerivadexSMT,
65
+ **kwargs,
66
+ ):
67
+ """
68
+ Process an InsuranceFundUpdate transaction. An
69
+ InsuranceFundUpdate consists of information relating to updates
70
+ for a trader's insurance fund contribution, such
71
+ as when their free or frozen balance has changed due to a
72
+ deposit or withdrawal. This will update the
73
+ InsuranceFundContribution leaf in the SMT.
74
+
75
+ Parameters
76
+ ----------
77
+ smt: DerivadexSMT
78
+ DerivaDEX Sparse Merkle Tree
79
+ **kwargs
80
+ Additional args specific to InsuranceFundUpdate transactions
81
+ """
82
+
83
+ insurance_fund_contribution_key: InsuranceFundContributionKey = (
84
+ InsuranceFundContributionKey(self.address)
85
+ )
86
+ insurance_fund_contribution = smt.insurance_fund_contribution(
87
+ insurance_fund_contribution_key
88
+ )
89
+
90
+ symbol = TokenSymbol.from_address(self.collateral_address)
91
+ if self.update_kind == InsuranceFundUpdateKind.Deposit:
92
+ # If InsuranceFundUpdate is of deposit type
93
+
94
+ if insurance_fund_contribution is None:
95
+ # If we haven't yet seen the InsuranceFundContribution
96
+ # leaf, create a new one
97
+ insurance_fund_contribution = InsuranceFundContribution.default()
98
+
99
+ # Adjust the existing strategy leaf by incrementing the
100
+ # free collateral by the amount in the deposit event
101
+ insurance_fund_contribution.set_avail_balance(
102
+ symbol,
103
+ insurance_fund_contribution.avail_balance[symbol] + self.amount,
104
+ )
105
+
106
+ insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
107
+ insurance_fund = smt.insurance_fund(insurance_fund_key)
108
+
109
+ insurance_fund[symbol] += self.amount
110
+
111
+ # Update the SMT with the H256 repr of the key and the
112
+ # InsuranceFundContribution leaf
113
+ smt.store_insurance_fund_contribution(
114
+ insurance_fund_contribution_key, insurance_fund_contribution
115
+ )
116
+ smt.store_insurance_fund(insurance_fund_key, insurance_fund)
117
+ elif self.update_kind == InsuranceFundUpdateKind.Withdraw:
118
+ # If InsuranceFundUpdate is of withdrawal (claimed) type
119
+ if (
120
+ insurance_fund_contribution is None
121
+ or insurance_fund_contribution.locked_balance[symbol] < self.amount
122
+ ):
123
+ raise Exception(
124
+ "InsuranceFundContribution leaf either non-existent or insufficiently capitalized to facilitate withdrawal"
125
+ )
126
+
127
+ # Adjust the existing InsuranceFundContribution leaf by decrementing the
128
+ # locked balance by the amount in the withdrawal event
129
+ insurance_fund_contribution.set_locked_balance(
130
+ symbol,
131
+ insurance_fund_contribution.locked_balance[symbol] - self.amount,
132
+ )
133
+
134
+ # Update the SMT with the H256 repr of the key and the
135
+ # Strategy leaf
136
+ smt.store_insurance_fund_contribution(
137
+ insurance_fund_contribution_key, insurance_fund_contribution
138
+ )
@@ -0,0 +1,100 @@
1
+ """
2
+ InsuranceFundWithdraw module
3
+ """
4
+
5
+ from attrs import define, field
6
+ from ddx.common.transactions.event import Event
7
+ from ddx._rust.common import TokenSymbol
8
+ from ddx._rust.common.state import DerivadexSMT, InsuranceFundContribution
9
+ from ddx._rust.common.state.keys import InsuranceFundContributionKey, InsuranceFundKey
10
+ from ddx._rust.decimal import Decimal
11
+
12
+
13
+ @define
14
+ class InsuranceFundWithdraw(Event):
15
+ """
16
+ Defines an InsuranceFundWithdraw Update
17
+
18
+ An InsuranceFundWithdraw is when a withdrawal of collateral is **signaled**.
19
+
20
+ Attributes:
21
+ recipient_address (str): Trader address DDX is being withdrawn to
22
+ amount (Decimal): The amount of DDX being withdrawn
23
+ request_index (int): Sequenced request index of transaction
24
+ """
25
+
26
+ recipient_address: str = field(eq=str.lower)
27
+ currency: str = field(eq=str.lower)
28
+ amount: Decimal
29
+ request_index: int = field(default=-1, eq=False)
30
+
31
+ @classmethod
32
+ def decode_value_into_cls(cls, raw_tx_log_event: dict):
33
+ """
34
+ Decode a raw transaction log event (dict) into a Withdraw
35
+ instance.
36
+
37
+ Parameters
38
+ ----------
39
+ raw_tx_log_event : dict
40
+ Raw transaction log event being processed
41
+ """
42
+
43
+ insurance_fund_withdraw_tx_event = raw_tx_log_event["event"]["c"]
44
+
45
+ return cls(
46
+ insurance_fund_withdraw_tx_event["recipientAddress"],
47
+ insurance_fund_withdraw_tx_event["currency"],
48
+ Decimal(insurance_fund_withdraw_tx_event["amount"]),
49
+ raw_tx_log_event["requestIndex"],
50
+ )
51
+
52
+ def process_tx(
53
+ self,
54
+ smt: DerivadexSMT,
55
+ **kwargs,
56
+ ):
57
+ """
58
+ Process a Withdraw transaction. A Withdraw event consists
59
+ of consists of information relating to withdrawal of collateral.
60
+
61
+ Parameters
62
+ ----------
63
+ smt: DerivadexSMT
64
+ DerivaDEX Sparse Merkle Tree
65
+ **kwargs
66
+ Additional args specific to InsuranceFundWithdraw transactions
67
+ """
68
+
69
+ insurance_fund_key: InsuranceFundKey = InsuranceFundKey()
70
+ insurance_fund = smt.insurance_fund(insurance_fund_key)
71
+
72
+ contributor_key: InsuranceFundContributionKey = InsuranceFundContributionKey(
73
+ self.recipient_address
74
+ )
75
+ contributor: InsuranceFundContribution = smt.insurance_fund_contribution(
76
+ contributor_key
77
+ )
78
+
79
+ # Decrement the free balance by the withdrawn amount
80
+ symbol = TokenSymbol.from_address(self.currency)
81
+ contributor.set_avail_balance(
82
+ symbol,
83
+ contributor.avail_balance[symbol] - self.amount,
84
+ )
85
+
86
+ # Increment the frozen balance by the withdrawn amount
87
+ contributor.set_locked_balance(
88
+ symbol,
89
+ contributor.locked_balance[symbol] + self.amount,
90
+ )
91
+
92
+ # Update the SMT with the H256 repr of the key and
93
+ # the InsuranceFundContribution leaf for the signer
94
+ smt.store_insurance_fund_contribution(contributor_key, contributor)
95
+
96
+ insurance_fund[symbol] -= self.amount
97
+
98
+ # Update the SMT with the H256 repr of the key and the
99
+ # Strategy leaf
100
+ smt.store_insurance_fund(insurance_fund_key, insurance_fund)