flexvaults 0.1.5__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.
- flexvaults/__init__.py +168 -0
- flexvaults/client/__init__.py +11 -0
- flexvaults/client/errors.py +33 -0
- flexvaults/client/flexvaults_client.py +358 -0
- flexvaults/client/http_client.py +86 -0
- flexvaults/py.typed +0 -0
- flexvaults/signatures/__init__.py +38 -0
- flexvaults/signatures/_signer.py +49 -0
- flexvaults/signatures/eip712_types.py +93 -0
- flexvaults/signatures/sign_lock.py +43 -0
- flexvaults/signatures/sign_transfer.py +36 -0
- flexvaults/signatures/sign_transfer_locked.py +36 -0
- flexvaults/signatures/sign_withdraw.py +36 -0
- flexvaults/types/__init__.py +105 -0
- flexvaults/types/chains.py +52 -0
- flexvaults/types/common.py +57 -0
- flexvaults/types/requests.py +76 -0
- flexvaults/types/responses.py +128 -0
- flexvaults/types/tokens.py +43 -0
- flexvaults/utils.py +79 -0
- flexvaults-0.1.5.dist-info/METADATA +140 -0
- flexvaults-0.1.5.dist-info/RECORD +23 -0
- flexvaults-0.1.5.dist-info/WHEEL +4 -0
flexvaults/__init__.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from .client import (
|
|
2
|
+
AccountingApiError,
|
|
3
|
+
FlexvaultsClient,
|
|
4
|
+
HttpClient,
|
|
5
|
+
NetworkError,
|
|
6
|
+
ValidationError,
|
|
7
|
+
)
|
|
8
|
+
from .signatures import (
|
|
9
|
+
LOCK_TYPES,
|
|
10
|
+
TRANSFER_LOCKED_TYPES,
|
|
11
|
+
TRANSFER_TYPES,
|
|
12
|
+
WITHDRAW_TYPES,
|
|
13
|
+
EIP712Domain,
|
|
14
|
+
LockMessage,
|
|
15
|
+
SignLockParams,
|
|
16
|
+
SignTransferLockedParams,
|
|
17
|
+
SignTransferParams,
|
|
18
|
+
SignWithdrawParams,
|
|
19
|
+
TransferLockedMessage,
|
|
20
|
+
TransferMessage,
|
|
21
|
+
WithdrawMessage,
|
|
22
|
+
create_domain,
|
|
23
|
+
create_lock_expiry,
|
|
24
|
+
sign_lock_message,
|
|
25
|
+
sign_transfer_locked_message,
|
|
26
|
+
sign_transfer_message,
|
|
27
|
+
sign_withdraw_message,
|
|
28
|
+
)
|
|
29
|
+
from .types import (
|
|
30
|
+
NETWORK_CONFIG,
|
|
31
|
+
SUPPORTED_CHAINS,
|
|
32
|
+
SUPPORTED_TOKENS,
|
|
33
|
+
Address,
|
|
34
|
+
BalanceResponse,
|
|
35
|
+
BatchBalancesRequest,
|
|
36
|
+
BatchBalancesResponse,
|
|
37
|
+
Bytes32,
|
|
38
|
+
ChainConfig,
|
|
39
|
+
DepositQuoteRequest,
|
|
40
|
+
DepositQuoteResponse,
|
|
41
|
+
ExpiredLocksResponse,
|
|
42
|
+
HexString,
|
|
43
|
+
IncludeDepositRequest,
|
|
44
|
+
IncludeDepositResponse,
|
|
45
|
+
LockedFundsResponse,
|
|
46
|
+
LockFundsRequest,
|
|
47
|
+
LockInfo,
|
|
48
|
+
Network,
|
|
49
|
+
NetworkConfig,
|
|
50
|
+
PendingWithdrawal,
|
|
51
|
+
PendingWithdrawalsResponse,
|
|
52
|
+
SupportedToken,
|
|
53
|
+
TokenBalance,
|
|
54
|
+
TokenConfig,
|
|
55
|
+
TokenInfoResponse,
|
|
56
|
+
TotalLockedBalanceResponse,
|
|
57
|
+
TransactionData,
|
|
58
|
+
TransactionSubmissionResponse,
|
|
59
|
+
TransferFundsRequest,
|
|
60
|
+
TransferLockedFundsRequest,
|
|
61
|
+
UnlockAllExpiredRequest,
|
|
62
|
+
UnlockFundsRequest,
|
|
63
|
+
WithdrawalInfoResponse,
|
|
64
|
+
WithdrawalRequest,
|
|
65
|
+
get_accounting_contract,
|
|
66
|
+
get_all_tokens,
|
|
67
|
+
get_api_url,
|
|
68
|
+
get_chain_by_id,
|
|
69
|
+
get_chain_id,
|
|
70
|
+
get_explorer_address_url,
|
|
71
|
+
get_token_by_id,
|
|
72
|
+
get_token_config,
|
|
73
|
+
is_valid_token,
|
|
74
|
+
normalize_address,
|
|
75
|
+
normalize_hex,
|
|
76
|
+
)
|
|
77
|
+
from .utils import (
|
|
78
|
+
format_relative_time,
|
|
79
|
+
format_time_remaining,
|
|
80
|
+
format_timestamp,
|
|
81
|
+
format_token_amount,
|
|
82
|
+
is_expired,
|
|
83
|
+
parse_token_amount,
|
|
84
|
+
shorten_address,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
__version__ = "0.1.5"
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"__version__",
|
|
91
|
+
"Address",
|
|
92
|
+
"Bytes32",
|
|
93
|
+
"HexString",
|
|
94
|
+
"Network",
|
|
95
|
+
"NetworkConfig",
|
|
96
|
+
"NETWORK_CONFIG",
|
|
97
|
+
"get_chain_id",
|
|
98
|
+
"get_accounting_contract",
|
|
99
|
+
"get_api_url",
|
|
100
|
+
"normalize_hex",
|
|
101
|
+
"normalize_address",
|
|
102
|
+
"TokenConfig",
|
|
103
|
+
"SupportedToken",
|
|
104
|
+
"SUPPORTED_TOKENS",
|
|
105
|
+
"get_token_config",
|
|
106
|
+
"get_token_by_id",
|
|
107
|
+
"is_valid_token",
|
|
108
|
+
"ChainConfig",
|
|
109
|
+
"SUPPORTED_CHAINS",
|
|
110
|
+
"get_chain_by_id",
|
|
111
|
+
"get_explorer_address_url",
|
|
112
|
+
"get_all_tokens",
|
|
113
|
+
"DepositQuoteRequest",
|
|
114
|
+
"IncludeDepositRequest",
|
|
115
|
+
"LockFundsRequest",
|
|
116
|
+
"UnlockFundsRequest",
|
|
117
|
+
"UnlockAllExpiredRequest",
|
|
118
|
+
"TransferFundsRequest",
|
|
119
|
+
"TransferLockedFundsRequest",
|
|
120
|
+
"WithdrawalRequest",
|
|
121
|
+
"BatchBalancesRequest",
|
|
122
|
+
"TransactionData",
|
|
123
|
+
"DepositQuoteResponse",
|
|
124
|
+
"IncludeDepositResponse",
|
|
125
|
+
"TransactionSubmissionResponse",
|
|
126
|
+
"BalanceResponse",
|
|
127
|
+
"TokenBalance",
|
|
128
|
+
"BatchBalancesResponse",
|
|
129
|
+
"TokenInfoResponse",
|
|
130
|
+
"LockInfo",
|
|
131
|
+
"LockedFundsResponse",
|
|
132
|
+
"ExpiredLocksResponse",
|
|
133
|
+
"TotalLockedBalanceResponse",
|
|
134
|
+
"PendingWithdrawal",
|
|
135
|
+
"PendingWithdrawalsResponse",
|
|
136
|
+
"WithdrawalInfoResponse",
|
|
137
|
+
"FlexvaultsClient",
|
|
138
|
+
"HttpClient",
|
|
139
|
+
"AccountingApiError",
|
|
140
|
+
"NetworkError",
|
|
141
|
+
"ValidationError",
|
|
142
|
+
"EIP712Domain",
|
|
143
|
+
"create_domain",
|
|
144
|
+
"LOCK_TYPES",
|
|
145
|
+
"TRANSFER_TYPES",
|
|
146
|
+
"TRANSFER_LOCKED_TYPES",
|
|
147
|
+
"WITHDRAW_TYPES",
|
|
148
|
+
"LockMessage",
|
|
149
|
+
"TransferMessage",
|
|
150
|
+
"TransferLockedMessage",
|
|
151
|
+
"WithdrawMessage",
|
|
152
|
+
"SignLockParams",
|
|
153
|
+
"sign_lock_message",
|
|
154
|
+
"create_lock_expiry",
|
|
155
|
+
"SignTransferParams",
|
|
156
|
+
"sign_transfer_message",
|
|
157
|
+
"SignTransferLockedParams",
|
|
158
|
+
"sign_transfer_locked_message",
|
|
159
|
+
"SignWithdrawParams",
|
|
160
|
+
"sign_withdraw_message",
|
|
161
|
+
"format_token_amount",
|
|
162
|
+
"parse_token_amount",
|
|
163
|
+
"shorten_address",
|
|
164
|
+
"format_timestamp",
|
|
165
|
+
"is_expired",
|
|
166
|
+
"format_time_remaining",
|
|
167
|
+
"format_relative_time",
|
|
168
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .errors import AccountingApiError, NetworkError, ValidationError
|
|
2
|
+
from .flexvaults_client import FlexvaultsClient
|
|
3
|
+
from .http_client import HttpClient
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"AccountingApiError",
|
|
7
|
+
"NetworkError",
|
|
8
|
+
"ValidationError",
|
|
9
|
+
"HttpClient",
|
|
10
|
+
"FlexvaultsClient",
|
|
11
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AccountingApiError(Exception):
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
message: str,
|
|
8
|
+
status_code: int,
|
|
9
|
+
detail: str | None = None,
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
self.status_code = status_code
|
|
13
|
+
self.detail = detail
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NetworkError(Exception):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
message: str,
|
|
20
|
+
cause: Exception | None = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.__cause__ = cause
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ValidationError(Exception):
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
message: str,
|
|
30
|
+
field: str | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
super().__init__(message)
|
|
33
|
+
self.field = field
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..types import (
|
|
6
|
+
normalize_address,
|
|
7
|
+
normalize_hex,
|
|
8
|
+
)
|
|
9
|
+
from ..types.requests import (
|
|
10
|
+
BatchBalancesRequest,
|
|
11
|
+
DepositQuoteRequest,
|
|
12
|
+
IncludeDepositRequest,
|
|
13
|
+
LockFundsRequest,
|
|
14
|
+
TransferFundsRequest,
|
|
15
|
+
TransferLockedFundsRequest,
|
|
16
|
+
UnlockAllExpiredRequest,
|
|
17
|
+
UnlockFundsRequest,
|
|
18
|
+
WithdrawalRequest,
|
|
19
|
+
)
|
|
20
|
+
from ..types.responses import (
|
|
21
|
+
BalanceResponse,
|
|
22
|
+
BatchBalancesResponse,
|
|
23
|
+
DepositQuoteResponse,
|
|
24
|
+
ExpiredLocksResponse,
|
|
25
|
+
IncludeDepositResponse,
|
|
26
|
+
LockedFundsResponse,
|
|
27
|
+
LockInfo,
|
|
28
|
+
PendingWithdrawal,
|
|
29
|
+
PendingWithdrawalsResponse,
|
|
30
|
+
TokenBalance,
|
|
31
|
+
TokenInfoResponse,
|
|
32
|
+
TotalLockedBalanceResponse,
|
|
33
|
+
TransactionData,
|
|
34
|
+
TransactionSubmissionResponse,
|
|
35
|
+
WithdrawalInfoResponse,
|
|
36
|
+
)
|
|
37
|
+
from .http_client import HttpClient
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _parse_transaction_data(data: dict[str, Any]) -> TransactionData:
|
|
41
|
+
return TransactionData(
|
|
42
|
+
to=data["to"],
|
|
43
|
+
value=data["value"],
|
|
44
|
+
data=data["data"],
|
|
45
|
+
chain_id=data["chain_id"],
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _parse_deposit_quote(data: dict[str, Any]) -> DepositQuoteResponse:
|
|
50
|
+
return DepositQuoteResponse(
|
|
51
|
+
user_address=data["user_address"],
|
|
52
|
+
token_id=data["token_id"],
|
|
53
|
+
amount=data["amount"],
|
|
54
|
+
deposit_address=data["deposit_address"],
|
|
55
|
+
transaction=_parse_transaction_data(data["transaction"]),
|
|
56
|
+
instructions=data["instructions"],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _parse_lock_info(data: dict[str, Any]) -> LockInfo:
|
|
61
|
+
return LockInfo(
|
|
62
|
+
lock_index=data["lock_index"],
|
|
63
|
+
user_address=data["user_address"],
|
|
64
|
+
service_address=data["service_address"],
|
|
65
|
+
token_id=data["token_id"],
|
|
66
|
+
amount=data["amount"],
|
|
67
|
+
expiry=data["expiry"],
|
|
68
|
+
is_expired=data["is_expired"],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _parse_token_balance(data: dict[str, Any]) -> TokenBalance:
|
|
73
|
+
return TokenBalance(
|
|
74
|
+
token_id=data["token_id"],
|
|
75
|
+
balance=data["balance"],
|
|
76
|
+
token_symbol=data["token_symbol"],
|
|
77
|
+
chain_id=data["chain_id"],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _parse_pending_withdrawal(data: dict[str, Any]) -> PendingWithdrawal:
|
|
82
|
+
return PendingWithdrawal(
|
|
83
|
+
index=data["index"],
|
|
84
|
+
user_address=data["user_address"],
|
|
85
|
+
token_id=data["token_id"],
|
|
86
|
+
amount=data["amount"],
|
|
87
|
+
status=data["status"],
|
|
88
|
+
created_at=data["created_at"],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FlexvaultsClient:
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
base_url: str,
|
|
96
|
+
timeout: float = 30.0,
|
|
97
|
+
headers: dict[str, str] | None = None,
|
|
98
|
+
) -> None:
|
|
99
|
+
self._http = HttpClient(
|
|
100
|
+
base_url=base_url,
|
|
101
|
+
timeout=timeout,
|
|
102
|
+
headers=headers,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
async def get_deposit_quote(self, request: DepositQuoteRequest) -> DepositQuoteResponse:
|
|
106
|
+
data = await self._http.post(
|
|
107
|
+
"/v1/accounting/quote/deposit",
|
|
108
|
+
{
|
|
109
|
+
"user_address": normalize_address(request.user_address),
|
|
110
|
+
"token_id": normalize_hex(request.token_id),
|
|
111
|
+
"amount": request.amount,
|
|
112
|
+
},
|
|
113
|
+
)
|
|
114
|
+
return _parse_deposit_quote(data)
|
|
115
|
+
|
|
116
|
+
async def include_deposit(self, request: IncludeDepositRequest) -> IncludeDepositResponse:
|
|
117
|
+
body: dict[str, Any] = {
|
|
118
|
+
"user_address": normalize_address(request.user_address),
|
|
119
|
+
"token_id": normalize_hex(request.token_id),
|
|
120
|
+
"evm_transaction_data": normalize_hex(request.evm_transaction_data),
|
|
121
|
+
}
|
|
122
|
+
if request.rlp_block_header is not None:
|
|
123
|
+
body["rlp_block_header"] = normalize_hex(request.rlp_block_header)
|
|
124
|
+
if request.transaction_index_rlp is not None:
|
|
125
|
+
body["transaction_index_rlp"] = normalize_hex(request.transaction_index_rlp)
|
|
126
|
+
if request.transaction_proof_stack is not None:
|
|
127
|
+
body["transaction_proof_stack"] = normalize_hex(request.transaction_proof_stack)
|
|
128
|
+
|
|
129
|
+
data = await self._http.post("/v1/accounting/deposits", body)
|
|
130
|
+
return IncludeDepositResponse(
|
|
131
|
+
submission_id=data["submission_id"],
|
|
132
|
+
status=data["status"],
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
async def get_balance(
|
|
136
|
+
self,
|
|
137
|
+
user_address: str,
|
|
138
|
+
token_id: str,
|
|
139
|
+
) -> BalanceResponse:
|
|
140
|
+
user = normalize_address(user_address)
|
|
141
|
+
token = normalize_hex(token_id)
|
|
142
|
+
data = await self._http.get(f"/v1/accounting/balances/{user}/{token}")
|
|
143
|
+
return BalanceResponse(
|
|
144
|
+
user_address=data["user_address"],
|
|
145
|
+
token_id=data["token_id"],
|
|
146
|
+
balance=data["balance"],
|
|
147
|
+
token_symbol=data["token_symbol"],
|
|
148
|
+
chain_id=data["chain_id"],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
async def get_batch_balances(
|
|
152
|
+
self, request: BatchBalancesRequest
|
|
153
|
+
) -> BatchBalancesResponse:
|
|
154
|
+
data = await self._http.post(
|
|
155
|
+
"/v1/accounting/balances/batch",
|
|
156
|
+
{
|
|
157
|
+
"user_address": normalize_address(request.user_address),
|
|
158
|
+
"token_ids": [normalize_hex(tid) for tid in request.token_ids],
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
return BatchBalancesResponse(
|
|
162
|
+
user_address=data["user_address"],
|
|
163
|
+
balances=[_parse_token_balance(b) for b in data.get("balances", [])],
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
async def get_token_info(self, token_id: str) -> TokenInfoResponse:
|
|
167
|
+
token = normalize_hex(token_id)
|
|
168
|
+
data = await self._http.get(f"/v1/accounting/tokens/{token}")
|
|
169
|
+
return TokenInfoResponse(
|
|
170
|
+
token_id=data["token_id"],
|
|
171
|
+
symbol=data["symbol"],
|
|
172
|
+
decimals=data["decimals"],
|
|
173
|
+
chain_id=data["chain_id"],
|
|
174
|
+
contract_address=data.get("contract_address"),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
async def lock_funds(
|
|
178
|
+
self, request: LockFundsRequest
|
|
179
|
+
) -> TransactionSubmissionResponse:
|
|
180
|
+
data = await self._http.post(
|
|
181
|
+
"/v1/accounting/funds/lock",
|
|
182
|
+
{
|
|
183
|
+
"user_address": normalize_address(request.user_address),
|
|
184
|
+
"service_address": normalize_address(request.service_address),
|
|
185
|
+
"token_id": normalize_hex(request.token_id),
|
|
186
|
+
"amount": request.amount,
|
|
187
|
+
"expiry": request.expiry,
|
|
188
|
+
"signature": normalize_hex(request.signature),
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
return TransactionSubmissionResponse(
|
|
192
|
+
submission_id=data["submission_id"],
|
|
193
|
+
status=data["status"],
|
|
194
|
+
detail=data.get("detail"),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
async def unlock_funds(
|
|
198
|
+
self, request: UnlockFundsRequest
|
|
199
|
+
) -> TransactionSubmissionResponse:
|
|
200
|
+
data = await self._http.post(
|
|
201
|
+
"/v1/accounting/funds/unlock",
|
|
202
|
+
{
|
|
203
|
+
"user_address": normalize_address(request.user_address),
|
|
204
|
+
"lock_index": request.lock_index,
|
|
205
|
+
},
|
|
206
|
+
)
|
|
207
|
+
return TransactionSubmissionResponse(
|
|
208
|
+
submission_id=data["submission_id"],
|
|
209
|
+
status=data["status"],
|
|
210
|
+
detail=data.get("detail"),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
async def unlock_all_expired(
|
|
214
|
+
self, request: UnlockAllExpiredRequest
|
|
215
|
+
) -> TransactionSubmissionResponse:
|
|
216
|
+
data = await self._http.post(
|
|
217
|
+
"/v1/accounting/funds/unlock-all-expired",
|
|
218
|
+
{
|
|
219
|
+
"user_address": normalize_address(request.user_address),
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
return TransactionSubmissionResponse(
|
|
223
|
+
submission_id=data["submission_id"],
|
|
224
|
+
status=data["status"],
|
|
225
|
+
detail=data.get("detail"),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
async def get_locked_funds(
|
|
229
|
+
self,
|
|
230
|
+
user_address: str,
|
|
231
|
+
service_address: str | None = None,
|
|
232
|
+
) -> LockedFundsResponse:
|
|
233
|
+
user = normalize_address(user_address)
|
|
234
|
+
query_params = ""
|
|
235
|
+
if service_address:
|
|
236
|
+
query_params = f"?service_address={normalize_address(service_address)}"
|
|
237
|
+
data = await self._http.get(f"/v1/accounting/funds/locked/{user}{query_params}")
|
|
238
|
+
return LockedFundsResponse(
|
|
239
|
+
user_address=data["user_address"],
|
|
240
|
+
service_address=data.get("service_address"),
|
|
241
|
+
locks=[_parse_lock_info(lock) for lock in data.get("locks", [])],
|
|
242
|
+
total_locked=data.get("total_locked", 0),
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
async def get_total_locked_balance(
|
|
246
|
+
self,
|
|
247
|
+
user_address: str,
|
|
248
|
+
token_id: str,
|
|
249
|
+
) -> TotalLockedBalanceResponse:
|
|
250
|
+
user = normalize_address(user_address)
|
|
251
|
+
token = normalize_hex(token_id)
|
|
252
|
+
data = await self._http.get(
|
|
253
|
+
f"/v1/accounting/funds/locked/total/{user}/{token}"
|
|
254
|
+
)
|
|
255
|
+
return TotalLockedBalanceResponse(
|
|
256
|
+
user_address=data["user_address"],
|
|
257
|
+
token_id=data["token_id"],
|
|
258
|
+
total_locked=data["total_locked"],
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
async def get_expired_locks(self, user_address: str) -> ExpiredLocksResponse:
|
|
262
|
+
user = normalize_address(user_address)
|
|
263
|
+
data = await self._http.get(f"/v1/accounting/funds/expired/{user}")
|
|
264
|
+
return ExpiredLocksResponse(
|
|
265
|
+
user_address=data["user_address"],
|
|
266
|
+
expired_locks=[_parse_lock_info(lock) for lock in data.get("expired_locks", [])],
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
async def transfer_funds(
|
|
270
|
+
self, request: TransferFundsRequest
|
|
271
|
+
) -> TransactionSubmissionResponse:
|
|
272
|
+
data = await self._http.post(
|
|
273
|
+
"/v1/accounting/funds/transfer",
|
|
274
|
+
{
|
|
275
|
+
"user_address": normalize_address(request.user_address),
|
|
276
|
+
"to_address": normalize_address(request.to_address),
|
|
277
|
+
"token_id": normalize_hex(request.token_id),
|
|
278
|
+
"amount": request.amount,
|
|
279
|
+
"signature": normalize_hex(request.signature),
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
return TransactionSubmissionResponse(
|
|
283
|
+
submission_id=data["submission_id"],
|
|
284
|
+
status=data["status"],
|
|
285
|
+
detail=data.get("detail"),
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
async def transfer_locked_funds(
|
|
289
|
+
self, request: TransferLockedFundsRequest
|
|
290
|
+
) -> TransactionSubmissionResponse:
|
|
291
|
+
data = await self._http.post(
|
|
292
|
+
"/v1/accounting/funds/transfer-locked",
|
|
293
|
+
{
|
|
294
|
+
"user_address": normalize_address(request.user_address),
|
|
295
|
+
"lock_index": request.lock_index,
|
|
296
|
+
"to_address": normalize_address(request.to_address),
|
|
297
|
+
"amount": request.amount,
|
|
298
|
+
"signature": normalize_hex(request.signature),
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
return TransactionSubmissionResponse(
|
|
302
|
+
submission_id=data["submission_id"],
|
|
303
|
+
status=data["status"],
|
|
304
|
+
detail=data.get("detail"),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
async def request_withdrawal(
|
|
308
|
+
self, request: WithdrawalRequest
|
|
309
|
+
) -> TransactionSubmissionResponse:
|
|
310
|
+
data = await self._http.post(
|
|
311
|
+
"/v1/accounting/withdraw",
|
|
312
|
+
{
|
|
313
|
+
"user_address": normalize_address(request.user_address),
|
|
314
|
+
"token_id": normalize_hex(request.token_id),
|
|
315
|
+
"amount": request.amount,
|
|
316
|
+
"nonce": request.nonce,
|
|
317
|
+
"signature": normalize_hex(request.signature),
|
|
318
|
+
},
|
|
319
|
+
)
|
|
320
|
+
return TransactionSubmissionResponse(
|
|
321
|
+
submission_id=data["submission_id"],
|
|
322
|
+
status=data["status"],
|
|
323
|
+
detail=data.get("detail"),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
async def get_pending_withdrawals(
|
|
327
|
+
self, user_address: str
|
|
328
|
+
) -> PendingWithdrawalsResponse:
|
|
329
|
+
user = normalize_address(user_address)
|
|
330
|
+
data = await self._http.get(f"/v1/accounting/withdraw/pending/{user}")
|
|
331
|
+
return PendingWithdrawalsResponse(
|
|
332
|
+
user_address=data["user_address"],
|
|
333
|
+
withdrawals=[
|
|
334
|
+
_parse_pending_withdrawal(w) for w in data.get("withdrawals", [])
|
|
335
|
+
],
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
async def get_withdrawal_info(self, index: int) -> WithdrawalInfoResponse:
|
|
339
|
+
data = await self._http.get(f"/v1/accounting/withdraw/{index}")
|
|
340
|
+
return WithdrawalInfoResponse(
|
|
341
|
+
index=data["index"],
|
|
342
|
+
user_address=data["user_address"],
|
|
343
|
+
token_id=data["token_id"],
|
|
344
|
+
amount=data["amount"],
|
|
345
|
+
status=data["status"],
|
|
346
|
+
created_at=data["created_at"],
|
|
347
|
+
completed_at=data.get("completed_at"),
|
|
348
|
+
transaction_hash=data.get("transaction_hash"),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
async def close(self) -> None:
|
|
352
|
+
await self._http.close()
|
|
353
|
+
|
|
354
|
+
async def __aenter__(self) -> FlexvaultsClient:
|
|
355
|
+
return self
|
|
356
|
+
|
|
357
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
358
|
+
await self.close()
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .errors import AccountingApiError, NetworkError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HttpClient:
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
base_url: str,
|
|
14
|
+
timeout: float = 30.0,
|
|
15
|
+
headers: dict[str, str] | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
self._base_url = base_url.rstrip("/")
|
|
18
|
+
self._timeout = timeout
|
|
19
|
+
self._headers = {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
**(headers or {}),
|
|
22
|
+
}
|
|
23
|
+
self._client = httpx.AsyncClient(
|
|
24
|
+
base_url=self._base_url,
|
|
25
|
+
headers=self._headers,
|
|
26
|
+
timeout=httpx.Timeout(self._timeout),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def get(self, path: str) -> Any:
|
|
30
|
+
return await self._request("GET", path)
|
|
31
|
+
|
|
32
|
+
async def post(self, path: str, body: Any | None = None) -> Any:
|
|
33
|
+
return await self._request("POST", path, body)
|
|
34
|
+
|
|
35
|
+
async def _request(
|
|
36
|
+
self,
|
|
37
|
+
method: str,
|
|
38
|
+
path: str,
|
|
39
|
+
body: Any | None = None,
|
|
40
|
+
) -> Any:
|
|
41
|
+
try:
|
|
42
|
+
response = await self._client.request(
|
|
43
|
+
method=method,
|
|
44
|
+
url=path,
|
|
45
|
+
json=body,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if not response.is_success:
|
|
49
|
+
detail: str | None = None
|
|
50
|
+
try:
|
|
51
|
+
error_body = response.json()
|
|
52
|
+
detail = error_body.get("detail") or error_body.get("message")
|
|
53
|
+
except Exception:
|
|
54
|
+
try:
|
|
55
|
+
detail = response.text
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
raise AccountingApiError(
|
|
60
|
+
f"API request failed: {response.status_code} {response.reason_phrase}",
|
|
61
|
+
response.status_code,
|
|
62
|
+
detail,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return response.json()
|
|
66
|
+
|
|
67
|
+
except AccountingApiError:
|
|
68
|
+
raise
|
|
69
|
+
except httpx.TimeoutException:
|
|
70
|
+
raise NetworkError(f"Request timeout after {self._timeout}s")
|
|
71
|
+
except httpx.HTTPError as e:
|
|
72
|
+
raise NetworkError(f"Network request failed: {str(e)}", e)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
if isinstance(e, (AccountingApiError, NetworkError)):
|
|
75
|
+
raise
|
|
76
|
+
cause = e if isinstance(e, Exception) else None
|
|
77
|
+
raise NetworkError(f"Unknown network error occurred: {str(e)}", cause)
|
|
78
|
+
|
|
79
|
+
async def close(self) -> None:
|
|
80
|
+
await self._client.aclose()
|
|
81
|
+
|
|
82
|
+
async def __aenter__(self) -> HttpClient:
|
|
83
|
+
return self
|
|
84
|
+
|
|
85
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
86
|
+
await self.close()
|
flexvaults/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from .eip712_types import (
|
|
2
|
+
LOCK_TYPES,
|
|
3
|
+
TRANSFER_LOCKED_TYPES,
|
|
4
|
+
TRANSFER_TYPES,
|
|
5
|
+
WITHDRAW_TYPES,
|
|
6
|
+
EIP712Domain,
|
|
7
|
+
LockMessage,
|
|
8
|
+
TransferLockedMessage,
|
|
9
|
+
TransferMessage,
|
|
10
|
+
WithdrawMessage,
|
|
11
|
+
create_domain,
|
|
12
|
+
)
|
|
13
|
+
from .sign_lock import SignLockParams, create_lock_expiry, sign_lock_message
|
|
14
|
+
from .sign_transfer import SignTransferParams, sign_transfer_message
|
|
15
|
+
from .sign_transfer_locked import SignTransferLockedParams, sign_transfer_locked_message
|
|
16
|
+
from .sign_withdraw import SignWithdrawParams, sign_withdraw_message
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"EIP712Domain",
|
|
20
|
+
"create_domain",
|
|
21
|
+
"LOCK_TYPES",
|
|
22
|
+
"TRANSFER_TYPES",
|
|
23
|
+
"TRANSFER_LOCKED_TYPES",
|
|
24
|
+
"WITHDRAW_TYPES",
|
|
25
|
+
"LockMessage",
|
|
26
|
+
"TransferMessage",
|
|
27
|
+
"TransferLockedMessage",
|
|
28
|
+
"WithdrawMessage",
|
|
29
|
+
"SignLockParams",
|
|
30
|
+
"sign_lock_message",
|
|
31
|
+
"create_lock_expiry",
|
|
32
|
+
"SignTransferParams",
|
|
33
|
+
"sign_transfer_message",
|
|
34
|
+
"SignTransferLockedParams",
|
|
35
|
+
"sign_transfer_locked_message",
|
|
36
|
+
"SignWithdrawParams",
|
|
37
|
+
"sign_withdraw_message",
|
|
38
|
+
]
|