derive-py 0.1.3__py3-none-any.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.
- derive_py/__init__.py +11 -0
- derive_py/_clients/__init__.py +14 -0
- derive_py/_clients/rest/__init__.py +0 -0
- derive_py/_clients/rest/async_http/__init__.py +1 -0
- derive_py/_clients/rest/async_http/account.py +277 -0
- derive_py/_clients/rest/async_http/api.py +2588 -0
- derive_py/_clients/rest/async_http/client.py +276 -0
- derive_py/_clients/rest/async_http/collateral.py +192 -0
- derive_py/_clients/rest/async_http/history.py +282 -0
- derive_py/_clients/rest/async_http/markets.py +298 -0
- derive_py/_clients/rest/async_http/mmp.py +75 -0
- derive_py/_clients/rest/async_http/orders.py +359 -0
- derive_py/_clients/rest/async_http/positions.py +149 -0
- derive_py/_clients/rest/async_http/rfq.py +470 -0
- derive_py/_clients/rest/async_http/session.py +154 -0
- derive_py/_clients/rest/async_http/subaccount.py +348 -0
- derive_py/_clients/rest/async_http/system.py +47 -0
- derive_py/_clients/rest/async_http/vaults.py +774 -0
- derive_py/_clients/rest/endpoints.py +158 -0
- derive_py/_clients/rest/http/__init__.py +1 -0
- derive_py/_clients/rest/http/account.py +277 -0
- derive_py/_clients/rest/http/api.py +2588 -0
- derive_py/_clients/rest/http/client.py +274 -0
- derive_py/_clients/rest/http/collateral.py +190 -0
- derive_py/_clients/rest/http/history.py +282 -0
- derive_py/_clients/rest/http/markets.py +294 -0
- derive_py/_clients/rest/http/mmp.py +75 -0
- derive_py/_clients/rest/http/orders.py +359 -0
- derive_py/_clients/rest/http/positions.py +149 -0
- derive_py/_clients/rest/http/rfq.py +470 -0
- derive_py/_clients/rest/http/session.py +153 -0
- derive_py/_clients/rest/http/subaccount.py +345 -0
- derive_py/_clients/rest/http/system.py +47 -0
- derive_py/_clients/rest/http/vaults.py +774 -0
- derive_py/_clients/utils.py +644 -0
- derive_py/_clients/websockets/__init__.py +1 -0
- derive_py/_clients/websockets/api.py +2766 -0
- derive_py/_clients/websockets/client.py +431 -0
- derive_py/_clients/websockets/session.py +792 -0
- derive_py/_web3/__init__.py +14 -0
- derive_py/_web3/abi.py +47 -0
- derive_py/_web3/action_signing/__init__.py +65 -0
- derive_py/_web3/action_signing/module_data/__init__.py +47 -0
- derive_py/_web3/action_signing/module_data/module_data.py +27 -0
- derive_py/_web3/action_signing/module_data/rfq.py +147 -0
- derive_py/_web3/action_signing/module_data/session_key.py +54 -0
- derive_py/_web3/action_signing/module_data/trade.py +40 -0
- derive_py/_web3/action_signing/module_data/transfer_positions.py +53 -0
- derive_py/_web3/action_signing/module_data/transfer_spot.py +47 -0
- derive_py/_web3/action_signing/module_data/transfer_spot_external.py +54 -0
- derive_py/_web3/action_signing/module_data/vault.py +302 -0
- derive_py/_web3/action_signing/module_data/whitelisted_recipients.py +32 -0
- derive_py/_web3/action_signing/module_data/withdraw.py +52 -0
- derive_py/_web3/action_signing/signed_action.py +129 -0
- derive_py/_web3/action_signing/utils.py +174 -0
- derive_py/_web3/async_utils.py +77 -0
- derive_py/_web3/deposits.py +280 -0
- derive_py/_web3/erc20.py +69 -0
- derive_py/_web3/provider.py +456 -0
- derive_py/_web3/tx.py +264 -0
- derive_py/cli/__init__.py +60 -0
- derive_py/cli/_account.py +53 -0
- derive_py/cli/_collateral.py +30 -0
- derive_py/cli/_columns.py +238 -0
- derive_py/cli/_markets.py +318 -0
- derive_py/cli/_mmp.py +118 -0
- derive_py/cli/_orders.py +151 -0
- derive_py/cli/_positions.py +73 -0
- derive_py/cli/_system.py +58 -0
- derive_py/cli/_tree.py +115 -0
- derive_py/cli/_utils.py +225 -0
- derive_py/config/__init__.py +32 -0
- derive_py/config/constants.py +46 -0
- derive_py/config/contracts.py +83 -0
- derive_py/config/rpc.py +60 -0
- derive_py/data/abis/erc20.json +222 -0
- derive_py/data/abis/sepolia/0x1573bde26338A9E6AA358638679A796c81E33246.json +112 -0
- derive_py/data/abis/sepolia/0x3FB79aafCD401CDD19e3d729241545955DDE0D48.json +112 -0
- derive_py/data/abis/sepolia/0x3fBcEB634d2582791B462059D946800c9f41bEfB.json +1210 -0
- derive_py/data/abis/sepolia/0xFbB62CE2BbFdFdc8a60DDC22115aC29cf91B4566.json +112 -0
- derive_py/data/abis/sepolia/0xc02CdE0d7b7B34C02AAa6cF753D8b4453297236c.json +243 -0
- derive_py/data/abis/sepolia/0xd3625eCf97E5554C62A48Ac1c9284C9dCeFceB68.json +112 -0
- derive_py/data/abis/sepolia/0xdBC22b75506FED598b1A114Ba51b9Ca005dE8B65.json +1635 -0
- derive_py/data/abis/sepolia/0xdFB882fDa2BcdB74358506D56D14E746c76eF864.json +782 -0
- derive_py/data/abis/sepolia/contracts.json +30 -0
- derive_py/data/abis/sepolia/proxy_mapping.json +6 -0
- derive_py/data/templates/api.py.jinja +226 -0
- derive_py/data/templates/endpoints.py.jinja +45 -0
- derive_py/data_types/__init__.py +81 -0
- derive_py/data_types/channel_models.py +235 -0
- derive_py/data_types/enums.py +257 -0
- derive_py/data_types/generated_models.py +2665 -0
- derive_py/data_types/models.py +473 -0
- derive_py/data_types/utils.py +24 -0
- derive_py/exceptions.py +159 -0
- derive_py/utils/__init__.py +7 -0
- derive_py/utils/logger.py +27 -0
- derive_py-0.1.3.dist-info/METADATA +374 -0
- derive_py-0.1.3.dist-info/RECORD +102 -0
- derive_py-0.1.3.dist-info/WHEEL +4 -0
- derive_py-0.1.3.dist-info/entry_points.txt +3 -0
- derive_py-0.1.3.dist-info/licenses/LICENSE +21 -0
derive_py/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Derive client package."""
|
|
2
|
+
|
|
3
|
+
from ._clients import AsyncHTTPClient, HTTPClient, WebSocketClient, async_wait_for_settlement, wait_for_settlement
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"HTTPClient",
|
|
7
|
+
"AsyncHTTPClient",
|
|
8
|
+
"WebSocketClient",
|
|
9
|
+
"async_wait_for_settlement",
|
|
10
|
+
"wait_for_settlement",
|
|
11
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Clients module"""
|
|
2
|
+
|
|
3
|
+
from .rest.async_http.client import AsyncHTTPClient
|
|
4
|
+
from .rest.http.client import HTTPClient
|
|
5
|
+
from .utils import async_wait_for_settlement, wait_for_settlement
|
|
6
|
+
from .websockets.client import WebSocketClient
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"HTTPClient",
|
|
10
|
+
"AsyncHTTPClient",
|
|
11
|
+
"WebSocketClient",
|
|
12
|
+
"async_wait_for_settlement",
|
|
13
|
+
"wait_for_settlement",
|
|
14
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""REST API AsyncHTTPClient."""
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Account management operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import msgspec
|
|
8
|
+
|
|
9
|
+
from derive_py._clients.rest.async_http.api import AsyncPrivateAPI, AsyncPublicAPI
|
|
10
|
+
from derive_py._clients.rest.async_http.history import HistoryOperations
|
|
11
|
+
from derive_py._clients.utils import AuthContext, log_session_key_status, unset_if_none
|
|
12
|
+
from derive_py._web3.action_signing import SessionKeyModuleData, WhitelistedRecipientModuleData
|
|
13
|
+
from derive_py.data_types import ChainConfig, ChecksumAddress, LoggerType, OffchainScope, ProtocolScope
|
|
14
|
+
from derive_py.data_types.generated_models import (
|
|
15
|
+
EditSessionKeyRequest,
|
|
16
|
+
GetAccountRequest,
|
|
17
|
+
GetAllPortfoliosRequest,
|
|
18
|
+
GetSubaccountsRequest,
|
|
19
|
+
PrivateGetAccountResponse,
|
|
20
|
+
PrivateGetSubaccountsResponse,
|
|
21
|
+
PrivateSessionKeysResponse,
|
|
22
|
+
PrivateSetSessionKeyResponse,
|
|
23
|
+
SessionKey,
|
|
24
|
+
SessionKeysRequest,
|
|
25
|
+
SetSessionKeyRequest,
|
|
26
|
+
Subaccount,
|
|
27
|
+
UpdateWhitelistedRecipientsRequest,
|
|
28
|
+
UpdateWhitelistedRecipientsResponse,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LightAccount:
|
|
33
|
+
"""LightAccount smart contract wallet operations."""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
auth: AuthContext,
|
|
39
|
+
config: ChainConfig,
|
|
40
|
+
logger: LoggerType,
|
|
41
|
+
public_api: AsyncPublicAPI,
|
|
42
|
+
private_api: AsyncPrivateAPI,
|
|
43
|
+
_state: PrivateGetAccountResponse | None = None,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize LightAccount (internal use - use from_api() instead).
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
auth: Authentication context for signing operations
|
|
50
|
+
config: Chain configuration
|
|
51
|
+
public_api: Public API interface
|
|
52
|
+
private_api: Private API interface for authenticated requests
|
|
53
|
+
_state: Initial state (internal use only)
|
|
54
|
+
"""
|
|
55
|
+
self._auth = auth
|
|
56
|
+
self._config = config
|
|
57
|
+
self._logger = logger
|
|
58
|
+
self._public_api = public_api
|
|
59
|
+
self._private_api = private_api
|
|
60
|
+
self._state = _state
|
|
61
|
+
self._history = HistoryOperations.for_wallet(self)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
async def from_api(
|
|
65
|
+
cls,
|
|
66
|
+
*,
|
|
67
|
+
auth: AuthContext,
|
|
68
|
+
config: ChainConfig,
|
|
69
|
+
logger: LoggerType,
|
|
70
|
+
public_api: AsyncPublicAPI,
|
|
71
|
+
private_api: AsyncPrivateAPI,
|
|
72
|
+
) -> LightAccount:
|
|
73
|
+
"""
|
|
74
|
+
Validate LightAccount by fetching its state from the API.
|
|
75
|
+
|
|
76
|
+
This performs a network call to verify the wallet exists and that
|
|
77
|
+
the provided session key is registered and valid.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
auth: Authentication context for signing operations
|
|
81
|
+
config: Chain configuration
|
|
82
|
+
public_api: Public API interface
|
|
83
|
+
private_api: Private API interface for authenticated requests
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Initialized LightAccount instance
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
APIError: If wallet does not exist
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
params = GetAccountRequest(wallet=auth.wallet)
|
|
93
|
+
state = await private_api.rpc.get_account(params)
|
|
94
|
+
logger.debug(f"LightAccount validated: {state.wallet}")
|
|
95
|
+
|
|
96
|
+
signer = auth.account.address
|
|
97
|
+
if signer == auth.wallet: # v3 does not require registration for the owner.
|
|
98
|
+
logger.debug(f"Signing as wallet owner {auth.wallet}")
|
|
99
|
+
else:
|
|
100
|
+
session_keys_params = SessionKeysRequest(wallet=auth.wallet)
|
|
101
|
+
session_keys_response = await private_api.rpc.session_keys(session_keys_params)
|
|
102
|
+
registered = {key.public_session_key: key for key in session_keys_response.public_session_keys}
|
|
103
|
+
log_session_key_status(registered.get(signer), signer=signer, wallet=auth.wallet, logger=logger)
|
|
104
|
+
|
|
105
|
+
return cls(
|
|
106
|
+
auth=auth,
|
|
107
|
+
config=config,
|
|
108
|
+
logger=logger,
|
|
109
|
+
public_api=public_api,
|
|
110
|
+
private_api=private_api,
|
|
111
|
+
_state=state,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def state(self) -> PrivateGetAccountResponse:
|
|
116
|
+
"""Current mutable state."""
|
|
117
|
+
if not self._state:
|
|
118
|
+
msg = "Account state not loaded. Use Account.from_api() to instantiate or call refresh() to load state."
|
|
119
|
+
raise RuntimeError(msg)
|
|
120
|
+
return self._state
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def address(self) -> ChecksumAddress:
|
|
124
|
+
"""LightAccount wallet address."""
|
|
125
|
+
return self._auth.wallet
|
|
126
|
+
|
|
127
|
+
async def refresh(self) -> LightAccount:
|
|
128
|
+
"""Refresh mutable state from API."""
|
|
129
|
+
params = GetAccountRequest(wallet=self._auth.wallet)
|
|
130
|
+
response = await self._private_api.rpc.get_account(params)
|
|
131
|
+
self._state = response
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
async def session_keys(self) -> PrivateSessionKeysResponse:
|
|
135
|
+
"""Registered session keys, including details (expiry, scope, IP whitelist)."""
|
|
136
|
+
|
|
137
|
+
params = SessionKeysRequest(wallet=self.address)
|
|
138
|
+
result = await self._private_api.rpc.session_keys(params)
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
async def set_session_key(
|
|
142
|
+
self,
|
|
143
|
+
*,
|
|
144
|
+
expiry_sec: int,
|
|
145
|
+
public_session_key: str,
|
|
146
|
+
offchain_scopes: list[OffchainScope],
|
|
147
|
+
protocol_scopes: list[ProtocolScope],
|
|
148
|
+
nonce: Optional[int] = None,
|
|
149
|
+
signature_expiry_sec: Optional[int] = None,
|
|
150
|
+
ip_whitelist: list[str] | None = None,
|
|
151
|
+
label: str | None = None,
|
|
152
|
+
subaccount_ids: list[int] | None = None,
|
|
153
|
+
) -> PrivateSetSessionKeyResponse:
|
|
154
|
+
"""Authorizes a new session key for a wallet from a signed action."""
|
|
155
|
+
|
|
156
|
+
wallet = self._auth.wallet
|
|
157
|
+
|
|
158
|
+
module_data = SessionKeyModuleData(
|
|
159
|
+
session_key=public_session_key,
|
|
160
|
+
expiry_sec=expiry_sec,
|
|
161
|
+
protocol_scopes=protocol_scopes,
|
|
162
|
+
subaccount_ids=subaccount_ids or [],
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
module_address = self._config.contracts.CREATE_SESSION_KEY_MODULE
|
|
166
|
+
signed_action = self._auth.sign_action(
|
|
167
|
+
subaccount_id=0,
|
|
168
|
+
nonce=nonce,
|
|
169
|
+
module_address=module_address,
|
|
170
|
+
module_data=module_data,
|
|
171
|
+
signature_expiry_sec=signature_expiry_sec,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
params = SetSessionKeyRequest(
|
|
175
|
+
expiry_sec=expiry_sec,
|
|
176
|
+
nonce=str(signed_action.nonce),
|
|
177
|
+
offchain_scopes=list(map(str, offchain_scopes)),
|
|
178
|
+
protocol_scopes=list(map(str, protocol_scopes)),
|
|
179
|
+
public_session_key=public_session_key,
|
|
180
|
+
signature=signed_action.signature,
|
|
181
|
+
signature_expiry_sec=signed_action.signature_expiry_sec,
|
|
182
|
+
signer=signed_action.signer,
|
|
183
|
+
wallet=wallet,
|
|
184
|
+
ip_whitelist=unset_if_none(ip_whitelist),
|
|
185
|
+
label=unset_if_none(label),
|
|
186
|
+
subaccount_ids=unset_if_none(subaccount_ids),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
result = await self._private_api.rpc.set_session_key(params)
|
|
190
|
+
return result
|
|
191
|
+
|
|
192
|
+
async def edit_session_key(
|
|
193
|
+
self,
|
|
194
|
+
*,
|
|
195
|
+
public_session_key: str,
|
|
196
|
+
ip_whitelist: Optional[list[str]] = None,
|
|
197
|
+
label: Optional[str] = None,
|
|
198
|
+
offchain_scopes: Optional[list[OffchainScope]] = None,
|
|
199
|
+
) -> SessionKey:
|
|
200
|
+
"""Edits session key parameters such as label and IP whitelist."""
|
|
201
|
+
|
|
202
|
+
params = EditSessionKeyRequest(
|
|
203
|
+
wallet=self.address,
|
|
204
|
+
public_session_key=public_session_key,
|
|
205
|
+
ip_whitelist=unset_if_none(ip_whitelist),
|
|
206
|
+
label=unset_if_none(label),
|
|
207
|
+
offchain_scopes=list(map(str, offchain_scopes)) if offchain_scopes is not None else msgspec.UNSET,
|
|
208
|
+
)
|
|
209
|
+
result = await self._private_api.rpc.edit_session_key(params)
|
|
210
|
+
return result
|
|
211
|
+
|
|
212
|
+
async def update_whitelisted_recipients(
|
|
213
|
+
self,
|
|
214
|
+
*,
|
|
215
|
+
add: list[str],
|
|
216
|
+
remove: list[str],
|
|
217
|
+
nonce: Optional[int] = None,
|
|
218
|
+
signature_expiry_sec: Optional[int] = None,
|
|
219
|
+
) -> UpdateWhitelistedRecipientsResponse:
|
|
220
|
+
"""Adds and/or removes recipient wallet addresses on an account's
|
|
221
|
+
transfer whitelist. Resulting list is (current UNION add) MINUS remove."""
|
|
222
|
+
|
|
223
|
+
wallet = self._auth.wallet
|
|
224
|
+
|
|
225
|
+
module_data = WhitelistedRecipientModuleData(add=add, remove=remove)
|
|
226
|
+
|
|
227
|
+
module_address = self._config.contracts.WHITELISTED_RECIPIENT_MODULE
|
|
228
|
+
signed_action = self._auth.sign_action(
|
|
229
|
+
subaccount_id=0,
|
|
230
|
+
nonce=nonce,
|
|
231
|
+
module_address=module_address,
|
|
232
|
+
module_data=module_data,
|
|
233
|
+
signature_expiry_sec=signature_expiry_sec,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
params = UpdateWhitelistedRecipientsRequest(
|
|
237
|
+
add=add,
|
|
238
|
+
remove=remove,
|
|
239
|
+
nonce=signed_action.nonce,
|
|
240
|
+
signature=signed_action.signature,
|
|
241
|
+
signature_expiry_sec=signed_action.signature_expiry_sec,
|
|
242
|
+
signer=signed_action.signer,
|
|
243
|
+
wallet=wallet,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
result = await self._private_api.rpc.update_whitelisted_recipients(params)
|
|
247
|
+
return result
|
|
248
|
+
|
|
249
|
+
async def get_all_portfolios(self) -> list[Subaccount]:
|
|
250
|
+
"""Get all subaccount portfolios of a wallet"""
|
|
251
|
+
|
|
252
|
+
params = GetAllPortfoliosRequest(wallet=self.address)
|
|
253
|
+
result = await self._private_api.rpc.get_all_portfolios(params)
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
async def get_subaccounts(self) -> PrivateGetSubaccountsResponse:
|
|
257
|
+
"""Get all subaccount IDs of an account / wallet"""
|
|
258
|
+
|
|
259
|
+
params = GetSubaccountsRequest(wallet=self.address)
|
|
260
|
+
result = await self._private_api.rpc.get_subaccounts(params)
|
|
261
|
+
return result
|
|
262
|
+
|
|
263
|
+
async def get(self) -> PrivateGetAccountResponse:
|
|
264
|
+
"""Account details getter"""
|
|
265
|
+
|
|
266
|
+
params = GetAccountRequest(wallet=self.address)
|
|
267
|
+
result = await self._private_api.rpc.get_account(params)
|
|
268
|
+
return result
|
|
269
|
+
|
|
270
|
+
@property
|
|
271
|
+
def history(self) -> HistoryOperations:
|
|
272
|
+
"""Historical records across every subaccount of this wallet."""
|
|
273
|
+
|
|
274
|
+
return self._history
|
|
275
|
+
|
|
276
|
+
def __repr__(self) -> str:
|
|
277
|
+
return f"<{self.__class__.__qualname__}({self.address}) object at {hex(id(self))}>"
|