afp-sdk 0.4.0__py3-none-any.whl → 0.5.0__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.
- afp/__init__.py +12 -7
- afp/afp.py +210 -0
- afp/api/admin.py +1 -14
- afp/api/base.py +82 -29
- afp/api/{clearing.py → margin_account.py} +172 -104
- afp/api/{builder.py → product.py} +103 -37
- afp/api/trading.py +12 -29
- afp/auth.py +66 -0
- afp/bindings/facade.py +25 -13
- afp/config.py +22 -36
- afp/constants.py +52 -0
- afp/decorators.py +3 -1
- afp/exceptions.py +7 -3
- afp/exchange.py +11 -5
- afp/{signing.py → hashing.py} +4 -12
- afp/schemas.py +9 -0
- afp/validators.py +1 -0
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.0.dist-info}/METADATA +54 -50
- afp_sdk-0.5.0.dist-info/RECORD +37 -0
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.0.dist-info}/WHEEL +1 -1
- afp/.ruff_cache/.gitignore +0 -2
- afp/.ruff_cache/0.12.7/14089225400260942471 +0 -0
- afp/.ruff_cache/CACHEDIR.TAG +0 -1
- afp/api/liquidation.py +0 -167
- afp_sdk-0.4.0.dist-info/RECORD +0 -38
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
2
|
from functools import cache
|
|
3
|
+
from typing import Iterable
|
|
3
4
|
|
|
4
5
|
from eth_typing.evm import ChecksumAddress
|
|
5
6
|
from hexbytes import HexBytes
|
|
@@ -8,10 +9,12 @@ from web3.exceptions import ContractCustomError
|
|
|
8
9
|
|
|
9
10
|
from .. import validators
|
|
10
11
|
from ..bindings import (
|
|
12
|
+
BidData,
|
|
11
13
|
ClearingDiamond,
|
|
12
|
-
MarginAccount,
|
|
14
|
+
MarginAccount as MarginContract,
|
|
13
15
|
MarginAccountRegistry,
|
|
14
16
|
ProductRegistry,
|
|
17
|
+
Side as OnChainOrderSide,
|
|
15
18
|
)
|
|
16
19
|
from ..bindings.erc20 import ERC20
|
|
17
20
|
from ..bindings.facade import CLEARING_DIAMOND_ABI
|
|
@@ -20,26 +23,41 @@ from ..bindings.margin_account_registry import ABI as MARGIN_ACCOUNT_REGISTRY_AB
|
|
|
20
23
|
from ..bindings.product_registry import ABI as PRODUCT_REGISTRY_ABI
|
|
21
24
|
from ..decorators import convert_web3_error
|
|
22
25
|
from ..exceptions import NotFoundError
|
|
23
|
-
from ..schemas import Position
|
|
26
|
+
from ..schemas import AuctionData, Bid, OrderSide, Position, Transaction
|
|
24
27
|
from .base import ClearingSystemAPI
|
|
25
|
-
from .builder import Builder
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
class
|
|
29
|
-
"""API for managing margin accounts.
|
|
30
|
+
class MarginAccount(ClearingSystemAPI):
|
|
31
|
+
"""API for managing margin accounts."""
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
### Factories ###
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def create_bid(product_id: str, price: Decimal, quantity: int, side: str) -> Bid:
|
|
37
|
+
"""Create a bid to be submitted to a liquidation auction.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
product_id : str
|
|
42
|
+
price: Decimal
|
|
43
|
+
quantity: int
|
|
44
|
+
side: str
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
afp.schemas.Bid
|
|
49
|
+
"""
|
|
50
|
+
return Bid(
|
|
51
|
+
product_id=product_id,
|
|
52
|
+
price=price,
|
|
53
|
+
quantity=quantity,
|
|
54
|
+
side=getattr(OrderSide, side.upper()),
|
|
55
|
+
)
|
|
38
56
|
|
|
39
57
|
### Transactions ###
|
|
40
58
|
|
|
41
59
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
42
|
-
def authorize(self, collateral_asset: str, intent_account_id: str) ->
|
|
60
|
+
def authorize(self, collateral_asset: str, intent_account_id: str) -> Transaction:
|
|
43
61
|
"""Authorizes a blockchain account to submit intents to the clearing system
|
|
44
62
|
using the margin account associated with the collateral asset.
|
|
45
63
|
|
|
@@ -52,24 +70,19 @@ class Clearing(ClearingSystemAPI):
|
|
|
52
70
|
|
|
53
71
|
Returns
|
|
54
72
|
-------
|
|
55
|
-
|
|
56
|
-
|
|
73
|
+
afp.schemas.Transaction
|
|
74
|
+
Transaction parameters.
|
|
57
75
|
"""
|
|
58
76
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
59
77
|
intent_account_id = validators.validate_address(intent_account_id)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
self._margin_contract(collateral_asset)
|
|
63
|
-
.authorize(intent_account_id)
|
|
64
|
-
.transact()
|
|
78
|
+
return self._transact(
|
|
79
|
+
self._margin_contract(collateral_asset).authorize(intent_account_id)
|
|
65
80
|
)
|
|
66
|
-
self._w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
67
|
-
return Web3.to_hex(tx_hash)
|
|
68
81
|
|
|
69
82
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
70
|
-
def
|
|
83
|
+
def deposit(
|
|
71
84
|
self, collateral_asset: str, amount: Decimal
|
|
72
|
-
) -> tuple[
|
|
85
|
+
) -> tuple[Transaction, Transaction]:
|
|
73
86
|
"""Deposits the specified amount of collateral tokens into the margin account
|
|
74
87
|
associated with the collateral asset.
|
|
75
88
|
|
|
@@ -85,32 +98,28 @@ class Clearing(ClearingSystemAPI):
|
|
|
85
98
|
|
|
86
99
|
Returns
|
|
87
100
|
-------
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
afp.schemas.Transaction
|
|
102
|
+
Parameters of the approval transaction.
|
|
103
|
+
afp.schemas.Transaction
|
|
104
|
+
Parameters of the deposit transaction.
|
|
92
105
|
"""
|
|
93
106
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
94
107
|
token_amount = int(amount * 10 ** self._decimals(collateral_asset))
|
|
95
108
|
token_contract = ERC20(self._w3, collateral_asset)
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
tx2_hash = (
|
|
104
|
-
self._margin_contract(collateral_asset).deposit(token_amount).transact()
|
|
110
|
+
tx1 = self._transact(
|
|
111
|
+
token_contract.approve(
|
|
112
|
+
self._margin_contract(collateral_asset)._contract.address, # type: ignore
|
|
113
|
+
token_amount,
|
|
114
|
+
)
|
|
105
115
|
)
|
|
106
|
-
self.
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
tx2 = self._transact(
|
|
117
|
+
self._margin_contract(collateral_asset).deposit(token_amount)
|
|
118
|
+
)
|
|
119
|
+
return (tx1, tx2)
|
|
109
120
|
|
|
110
121
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
111
|
-
def
|
|
112
|
-
self, collateral_asset: str, amount: Decimal
|
|
113
|
-
) -> str:
|
|
122
|
+
def withdraw(self, collateral_asset: str, amount: Decimal) -> Transaction:
|
|
114
123
|
"""Withdraws the specified amount of collateral tokens from the margin account
|
|
115
124
|
associated with the collateral asset.
|
|
116
125
|
|
|
@@ -123,46 +132,83 @@ class Clearing(ClearingSystemAPI):
|
|
|
123
132
|
|
|
124
133
|
Returns
|
|
125
134
|
-------
|
|
126
|
-
|
|
127
|
-
|
|
135
|
+
afp.schemas.Transaction
|
|
136
|
+
Transaction parameters.
|
|
128
137
|
"""
|
|
129
138
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
130
139
|
token_amount = int(amount * 10 ** self._decimals(collateral_asset))
|
|
131
|
-
|
|
132
|
-
self._margin_contract(collateral_asset).withdraw(token_amount)
|
|
140
|
+
return self._transact(
|
|
141
|
+
self._margin_contract(collateral_asset).withdraw(token_amount)
|
|
133
142
|
)
|
|
134
|
-
self._w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
135
|
-
return Web3.to_hex(tx_hash)
|
|
136
143
|
|
|
137
144
|
@convert_web3_error(CLEARING_DIAMOND_ABI)
|
|
138
|
-
def
|
|
139
|
-
|
|
145
|
+
def request_liquidation(
|
|
146
|
+
self, margin_account_id: str, collateral_asset: str
|
|
147
|
+
) -> Transaction:
|
|
148
|
+
"""Request a liquidation auction to be started.
|
|
140
149
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
margin_account_id : str
|
|
153
|
+
The ID of the margin account to be liquidated.
|
|
154
|
+
collateral_asset : str
|
|
155
|
+
The address of the collateral token that the margin account is trading with.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
afp.schemas.Transaction
|
|
160
|
+
Transaction parameters.
|
|
161
|
+
"""
|
|
162
|
+
margin_account_id = validators.validate_address(margin_account_id)
|
|
163
|
+
collateral_asset = validators.validate_address(collateral_asset)
|
|
164
|
+
|
|
165
|
+
clearing_contract = ClearingDiamond(
|
|
166
|
+
self._w3, self._config.clearing_diamond_address
|
|
167
|
+
)
|
|
168
|
+
return self._transact(
|
|
169
|
+
clearing_contract.request_liquidation(margin_account_id, collateral_asset)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
@convert_web3_error(CLEARING_DIAMOND_ABI, PRODUCT_REGISTRY_ABI)
|
|
173
|
+
def submit_bids(
|
|
174
|
+
self, margin_account_id: str, collateral_asset: str, bids: Iterable[Bid]
|
|
175
|
+
) -> Transaction:
|
|
176
|
+
"""Submit bids to a liquidation auction.
|
|
144
177
|
|
|
145
178
|
Parameters
|
|
146
179
|
----------
|
|
147
|
-
|
|
148
|
-
The ID of the
|
|
149
|
-
|
|
150
|
-
|
|
180
|
+
margin_account_id : str
|
|
181
|
+
The ID of the margin account that is being liquidated.
|
|
182
|
+
collateral_asset : str
|
|
183
|
+
The address of the collateral token that the margin account is trading with.
|
|
184
|
+
bids: list of afp.schemas.Bid
|
|
151
185
|
|
|
152
186
|
Returns
|
|
153
187
|
-------
|
|
154
|
-
|
|
155
|
-
|
|
188
|
+
afp.schemas.Transaction
|
|
189
|
+
Transaction parameters.
|
|
156
190
|
"""
|
|
157
|
-
|
|
158
|
-
|
|
191
|
+
margin_account_id = validators.validate_address(margin_account_id)
|
|
192
|
+
collateral_asset = validators.validate_address(collateral_asset)
|
|
159
193
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
194
|
+
converted_bids = [
|
|
195
|
+
BidData(
|
|
196
|
+
product_id=HexBytes(bid.product_id),
|
|
197
|
+
price=int(bid.price * 10 ** self._tick_size(bid.product_id)),
|
|
198
|
+
quantity=bid.quantity,
|
|
199
|
+
side=getattr(OnChainOrderSide, bid.side.name),
|
|
200
|
+
)
|
|
201
|
+
for bid in bids
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
clearing_contract = ClearingDiamond(
|
|
205
|
+
self._w3, self._config.clearing_diamond_address
|
|
206
|
+
)
|
|
207
|
+
return self._transact(
|
|
208
|
+
clearing_contract.bid_auction(
|
|
209
|
+
margin_account_id, collateral_asset, converted_bids
|
|
210
|
+
)
|
|
211
|
+
)
|
|
166
212
|
|
|
167
213
|
### Views ###
|
|
168
214
|
|
|
@@ -181,7 +227,9 @@ class Clearing(ClearingSystemAPI):
|
|
|
181
227
|
Decimal
|
|
182
228
|
"""
|
|
183
229
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
184
|
-
amount = self._margin_contract(collateral_asset).capital(
|
|
230
|
+
amount = self._margin_contract(collateral_asset).capital(
|
|
231
|
+
self._authenticator.address
|
|
232
|
+
)
|
|
185
233
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
186
234
|
|
|
187
235
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
@@ -202,7 +250,7 @@ class Clearing(ClearingSystemAPI):
|
|
|
202
250
|
"""
|
|
203
251
|
validators.validate_hexstr32(position_id)
|
|
204
252
|
data = self._margin_contract(collateral_asset).position_data(
|
|
205
|
-
self.
|
|
253
|
+
self._authenticator.address, HexBytes(position_id)
|
|
206
254
|
)
|
|
207
255
|
decimals = self._decimals(collateral_asset)
|
|
208
256
|
return Position(
|
|
@@ -229,12 +277,12 @@ class Clearing(ClearingSystemAPI):
|
|
|
229
277
|
"""
|
|
230
278
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
231
279
|
position_ids = self._margin_contract(collateral_asset).positions(
|
|
232
|
-
self.
|
|
280
|
+
self._authenticator.address
|
|
233
281
|
)
|
|
234
282
|
return [self.position(collateral_asset, Web3.to_hex(id)) for id in position_ids]
|
|
235
283
|
|
|
236
284
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
237
|
-
def
|
|
285
|
+
def equity(self, collateral_asset: str) -> Decimal:
|
|
238
286
|
"""Returns the margin account equity in the margin account associated with the
|
|
239
287
|
collateral asset.
|
|
240
288
|
|
|
@@ -248,7 +296,9 @@ class Clearing(ClearingSystemAPI):
|
|
|
248
296
|
Decimal
|
|
249
297
|
"""
|
|
250
298
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
251
|
-
amount = self._margin_contract(collateral_asset).mae(
|
|
299
|
+
amount = self._margin_contract(collateral_asset).mae(
|
|
300
|
+
self._authenticator.address
|
|
301
|
+
)
|
|
252
302
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
253
303
|
|
|
254
304
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
@@ -266,7 +316,9 @@ class Clearing(ClearingSystemAPI):
|
|
|
266
316
|
Decimal
|
|
267
317
|
"""
|
|
268
318
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
269
|
-
amount = self._margin_contract(collateral_asset).mma(
|
|
319
|
+
amount = self._margin_contract(collateral_asset).mma(
|
|
320
|
+
self._authenticator.address
|
|
321
|
+
)
|
|
270
322
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
271
323
|
|
|
272
324
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
@@ -284,7 +336,9 @@ class Clearing(ClearingSystemAPI):
|
|
|
284
336
|
Decimal
|
|
285
337
|
"""
|
|
286
338
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
287
|
-
amount = self._margin_contract(collateral_asset).mmu(
|
|
339
|
+
amount = self._margin_contract(collateral_asset).mmu(
|
|
340
|
+
self._authenticator.address
|
|
341
|
+
)
|
|
288
342
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
289
343
|
|
|
290
344
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
@@ -302,7 +356,9 @@ class Clearing(ClearingSystemAPI):
|
|
|
302
356
|
Decimal
|
|
303
357
|
"""
|
|
304
358
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
305
|
-
amount = self._margin_contract(collateral_asset).pnl(
|
|
359
|
+
amount = self._margin_contract(collateral_asset).pnl(
|
|
360
|
+
self._authenticator.address
|
|
361
|
+
)
|
|
306
362
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
307
363
|
|
|
308
364
|
@convert_web3_error(MARGIN_CONTRACT_ABI)
|
|
@@ -321,51 +377,56 @@ class Clearing(ClearingSystemAPI):
|
|
|
321
377
|
"""
|
|
322
378
|
collateral_asset = validators.validate_address(collateral_asset)
|
|
323
379
|
amount = self._margin_contract(collateral_asset).withdrawable(
|
|
324
|
-
self.
|
|
380
|
+
self._authenticator.address
|
|
325
381
|
)
|
|
326
382
|
return Decimal(amount) / 10 ** self._decimals(collateral_asset)
|
|
327
383
|
|
|
328
|
-
@convert_web3_error(
|
|
329
|
-
def
|
|
330
|
-
|
|
384
|
+
@convert_web3_error(CLEARING_DIAMOND_ABI)
|
|
385
|
+
def auction_data(
|
|
386
|
+
self, margin_account_id: str, collateral_asset: str
|
|
387
|
+
) -> AuctionData:
|
|
388
|
+
"""Returns information on a liquidation auction.
|
|
331
389
|
|
|
332
390
|
Parameters
|
|
333
391
|
----------
|
|
334
|
-
|
|
335
|
-
The ID of the
|
|
392
|
+
margin_account_id : str
|
|
393
|
+
The ID of the margin account to be liquidated.
|
|
394
|
+
collateral_asset : str
|
|
395
|
+
The address of the collateral token that the margin account is trading with.
|
|
336
396
|
|
|
337
397
|
Returns
|
|
338
398
|
-------
|
|
339
399
|
str
|
|
400
|
+
The hash of the transaction.
|
|
340
401
|
"""
|
|
341
|
-
|
|
342
|
-
collateral_asset =
|
|
343
|
-
HexBytes(product_id)
|
|
344
|
-
)
|
|
345
|
-
if Web3.to_int(hexstr=collateral_asset) == 0:
|
|
346
|
-
raise NotFoundError("Product not found in the product registry")
|
|
347
|
-
return collateral_asset
|
|
348
|
-
|
|
349
|
-
def product_state(self, product_id: str) -> str:
|
|
350
|
-
"""Returns the current state of a product.
|
|
351
|
-
|
|
352
|
-
Parameters
|
|
353
|
-
----------
|
|
354
|
-
product_id : str
|
|
355
|
-
The ID of the product.
|
|
402
|
+
margin_account_id = validators.validate_address(margin_account_id)
|
|
403
|
+
collateral_asset = validators.validate_address(collateral_asset)
|
|
356
404
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
405
|
+
clearing_contract = ClearingDiamond(
|
|
406
|
+
self._w3, self._config.clearing_diamond_address
|
|
407
|
+
)
|
|
408
|
+
data = clearing_contract.auction_data(margin_account_id, collateral_asset)
|
|
409
|
+
divisor = 10 ** self._decimals(collateral_asset)
|
|
410
|
+
return AuctionData(
|
|
411
|
+
start_block=data.start_block,
|
|
412
|
+
margin_account_equity_at_initiation=(
|
|
413
|
+
Decimal(data.mae_at_initiation) / divisor
|
|
414
|
+
),
|
|
415
|
+
maintenance_margin_used_at_initiation=(
|
|
416
|
+
Decimal(data.mmu_at_initiation) / divisor
|
|
417
|
+
),
|
|
418
|
+
margin_account_equity_now=(Decimal(data.mae_now) / divisor),
|
|
419
|
+
maintenance_margin_used_now=(Decimal(data.mmu_now) / divisor),
|
|
420
|
+
)
|
|
362
421
|
|
|
363
422
|
### Internal getters ###
|
|
364
423
|
|
|
365
424
|
@cache
|
|
366
425
|
@convert_web3_error(MARGIN_ACCOUNT_REGISTRY_ABI)
|
|
367
|
-
def _margin_contract(self, collateral_asset: ChecksumAddress) ->
|
|
368
|
-
margin_account_registry_contract = MarginAccountRegistry(
|
|
426
|
+
def _margin_contract(self, collateral_asset: ChecksumAddress) -> MarginContract:
|
|
427
|
+
margin_account_registry_contract = MarginAccountRegistry(
|
|
428
|
+
self._w3, self._config.margin_account_registry_address
|
|
429
|
+
)
|
|
369
430
|
try:
|
|
370
431
|
margin_contract_address = (
|
|
371
432
|
margin_account_registry_contract.get_margin_account(
|
|
@@ -374,4 +435,11 @@ class Clearing(ClearingSystemAPI):
|
|
|
374
435
|
)
|
|
375
436
|
except ContractCustomError:
|
|
376
437
|
raise NotFoundError("No margin account found for collateral asset")
|
|
377
|
-
return
|
|
438
|
+
return MarginContract(self._w3, margin_contract_address)
|
|
439
|
+
|
|
440
|
+
@cache
|
|
441
|
+
def _tick_size(self, product_id: str) -> int:
|
|
442
|
+
product_registry_contract = ProductRegistry(
|
|
443
|
+
self._w3, self._config.product_registry_address
|
|
444
|
+
)
|
|
445
|
+
return product_registry_contract.tick_size(HexBytes(product_id))
|
|
@@ -6,31 +6,27 @@ from eth_typing.evm import ChecksumAddress
|
|
|
6
6
|
from hexbytes import HexBytes
|
|
7
7
|
from web3 import Web3
|
|
8
8
|
|
|
9
|
-
from .. import
|
|
9
|
+
from .. import constants, hashing, validators
|
|
10
10
|
from ..bindings import (
|
|
11
|
+
ClearingDiamond,
|
|
11
12
|
OracleSpecification,
|
|
12
|
-
Product,
|
|
13
|
+
Product as OnChainProduct,
|
|
13
14
|
ProductMetadata,
|
|
14
15
|
ProductRegistry,
|
|
15
16
|
)
|
|
16
17
|
from ..bindings.erc20 import ERC20
|
|
18
|
+
from ..bindings.facade import CLEARING_DIAMOND_ABI
|
|
17
19
|
from ..bindings.product_registry import ABI as PRODUCT_REGISTRY_ABI
|
|
18
20
|
from ..decorators import convert_web3_error
|
|
19
21
|
from ..exceptions import NotFoundError
|
|
20
|
-
from ..schemas import ProductSpecification
|
|
22
|
+
from ..schemas import ProductSpecification, Transaction
|
|
21
23
|
from .base import ClearingSystemAPI
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
class
|
|
25
|
-
"""API for
|
|
26
|
+
class Product(ClearingSystemAPI):
|
|
27
|
+
"""API for managing products."""
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
----------
|
|
29
|
-
private_key : str
|
|
30
|
-
The private key of the blockchain account that submits the product.
|
|
31
|
-
autonity_rpc_url : str
|
|
32
|
-
The URL of a JSON-RPC provider for Autonity. (HTTPS only.)
|
|
33
|
-
"""
|
|
29
|
+
### Factories ###
|
|
34
30
|
|
|
35
31
|
@convert_web3_error()
|
|
36
32
|
def create_product(
|
|
@@ -38,7 +34,6 @@ class Builder(ClearingSystemAPI):
|
|
|
38
34
|
*,
|
|
39
35
|
symbol: str,
|
|
40
36
|
description: str,
|
|
41
|
-
oracle_address: str,
|
|
42
37
|
fsv_decimals: int,
|
|
43
38
|
fsp_alpha: Decimal,
|
|
44
39
|
fsp_beta: Decimal,
|
|
@@ -53,6 +48,7 @@ class Builder(ClearingSystemAPI):
|
|
|
53
48
|
auction_bounty: Decimal,
|
|
54
49
|
tradeout_interval: int,
|
|
55
50
|
extended_metadata: str,
|
|
51
|
+
oracle_address: str | None = None,
|
|
56
52
|
) -> ProductSpecification:
|
|
57
53
|
"""Creates a product specification with the given product data.
|
|
58
54
|
|
|
@@ -63,7 +59,6 @@ class Builder(ClearingSystemAPI):
|
|
|
63
59
|
----------
|
|
64
60
|
symbol : str
|
|
65
61
|
description : str
|
|
66
|
-
oracle_address: str
|
|
67
62
|
fsv_decimals: int
|
|
68
63
|
fsp_alpha: Decimal
|
|
69
64
|
fsp_beta: int
|
|
@@ -78,25 +73,30 @@ class Builder(ClearingSystemAPI):
|
|
|
78
73
|
auction_bounty : Decimal
|
|
79
74
|
tradeout_interval : int
|
|
80
75
|
extended_metadata : str
|
|
76
|
+
oracle_address: str, optional
|
|
81
77
|
|
|
82
78
|
Returns
|
|
83
79
|
-------
|
|
84
80
|
afp.schemas.ProductSpecification
|
|
85
81
|
"""
|
|
86
|
-
|
|
82
|
+
if oracle_address is None:
|
|
83
|
+
oracle_address = self._config.oracle_provider_address
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
price_quotation = erc20_contract.symbol()
|
|
90
|
-
|
|
91
|
-
if not price_quotation:
|
|
85
|
+
if len(self._w3.eth.get_code(Web3.to_checksum_address(collateral_asset))) == 0:
|
|
92
86
|
raise NotFoundError(f"No ERC20 token found at address {collateral_asset}")
|
|
93
|
-
|
|
94
87
|
if len(self._w3.eth.get_code(Web3.to_checksum_address(oracle_address))) == 0:
|
|
95
88
|
raise NotFoundError(f"No contract found at oracle address {oracle_address}")
|
|
96
89
|
|
|
90
|
+
erc20_contract = ERC20(self._w3, Web3.to_checksum_address(collateral_asset))
|
|
91
|
+
price_quotation = erc20_contract.symbol()
|
|
92
|
+
|
|
93
|
+
product_id = Web3.to_hex(
|
|
94
|
+
hashing.generate_product_id(self._authenticator.address, symbol)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
97
|
return ProductSpecification(
|
|
98
98
|
id=product_id,
|
|
99
|
-
builder_id=self.
|
|
99
|
+
builder_id=self._authenticator.address,
|
|
100
100
|
symbol=symbol,
|
|
101
101
|
description=description,
|
|
102
102
|
oracle_address=oracle_address,
|
|
@@ -117,8 +117,10 @@ class Builder(ClearingSystemAPI):
|
|
|
117
117
|
extended_metadata=extended_metadata,
|
|
118
118
|
)
|
|
119
119
|
|
|
120
|
+
### Transactions ###
|
|
121
|
+
|
|
120
122
|
@convert_web3_error(PRODUCT_REGISTRY_ABI)
|
|
121
|
-
def register_product(self, product: ProductSpecification) ->
|
|
123
|
+
def register_product(self, product: ProductSpecification) -> Transaction:
|
|
122
124
|
"""Submits a product specification to the clearing system.
|
|
123
125
|
|
|
124
126
|
Parameters
|
|
@@ -127,20 +129,56 @@ class Builder(ClearingSystemAPI):
|
|
|
127
129
|
|
|
128
130
|
Returns
|
|
129
131
|
-------
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
afp.schemas.Transaction
|
|
133
|
+
Transaction parameters.
|
|
132
134
|
"""
|
|
133
135
|
erc20_contract = ERC20(
|
|
134
136
|
self._w3, cast(ChecksumAddress, product.collateral_asset)
|
|
135
137
|
)
|
|
136
138
|
decimals = erc20_contract.decimals()
|
|
137
139
|
|
|
138
|
-
product_registry_contract = ProductRegistry(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
product_registry_contract = ProductRegistry(
|
|
141
|
+
self._w3, self._config.product_registry_address
|
|
142
|
+
)
|
|
143
|
+
return self._transact(
|
|
144
|
+
product_registry_contract.register(
|
|
145
|
+
self._convert_product_specification(product, decimals)
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
@convert_web3_error(CLEARING_DIAMOND_ABI)
|
|
150
|
+
def initiate_final_settlement(
|
|
151
|
+
self, product_id: str, accounts: list[str]
|
|
152
|
+
) -> Transaction:
|
|
153
|
+
"""Initiate final settlement (closeout) process for the specified accounts.
|
|
154
|
+
|
|
155
|
+
The product must be in Final Settlement state. The accounts must hold non-zero
|
|
156
|
+
positions in the product that offset each other (i.e. the sum of their position
|
|
157
|
+
sizes is 0.)
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
product_id : str
|
|
162
|
+
The ID of the product.
|
|
163
|
+
accounts : list of str
|
|
164
|
+
List of margin account IDs to initiate settlement for.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
afp.schemas.Transaction
|
|
169
|
+
Transaction parameters.
|
|
170
|
+
"""
|
|
171
|
+
product_id = validators.validate_hexstr32(product_id)
|
|
172
|
+
addresses = [validators.validate_address(account) for account in accounts]
|
|
173
|
+
|
|
174
|
+
clearing_contract = ClearingDiamond(
|
|
175
|
+
self._w3, self._config.clearing_diamond_address
|
|
176
|
+
)
|
|
177
|
+
return self._transact(
|
|
178
|
+
clearing_contract.initiate_final_settlement(HexBytes(product_id), addresses)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
### Views ###
|
|
144
182
|
|
|
145
183
|
@convert_web3_error(PRODUCT_REGISTRY_ABI)
|
|
146
184
|
def product_state(self, product_id: str) -> str:
|
|
@@ -156,15 +194,43 @@ class Builder(ClearingSystemAPI):
|
|
|
156
194
|
str
|
|
157
195
|
"""
|
|
158
196
|
product_id = validators.validate_hexstr32(product_id)
|
|
159
|
-
product_registry_contract = ProductRegistry(
|
|
197
|
+
product_registry_contract = ProductRegistry(
|
|
198
|
+
self._w3, self._config.product_registry_address
|
|
199
|
+
)
|
|
160
200
|
state = product_registry_contract.state(HexBytes(product_id))
|
|
161
201
|
return state.name
|
|
162
202
|
|
|
203
|
+
@convert_web3_error(PRODUCT_REGISTRY_ABI)
|
|
204
|
+
def collateral_asset(self, product_id: str) -> str:
|
|
205
|
+
"""Returns the collateral asset of a product.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
product_id : str
|
|
210
|
+
The ID of the product.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
str
|
|
215
|
+
"""
|
|
216
|
+
product_id = validators.validate_hexstr32(product_id)
|
|
217
|
+
product_registry_contract = ProductRegistry(
|
|
218
|
+
self._w3, self._config.product_registry_address
|
|
219
|
+
)
|
|
220
|
+
collateral_asset = product_registry_contract.collateral_asset(
|
|
221
|
+
HexBytes(product_id)
|
|
222
|
+
)
|
|
223
|
+
if Web3.to_int(hexstr=collateral_asset) == 0:
|
|
224
|
+
raise NotFoundError("Product not found in the product registry")
|
|
225
|
+
return collateral_asset
|
|
226
|
+
|
|
227
|
+
### Internal helpers ###
|
|
228
|
+
|
|
163
229
|
@staticmethod
|
|
164
230
|
def _convert_product_specification(
|
|
165
231
|
product: ProductSpecification, decimals: int
|
|
166
|
-
) ->
|
|
167
|
-
return
|
|
232
|
+
) -> OnChainProduct:
|
|
233
|
+
return OnChainProduct(
|
|
168
234
|
metadata=ProductMetadata(
|
|
169
235
|
builder=cast(ChecksumAddress, product.builder_id),
|
|
170
236
|
symbol=product.symbol,
|
|
@@ -173,7 +239,7 @@ class Builder(ClearingSystemAPI):
|
|
|
173
239
|
oracle_spec=OracleSpecification(
|
|
174
240
|
oracle_address=cast(ChecksumAddress, product.oracle_address),
|
|
175
241
|
fsv_decimals=product.fsv_decimals,
|
|
176
|
-
fsp_alpha=int(product.fsp_alpha *
|
|
242
|
+
fsp_alpha=int(product.fsp_alpha * constants.FULL_PRECISION_MULTIPLIER),
|
|
177
243
|
fsp_beta=int(product.fsp_beta * 10**product.fsv_decimals),
|
|
178
244
|
fsv_calldata=HexBytes(product.fsv_calldata),
|
|
179
245
|
),
|
|
@@ -186,12 +252,12 @@ class Builder(ClearingSystemAPI):
|
|
|
186
252
|
tick_size=product.tick_size,
|
|
187
253
|
unit_value=int(product.unit_value * 10**decimals),
|
|
188
254
|
initial_margin_requirement=int(
|
|
189
|
-
product.initial_margin_requirement *
|
|
255
|
+
product.initial_margin_requirement * constants.RATE_MULTIPLIER
|
|
190
256
|
),
|
|
191
257
|
maintenance_margin_requirement=int(
|
|
192
|
-
product.maintenance_margin_requirement *
|
|
258
|
+
product.maintenance_margin_requirement * constants.RATE_MULTIPLIER
|
|
193
259
|
),
|
|
194
|
-
auction_bounty=int(product.auction_bounty *
|
|
260
|
+
auction_bounty=int(product.auction_bounty * constants.RATE_MULTIPLIER),
|
|
195
261
|
tradeout_interval=product.tradeout_interval,
|
|
196
262
|
extended_metadata=product.extended_metadata,
|
|
197
263
|
)
|