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.
- ddx/.gitignore +1 -0
- ddx/__init__.py +58 -0
- ddx/_rust/__init__.pyi +2685 -0
- ddx/_rust/common/__init__.pyi +17 -0
- ddx/_rust/common/accounting.pyi +6 -0
- ddx/_rust/common/enums.pyi +3 -0
- ddx/_rust/common/requests/__init__.pyi +23 -0
- ddx/_rust/common/requests/intents.pyi +19 -0
- ddx/_rust/common/specs.pyi +17 -0
- ddx/_rust/common/state/__init__.pyi +41 -0
- ddx/_rust/common/state/keys.pyi +29 -0
- ddx/_rust/common/transactions.pyi +7 -0
- ddx/_rust/decimal.pyi +3 -0
- ddx/_rust/h256.pyi +3 -0
- ddx/_rust.abi3.so +0 -0
- ddx/app_config/ethereum/addresses.json +526 -0
- ddx/auditor/README.md +32 -0
- ddx/auditor/__init__.py +0 -0
- ddx/auditor/auditor_driver.py +1043 -0
- ddx/auditor/websocket_message.py +54 -0
- ddx/common/__init__.py +0 -0
- ddx/common/epoch_params.py +28 -0
- ddx/common/fill_context.py +141 -0
- ddx/common/logging.py +184 -0
- ddx/common/market_aware_account.py +259 -0
- ddx/common/market_specs.py +64 -0
- ddx/common/trade_mining_params.py +19 -0
- ddx/common/transaction_utils.py +85 -0
- ddx/common/transactions/__init__.py +0 -0
- ddx/common/transactions/advance_epoch.py +91 -0
- ddx/common/transactions/advance_settlement_epoch.py +63 -0
- ddx/common/transactions/all_price_checkpoints.py +84 -0
- ddx/common/transactions/cancel.py +76 -0
- ddx/common/transactions/cancel_all.py +88 -0
- ddx/common/transactions/complete_fill.py +103 -0
- ddx/common/transactions/disaster_recovery.py +96 -0
- ddx/common/transactions/event.py +48 -0
- ddx/common/transactions/fee_distribution.py +119 -0
- ddx/common/transactions/funding.py +292 -0
- ddx/common/transactions/futures_expiry.py +123 -0
- ddx/common/transactions/genesis.py +108 -0
- ddx/common/transactions/inner/__init__.py +0 -0
- ddx/common/transactions/inner/adl_outcome.py +25 -0
- ddx/common/transactions/inner/fill.py +232 -0
- ddx/common/transactions/inner/liquidated_position.py +41 -0
- ddx/common/transactions/inner/liquidation_entry.py +41 -0
- ddx/common/transactions/inner/liquidation_fill.py +118 -0
- ddx/common/transactions/inner/outcome.py +32 -0
- ddx/common/transactions/inner/trade_fill.py +292 -0
- ddx/common/transactions/insurance_fund_update.py +138 -0
- ddx/common/transactions/insurance_fund_withdraw.py +100 -0
- ddx/common/transactions/liquidation.py +353 -0
- ddx/common/transactions/partial_fill.py +125 -0
- ddx/common/transactions/pnl_realization.py +120 -0
- ddx/common/transactions/post.py +72 -0
- ddx/common/transactions/post_order.py +95 -0
- ddx/common/transactions/price_checkpoint.py +97 -0
- ddx/common/transactions/signer_registered.py +62 -0
- ddx/common/transactions/specs_update.py +61 -0
- ddx/common/transactions/strategy_update.py +158 -0
- ddx/common/transactions/tradable_product_update.py +98 -0
- ddx/common/transactions/trade_mining.py +147 -0
- ddx/common/transactions/trader_update.py +131 -0
- ddx/common/transactions/withdraw.py +90 -0
- ddx/common/transactions/withdraw_ddx.py +74 -0
- ddx/common/utils.py +176 -0
- ddx/config.py +17 -0
- ddx/derivadex_client.py +270 -0
- ddx/models/__init__.py +0 -0
- ddx/models/base.py +132 -0
- ddx/py.typed +0 -0
- ddx/realtime_client/__init__.py +2 -0
- ddx/realtime_client/config.py +2 -0
- ddx/realtime_client/models/__init__.py +611 -0
- ddx/realtime_client/realtime_client.py +646 -0
- ddx/rest_client/__init__.py +0 -0
- ddx/rest_client/clients/__init__.py +0 -0
- ddx/rest_client/clients/base_client.py +60 -0
- ddx/rest_client/clients/market_client.py +1243 -0
- ddx/rest_client/clients/on_chain_client.py +439 -0
- ddx/rest_client/clients/signed_client.py +292 -0
- ddx/rest_client/clients/system_client.py +843 -0
- ddx/rest_client/clients/trade_client.py +357 -0
- ddx/rest_client/constants/__init__.py +0 -0
- ddx/rest_client/constants/endpoints.py +66 -0
- ddx/rest_client/contracts/__init__.py +0 -0
- ddx/rest_client/contracts/checkpoint/__init__.py +560 -0
- ddx/rest_client/contracts/ddx/__init__.py +1949 -0
- ddx/rest_client/contracts/dummy_token/__init__.py +1014 -0
- ddx/rest_client/contracts/i_collateral/__init__.py +1414 -0
- ddx/rest_client/contracts/i_stake/__init__.py +696 -0
- ddx/rest_client/exceptions/__init__.py +0 -0
- ddx/rest_client/exceptions/exceptions.py +32 -0
- ddx/rest_client/http/__init__.py +0 -0
- ddx/rest_client/http/http_client.py +336 -0
- ddx/rest_client/models/__init__.py +0 -0
- ddx/rest_client/models/market.py +693 -0
- ddx/rest_client/models/signed.py +61 -0
- ddx/rest_client/models/system.py +311 -0
- ddx/rest_client/models/trade.py +185 -0
- ddx/rest_client/utils/__init__.py +0 -0
- ddx/rest_client/utils/encryption_utils.py +26 -0
- ddx/utils/__init__.py +0 -0
- ddx_python-1.0.4.dist-info/METADATA +63 -0
- ddx_python-1.0.4.dist-info/RECORD +106 -0
- ddx_python-1.0.4.dist-info/WHEEL +5 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import Optional, Dict, Union, Any, Literal
|
|
2
|
+
from ddx.models.base import CamelModel, HexStr
|
|
3
|
+
from pydantic import Field, field_validator, ConfigDict
|
|
4
|
+
from ddx._rust.common.requests import SafetyFailure
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SequencedReceiptContent(CamelModel):
|
|
8
|
+
"""Content of a successful sequenced receipt."""
|
|
9
|
+
|
|
10
|
+
nonce: HexStr
|
|
11
|
+
request_hash: HexStr
|
|
12
|
+
request_index: int = Field(..., ge=0)
|
|
13
|
+
sender: HexStr
|
|
14
|
+
enclave_signature: HexStr
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ErrorReceiptContent(CamelModel):
|
|
18
|
+
"""Content of an error receipt."""
|
|
19
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
20
|
+
|
|
21
|
+
message: str
|
|
22
|
+
inner: SafetyFailure
|
|
23
|
+
|
|
24
|
+
@field_validator("inner", mode="before")
|
|
25
|
+
def _parse_safety_failure(cls, v):
|
|
26
|
+
if isinstance(v, SafetyFailure):
|
|
27
|
+
return v
|
|
28
|
+
return SafetyFailure[v] if isinstance(v, str) else SafetyFailure(v)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TradeReceipt(CamelModel):
|
|
32
|
+
"""Model for trade request receipts."""
|
|
33
|
+
|
|
34
|
+
t: Literal["Sequenced", "SafetyFailure"]
|
|
35
|
+
c: Union[SequencedReceiptContent, ErrorReceiptContent, Dict[str, Any]]
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def is_success(self) -> bool:
|
|
39
|
+
"""Check if the receipt indicates success."""
|
|
40
|
+
|
|
41
|
+
return self.t == "Sequenced"
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def is_error(self) -> bool:
|
|
45
|
+
"""Check if the receipt indicates an error."""
|
|
46
|
+
|
|
47
|
+
return self.t in ("Error", "SafetyFailure")
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def error_message(self) -> Optional[str]:
|
|
51
|
+
"""Get the error message if this is an error receipt."""
|
|
52
|
+
|
|
53
|
+
if self.is_error and isinstance(self.c, ErrorReceiptContent):
|
|
54
|
+
return self.c.message
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def error(self) -> Optional[SafetyFailure]:
|
|
59
|
+
if self.is_error and isinstance(self.c, ErrorReceiptContent):
|
|
60
|
+
return self.c.inner
|
|
61
|
+
return None
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from pydantic import Field, ConfigDict, field_validator
|
|
4
|
+
from ddx.models.base import CamelModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SettlementInfo(CamelModel):
|
|
8
|
+
"""Settlement information model."""
|
|
9
|
+
|
|
10
|
+
type: str
|
|
11
|
+
duration_value: str
|
|
12
|
+
duration_unit: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SymbolInfo(CamelModel):
|
|
16
|
+
"""Symbol information model."""
|
|
17
|
+
|
|
18
|
+
symbol: str
|
|
19
|
+
tick_size: str
|
|
20
|
+
max_order_notional: str
|
|
21
|
+
max_taker_price_deviation: str
|
|
22
|
+
min_order_size: str
|
|
23
|
+
kind: str # SingleNamePerpetual, IndexFundPerpetual, FixedExpiryFuture
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ExchangeInfo(CamelModel):
|
|
27
|
+
"""Exchange information model."""
|
|
28
|
+
|
|
29
|
+
settlements_info: List[SettlementInfo]
|
|
30
|
+
assets: List[str]
|
|
31
|
+
symbols: List[SymbolInfo]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ExchangeInfoResponse(CamelModel):
|
|
35
|
+
"""Response model for exchange info endpoint."""
|
|
36
|
+
|
|
37
|
+
value: ExchangeInfo
|
|
38
|
+
success: bool
|
|
39
|
+
timestamp: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PingResponse(CamelModel):
|
|
43
|
+
"""Response model for ping endpoint."""
|
|
44
|
+
|
|
45
|
+
model_config = CamelModel.model_config | ConfigDict(extra="allow")
|
|
46
|
+
|
|
47
|
+
# The ping endpoint returns an empty object on success
|
|
48
|
+
# We'll allow extra fields in case the response changes in the future
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Symbol(CamelModel):
|
|
52
|
+
"""Tradable product symbol model."""
|
|
53
|
+
|
|
54
|
+
kind: int # 0: Perpetual Market, 2: Index Market, 4: Futures Market
|
|
55
|
+
symbol: str
|
|
56
|
+
name: str
|
|
57
|
+
is_active: bool
|
|
58
|
+
created_at: datetime
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SymbolsResponse(CamelModel):
|
|
62
|
+
"""Response model for symbols endpoint."""
|
|
63
|
+
|
|
64
|
+
value: List[Symbol]
|
|
65
|
+
success: bool
|
|
66
|
+
timestamp: int
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ServerTimeResponse(CamelModel):
|
|
70
|
+
"""Response model for server time endpoint."""
|
|
71
|
+
|
|
72
|
+
server_time: int
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class Epoch(CamelModel):
|
|
76
|
+
"""Epoch data model."""
|
|
77
|
+
|
|
78
|
+
epoch_id: int
|
|
79
|
+
start_time: datetime
|
|
80
|
+
end_time: Optional[datetime] = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class EpochHistoryResponse(CamelModel):
|
|
84
|
+
"""Response model for epoch history endpoint."""
|
|
85
|
+
|
|
86
|
+
value: List[Epoch]
|
|
87
|
+
success: bool
|
|
88
|
+
timestamp: int
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class InsuranceFund(CamelModel):
|
|
92
|
+
"""Insurance fund update data model."""
|
|
93
|
+
|
|
94
|
+
epoch_id: int
|
|
95
|
+
tx_ordinal: int
|
|
96
|
+
symbol: str
|
|
97
|
+
total_capitalization: Optional[str] = None
|
|
98
|
+
kind: int # 0: fill, 1: liquidation, 2: deposit, 3: withdrawal
|
|
99
|
+
created_at: datetime
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InsuranceFundHistoryResponse(CamelModel):
|
|
103
|
+
"""Response model for insurance fund history endpoint with cursor pagination."""
|
|
104
|
+
|
|
105
|
+
value: List[InsuranceFund]
|
|
106
|
+
next_epoch: Optional[int] = Field(None, ge=0)
|
|
107
|
+
next_tx_ordinal: Optional[int] = Field(None, ge=0)
|
|
108
|
+
next_ordinal: Optional[int] = Field(None, ge=0)
|
|
109
|
+
success: bool
|
|
110
|
+
timestamp: int
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SpecValue(CamelModel):
|
|
114
|
+
"""Spec value model."""
|
|
115
|
+
|
|
116
|
+
model_config = CamelModel.model_config | ConfigDict(extra="allow")
|
|
117
|
+
|
|
118
|
+
# This is a flexible model that can contain various fields
|
|
119
|
+
# based on the kind of spec
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class Spec(CamelModel):
|
|
123
|
+
"""Spec data model."""
|
|
124
|
+
|
|
125
|
+
kind: int = Field(..., description="0: Market, 1: SpotGateway")
|
|
126
|
+
name: str
|
|
127
|
+
expr: str
|
|
128
|
+
value: Optional[SpecValue] = None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SpecsResponse(CamelModel):
|
|
132
|
+
"""Response model for specs endpoint."""
|
|
133
|
+
|
|
134
|
+
value: List[Spec]
|
|
135
|
+
success: bool
|
|
136
|
+
timestamp: int
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class OnChainCheckpoint(CamelModel):
|
|
140
|
+
"""On-chain checkpoint information model."""
|
|
141
|
+
|
|
142
|
+
latest_on_chain_checkpoint: int
|
|
143
|
+
latest_checkpoint_transaction_link: Optional[str] = None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ExchangeStatus(CamelModel):
|
|
147
|
+
"""Exchange status information model."""
|
|
148
|
+
|
|
149
|
+
current_epoch: str
|
|
150
|
+
latest_on_chain_checkpoint: Optional[OnChainCheckpoint] = None
|
|
151
|
+
active_addresses: str
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ExchangeStatusResponse(CamelModel):
|
|
155
|
+
"""Response model for exchange status endpoint."""
|
|
156
|
+
|
|
157
|
+
value: ExchangeStatus
|
|
158
|
+
success: bool
|
|
159
|
+
timestamp: int
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class DDXSupply(CamelModel):
|
|
163
|
+
"""DDX supply information model."""
|
|
164
|
+
|
|
165
|
+
circulating_supply: str
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class DDXSupplyResponse(CamelModel):
|
|
169
|
+
"""Response model for DDX supply endpoint."""
|
|
170
|
+
|
|
171
|
+
value: DDXSupply
|
|
172
|
+
success: bool
|
|
173
|
+
timestamp: int
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TradableProduct(CamelModel):
|
|
177
|
+
"""Tradable product data model."""
|
|
178
|
+
|
|
179
|
+
kind: int # 0: Perpetual Market, 2: Index Market, 4: Futures Market
|
|
180
|
+
symbol: str
|
|
181
|
+
name: str
|
|
182
|
+
is_active: bool
|
|
183
|
+
created_at: datetime
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class TradableProductsResponse(CamelModel):
|
|
187
|
+
"""Response model for tradable products endpoint."""
|
|
188
|
+
|
|
189
|
+
value: List[TradableProduct]
|
|
190
|
+
success: bool
|
|
191
|
+
timestamp: int
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class CollateralAggregation(CamelModel):
|
|
195
|
+
"""Collateral aggregation data model."""
|
|
196
|
+
|
|
197
|
+
model_config = CamelModel.model_config | ConfigDict(extra="allow")
|
|
198
|
+
|
|
199
|
+
timestamp: int
|
|
200
|
+
# Dynamic fields will be handled with extra='allow'
|
|
201
|
+
# Fields like collateral_deposits, collateral_withdrawals, etc.
|
|
202
|
+
|
|
203
|
+
# Add method to access dynamic fields
|
|
204
|
+
def get_value(self, field_name: str) -> Optional[str]:
|
|
205
|
+
"""Get value for a specific collateral field."""
|
|
206
|
+
return getattr(self, f"collateral_{field_name}", None)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class CollateralAggregationResponse(CamelModel):
|
|
210
|
+
"""Response model for collateral aggregation endpoint."""
|
|
211
|
+
|
|
212
|
+
value: List[CollateralAggregation]
|
|
213
|
+
next_starting_value: Optional[str] = None
|
|
214
|
+
success: bool
|
|
215
|
+
timestamp: int
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class DDXAggregation(CamelModel):
|
|
219
|
+
"""DDX aggregation data model."""
|
|
220
|
+
|
|
221
|
+
model_config = CamelModel.model_config | ConfigDict(extra="allow")
|
|
222
|
+
|
|
223
|
+
timestamp: int
|
|
224
|
+
# Dynamic fields will be available through extra="allow"
|
|
225
|
+
# Fields like ddx_deposits, ddx_withdrawals, etc.
|
|
226
|
+
|
|
227
|
+
# Add method to get DDX value by type
|
|
228
|
+
def get_ddx_value(self, value_type: str) -> Optional[str]:
|
|
229
|
+
"""Get DDX value for a specific type."""
|
|
230
|
+
return getattr(self, f"ddx_{value_type}", None)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class DDXAggregationResponse(CamelModel):
|
|
234
|
+
"""Response model for DDX aggregation endpoint."""
|
|
235
|
+
|
|
236
|
+
value: List[DDXAggregation]
|
|
237
|
+
next_starting_value: Optional[str] = None
|
|
238
|
+
success: bool
|
|
239
|
+
timestamp: int
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class InsuranceFundAggregation(CamelModel):
|
|
243
|
+
"""Insurance fund aggregation data model."""
|
|
244
|
+
|
|
245
|
+
model_config = CamelModel.model_config | ConfigDict(extra="allow")
|
|
246
|
+
|
|
247
|
+
timestamp: int
|
|
248
|
+
# Dynamic fields will be handled with extra='allow'
|
|
249
|
+
# Fields like insurance-fund_fees, insurance-fund_overall, etc.
|
|
250
|
+
|
|
251
|
+
def get_value(self, field: str) -> Optional[str]:
|
|
252
|
+
"""Get a specific field value."""
|
|
253
|
+
return getattr(self, field, None)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class InsuranceFundAggregationResponse(CamelModel):
|
|
257
|
+
"""Response model for insurance fund aggregation endpoint."""
|
|
258
|
+
|
|
259
|
+
value: List[InsuranceFundAggregation]
|
|
260
|
+
next_starting_value: Optional[str] = None
|
|
261
|
+
success: bool
|
|
262
|
+
timestamp: int
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class DeploymentAddresses(CamelModel):
|
|
266
|
+
"""Model for contract deployment addresses."""
|
|
267
|
+
|
|
268
|
+
ddx_address: str
|
|
269
|
+
ddx_wallet_cloneable_address: str
|
|
270
|
+
derivadex_address: str = Field(alias="derivaDEXAddress") # Custom alias
|
|
271
|
+
di_fund_token_factory_address: str
|
|
272
|
+
governance_address: str
|
|
273
|
+
governance_dip12_address: str
|
|
274
|
+
gas_consumer_address: str
|
|
275
|
+
insurance_fund_address: str
|
|
276
|
+
pause_address: str
|
|
277
|
+
trader_address: str
|
|
278
|
+
usdt_address: str
|
|
279
|
+
ausdt_address: str
|
|
280
|
+
cusdt_address: str
|
|
281
|
+
usdc_address: str
|
|
282
|
+
cusdc_address: str
|
|
283
|
+
ausdc_address: str
|
|
284
|
+
husd_address: str
|
|
285
|
+
gusd_address: str
|
|
286
|
+
gnosis_safe_address: str
|
|
287
|
+
gnosis_safe_proxy_factory_address: str
|
|
288
|
+
gnosis_safe_proxy_address: str
|
|
289
|
+
create_call_address: str
|
|
290
|
+
banner_address: str
|
|
291
|
+
funded_insurance_fund_address: str
|
|
292
|
+
funded_insurance_fund_dip12_address: str
|
|
293
|
+
checkpoint_address: str
|
|
294
|
+
registration_address: str
|
|
295
|
+
initialize_app_address: str
|
|
296
|
+
specs_address: str
|
|
297
|
+
collateral_address: str
|
|
298
|
+
collateral_dip12_address: str
|
|
299
|
+
custodian_address: str
|
|
300
|
+
reject_address: str
|
|
301
|
+
stake_address: str
|
|
302
|
+
stake_dip12_address: str
|
|
303
|
+
pilot_reset_address: str
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class DeploymentInfo(CamelModel):
|
|
307
|
+
"""Model for deployment information."""
|
|
308
|
+
|
|
309
|
+
addresses: DeploymentAddresses
|
|
310
|
+
chain_id: int
|
|
311
|
+
eth_rpc_url: str
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Literal, Optional
|
|
3
|
+
from pydantic import Field, field_validator
|
|
4
|
+
from ddx.models.base import (
|
|
5
|
+
CamelModel,
|
|
6
|
+
HexStr,
|
|
7
|
+
validate_decimal_str,
|
|
8
|
+
PositionSide,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from ddx._rust.decimal import Decimal
|
|
12
|
+
from ddx._rust.common.state import (
|
|
13
|
+
Strategy as SMTStrategy,
|
|
14
|
+
Balance,
|
|
15
|
+
Trader as SMTTrader,
|
|
16
|
+
Position as SMTPosition,
|
|
17
|
+
)
|
|
18
|
+
from ddx._rust.common import TokenSymbol
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Strategy Fees History Models
|
|
22
|
+
class Fee(CamelModel):
|
|
23
|
+
"""Strategy fee data model."""
|
|
24
|
+
|
|
25
|
+
epoch_id: int = Field(..., ge=0)
|
|
26
|
+
tx_ordinal: int = Field(..., ge=0)
|
|
27
|
+
ordinal: int = Field(..., ge=0)
|
|
28
|
+
amount: str
|
|
29
|
+
fee_symbol: str
|
|
30
|
+
symbol: str
|
|
31
|
+
created_at: datetime
|
|
32
|
+
|
|
33
|
+
@field_validator("amount")
|
|
34
|
+
@classmethod
|
|
35
|
+
def validate_nonnegative_decimals(cls, v, info):
|
|
36
|
+
return validate_decimal_str(v, f"StrategyFee.{info.field_name}", nonnegative=True)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FeesHistoryResponse(CamelModel):
|
|
40
|
+
"""Response model for strategy fees history endpoint."""
|
|
41
|
+
|
|
42
|
+
value: List[Fee]
|
|
43
|
+
next_epoch: Optional[int] = Field(None, ge=0)
|
|
44
|
+
next_tx_ordinal: Optional[int] = Field(None, ge=0)
|
|
45
|
+
next_ordinal: Optional[int] = Field(None, ge=0)
|
|
46
|
+
success: bool
|
|
47
|
+
timestamp: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Strategy Positions Models
|
|
51
|
+
class Position(CamelModel):
|
|
52
|
+
"""Position data model."""
|
|
53
|
+
|
|
54
|
+
trader: HexStr
|
|
55
|
+
symbol: str
|
|
56
|
+
strategy_id_hash: HexStr
|
|
57
|
+
strategy_id: str
|
|
58
|
+
side: PositionSide
|
|
59
|
+
balance: str
|
|
60
|
+
avg_entry_price: str
|
|
61
|
+
last_modified_in_epoch: Optional[int] = Field(None, ge=0)
|
|
62
|
+
|
|
63
|
+
@field_validator("balance", "avg_entry_price")
|
|
64
|
+
@classmethod
|
|
65
|
+
def validate_nonnegative_decimals(cls, v, info):
|
|
66
|
+
return validate_decimal_str(v, f"Position.{info.field_name}", nonnegative=True)
|
|
67
|
+
|
|
68
|
+
def raw_position(self) -> SMTPosition:
|
|
69
|
+
return SMTPosition(
|
|
70
|
+
self.side.raw_position_side(),
|
|
71
|
+
Decimal(self.balance),
|
|
72
|
+
Decimal(self.avg_entry_price),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PositionsResponse(CamelModel):
|
|
77
|
+
"""Response model for strategy positions endpoint."""
|
|
78
|
+
|
|
79
|
+
value: List[Position]
|
|
80
|
+
success: bool
|
|
81
|
+
timestamp: int
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Strategy Models
|
|
85
|
+
class Strategy(CamelModel):
|
|
86
|
+
"""Srategy data model."""
|
|
87
|
+
|
|
88
|
+
trader: HexStr
|
|
89
|
+
strategy_id_hash: HexStr
|
|
90
|
+
strategy_id: str
|
|
91
|
+
max_leverage: int = Field(..., ge=1)
|
|
92
|
+
avail_collateral: str
|
|
93
|
+
locked_collateral: str
|
|
94
|
+
frozen: bool
|
|
95
|
+
|
|
96
|
+
@field_validator("avail_collateral", "locked_collateral")
|
|
97
|
+
@classmethod
|
|
98
|
+
def validate_nonnegative_decimals(cls, v, info):
|
|
99
|
+
return validate_decimal_str(v, f"Strategy.{info.field_name}", nonnegative=True)
|
|
100
|
+
|
|
101
|
+
def raw_strategy(self) -> SMTStrategy:
|
|
102
|
+
return SMTStrategy(
|
|
103
|
+
Balance(
|
|
104
|
+
Decimal(self.avail_collateral),
|
|
105
|
+
TokenSymbol.USDC,
|
|
106
|
+
),
|
|
107
|
+
Balance(
|
|
108
|
+
Decimal(self.locked_collateral),
|
|
109
|
+
TokenSymbol.USDC,
|
|
110
|
+
),
|
|
111
|
+
self.max_leverage,
|
|
112
|
+
self.frozen,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class StrategyResponse(CamelModel):
|
|
117
|
+
"""Response model for strategy endpoint."""
|
|
118
|
+
|
|
119
|
+
value: Optional[Strategy] = None
|
|
120
|
+
success: bool
|
|
121
|
+
timestamp: int
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# Strategies Models
|
|
125
|
+
class StrategiesResponse(CamelModel):
|
|
126
|
+
"""Response model for strategies endpoint."""
|
|
127
|
+
|
|
128
|
+
value: List[Strategy]
|
|
129
|
+
success: bool
|
|
130
|
+
timestamp: int
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Strategy Metrics Models
|
|
134
|
+
class StrategyMetrics(CamelModel):
|
|
135
|
+
"""Strategy metrics data model."""
|
|
136
|
+
|
|
137
|
+
margin_fraction: str
|
|
138
|
+
mmr: str
|
|
139
|
+
leverage: str
|
|
140
|
+
strategy_margin: str
|
|
141
|
+
strategy_value: str
|
|
142
|
+
|
|
143
|
+
@field_validator("margin_fraction", "mmr", "leverage", "strategy_margin", "strategy_value")
|
|
144
|
+
@classmethod
|
|
145
|
+
def validate_nonnegative_decimals(cls, v, info):
|
|
146
|
+
return validate_decimal_str(v, f"StrategyMetrics.{info.field_name}", nonnegative=True)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class StrategyMetricsResponse(CamelModel):
|
|
150
|
+
"""Response model for strategy metrics endpoint."""
|
|
151
|
+
|
|
152
|
+
value: StrategyMetrics
|
|
153
|
+
success: bool
|
|
154
|
+
timestamp: int
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# Trader Models
|
|
158
|
+
class Trader(CamelModel):
|
|
159
|
+
"""Trader data model."""
|
|
160
|
+
|
|
161
|
+
trader: str
|
|
162
|
+
avail_ddx: str
|
|
163
|
+
locked_ddx: str
|
|
164
|
+
pay_fees_in_ddx: bool
|
|
165
|
+
referral_address: Optional[HexStr] = None
|
|
166
|
+
|
|
167
|
+
@field_validator("avail_ddx", "locked_ddx")
|
|
168
|
+
@classmethod
|
|
169
|
+
def validate_nonnegative_decimals(cls, v, info):
|
|
170
|
+
return validate_decimal_str(v, f"TraderProfile.{info.field_name}", nonnegative=True)
|
|
171
|
+
|
|
172
|
+
def raw_trader(self) -> SMTTrader:
|
|
173
|
+
return SMTTrader(
|
|
174
|
+
Decimal(self.avail_ddx),
|
|
175
|
+
Decimal(self.locked_ddx),
|
|
176
|
+
self.pay_fees_in_ddx,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class TraderResponse(CamelModel):
|
|
181
|
+
"""Response model for trader profile endpoint."""
|
|
182
|
+
|
|
183
|
+
value: Optional[Trader] = None
|
|
184
|
+
success: bool
|
|
185
|
+
timestamp: int
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DerivaDEX signature utils
|
|
3
|
+
"""
|
|
4
|
+
from coincurve import PublicKey, PrivateKey
|
|
5
|
+
from Crypto.Cipher import AES
|
|
6
|
+
from Crypto.Random import get_random_bytes
|
|
7
|
+
from Crypto.Hash import keccak
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def encrypt_with_nonce(encryption_key: str, msg: str) -> bytes:
|
|
11
|
+
network_public_key = PublicKey(bytes.fromhex(encryption_key[2:]))
|
|
12
|
+
my_secret_key = PrivateKey(get_random_bytes(32))
|
|
13
|
+
my_public_key = my_secret_key.public_key
|
|
14
|
+
shared_pub = network_public_key.multiply(my_secret_key.secret)
|
|
15
|
+
keccak_256 = keccak.new(digest_bits=256)
|
|
16
|
+
keccak_256.update(shared_pub.format())
|
|
17
|
+
derived_key = keccak_256.digest()[:16]
|
|
18
|
+
nonce = get_random_bytes(12)
|
|
19
|
+
|
|
20
|
+
cipher = AES.new(derived_key, AES.MODE_GCM, nonce=nonce)
|
|
21
|
+
encoded_message = msg.encode("utf8")
|
|
22
|
+
ciphertext, tag = cipher.encrypt_and_digest(
|
|
23
|
+
len(encoded_message).to_bytes(4, byteorder="big") + encoded_message
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
return ciphertext + tag + nonce + my_public_key.format()
|
ddx/utils/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ddx-python
|
|
3
|
+
Version: 1.0.4
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
7
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
8
|
+
Classifier: Programming Language :: Rust
|
|
9
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Requires-Dist: maturin>=1.5.0,<1.6.0
|
|
16
|
+
Requires-Dist: pydantic>=2.0
|
|
17
|
+
Requires-Dist: websockets>=12.0,<13
|
|
18
|
+
Requires-Dist: 0x-contract-wrappers>=2.0.0,<3
|
|
19
|
+
Requires-Dist: aiohttp>=3.10.3,<4
|
|
20
|
+
Requires-Dist: asyncpg>=0.30.0,<1
|
|
21
|
+
Requires-Dist: attrs>=24.2.0,<25
|
|
22
|
+
Requires-Dist: cattrs>=23.2.3,<24
|
|
23
|
+
Requires-Dist: coloredlogs>=15.0.1,<16
|
|
24
|
+
Requires-Dist: configargparse>=1.7,<2
|
|
25
|
+
Requires-Dist: numpy>=2.2.6,<3
|
|
26
|
+
Requires-Dist: pandas>=2.2.2,<3
|
|
27
|
+
Requires-Dist: path>=17.0.0,<18
|
|
28
|
+
Requires-Dist: pathlib>=1.0.1,<2
|
|
29
|
+
Requires-Dist: plotly>=5.23.0,<6
|
|
30
|
+
Requires-Dist: psutil>=6.0.0,<7
|
|
31
|
+
Requires-Dist: requests>=2.32.3,<3
|
|
32
|
+
Requires-Dist: simplejson>=3.19.2,<4
|
|
33
|
+
Requires-Dist: sphinx>=8.0.2,<9
|
|
34
|
+
Requires-Dist: verboselogs>=1.7,<2
|
|
35
|
+
Requires-Dist: btrees>=6.0,<7
|
|
36
|
+
Requires-Dist: ccxt>=4.3.79,<5
|
|
37
|
+
Requires-Dist: coincurve>=20.0.0,<21
|
|
38
|
+
Requires-Dist: dash-bootstrap-components>=1.6.0,<2
|
|
39
|
+
Requires-Dist: python-dotenv>=1.0.1,<2
|
|
40
|
+
Requires-Dist: sexpdata>=1.0.2,<2
|
|
41
|
+
Requires-Dist: web3>=6.20.1,<7
|
|
42
|
+
Requires-Dist: werkzeug>=3.0.3,<4
|
|
43
|
+
Requires-Dist: gevent>=24.2.1,<25
|
|
44
|
+
Requires-Dist: sortedcontainers>=2.4.0,<3
|
|
45
|
+
Requires-Dist: aioresponses>=0.7.6,<0.8
|
|
46
|
+
Requires-Dist: colorama>=0.4.6,<0.5
|
|
47
|
+
Requires-Dist: loguru
|
|
48
|
+
Requires-Dist: black>=24.4.2,<25 ; extra == 'dev'
|
|
49
|
+
Requires-Dist: basedpyright ; extra == 'dev'
|
|
50
|
+
Requires-Dist: pipdeptree ; extra == 'dev'
|
|
51
|
+
Requires-Dist: jupyter-book>=0.6.5,<0.7 ; extra == 'notebook'
|
|
52
|
+
Requires-Dist: jupyter-dash>=0.4.2,<0.5 ; extra == 'notebook'
|
|
53
|
+
Requires-Dist: jupyterlab-lsp>=5.1.0,<6 ; extra == 'notebook'
|
|
54
|
+
Requires-Dist: jupyter>=1.0.0,<2 ; extra == 'notebook'
|
|
55
|
+
Requires-Dist: jupyterlab ; extra == 'notebook'
|
|
56
|
+
Requires-Dist: matplotlib>=3.9.1,<4 ; extra == 'notebook'
|
|
57
|
+
Requires-Dist: pytest>=8.3.5,<9 ; extra == 'test'
|
|
58
|
+
Requires-Dist: pytest-asyncio>=0.26.0,<0.27 ; extra == 'test'
|
|
59
|
+
Requires-Dist: pytest-pretty>=1.2.0,<2 ; extra == 'test'
|
|
60
|
+
Provides-Extra: dev
|
|
61
|
+
Provides-Extra: notebook
|
|
62
|
+
Provides-Extra: test
|
|
63
|
+
Requires-Python: >=3.10
|