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,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TradeMining module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from attrs import define, field
|
|
8
|
+
from ddx.common.transactions.event import Event
|
|
9
|
+
from ddx._rust.common.state import DerivadexSMT, Stats
|
|
10
|
+
from ddx._rust.common.state.keys import TraderKey
|
|
11
|
+
from ddx._rust.decimal import Decimal
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@define
|
|
17
|
+
class TradeMining(Event):
|
|
18
|
+
"""
|
|
19
|
+
Defines a TradeMining
|
|
20
|
+
|
|
21
|
+
A TradeMining is when a there is a trade mining distribution.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
settlement_epoch_id (int): Settlement epoch id
|
|
25
|
+
ddx_distributed (Decimal): The total DDX distributed in this interval.
|
|
26
|
+
total_volume (Stats): The total maker and taker volume for this interval.
|
|
27
|
+
request_index (int): Sequenced request index of transaction
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
settlement_epoch_id: int
|
|
31
|
+
ddx_distributed: Decimal
|
|
32
|
+
total_volume: Stats
|
|
33
|
+
request_index: int = field(default=-1, eq=False)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
37
|
+
"""
|
|
38
|
+
Decode a raw transaction log event (dict) into a Funding
|
|
39
|
+
instance.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
raw_tx_log_event : dict
|
|
44
|
+
Raw transaction log event being processed
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
trade_mining_tx_event = raw_tx_log_event["event"]["c"]
|
|
48
|
+
|
|
49
|
+
return cls(
|
|
50
|
+
trade_mining_tx_event["settlementEpochId"],
|
|
51
|
+
Decimal(trade_mining_tx_event["ddxDistributed"]),
|
|
52
|
+
Stats(
|
|
53
|
+
Decimal(trade_mining_tx_event["totalVolume"]["makerVolume"]),
|
|
54
|
+
Decimal(trade_mining_tx_event["totalVolume"]["takerVolume"]),
|
|
55
|
+
),
|
|
56
|
+
raw_tx_log_event["requestIndex"],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def process_tx(
|
|
60
|
+
self,
|
|
61
|
+
smt: DerivadexSMT,
|
|
62
|
+
**kwargs,
|
|
63
|
+
):
|
|
64
|
+
"""
|
|
65
|
+
Process a TradeMining transaction. A TradeMining event consists
|
|
66
|
+
of consists of information relating to the trade mining
|
|
67
|
+
distribution, when DDX will be allocated to traders due to
|
|
68
|
+
their maker and taker volume contributions as a proportion to
|
|
69
|
+
the overall exchange volume.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
smt: DerivadexSMT
|
|
74
|
+
DerivaDEX Sparse Merkle Tree
|
|
75
|
+
**kwargs
|
|
76
|
+
Additional args specific to TradeMining transactions
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
if not kwargs["trade_mining_active"]:
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
# The overall trade mining allocation is 75% of the
|
|
83
|
+
# liquidity mining supply (50mm DDX) issued over a 10-year
|
|
84
|
+
# schedule, 3 times a day
|
|
85
|
+
ddx_reward_per_epoch = kwargs["trade_mining_reward_per_epoch"]
|
|
86
|
+
trade_mining_maker_reward_percentage = kwargs[
|
|
87
|
+
"trade_mining_maker_reward_percentage"
|
|
88
|
+
]
|
|
89
|
+
trade_mining_taker_reward_percentage = kwargs[
|
|
90
|
+
"trade_mining_taker_reward_percentage"
|
|
91
|
+
]
|
|
92
|
+
if self.ddx_distributed != Decimal("0"):
|
|
93
|
+
# If trade mining did distribute DDX during the trade
|
|
94
|
+
# mining period, we should handle DDX distributions,
|
|
95
|
+
# otherwise we can gracefully skip
|
|
96
|
+
|
|
97
|
+
# Loop through all the stats leaves
|
|
98
|
+
for stats_key, stats in smt.all_stats():
|
|
99
|
+
# Compute the DDX gained as a result of maker volume
|
|
100
|
+
# for the trader (20% of the DDX rewards go to makers)
|
|
101
|
+
ddx_accrued_as_maker = (
|
|
102
|
+
stats.maker_volume
|
|
103
|
+
/ self.total_volume.maker_volume
|
|
104
|
+
* ddx_reward_per_epoch
|
|
105
|
+
* trade_mining_maker_reward_percentage
|
|
106
|
+
if self.total_volume.maker_volume != Decimal("0")
|
|
107
|
+
else Decimal("0")
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Compute the DDX gained as a result of taker volume
|
|
111
|
+
# for the trader (80% of the DDX rewards go to takers)
|
|
112
|
+
ddx_accrued_as_taker = (
|
|
113
|
+
stats.taker_volume
|
|
114
|
+
/ self.total_volume.taker_volume
|
|
115
|
+
* ddx_reward_per_epoch
|
|
116
|
+
* trade_mining_taker_reward_percentage
|
|
117
|
+
if self.total_volume.taker_volume != Decimal("0")
|
|
118
|
+
else Decimal("0")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if ddx_accrued_as_maker != Decimal(
|
|
122
|
+
"0"
|
|
123
|
+
) or ddx_accrued_as_taker != Decimal("0"):
|
|
124
|
+
# If DDX accrued for trader is non-zero, handle
|
|
125
|
+
# distribution
|
|
126
|
+
|
|
127
|
+
# Derive TraderKey given the key
|
|
128
|
+
trader_key: TraderKey = stats_key.as_trader_key()
|
|
129
|
+
|
|
130
|
+
# Get Trader leaf given the key from above
|
|
131
|
+
trader = smt.trader(trader_key)
|
|
132
|
+
|
|
133
|
+
# Increment the Trader leaf's available balance by the
|
|
134
|
+
# DDX accrued (both maker and taker)
|
|
135
|
+
trader.avail_ddx_balance = (
|
|
136
|
+
trader.avail_ddx_balance
|
|
137
|
+
+ ddx_accrued_as_maker
|
|
138
|
+
+ ddx_accrued_as_taker
|
|
139
|
+
).recorded_amount()
|
|
140
|
+
|
|
141
|
+
# Update the SMT with the H256 repr of the key and
|
|
142
|
+
# the Trader leaf
|
|
143
|
+
smt.store_trader(trader_key, trader)
|
|
144
|
+
|
|
145
|
+
# Update the SMT with the H256 repr of the key and
|
|
146
|
+
# the Stats leaf
|
|
147
|
+
smt.store_stats(stats_key, None)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Trader Update module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.transactions.event import Event
|
|
7
|
+
from ddx._rust.common.state import DerivadexSMT, Trader
|
|
8
|
+
from ddx._rust.common.state.keys import TraderKey
|
|
9
|
+
from ddx._rust.common.transactions import TraderUpdateKind
|
|
10
|
+
from ddx._rust.decimal import Decimal
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _is_zero_address(addr: Optional[str]) -> bool:
|
|
15
|
+
if addr is None:
|
|
16
|
+
return True
|
|
17
|
+
try:
|
|
18
|
+
return int(str(addr).replace("0x", ""), 16) == 0
|
|
19
|
+
except Exception:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@define
|
|
24
|
+
class TraderUpdate(Event):
|
|
25
|
+
"""
|
|
26
|
+
Defines a TraderUpdate
|
|
27
|
+
|
|
28
|
+
A TraderUpdate is an update to a trader's DDX account (such as depositing
|
|
29
|
+
or withdrawing DDX).
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
trader_address (str): Trader's Ethereum address this strategy belongs to
|
|
33
|
+
amount (Decimal): The amount of collateral deposited or withdrawn
|
|
34
|
+
update_kind (TraderUpdateKind): Update kind (Deposit, Withdraw, Profile)
|
|
35
|
+
pay_fees_in_ddx (bool): Whether trader has opted to pay fees in DDX by default
|
|
36
|
+
referral_address (Optional[str]): Optional referral address to set exactly once
|
|
37
|
+
tx_hash (str): The Ethereum transaction's hash
|
|
38
|
+
request_index (int): Sequenced request index of transaction
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
trader_address: str = field(eq=str.lower)
|
|
42
|
+
amount: Decimal
|
|
43
|
+
update_kind: TraderUpdateKind
|
|
44
|
+
pay_fees_in_ddx: bool
|
|
45
|
+
referral_address: Optional[str] = None
|
|
46
|
+
request_index: int = field(default=-1, eq=False)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
50
|
+
"""
|
|
51
|
+
Decode a raw transaction log event (dict) into a StrategyUpdate
|
|
52
|
+
instance.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
raw_tx_log_event : dict
|
|
57
|
+
Raw transaction log event being processed
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
trader_update_tx_event = raw_tx_log_event["event"]["c"]
|
|
61
|
+
|
|
62
|
+
amount_raw = trader_update_tx_event.get("amount")
|
|
63
|
+
amount = Decimal(amount_raw) if amount_raw is not None else Decimal("0")
|
|
64
|
+
|
|
65
|
+
return cls(
|
|
66
|
+
trader_update_tx_event["trader"],
|
|
67
|
+
amount,
|
|
68
|
+
TraderUpdateKind(trader_update_tx_event["updateKind"]),
|
|
69
|
+
trader_update_tx_event.get("payFeesInDdx", False),
|
|
70
|
+
trader_update_tx_event.get("referralAddress"),
|
|
71
|
+
raw_tx_log_event["requestIndex"],
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def process_tx(
|
|
75
|
+
self,
|
|
76
|
+
smt: DerivadexSMT,
|
|
77
|
+
**kwargs,
|
|
78
|
+
):
|
|
79
|
+
"""
|
|
80
|
+
Process a TraderUpdate transaction. A TraderUpdate consists
|
|
81
|
+
of information relating to updates to a trader. This will
|
|
82
|
+
update the Trader leaf in the SMT.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
smt: DerivadexSMT
|
|
87
|
+
DerivaDEX Sparse Merkle Tree
|
|
88
|
+
**kwargs
|
|
89
|
+
Additional args specific to TraderUpdate transactions
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
trader_key: TraderKey = TraderKey(self.trader_address)
|
|
93
|
+
trader = smt.trader(trader_key)
|
|
94
|
+
|
|
95
|
+
if self.update_kind == TraderUpdateKind.DepositDDX:
|
|
96
|
+
# If TraderUpdate is of deposit type
|
|
97
|
+
if trader is None:
|
|
98
|
+
# Initialize a new Trader Leaf
|
|
99
|
+
trader = Trader.default()
|
|
100
|
+
|
|
101
|
+
trader.avail_ddx_balance += self.amount
|
|
102
|
+
elif self.update_kind == TraderUpdateKind.WithdrawDDX:
|
|
103
|
+
# If TraderUpdate is of withdrawal (claimed) type
|
|
104
|
+
if trader is None or trader.locked_ddx_balance < self.amount:
|
|
105
|
+
raise Exception(
|
|
106
|
+
"Trader leaf either non-existent or insufficiently capitalized to facilitate withdrawal"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Adjust the existing Trader leaf by decrementing the
|
|
110
|
+
# free collateral by the amount in the withdrawal event
|
|
111
|
+
trader.locked_ddx_balance -= self.amount
|
|
112
|
+
elif self.update_kind == TraderUpdateKind.Profile:
|
|
113
|
+
# If TraderUpdate is of profile type
|
|
114
|
+
if trader is None:
|
|
115
|
+
raise Exception("Trader leaf non-existent")
|
|
116
|
+
|
|
117
|
+
# Adjust the existing Trader leaf by setting the
|
|
118
|
+
# flag to pay fees in DDX
|
|
119
|
+
trader.pay_fees_in_ddx = self.pay_fees_in_ddx
|
|
120
|
+
if self.referral_address and not _is_zero_address(self.referral_address):
|
|
121
|
+
# Set-once semantics: only set when currently unset/zero.
|
|
122
|
+
if _is_zero_address(trader.referral_address):
|
|
123
|
+
# Ensure the referral uses the 21-byte TraderAddress format (chain byte + EOA).
|
|
124
|
+
referral = self.referral_address
|
|
125
|
+
if referral.startswith("0x") and len(referral) == 42:
|
|
126
|
+
referral = f"0x00{referral[2:]}"
|
|
127
|
+
trader.referral_address = referral
|
|
128
|
+
elif trader.referral_address != self.referral_address:
|
|
129
|
+
raise Exception("Referral address already set for trader")
|
|
130
|
+
|
|
131
|
+
smt.store_trader(trader_key, trader)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Withdraw 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, Strategy, Trader
|
|
9
|
+
from ddx._rust.common.state.keys import StrategyKey
|
|
10
|
+
from ddx._rust.decimal import Decimal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@define
|
|
14
|
+
class Withdraw(Event):
|
|
15
|
+
"""
|
|
16
|
+
Defines a Withdraw Update
|
|
17
|
+
|
|
18
|
+
A Withdraw Update is when a withdrawal of collateral is **signaled**.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
recipient_address (str): Trader address DDX is being withdrawn to
|
|
22
|
+
strategy_id_hash (str): Cross-margined strategy ID hash for which this withdrawal applies
|
|
23
|
+
collateral_address (str): Collateral ERC-20 token address being withdrawn
|
|
24
|
+
amount (Decimal): The amount of DDX being withdrawn
|
|
25
|
+
request_index (int): Sequenced request index of transaction
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
recipient_address: str = field(eq=str.lower)
|
|
29
|
+
strategy_id_hash: str = field(eq=str.lower)
|
|
30
|
+
collateral_address: str = field(eq=str.lower)
|
|
31
|
+
amount: Decimal
|
|
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 Withdraw
|
|
38
|
+
instance.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
raw_tx_log_event : dict
|
|
43
|
+
Raw transaction log event being processed
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
withdraw_tx_event = raw_tx_log_event["event"]["c"]
|
|
47
|
+
|
|
48
|
+
return cls(
|
|
49
|
+
withdraw_tx_event["recipientAddress"],
|
|
50
|
+
withdraw_tx_event["strategy"],
|
|
51
|
+
withdraw_tx_event["currency"],
|
|
52
|
+
Decimal(withdraw_tx_event["amount"]),
|
|
53
|
+
raw_tx_log_event["requestIndex"],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def process_tx(
|
|
57
|
+
self,
|
|
58
|
+
smt: DerivadexSMT,
|
|
59
|
+
**kwargs,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Process a Withdraw transaction. A Withdraw event consists
|
|
63
|
+
of consists of information relating to withdrawal of collateral.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
smt: DerivadexSMT
|
|
68
|
+
DerivaDEX Sparse Merkle Tree
|
|
69
|
+
**kwargs
|
|
70
|
+
Additional args specific to Withdraw transactions
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
strategy_key: StrategyKey = StrategyKey(
|
|
74
|
+
self.recipient_address, self.strategy_id_hash
|
|
75
|
+
)
|
|
76
|
+
strategy: Strategy = smt.strategy(strategy_key)
|
|
77
|
+
|
|
78
|
+
# Decrement the free balance by the withdrawn amount
|
|
79
|
+
symbol = TokenSymbol.from_address(self.collateral_address)
|
|
80
|
+
strategy.set_avail_collateral(
|
|
81
|
+
symbol,
|
|
82
|
+
strategy.avail_collateral[symbol] - self.amount,
|
|
83
|
+
)
|
|
84
|
+
# Increment the frozen balance by the withdrawn amount
|
|
85
|
+
strategy.set_locked_collateral(
|
|
86
|
+
symbol,
|
|
87
|
+
strategy.locked_collateral[symbol] + self.amount,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
smt.store_strategy(strategy_key, strategy)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WithdrawDDX module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from attrs import define, field
|
|
6
|
+
from ddx.common.transactions.event import Event
|
|
7
|
+
from ddx._rust.common.state import DerivadexSMT
|
|
8
|
+
from ddx._rust.common.state.keys import TraderKey
|
|
9
|
+
from ddx._rust.decimal import Decimal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@define
|
|
13
|
+
class WithdrawDDX(Event):
|
|
14
|
+
"""
|
|
15
|
+
Defines a WithdrawDDX Update
|
|
16
|
+
|
|
17
|
+
A WithdrawDDX is when a withdrawal of DDX is signaled.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
recipient_address (str): Ethereum address DDX is being withdrawn to
|
|
21
|
+
amount (Decimal): The amount of DDX being withdrawn
|
|
22
|
+
request_index (int): Sequenced request index of transaction
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
recipient_address: str = field(eq=str.lower)
|
|
26
|
+
amount: Decimal
|
|
27
|
+
request_index: int = field(default=-1, eq=False)
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def decode_value_into_cls(cls, raw_tx_log_event: dict):
|
|
31
|
+
"""
|
|
32
|
+
Decode a raw transaction log event (dict) into a WithdrawDDX
|
|
33
|
+
instance.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
raw_tx_log_event : dict
|
|
38
|
+
Raw transaction log event being processed
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
withdraw_ddx_tx_event = raw_tx_log_event["event"]["c"]
|
|
42
|
+
|
|
43
|
+
return cls(
|
|
44
|
+
withdraw_ddx_tx_event["recipientAddress"],
|
|
45
|
+
Decimal(withdraw_ddx_tx_event["amount"]),
|
|
46
|
+
raw_tx_log_event["requestIndex"],
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def process_tx(
|
|
50
|
+
self,
|
|
51
|
+
smt: DerivadexSMT,
|
|
52
|
+
**kwargs,
|
|
53
|
+
):
|
|
54
|
+
"""
|
|
55
|
+
Process a WithdrawDDX transaction. A WithdrawDDX event consists
|
|
56
|
+
of consists of information relating to withdrawal of DDX.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
smt: DerivadexSMT
|
|
61
|
+
DerivaDEX Sparse Merkle Tree
|
|
62
|
+
**kwargs
|
|
63
|
+
Additional args specific to WithdrawDDX transactions
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
trader_key: TraderKey = TraderKey(self.recipient_address)
|
|
67
|
+
trader = smt.trader(trader_key)
|
|
68
|
+
|
|
69
|
+
# Decrement the free balance by the withdrawn amount
|
|
70
|
+
trader.avail_ddx_balance -= self.amount
|
|
71
|
+
# Increment the frozen balance by the withdrawn amount
|
|
72
|
+
trader.locked_ddx_balance += self.amount
|
|
73
|
+
|
|
74
|
+
smt.store_trader(trader_key, trader)
|
ddx/common/utils.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utils module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal as PyDecimal
|
|
6
|
+
import simplejson as json
|
|
7
|
+
|
|
8
|
+
from ddx._rust.common.enums import OrderSide
|
|
9
|
+
from ddx._rust.decimal import Decimal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def round_to_unit(val: Decimal) -> Decimal:
|
|
13
|
+
"""
|
|
14
|
+
Round a decimal value down to 6 units of precision.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
val : Decimal
|
|
19
|
+
Value to be rounded
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
return val.quantize(6)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def to_base_unit_amount(val: Decimal, decimals: int) -> int:
|
|
26
|
+
"""
|
|
27
|
+
Convert a value to grains format (e.g. DDX grains would be
|
|
28
|
+
multiplying by 10 ** 18).
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
val : Decimal
|
|
33
|
+
Value to be scaled up
|
|
34
|
+
decimals : int
|
|
35
|
+
Number of decimal places to scale up to
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
return int(round_to_unit(val) * 10**decimals)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def to_unit_amount(val: int, decimals: int) -> Decimal:
|
|
42
|
+
"""
|
|
43
|
+
Convert a value from grains format (e.g. from DDX grains would be
|
|
44
|
+
dividing by 10 ** 18).
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
val : Decimal
|
|
49
|
+
Value to be scaled down
|
|
50
|
+
decimals : int
|
|
51
|
+
Number of decimal places to scale down by
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
return Decimal(str(val)) / 10**decimals
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def to_base_unit_amount_list(vals: list[Decimal], decimals: int) -> list[int]:
|
|
58
|
+
"""
|
|
59
|
+
Convert values to grains format (e.g. DDX grains would be
|
|
60
|
+
multiplying by 10 ** 18).
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
vals : list[Decimal]
|
|
65
|
+
Values to be scaled up
|
|
66
|
+
decimals : int
|
|
67
|
+
Number of decimal places to scale up to
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
return [to_base_unit_amount(val, decimals) for val in vals]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def to_unit_amount_list(vals: list[int], decimals: int):
|
|
74
|
+
"""
|
|
75
|
+
Convert values from grains format (e.g. from DDX grains would be
|
|
76
|
+
dividing by 10 ** 18).
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
vals : list[int]
|
|
81
|
+
Values to be scaled down
|
|
82
|
+
decimals : int
|
|
83
|
+
Number of decimal places to scale down by
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
return [to_unit_amount(val, decimals) for val in vals]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def to_adjusted_encoding_for_negative_val(val: int):
|
|
90
|
+
"""
|
|
91
|
+
Adjust encoding for a potentially negative value
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
vals : int
|
|
96
|
+
Values to be adjusted
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
return 16**32 + abs(val) if val < 0 else val
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def to_adjusted_encoding_for_negative_val_list(vals: list[int]):
|
|
103
|
+
"""
|
|
104
|
+
Adjust encoding for list of potentially negative values
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
vals : list[int]
|
|
109
|
+
list of values to be adjusted
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
return [to_adjusted_encoding_for_negative_val(val) for val in vals]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def from_adjusted_encoding_for_negative_val(val: int):
|
|
116
|
+
"""
|
|
117
|
+
Adjust encoding for a potentially negative value
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
vals : int
|
|
122
|
+
Values to be adjusted
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
return val if val < 16**32 else -(val - 16**32)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_parsed_tx_log_entry(tx_log_entry: dict):
|
|
129
|
+
"""
|
|
130
|
+
Parse an individual transaction log entry into a format
|
|
131
|
+
suitable for the Auditor. This format was selected so
|
|
132
|
+
that the Auditor can be reused as-is by the integration
|
|
133
|
+
tests with no changes needed.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
tx_log_entry : dict
|
|
138
|
+
Transaction log message
|
|
139
|
+
"""
|
|
140
|
+
return {
|
|
141
|
+
"event": tx_log_entry["event"],
|
|
142
|
+
"requestIndex": int(tx_log_entry["requestIndex"]),
|
|
143
|
+
"epochId": int(tx_log_entry["epochId"]),
|
|
144
|
+
"txOrdinal": int(tx_log_entry["ordinal"]),
|
|
145
|
+
"batchId": int(tx_log_entry["batchId"]),
|
|
146
|
+
"timeValue": int(tx_log_entry["time"]["value"]),
|
|
147
|
+
"timestamp": int(tx_log_entry["time"]["timestamp"]),
|
|
148
|
+
"stateRootHash": tx_log_entry["stateRootHash"],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def calculate_max_collateral(
|
|
153
|
+
collateral_tranches: list[tuple[Decimal, Decimal]], ddx_balance: Decimal
|
|
154
|
+
) -> Decimal:
|
|
155
|
+
limit = Decimal("0")
|
|
156
|
+
for ddx_threshold, max_collateral in collateral_tranches:
|
|
157
|
+
limit = max_collateral
|
|
158
|
+
if ddx_balance < ddx_threshold:
|
|
159
|
+
break
|
|
160
|
+
return limit
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ComplexOutputEncoder(json.JSONEncoder):
|
|
164
|
+
"""
|
|
165
|
+
Custom JSON-encoder for serializing objects
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
def __init__(self, **kwargs):
|
|
169
|
+
super(ComplexOutputEncoder, self).__init__(**kwargs)
|
|
170
|
+
|
|
171
|
+
def default(self, o):
|
|
172
|
+
if hasattr(o, "repr_json"):
|
|
173
|
+
return o.repr_json()
|
|
174
|
+
elif type(o) == Decimal:
|
|
175
|
+
return PyDecimal(str(o))
|
|
176
|
+
return json.JSONEncoder.default(self, o)
|
ddx/config.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
# from ddx._rust import common
|
|
4
|
+
from ddx._rust.common import reinit_operator_context
|
|
5
|
+
|
|
6
|
+
# FIXME: might need to change these environment variables to `DDX_CONTRACT_DEPLOYMENT` for better convention
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load_mainnet():
|
|
10
|
+
os.environ["CONTRACT_DEPLOYMENT"] = "derivadex"
|
|
11
|
+
# common.reinit_operator_context()
|
|
12
|
+
reinit_operator_context()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_testnet():
|
|
16
|
+
os.environ["CONTRACT_DEPLOYMENT"] = "testnet"
|
|
17
|
+
reinit_operator_context()
|