ddx-python 1.0.5__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 (104) hide show
  1. ddx/.gitignore +1 -0
  2. ddx/__init__.py +58 -0
  3. ddx/_rust/__init__.pyi +2009 -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 +21 -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 +541 -0
  17. ddx/auditor/README.md +32 -0
  18. ddx/auditor/__init__.py +0 -0
  19. ddx/auditor/auditor_driver.py +1034 -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 +144 -0
  24. ddx/common/item_utils.py +38 -0
  25. ddx/common/logging.py +184 -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 +97 -0
  37. ddx/common/transactions/event.py +48 -0
  38. ddx/common/transactions/fee_distribution.py +119 -0
  39. ddx/common/transactions/funding.py +294 -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 +227 -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 +125 -0
  50. ddx/common/transactions/insurance_fund_update.py +142 -0
  51. ddx/common/transactions/insurance_fund_withdraw.py +99 -0
  52. ddx/common/transactions/liquidation.py +357 -0
  53. ddx/common/transactions/partial_fill.py +125 -0
  54. ddx/common/transactions/pnl_realization.py +122 -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 +96 -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 +156 -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 +105 -0
  64. ddx/common/transactions/withdraw.py +91 -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 +254 -0
  69. ddx/py.typed +0 -0
  70. ddx/realtime_client/__init__.py +2 -0
  71. ddx/realtime_client/config.py +2 -0
  72. ddx/realtime_client/logs/pytest.log +0 -0
  73. ddx/realtime_client/models/__init__.py +683 -0
  74. ddx/realtime_client/realtime_client.py +567 -0
  75. ddx/rest_client/__init__.py +0 -0
  76. ddx/rest_client/clients/__init__.py +0 -0
  77. ddx/rest_client/clients/base_client.py +60 -0
  78. ddx/rest_client/clients/market_client.py +1241 -0
  79. ddx/rest_client/clients/on_chain_client.py +432 -0
  80. ddx/rest_client/clients/signed_client.py +301 -0
  81. ddx/rest_client/clients/system_client.py +843 -0
  82. ddx/rest_client/clients/trade_client.py +335 -0
  83. ddx/rest_client/constants/__init__.py +0 -0
  84. ddx/rest_client/constants/endpoints.py +67 -0
  85. ddx/rest_client/contracts/__init__.py +0 -0
  86. ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
  87. ddx/rest_client/contracts/ddx/__init__.py +1949 -0
  88. ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
  89. ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
  90. ddx/rest_client/contracts/i_stake/__init__.py +696 -0
  91. ddx/rest_client/exceptions/__init__.py +0 -0
  92. ddx/rest_client/exceptions/exceptions.py +32 -0
  93. ddx/rest_client/http/__init__.py +0 -0
  94. ddx/rest_client/http/http_client.py +305 -0
  95. ddx/rest_client/models/__init__.py +0 -0
  96. ddx/rest_client/models/market.py +683 -0
  97. ddx/rest_client/models/signed.py +60 -0
  98. ddx/rest_client/models/system.py +390 -0
  99. ddx/rest_client/models/trade.py +140 -0
  100. ddx/rest_client/utils/__init__.py +0 -0
  101. ddx/rest_client/utils/encryption_utils.py +26 -0
  102. ddx_python-1.0.5.dist-info/METADATA +63 -0
  103. ddx_python-1.0.5.dist-info/RECORD +104 -0
  104. ddx_python-1.0.5.dist-info/WHEEL +4 -0
