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.
Files changed (102) hide show
  1. derive_py/__init__.py +11 -0
  2. derive_py/_clients/__init__.py +14 -0
  3. derive_py/_clients/rest/__init__.py +0 -0
  4. derive_py/_clients/rest/async_http/__init__.py +1 -0
  5. derive_py/_clients/rest/async_http/account.py +277 -0
  6. derive_py/_clients/rest/async_http/api.py +2588 -0
  7. derive_py/_clients/rest/async_http/client.py +276 -0
  8. derive_py/_clients/rest/async_http/collateral.py +192 -0
  9. derive_py/_clients/rest/async_http/history.py +282 -0
  10. derive_py/_clients/rest/async_http/markets.py +298 -0
  11. derive_py/_clients/rest/async_http/mmp.py +75 -0
  12. derive_py/_clients/rest/async_http/orders.py +359 -0
  13. derive_py/_clients/rest/async_http/positions.py +149 -0
  14. derive_py/_clients/rest/async_http/rfq.py +470 -0
  15. derive_py/_clients/rest/async_http/session.py +154 -0
  16. derive_py/_clients/rest/async_http/subaccount.py +348 -0
  17. derive_py/_clients/rest/async_http/system.py +47 -0
  18. derive_py/_clients/rest/async_http/vaults.py +774 -0
  19. derive_py/_clients/rest/endpoints.py +158 -0
  20. derive_py/_clients/rest/http/__init__.py +1 -0
  21. derive_py/_clients/rest/http/account.py +277 -0
  22. derive_py/_clients/rest/http/api.py +2588 -0
  23. derive_py/_clients/rest/http/client.py +274 -0
  24. derive_py/_clients/rest/http/collateral.py +190 -0
  25. derive_py/_clients/rest/http/history.py +282 -0
  26. derive_py/_clients/rest/http/markets.py +294 -0
  27. derive_py/_clients/rest/http/mmp.py +75 -0
  28. derive_py/_clients/rest/http/orders.py +359 -0
  29. derive_py/_clients/rest/http/positions.py +149 -0
  30. derive_py/_clients/rest/http/rfq.py +470 -0
  31. derive_py/_clients/rest/http/session.py +153 -0
  32. derive_py/_clients/rest/http/subaccount.py +345 -0
  33. derive_py/_clients/rest/http/system.py +47 -0
  34. derive_py/_clients/rest/http/vaults.py +774 -0
  35. derive_py/_clients/utils.py +644 -0
  36. derive_py/_clients/websockets/__init__.py +1 -0
  37. derive_py/_clients/websockets/api.py +2766 -0
  38. derive_py/_clients/websockets/client.py +431 -0
  39. derive_py/_clients/websockets/session.py +792 -0
  40. derive_py/_web3/__init__.py +14 -0
  41. derive_py/_web3/abi.py +47 -0
  42. derive_py/_web3/action_signing/__init__.py +65 -0
  43. derive_py/_web3/action_signing/module_data/__init__.py +47 -0
  44. derive_py/_web3/action_signing/module_data/module_data.py +27 -0
  45. derive_py/_web3/action_signing/module_data/rfq.py +147 -0
  46. derive_py/_web3/action_signing/module_data/session_key.py +54 -0
  47. derive_py/_web3/action_signing/module_data/trade.py +40 -0
  48. derive_py/_web3/action_signing/module_data/transfer_positions.py +53 -0
  49. derive_py/_web3/action_signing/module_data/transfer_spot.py +47 -0
  50. derive_py/_web3/action_signing/module_data/transfer_spot_external.py +54 -0
  51. derive_py/_web3/action_signing/module_data/vault.py +302 -0
  52. derive_py/_web3/action_signing/module_data/whitelisted_recipients.py +32 -0
  53. derive_py/_web3/action_signing/module_data/withdraw.py +52 -0
  54. derive_py/_web3/action_signing/signed_action.py +129 -0
  55. derive_py/_web3/action_signing/utils.py +174 -0
  56. derive_py/_web3/async_utils.py +77 -0
  57. derive_py/_web3/deposits.py +280 -0
  58. derive_py/_web3/erc20.py +69 -0
  59. derive_py/_web3/provider.py +456 -0
  60. derive_py/_web3/tx.py +264 -0
  61. derive_py/cli/__init__.py +60 -0
  62. derive_py/cli/_account.py +53 -0
  63. derive_py/cli/_collateral.py +30 -0
  64. derive_py/cli/_columns.py +238 -0
  65. derive_py/cli/_markets.py +318 -0
  66. derive_py/cli/_mmp.py +118 -0
  67. derive_py/cli/_orders.py +151 -0
  68. derive_py/cli/_positions.py +73 -0
  69. derive_py/cli/_system.py +58 -0
  70. derive_py/cli/_tree.py +115 -0
  71. derive_py/cli/_utils.py +225 -0
  72. derive_py/config/__init__.py +32 -0
  73. derive_py/config/constants.py +46 -0
  74. derive_py/config/contracts.py +83 -0
  75. derive_py/config/rpc.py +60 -0
  76. derive_py/data/abis/erc20.json +222 -0
  77. derive_py/data/abis/sepolia/0x1573bde26338A9E6AA358638679A796c81E33246.json +112 -0
  78. derive_py/data/abis/sepolia/0x3FB79aafCD401CDD19e3d729241545955DDE0D48.json +112 -0
  79. derive_py/data/abis/sepolia/0x3fBcEB634d2582791B462059D946800c9f41bEfB.json +1210 -0
  80. derive_py/data/abis/sepolia/0xFbB62CE2BbFdFdc8a60DDC22115aC29cf91B4566.json +112 -0
  81. derive_py/data/abis/sepolia/0xc02CdE0d7b7B34C02AAa6cF753D8b4453297236c.json +243 -0
  82. derive_py/data/abis/sepolia/0xd3625eCf97E5554C62A48Ac1c9284C9dCeFceB68.json +112 -0
  83. derive_py/data/abis/sepolia/0xdBC22b75506FED598b1A114Ba51b9Ca005dE8B65.json +1635 -0
  84. derive_py/data/abis/sepolia/0xdFB882fDa2BcdB74358506D56D14E746c76eF864.json +782 -0
  85. derive_py/data/abis/sepolia/contracts.json +30 -0
  86. derive_py/data/abis/sepolia/proxy_mapping.json +6 -0
  87. derive_py/data/templates/api.py.jinja +226 -0
  88. derive_py/data/templates/endpoints.py.jinja +45 -0
  89. derive_py/data_types/__init__.py +81 -0
  90. derive_py/data_types/channel_models.py +235 -0
  91. derive_py/data_types/enums.py +257 -0
  92. derive_py/data_types/generated_models.py +2665 -0
  93. derive_py/data_types/models.py +473 -0
  94. derive_py/data_types/utils.py +24 -0
  95. derive_py/exceptions.py +159 -0
  96. derive_py/utils/__init__.py +7 -0
  97. derive_py/utils/logger.py +27 -0
  98. derive_py-0.1.3.dist-info/METADATA +374 -0
  99. derive_py-0.1.3.dist-info/RECORD +102 -0
  100. derive_py-0.1.3.dist-info/WHEEL +4 -0
  101. derive_py-0.1.3.dist-info/entry_points.txt +3 -0
  102. 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))}>"