@@ -0,0 +1,432 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Optional
4
+ from eth_account.signers.local import LocalAccount
5
+ from web3 import Web3
6
+ from web3.types import TxReceipt
7
+ from zero_ex.contract_wrappers import TxParams
8
+ from eth_abi.utils.padding import zpad32_right
9
+
10
+ from ddx._rust.decimal import Decimal
11
+ from ddx._rust.common.state.keys import StrategyKey
12
+ from ddx.common.utils import to_base_unit_amount_list, to_base_unit_amount
13
+ from ddx.rest_client.clients.base_client import BaseClient
14
+ from ddx.rest_client.contracts.i_collateral import ICollateral
15
+ from ddx.rest_client.contracts.ddx import DDX
16
+ from ddx.rest_client.contracts.dummy_token import DummyToken
17
+ from ddx.rest_client.contracts.checkpoint import Checkpoint
18
+ from ddx.rest_client.contracts.i_stake import IStake
19
+ from ddx.rest_client.constants.endpoints import OnChain
20
+ from ddx.rest_client.http.http_client import HTTPClient
21
+
22
+ COLLATERAL_DECIMALS = 6
23
+ DDX_DECIMALS = 18
24
+ DEFAULT_GAS_LIMIT = 500_000
25
+
26
+
27
+ class OnChainClient(BaseClient):
28
+ """
29
+ On-chain operations for depositing, withdrawing, and other activities.
30
+
31
+ Parameters
32
+ ----------
33
+ http : HTTPClient
34
+ The HTTP client instance to use for requests
35
+ base_url : str
36
+ The base URL for trading endpoints
37
+ web3_account : Any
38
+ The web3 account for signing requests
39
+ verifying_contract : str
40
+ The contract address
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ http: HTTPClient,
46
+ base_url: str,
47
+ web3_account: LocalAccount,
48
+ web3: Web3,
49
+ verifying_contract: str,
50
+ ):
51
+ super().__init__(http, base_url)
52
+ self._web3_account = web3_account
53
+ self._web3 = web3
54
+ self._verifying_contract = verifying_contract
55
+ self._logger = logging.getLogger(__name__)
56
+
57
+ def _send_transaction(
58
+ self,
59
+ contract_method,
60
+ method_params: list,
61
+ nonce: Optional[int] = None,
62
+ gas: int = 500_000,
63
+ local_account: Optional[LocalAccount] = None,
64
+ ) -> TxReceipt:
65
+ """
66
+ Helper to build, sign and send a transaction.
67
+
68
+ Parameters
69
+ ----------
70
+ contract_method : ContractMethod
71
+ The contract method object
72
+ method_params : list
73
+ Parameters to pass to the contract method
74
+ nonce : Optional[int]
75
+ Custom nonce or None for auto
76
+ gas : int
77
+ Gas limit for transaction
78
+ local_account : Optional[LocalAccount]
79
+ Local account to sign with, defaults to client's account
80
+
81
+ Returns
82
+ -------
83
+ TxReceipt
84
+ Transaction receipt
85
+ """
86
+
87
+ account = local_account or self._web3_account
88
+
89
+ if nonce is None:
90
+ nonce = self._web3.eth.get_transaction_count(account.address)
91
+
92
+ tx = contract_method.build_transaction(
93
+ *method_params,
94
+ tx_params=TxParams(from_=account.address, nonce=nonce, gas=gas),
95
+ )
96
+ signed_tx = self._web3.eth.account.sign_transaction(tx, private_key=account.key)
97
+ tx_hash = self._web3.eth.send_raw_transaction(signed_tx.rawTransaction)
98
+ receipt = self._web3.eth.wait_for_transaction_receipt(tx_hash, poll_latency=0.5)
99
+
100
+ return receipt
101
+
102
+ def approve(
103
+ self,
104
+ collateral_address: str,
105
+ amount: Decimal,
106
+ nonce: Optional[int] = None,
107
+ local_account: Optional[LocalAccount] = None,
108
+ ) -> TxReceipt:
109
+ """
110
+ Approve ERC-20 collateral for transfer to the DerivaDEX contract.
111
+
112
+ Parameters
113
+ ----------
114
+ collateral_address : str
115
+ The token contract address to approve
116
+ amount : Decimal
117
+ Amount to approve for transfer
118
+ nonce : Optional[int]
119
+ Custom nonce for the transaction, if None uses next available
120
+ local_account : Optional[LocalAccount]
121
+ Local account to sign with, defaults to client's account
122
+
123
+ Returns
124
+ -------
125
+ TxReceipt
126
+ Transaction receipt
127
+ """
128
+
129
+ dummy_token = DummyToken(self._web3, collateral_address)
130
+ approve_amount = to_base_unit_amount(amount, COLLATERAL_DECIMALS)
131
+
132
+ return self._send_transaction(
133
+ dummy_token.approve,
134
+ method_params=[self._verifying_contract, approve_amount],
135
+ nonce=nonce,
136
+ local_account=local_account,
137
+ )
138
+
139
+ async def deposit(
140
+ self,
141
+ collateral_address: str,
142
+ strategy_id: str,
143
+ amount: Decimal,
144
+ nonce: Optional[int] = None,
145
+ local_account: Optional[LocalAccount] = None,
146
+ ) -> TxReceipt:
147
+ """
148
+ Deposit ERC-20 collateral into a strategy.
149
+
150
+ Parameters
151
+ ----------
152
+ collateral_address : str
153
+ The token contract address to deposit
154
+ strategy_id : str
155
+ Strategy identifier to deposit into
156
+ amount : Decimal
157
+ Amount to deposit
158
+ nonce : Optional[int]
159
+ Custom nonce for the transaction, if None uses next available
160
+ local_account : Optional[LocalAccount]
161
+ Local account to sign with, defaults to client's account
162
+
163
+ Returns
164
+ -------
165
+ TxReceipt
166
+ Transaction receipt
167
+ """
168
+
169
+ account = local_account or self._web3_account
170
+ kyc_auth = await self._http.get(
171
+ self._build_url(OnChain.KYC_AUTH), params={"trader": account.address}
172
+ )
173
+
174
+ encoded_strategy = zpad32_right(
175
+ len(strategy_id).to_bytes(1, byteorder="little")
176
+ + strategy_id.encode("utf8")
177
+ )
178
+ deposit_amount = to_base_unit_amount(amount, COLLATERAL_DECIMALS)
179
+ i_collateral_contract = ICollateral(self._web3, self._verifying_contract)
180
+
181
+ return self._send_transaction(
182
+ i_collateral_contract.deposit,
183
+ method_params=[
184
+ collateral_address,
185
+ encoded_strategy,
186
+ deposit_amount,
187
+ kyc_auth["kycAuth"]["expiryBlock"],
188
+ kyc_auth["signature"],
189
+ ],
190
+ nonce=nonce,
191
+ gas=DEFAULT_GAS_LIMIT,
192
+ local_account=local_account,
193
+ )
194
+
195
+ def approve_ddx(
196
+ self,
197
+ ddx_address: str,
198
+ amount: Decimal,
199
+ nonce: Optional[int] = None,
200
+ local_account: Optional[LocalAccount] = None,
201
+ ) -> TxReceipt:
202
+ """
203
+ Approve DDX for transfer to the DerivaDEX contract.
204
+
205
+ Parameters
206
+ ----------
207
+ ddx_address : str
208
+ The DDX token contract address
209
+ amount : Decimal
210
+ Amount to approve for transfer
211
+ nonce : Optional[int]
212
+ Custom nonce for the transaction, if None uses next available
213
+ local_account : Optional[LocalAccount]
214
+ Local account to sign with, defaults to client's account
215
+
216
+ Returns
217
+ -------
218
+ TxReceipt
219
+ Transaction receipt
220
+ """
221
+
222
+ ddx_contract = DDX(self._web3, ddx_address)
223
+ approve_amount = to_base_unit_amount(amount, DDX_DECIMALS)
224
+
225
+ return self._send_transaction(
226
+ ddx_contract.approve,
227
+ method_params=[self._verifying_contract, approve_amount],
228
+ nonce=nonce,
229
+ local_account=local_account,
230
+ )
231
+
232
+ async def deposit_ddx(
233
+ self,
234
+ amount: Decimal,
235
+ nonce: Optional[int] = None,
236
+ local_account: Optional[LocalAccount] = None,
237
+ ) -> TxReceipt:
238
+ """
239
+ Deposit DDX to exchange.
240
+
241
+ Parameters
242
+ ----------
243
+ amount : Decimal
244
+ Amount of DDX to deposit
245
+ nonce : Optional[int]
246
+ Custom nonce for the transaction, if None uses next available
247
+ local_account : Optional[LocalAccount]
248
+ Local account to sign with, defaults to client's account
249
+
250
+ Returns
251
+ -------
252
+ TxReceipt
253
+ Transaction receipt
254
+ """
255
+
256
+ account = local_account or self._web3_account
257
+ kyc_auth = await self._http.get(
258
+ self._build_url(OnChain.KYC_AUTH), params={"trader": account.address}
259
+ )
260
+
261
+ deposit_amount = to_base_unit_amount(amount, DDX_DECIMALS)
262
+ i_stake_contract = IStake(self._web3, self._verifying_contract)
263
+
264
+ return self._send_transaction(
265
+ i_stake_contract.deposit_ddx,
266
+ method_params=[
267
+ deposit_amount,
268
+ kyc_auth["kycAuth"]["expiryBlock"],
269
+ kyc_auth["signature"],
270
+ ],
271
+ nonce=nonce,
272
+ gas=DEFAULT_GAS_LIMIT,
273
+ local_account=local_account,
274
+ )
275
+
276
+ async def withdraw(
277
+ self,
278
+ collateral_address: str,
279
+ strategy_id: str,
280
+ amount: Decimal,
281
+ nonce: Optional[int] = None,
282
+ local_account: Optional[LocalAccount] = None,
283
+ ) -> TxReceipt:
284
+ """
285
+ Withdraw collateral from a strategy.
286
+
287
+ Parameters
288
+ ----------
289
+ collateral_address : str
290
+ The token contract address to withdraw
291
+ strategy_id : str
292
+ Strategy identifier to withdraw from
293
+ amount : Decimal
294
+ Amount to deposit
295
+ nonce : Optional[int]
296
+ Custom nonce for the transaction, if None uses next available
297
+ local_account : Optional[LocalAccount]
298
+ Local account to sign with, defaults to client's account
299
+
300
+ Returns
301
+ -------
302
+ TxReceipt
303
+ Transaction receipt
304
+ """
305
+
306
+ account = local_account or self._web3_account
307
+
308
+ # Get checkpointed epoch id
309
+ checkpoint_contract = Checkpoint(self._web3, self._verifying_contract)
310
+ checkpointed_epoch_id = checkpoint_contract.get_latest_checkpoint.call()[3]
311
+
312
+ # Get withdrawal proof
313
+ strategy_key = StrategyKey(
314
+ f"0x00{account.address[2:]}",
315
+ StrategyKey.generate_strategy_id_hash(strategy_id),
316
+ )
317
+
318
+ proof_response = await self._http.get(
319
+ self._build_url(OnChain.PROOF),
320
+ params={
321
+ "key": str(strategy_key.encode_key()),
322
+ "epochId": checkpointed_epoch_id,
323
+ },
324
+ )
325
+
326
+ collateral_address = self._web3.to_checksum_address(collateral_address)
327
+
328
+ # Prepare strategy data
329
+ checkpointed_strategy = {
330
+ "maxLeverage": 3,
331
+ "frozen": False,
332
+ "availCollateral": {
333
+ "tokens": [
334
+ self._web3.to_checksum_address(key)
335
+ for key in list(
336
+ proof_response["item"]["Strategy"]["availCollateral"].keys()
337
+ )
338
+ ],
339
+ "amounts": to_base_unit_amount_list(
340
+ [
341
+ Decimal(val)
342
+ for val in list(
343
+ proof_response["item"]["Strategy"][
344
+ "availCollateral"
345
+ ].values()
346
+ )
347
+ ],
348
+ 6,
349
+ ),
350
+ },
351
+ "lockedCollateral": {
352
+ "tokens": [
353
+ self._web3.to_checksum_address(key)
354
+ for key in list(
355
+ proof_response["item"]["Strategy"]["lockedCollateral"].keys()
356
+ )
357
+ ],
358
+ "amounts": to_base_unit_amount_list(
359
+ [
360
+ Decimal(val)
361
+ for val in list(
362
+ proof_response["item"]["Strategy"][
363
+ "lockedCollateral"
364
+ ].values()
365
+ )
366
+ ],
367
+ 6,
368
+ ),
369
+ },
370
+ }
371
+
372
+ withdraw_data = {
373
+ "tokens": [collateral_address],
374
+ "amounts": [int(amount * Decimal("1e6"))],
375
+ }
376
+
377
+ # Execute withdrawal
378
+ i_collateral_contract = ICollateral(self._web3, self._verifying_contract)
379
+ return self._send_transaction(
380
+ i_collateral_contract.withdraw,
381
+ method_params=[
382
+ StrategyKey.generate_strategy_id_hash(strategy_id),
383
+ withdraw_data,
384
+ checkpointed_strategy,
385
+ f'0x{bytes(proof_response["proof"]).hex()}',
386
+ ],
387
+ nonce=nonce,
388
+ local_account=local_account,
389
+ )
390
+
391
+ async def wait_for_confirmations(
392
+ self,
393
+ tx_receipt: TxReceipt,
394
+ confirmations: int = 6,
395
+ check_interval: float = 1.0,
396
+ ) -> None:
397
+ """
398
+ Wait for required number of block confirmations.
399
+
400
+ Parameters
401
+ ----------
402
+ tx_receipt : TxReceipt
403
+ Transaction receipt containing block number
404
+ confirmations : int, default=6
405
+ Number of block confirmations to wait for
406
+ check_interval : float, default=1.0
407
+ How often to check for new blocks in seconds
408
+
409
+ Raises
410
+ ------
411
+ TimeoutError
412
+ If confirmations don't arrive within reasonable time
413
+ """
414
+
415
+ tx_block_number = tx_receipt.blockNumber
416
+ timeout = (confirmations * 2) * 12
417
+ start_time = asyncio.get_event_loop().time()
418
+
419
+ while True:
420
+ current_block = self._web3.eth.block_number
421
+ confirmed_blocks = current_block - tx_block_number
422
+
423
+ if confirmed_blocks >= confirmations:
424
+ return
425
+
426
+ if asyncio.get_event_loop().time() - start_time > timeout:
427
+ raise TimeoutError(
428
+ f"Timeout waiting for {confirmations} confirmations. "
429
+ f"Got {confirmed_blocks} after {timeout} seconds"
430
+ )
431
+
432
+ await asyncio.sleep(check_interval)