afp-sdk 0.5.3__py3-none-any.whl → 0.6.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 +4 -1
- afp/afp.py +11 -0
- afp/api/admin.py +1 -1
- afp/api/base.py +11 -2
- afp/api/margin_account.py +12 -148
- afp/api/product.py +302 -148
- afp/api/trading.py +19 -22
- afp/auth.py +14 -0
- afp/bindings/__init__.py +30 -16
- afp/bindings/admin_facet.py +890 -0
- afp/bindings/clearing_facet.py +356 -773
- afp/bindings/facade.py +9 -6
- afp/bindings/final_settlement_facet.py +258 -99
- afp/bindings/margin_account.py +524 -839
- afp/bindings/margin_account_facet.py +722 -0
- afp/bindings/margin_account_registry.py +184 -310
- afp/bindings/mark_price_tracker_facet.py +74 -16
- afp/bindings/product_registry.py +1577 -541
- afp/bindings/product_registry_facet.py +1467 -0
- afp/bindings/system_viewer.py +592 -369
- afp/bindings/types.py +223 -0
- afp/config.py +4 -0
- afp/constants.py +49 -6
- afp/decorators.py +25 -3
- afp/dtos.py +142 -0
- afp/exceptions.py +14 -0
- afp/exchange.py +13 -8
- afp/hashing.py +7 -5
- afp/ipfs.py +245 -0
- afp/json-schemas/bafyreiaw34o6l3rmatabzbds2i2myazdw2yolevcpsoyd2i2g3ms7wa2eq.json +1 -0
- afp/json-schemas/bafyreibnfg6nq74dvpkre5rakkccij7iadp5rxpim7omsatjnrpmj3y7v4.json +1 -0
- afp/json-schemas/bafyreicgr6dfo5yduixjkcifghiulskfegwojvuwodtouvivl362zndhxe.json +1 -0
- afp/json-schemas/bafyreicheoypx6synljushh7mq2572iyhlolf4nake2p5dwobgnj3r5eua.json +1 -0
- afp/json-schemas/bafyreid35a67db4sqh4fs6boddyt2xvscbqy6nqvsp5jjur56qhkw4ixre.json +1 -0
- afp/json-schemas/bafyreidzs7okcpqiss6ztftltyptqwnw5e5opsy5yntospekjha4kpykaa.json +1 -0
- afp/json-schemas/bafyreifcec2km7hxwq6oqzjlspni2mgipetjb7pqtaewh2efislzoctboi.json +1 -0
- afp/json-schemas/bafyreihn3oiaxffe4e2w7pwtreadpw3obfd7gqlogbcxm56jc2hzfvco74.json +1 -0
- afp/json-schemas/bafyreihur3dzwhja6uxsbcw6eeoj3xmmc4e3zkmyzpot5v5dleevxe5zam.json +1 -0
- afp/schemas.py +228 -176
- afp/types.py +169 -0
- afp/validators.py +218 -8
- {afp_sdk-0.5.3.dist-info → afp_sdk-0.6.0.dist-info}/METADATA +73 -10
- afp_sdk-0.6.0.dist-info/RECORD +50 -0
- afp/bindings/auctioneer_facet.py +0 -752
- afp/bindings/bankruptcy_facet.py +0 -391
- afp/bindings/trading_protocol.py +0 -1158
- afp_sdk-0.5.3.dist-info/RECORD +0 -37
- {afp_sdk-0.5.3.dist-info → afp_sdk-0.6.0.dist-info}/WHEEL +0 -0
- {afp_sdk-0.5.3.dist-info → afp_sdk-0.6.0.dist-info}/licenses/LICENSE +0 -0
afp/api/product.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import warnings
|
|
2
1
|
from datetime import datetime
|
|
3
2
|
from decimal import Decimal
|
|
4
3
|
from typing import Any, cast
|
|
@@ -8,142 +7,207 @@ from hexbytes import HexBytes
|
|
|
8
7
|
from web3 import Web3
|
|
9
8
|
|
|
10
9
|
from .. import constants, hashing, validators
|
|
10
|
+
from ..auth import Authenticator
|
|
11
|
+
from ..config import Config
|
|
11
12
|
from ..bindings import (
|
|
13
|
+
BaseProduct as OnChainBaseProduct,
|
|
12
14
|
ClearingDiamond,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
ExpirySpecification as OnChainExpirySpecification,
|
|
16
|
+
OracleSpecification as OnChainOracleSpecification,
|
|
17
|
+
PredictionProductV1 as OnChainPredictionProductV1,
|
|
18
|
+
ProductMetadata as OnChainProductMetadata,
|
|
16
19
|
ProductRegistry,
|
|
17
20
|
)
|
|
18
21
|
from ..bindings.erc20 import ERC20
|
|
19
22
|
from ..bindings.facade import CLEARING_DIAMOND_ABI
|
|
20
23
|
from ..bindings.product_registry import ABI as PRODUCT_REGISTRY_ABI
|
|
21
24
|
from ..decorators import convert_web3_error
|
|
22
|
-
from ..
|
|
23
|
-
from ..
|
|
24
|
-
from
|
|
25
|
+
from ..dtos import ExtendedMetadata
|
|
26
|
+
from ..exceptions import NotFoundError, ValidationError
|
|
27
|
+
from ..schemas import (
|
|
28
|
+
BaseProduct,
|
|
29
|
+
ExpirySpecification,
|
|
30
|
+
OracleSpecification,
|
|
31
|
+
PredictionProduct,
|
|
32
|
+
PredictionProductV1,
|
|
33
|
+
ProductMetadata,
|
|
34
|
+
Transaction,
|
|
35
|
+
)
|
|
36
|
+
from .base import ClearingSystemAPI, IPFSManager
|
|
25
37
|
|
|
26
38
|
|
|
27
|
-
class Product(ClearingSystemAPI):
|
|
39
|
+
class Product(ClearingSystemAPI, IPFSManager):
|
|
28
40
|
"""API for managing products."""
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
fsp_beta: Decimal,
|
|
41
|
-
fsv_calldata: str,
|
|
42
|
-
start_time: datetime,
|
|
43
|
-
earliest_fsp_submission_time: datetime,
|
|
44
|
-
collateral_asset: str,
|
|
45
|
-
tick_size: int,
|
|
46
|
-
unit_value: Decimal,
|
|
47
|
-
initial_margin_requirement: Decimal,
|
|
48
|
-
maintenance_margin_requirement: Decimal,
|
|
49
|
-
auction_bounty: Decimal,
|
|
50
|
-
tradeout_interval: int,
|
|
51
|
-
extended_metadata: str,
|
|
52
|
-
oracle_address: str | None = None,
|
|
53
|
-
) -> ProductSpecification:
|
|
54
|
-
"""Creates a product specification with the given product data.
|
|
55
|
-
|
|
56
|
-
The builder account's address is derived from the private key; the price
|
|
57
|
-
quotation symbol is retrieved from the collateral asset.
|
|
42
|
+
def __init__(self, config: Config, authenticator: Authenticator | None = None):
|
|
43
|
+
ClearingSystemAPI.__init__(self, config, authenticator)
|
|
44
|
+
IPFSManager.__init__(self, config)
|
|
45
|
+
|
|
46
|
+
### Product Specification ###
|
|
47
|
+
|
|
48
|
+
def validate(self, product_dict: dict[str, Any]) -> PredictionProduct:
|
|
49
|
+
"""Creates a product specification from a dictionary.
|
|
50
|
+
|
|
51
|
+
The dictionary must follow the schema of the afp.schemas.ProductSpec model.
|
|
58
52
|
|
|
59
53
|
Parameters
|
|
60
54
|
----------
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
fsv_decimals: int
|
|
64
|
-
fsp_alpha: Decimal
|
|
65
|
-
fsp_beta: int
|
|
66
|
-
fsv_calldata: str
|
|
67
|
-
start_time : datetime
|
|
68
|
-
earliest_fsp_submission_time : datetime
|
|
69
|
-
collateral_asset : str
|
|
70
|
-
tick_size : int
|
|
71
|
-
unit_value : Decimal
|
|
72
|
-
initial_margin_requirement : Decimal
|
|
73
|
-
maintenance_margin_requirement : Decimal
|
|
74
|
-
auction_bounty : Decimal
|
|
75
|
-
tradeout_interval : int
|
|
76
|
-
extended_metadata : str
|
|
77
|
-
oracle_address: str, optional
|
|
55
|
+
product_dict : dict
|
|
56
|
+
A dictionary that follows the PredictionProduct schema.
|
|
78
57
|
|
|
79
58
|
Returns
|
|
80
59
|
-------
|
|
81
|
-
afp.schemas.
|
|
60
|
+
afp.schemas.PredictionProduct
|
|
61
|
+
|
|
62
|
+
Raises
|
|
63
|
+
------
|
|
64
|
+
ValueError, afp.exceptions.ValidationError
|
|
65
|
+
If the product specification fails validation.
|
|
82
66
|
"""
|
|
83
|
-
|
|
84
|
-
oracle_address = self._config.oracle_provider_address
|
|
67
|
+
return self._verify_product_spec(PredictionProduct.model_validate(product_dict))
|
|
85
68
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if len(self._w3.eth.get_code(Web3.to_checksum_address(oracle_address))) == 0:
|
|
89
|
-
raise NotFoundError(f"No contract found at oracle address {oracle_address}")
|
|
69
|
+
def validate_json(self, product_json: str) -> PredictionProduct:
|
|
70
|
+
"""Creates a product specification from a dictionary.
|
|
90
71
|
|
|
91
|
-
|
|
92
|
-
price_quotation = erc20_contract.symbol()
|
|
72
|
+
The dictionary must follow the schema of the afp.schemas.ProductSpec model.
|
|
93
73
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
product_json : str
|
|
77
|
+
A JSON string that follows the PredictionProduct schema.
|
|
97
78
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
collateral_asset=collateral_asset,
|
|
110
|
-
start_time=start_time,
|
|
111
|
-
earliest_fsp_submission_time=earliest_fsp_submission_time,
|
|
112
|
-
tick_size=tick_size,
|
|
113
|
-
unit_value=unit_value,
|
|
114
|
-
initial_margin_requirement=initial_margin_requirement,
|
|
115
|
-
maintenance_margin_requirement=maintenance_margin_requirement,
|
|
116
|
-
auction_bounty=auction_bounty,
|
|
117
|
-
tradeout_interval=tradeout_interval,
|
|
118
|
-
extended_metadata=extended_metadata,
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
afp.schemas.PredictionProduct
|
|
82
|
+
|
|
83
|
+
Raises
|
|
84
|
+
------
|
|
85
|
+
ValueError, afp.exceptions.ValidationError
|
|
86
|
+
If the product specification fails validation.
|
|
87
|
+
"""
|
|
88
|
+
return self._verify_product_spec(
|
|
89
|
+
PredictionProduct.model_validate_json(product_json)
|
|
119
90
|
)
|
|
120
91
|
|
|
121
|
-
def
|
|
122
|
-
"""
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
92
|
+
def dump(self, product_spec: PredictionProduct) -> dict[str, Any]:
|
|
93
|
+
"""Creates a dictionary from a product specification.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
product_spec : afp.schemas.PredictionProduct
|
|
98
|
+
The product specification.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
dict
|
|
103
|
+
"""
|
|
104
|
+
return product_spec.model_dump()
|
|
105
|
+
|
|
106
|
+
def dump_json(self, product_spec: PredictionProduct) -> str:
|
|
107
|
+
"""Creates a JSON string from a product specification.
|
|
108
|
+
|
|
109
|
+
The producted JSON is JSON Canonicalization Scheme (RFC 8785) compliant.
|
|
110
|
+
|
|
111
|
+
Parameters
|
|
112
|
+
----------
|
|
113
|
+
product_spec : afp.schemas.PredictionProduct
|
|
114
|
+
The product specification.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
str
|
|
119
|
+
"""
|
|
120
|
+
return product_spec.model_dump_canonical_json()
|
|
121
|
+
|
|
122
|
+
def id(self, product_spec: PredictionProduct) -> str:
|
|
123
|
+
"""Generates the product ID for a product specification.
|
|
124
|
+
|
|
125
|
+
This is the ID that the product will get after successful registration.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
product_spec : afp.schemas.PredictionProduct
|
|
130
|
+
The product specification.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
str
|
|
135
|
+
"""
|
|
136
|
+
return Web3.to_hex(
|
|
137
|
+
hashing.generate_product_id(
|
|
138
|
+
cast(ChecksumAddress, product_spec.product.base.metadata.builder),
|
|
139
|
+
product_spec.product.base.metadata.symbol,
|
|
140
|
+
)
|
|
127
141
|
)
|
|
128
|
-
return self.create(**kwargs)
|
|
129
142
|
|
|
130
143
|
### Transactions ###
|
|
131
144
|
|
|
132
|
-
|
|
133
|
-
|
|
145
|
+
def pin(self, product_spec: PredictionProduct) -> PredictionProduct:
|
|
146
|
+
"""Uploads the product's extended metadata to IPFS and pins the CID.
|
|
147
|
+
|
|
148
|
+
Adds the CID to the product specification and returns the modified specification
|
|
149
|
+
with the extended metadata CID included.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
product_spec : afp.schemas.PredictionProduct or afp.schemas.PredictionProductV1
|
|
154
|
+
The product specification.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
afp.schemas.PredictionProduct
|
|
159
|
+
The product specification with extended metadata CID included.
|
|
160
|
+
|
|
161
|
+
Raises
|
|
162
|
+
------
|
|
163
|
+
afp.exceptions.IPFSError
|
|
164
|
+
If there is an error uploading to the IPFS node.
|
|
165
|
+
"""
|
|
166
|
+
extended_metadata_cid = self._ipfs_client.upload_extended_metadata(
|
|
167
|
+
ExtendedMetadata(
|
|
168
|
+
outcome_space=product_spec.outcome_space,
|
|
169
|
+
outcome_point=product_spec.outcome_point,
|
|
170
|
+
oracle_config=product_spec.oracle_config,
|
|
171
|
+
oracle_fallback=product_spec.oracle_fallback,
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
updated_base_product = product_spec.product.base.model_copy(
|
|
175
|
+
update=dict(extended_metadata=extended_metadata_cid)
|
|
176
|
+
)
|
|
177
|
+
updated_product = product_spec.product.model_copy(
|
|
178
|
+
update=dict(base=updated_base_product)
|
|
179
|
+
)
|
|
180
|
+
return product_spec.model_copy(update=dict(product=updated_product))
|
|
181
|
+
|
|
182
|
+
@convert_web3_error(PRODUCT_REGISTRY_ABI, CLEARING_DIAMOND_ABI)
|
|
183
|
+
def register(
|
|
184
|
+
self, product_spec: PredictionProduct, initial_builder_stake: Decimal
|
|
185
|
+
) -> Transaction:
|
|
134
186
|
"""Submits a product specification to the clearing system.
|
|
135
187
|
|
|
188
|
+
The extended metadata should already be pinned to IPFS and the CID should be
|
|
189
|
+
specified in the afp.schemas.BaseProduct object in the product specification.
|
|
190
|
+
|
|
136
191
|
Parameters
|
|
137
192
|
----------
|
|
138
|
-
|
|
193
|
+
product_spec : afp.schemas.PredictionProduct or afp.schemas.PredictionProductV1
|
|
194
|
+
The product specification.
|
|
195
|
+
initial_builder_stake : Decimal
|
|
196
|
+
Registration stake (product maintenance fee) in units of the collateral
|
|
197
|
+
asset.
|
|
139
198
|
|
|
140
199
|
Returns
|
|
141
200
|
-------
|
|
142
201
|
afp.schemas.Transaction
|
|
143
202
|
Transaction parameters.
|
|
144
203
|
"""
|
|
204
|
+
if product_spec.product.base.extended_metadata is None:
|
|
205
|
+
raise ValidationError(
|
|
206
|
+
"Extended metadata CID missing from product specification"
|
|
207
|
+
)
|
|
208
|
+
|
|
145
209
|
erc20_contract = ERC20(
|
|
146
|
-
self._w3, cast(ChecksumAddress,
|
|
210
|
+
self._w3, cast(ChecksumAddress, product_spec.product.base.collateral_asset)
|
|
147
211
|
)
|
|
148
212
|
decimals = erc20_contract.decimals()
|
|
149
213
|
|
|
@@ -151,20 +215,14 @@ class Product(ClearingSystemAPI):
|
|
|
151
215
|
self._w3, self._config.product_registry_address
|
|
152
216
|
)
|
|
153
217
|
return self._transact(
|
|
154
|
-
product_registry_contract.
|
|
155
|
-
self.
|
|
218
|
+
product_registry_contract.register_prediction_product(
|
|
219
|
+
self._convert_prediction_product_specification(
|
|
220
|
+
product_spec.product, decimals
|
|
221
|
+
),
|
|
222
|
+
int(initial_builder_stake * 10**decimals),
|
|
156
223
|
)
|
|
157
224
|
)
|
|
158
225
|
|
|
159
|
-
def register_product(self, product: ProductSpecification) -> Transaction:
|
|
160
|
-
"""Deprecated alias of `Product.register`."""
|
|
161
|
-
warnings.warn(
|
|
162
|
-
"Product.register_product() is deprecated. Use Product.register() instead.",
|
|
163
|
-
DeprecationWarning,
|
|
164
|
-
stacklevel=2,
|
|
165
|
-
)
|
|
166
|
-
return self.register(product)
|
|
167
|
-
|
|
168
226
|
@convert_web3_error(CLEARING_DIAMOND_ABI)
|
|
169
227
|
def initiate_final_settlement(
|
|
170
228
|
self, product_id: str, accounts: list[str]
|
|
@@ -199,7 +257,49 @@ class Product(ClearingSystemAPI):
|
|
|
199
257
|
|
|
200
258
|
### Views ###
|
|
201
259
|
|
|
202
|
-
@convert_web3_error(PRODUCT_REGISTRY_ABI)
|
|
260
|
+
@convert_web3_error(PRODUCT_REGISTRY_ABI, CLEARING_DIAMOND_ABI)
|
|
261
|
+
def get(self, product_id: str) -> PredictionProduct:
|
|
262
|
+
"""Retrieves a product registered on chain.
|
|
263
|
+
|
|
264
|
+
Parameters
|
|
265
|
+
----------
|
|
266
|
+
product_id : str
|
|
267
|
+
The ID of the product.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
afp.schemas.PredictionProduct
|
|
272
|
+
"""
|
|
273
|
+
product_id = validators.validate_hexstr32(product_id)
|
|
274
|
+
|
|
275
|
+
product_registry_contract = ProductRegistry(
|
|
276
|
+
self._w3, self._config.product_registry_address
|
|
277
|
+
)
|
|
278
|
+
# Note: when FuturesProductV1 support is added, call type_of(product_id) first
|
|
279
|
+
# to figure out which view function should be used
|
|
280
|
+
product = product_registry_contract.prediction_product_v1(HexBytes(product_id))
|
|
281
|
+
|
|
282
|
+
erc20_contract = ERC20(self._w3, product.base.collateral_asset)
|
|
283
|
+
decimals = erc20_contract.decimals()
|
|
284
|
+
|
|
285
|
+
product = self._convert_on_chain_prediction_product(product, decimals)
|
|
286
|
+
if product.base.extended_metadata is None:
|
|
287
|
+
raise ValidationError(
|
|
288
|
+
"Extended metadata CID missing from product specification"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
extended_metadata = self._ipfs_client.download_extended_metadata(
|
|
292
|
+
product.base.extended_metadata
|
|
293
|
+
)
|
|
294
|
+
return PredictionProduct(
|
|
295
|
+
product=product,
|
|
296
|
+
outcome_space=extended_metadata.outcome_space,
|
|
297
|
+
outcome_point=extended_metadata.outcome_point,
|
|
298
|
+
oracle_config=extended_metadata.oracle_config,
|
|
299
|
+
oracle_fallback=extended_metadata.oracle_fallback,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
@convert_web3_error(PRODUCT_REGISTRY_ABI, CLEARING_DIAMOND_ABI)
|
|
203
303
|
def state(self, product_id: str) -> str:
|
|
204
304
|
"""Returns the current state of a product.
|
|
205
305
|
|
|
@@ -219,16 +319,7 @@ class Product(ClearingSystemAPI):
|
|
|
219
319
|
state = product_registry_contract.state(HexBytes(product_id))
|
|
220
320
|
return state.name
|
|
221
321
|
|
|
222
|
-
|
|
223
|
-
"""Deprecated alias of `Product.state`."""
|
|
224
|
-
warnings.warn(
|
|
225
|
-
"Product.product_state() is deprecated. Use Product.state() instead.",
|
|
226
|
-
DeprecationWarning,
|
|
227
|
-
stacklevel=2,
|
|
228
|
-
)
|
|
229
|
-
return self.state(product_id)
|
|
230
|
-
|
|
231
|
-
@convert_web3_error(PRODUCT_REGISTRY_ABI)
|
|
322
|
+
@convert_web3_error(PRODUCT_REGISTRY_ABI, CLEARING_DIAMOND_ABI)
|
|
232
323
|
def collateral_asset(self, product_id: str) -> str:
|
|
233
324
|
"""Returns the collateral asset of a product.
|
|
234
325
|
|
|
@@ -254,38 +345,101 @@ class Product(ClearingSystemAPI):
|
|
|
254
345
|
|
|
255
346
|
### Internal helpers ###
|
|
256
347
|
|
|
348
|
+
def _verify_product_spec(
|
|
349
|
+
self, product_spec: PredictionProduct
|
|
350
|
+
) -> PredictionProduct:
|
|
351
|
+
validators.verify_collateral_asset(
|
|
352
|
+
self._w3, product_spec.product.base.collateral_asset
|
|
353
|
+
)
|
|
354
|
+
validators.verify_oracle(
|
|
355
|
+
self._w3, product_spec.product.base.oracle_spec.oracle_address
|
|
356
|
+
)
|
|
357
|
+
return product_spec
|
|
358
|
+
|
|
257
359
|
@staticmethod
|
|
258
|
-
def
|
|
259
|
-
product:
|
|
260
|
-
) ->
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
360
|
+
def _convert_prediction_product_specification(
|
|
361
|
+
product: PredictionProductV1, collateral_asset_decimals: int
|
|
362
|
+
) -> OnChainPredictionProductV1:
|
|
363
|
+
assert product.base.extended_metadata is not None
|
|
364
|
+
|
|
365
|
+
return OnChainPredictionProductV1(
|
|
366
|
+
base=OnChainBaseProduct(
|
|
367
|
+
metadata=OnChainProductMetadata(
|
|
368
|
+
builder=cast(ChecksumAddress, product.base.metadata.builder),
|
|
369
|
+
symbol=product.base.metadata.symbol,
|
|
370
|
+
description=product.base.metadata.description,
|
|
371
|
+
),
|
|
372
|
+
oracle_spec=OnChainOracleSpecification(
|
|
373
|
+
oracle_address=cast(
|
|
374
|
+
ChecksumAddress, product.base.oracle_spec.oracle_address
|
|
375
|
+
),
|
|
376
|
+
fsv_decimals=product.base.oracle_spec.fsv_decimals,
|
|
377
|
+
fsp_alpha=int(
|
|
378
|
+
product.base.oracle_spec.fsp_alpha
|
|
379
|
+
* constants.FULL_PRECISION_MULTIPLIER
|
|
380
|
+
),
|
|
381
|
+
fsp_beta=int(
|
|
382
|
+
product.base.oracle_spec.fsp_beta
|
|
383
|
+
* 10**product.base.oracle_spec.fsv_decimals
|
|
384
|
+
),
|
|
385
|
+
fsv_calldata=HexBytes(product.base.oracle_spec.fsv_calldata),
|
|
386
|
+
),
|
|
387
|
+
collateral_asset=cast(ChecksumAddress, product.base.collateral_asset),
|
|
388
|
+
start_time=int(product.base.start_time.timestamp()),
|
|
389
|
+
point_value=int(
|
|
390
|
+
product.base.point_value * 10**collateral_asset_decimals
|
|
391
|
+
),
|
|
392
|
+
price_decimals=product.base.price_decimals,
|
|
393
|
+
extended_metadata=product.base.extended_metadata,
|
|
273
394
|
),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
product.
|
|
395
|
+
expiry_spec=OnChainExpirySpecification(
|
|
396
|
+
earliest_fsp_submission_time=int(
|
|
397
|
+
product.expiry_spec.earliest_fsp_submission_time.timestamp()
|
|
398
|
+
),
|
|
399
|
+
tradeout_interval=product.expiry_spec.tradeout_interval,
|
|
279
400
|
),
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
401
|
+
max_price=int(product.max_price * 10**product.base.price_decimals),
|
|
402
|
+
min_price=int(product.min_price * 10**product.base.price_decimals),
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
@staticmethod
|
|
406
|
+
def _convert_on_chain_prediction_product(
|
|
407
|
+
product: OnChainPredictionProductV1, collateral_asset_decimals: int
|
|
408
|
+
) -> PredictionProductV1:
|
|
409
|
+
return PredictionProductV1(
|
|
410
|
+
base=BaseProduct(
|
|
411
|
+
metadata=ProductMetadata(
|
|
412
|
+
builder=product.base.metadata.builder,
|
|
413
|
+
symbol=product.base.metadata.symbol,
|
|
414
|
+
description=product.base.metadata.description,
|
|
415
|
+
),
|
|
416
|
+
oracle_spec=OracleSpecification(
|
|
417
|
+
oracle_address=product.base.oracle_spec.oracle_address,
|
|
418
|
+
fsv_decimals=product.base.oracle_spec.fsv_decimals,
|
|
419
|
+
fsp_alpha=(
|
|
420
|
+
Decimal(product.base.oracle_spec.fsp_alpha)
|
|
421
|
+
/ constants.FULL_PRECISION_MULTIPLIER
|
|
422
|
+
),
|
|
423
|
+
fsp_beta=(
|
|
424
|
+
Decimal(product.base.oracle_spec.fsp_beta)
|
|
425
|
+
/ 10**product.base.oracle_spec.fsv_decimals
|
|
426
|
+
),
|
|
427
|
+
fsv_calldata=product.base.oracle_spec.fsv_calldata.to_0x_hex(),
|
|
428
|
+
),
|
|
429
|
+
collateral_asset=product.base.collateral_asset,
|
|
430
|
+
start_time=datetime.fromtimestamp(product.base.start_time),
|
|
431
|
+
point_value=(
|
|
432
|
+
Decimal(product.base.point_value) / 10**collateral_asset_decimals
|
|
433
|
+
),
|
|
434
|
+
price_decimals=product.base.price_decimals,
|
|
435
|
+
extended_metadata=product.base.extended_metadata,
|
|
284
436
|
),
|
|
285
|
-
|
|
286
|
-
|
|
437
|
+
expiry_spec=ExpirySpecification(
|
|
438
|
+
earliest_fsp_submission_time=datetime.fromtimestamp(
|
|
439
|
+
product.expiry_spec.earliest_fsp_submission_time
|
|
440
|
+
),
|
|
441
|
+
tradeout_interval=product.expiry_spec.tradeout_interval,
|
|
287
442
|
),
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
extended_metadata=product.extended_metadata,
|
|
443
|
+
max_price=Decimal(product.max_price) / 10**product.base.price_decimals,
|
|
444
|
+
min_price=Decimal(product.min_price) / 10**product.base.price_decimals,
|
|
291
445
|
)
|
afp/api/trading.py
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
import secrets
|
|
2
|
-
import warnings
|
|
3
2
|
from datetime import datetime, timedelta
|
|
4
3
|
from decimal import Decimal
|
|
5
4
|
from typing import Generator, Iterable
|
|
6
5
|
|
|
7
6
|
from web3 import Web3
|
|
7
|
+
from web3.constants import CHECKSUM_ADDRESSS_ZERO
|
|
8
8
|
|
|
9
9
|
from .. import hashing, validators
|
|
10
10
|
from ..constants import DEFAULT_BATCH_SIZE
|
|
11
11
|
from ..decorators import refresh_token_on_expiry
|
|
12
|
+
from ..dtos import ExchangeProductFilter, OrderFilter, OrderFillFilter, OrderSubmission
|
|
12
13
|
from ..enums import OrderSide, OrderState, OrderType, TradeState
|
|
13
14
|
from ..schemas import (
|
|
14
15
|
ExchangeProduct,
|
|
15
|
-
ExchangeProductFilter,
|
|
16
16
|
Intent,
|
|
17
17
|
IntentData,
|
|
18
18
|
MarketDepthData,
|
|
19
19
|
OHLCVItem,
|
|
20
20
|
Order,
|
|
21
|
-
OrderFilter,
|
|
22
21
|
OrderCancellationData,
|
|
23
22
|
OrderFill,
|
|
24
|
-
OrderFillFilter,
|
|
25
|
-
OrderSubmission,
|
|
26
23
|
)
|
|
27
24
|
from .base import ExchangeAPI
|
|
28
25
|
|
|
@@ -44,6 +41,7 @@ class Trading(ExchangeAPI):
|
|
|
44
41
|
max_trading_fee_rate: Decimal,
|
|
45
42
|
good_until_time: datetime,
|
|
46
43
|
margin_account_id: str | None = None,
|
|
44
|
+
referral: str | None = None,
|
|
47
45
|
rounding: str | None = None,
|
|
48
46
|
) -> Intent:
|
|
49
47
|
"""Creates an intent with the given intent data, generates its hash and signs it
|
|
@@ -70,6 +68,7 @@ class Trading(ExchangeAPI):
|
|
|
70
68
|
max_trading_fee_rate : decimal.Decimal
|
|
71
69
|
good_until_time : datetime.datetime
|
|
72
70
|
margin_account_id : str, optional
|
|
71
|
+
referral : str, optional
|
|
73
72
|
rounding : str, optional
|
|
74
73
|
A rounding mode of the `decimal` module or `None` for no rounding.
|
|
75
74
|
|
|
@@ -87,13 +86,18 @@ class Trading(ExchangeAPI):
|
|
|
87
86
|
trading_protocol_id=self._trading_protocol_id,
|
|
88
87
|
product_id=product.id,
|
|
89
88
|
limit_price=validators.validate_limit_price(
|
|
90
|
-
Decimal(limit_price),
|
|
89
|
+
Decimal(limit_price),
|
|
90
|
+
product.min_price,
|
|
91
|
+
product.max_price,
|
|
92
|
+
product.tick_size,
|
|
93
|
+
rounding,
|
|
91
94
|
),
|
|
92
95
|
quantity=quantity,
|
|
93
96
|
max_trading_fee_rate=max_trading_fee_rate,
|
|
94
97
|
side=OrderSide(side.upper()),
|
|
95
98
|
good_until_time=good_until_time,
|
|
96
99
|
nonce=self._generate_nonce(),
|
|
100
|
+
referral=(referral if referral is not None else CHECKSUM_ADDRESSS_ZERO),
|
|
97
101
|
)
|
|
98
102
|
intent_hash = hashing.generate_intent_hash(
|
|
99
103
|
intent_data=intent_data,
|
|
@@ -124,8 +128,9 @@ class Trading(ExchangeAPI):
|
|
|
124
128
|
|
|
125
129
|
Raises
|
|
126
130
|
------
|
|
127
|
-
afp.exceptions.
|
|
128
|
-
If the exchange rejects the
|
|
131
|
+
afp.exceptions.RateLimitExceeded
|
|
132
|
+
If the exchange rejects the order because of too many requests from the
|
|
133
|
+
authenticated account.
|
|
129
134
|
"""
|
|
130
135
|
submission = OrderSubmission(
|
|
131
136
|
type=OrderType.LIMIT_ORDER,
|
|
@@ -147,8 +152,13 @@ class Trading(ExchangeAPI):
|
|
|
147
152
|
|
|
148
153
|
Raises
|
|
149
154
|
------
|
|
155
|
+
afp.exceptions.NotFoundError
|
|
156
|
+
If the exchange rejects the cancellation because the intent does not exist.
|
|
157
|
+
afp.exceptions.RateLimitExceeded
|
|
158
|
+
If the exchange rejects the cancellation because of too many requests from
|
|
159
|
+
the authenticated account.
|
|
150
160
|
afp.exceptions.ValidationError
|
|
151
|
-
If the exchange rejects the cancellation.
|
|
161
|
+
If the exchange rejects the cancellation because it is invalid.
|
|
152
162
|
"""
|
|
153
163
|
nonce = self._generate_nonce()
|
|
154
164
|
cancellation_hash = hashing.generate_order_cancellation_hash(nonce, intent_hash)
|
|
@@ -295,19 +305,6 @@ class Trading(ExchangeAPI):
|
|
|
295
305
|
)
|
|
296
306
|
return self._exchange.get_orders(filter)
|
|
297
307
|
|
|
298
|
-
@refresh_token_on_expiry
|
|
299
|
-
def open_orders(self, product_id: str | None = None) -> list[Order]:
|
|
300
|
-
"""Deprecated alias of Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL"))."""
|
|
301
|
-
warnings.warn(
|
|
302
|
-
"Trading.open_orders() is deprecated. Use "
|
|
303
|
-
'Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL")) instead.',
|
|
304
|
-
DeprecationWarning,
|
|
305
|
-
stacklevel=2,
|
|
306
|
-
)
|
|
307
|
-
return self.orders(
|
|
308
|
-
type_="limit_order", states=("open", "partial"), product_id=product_id
|
|
309
|
-
)
|
|
310
|
-
|
|
311
308
|
@refresh_token_on_expiry
|
|
312
309
|
def order_fills(
|
|
313
310
|
self,
|
afp/auth.py
CHANGED
|
@@ -9,6 +9,7 @@ from eth_account.signers.local import LocalAccount
|
|
|
9
9
|
from eth_account.types import TransactionDictType
|
|
10
10
|
from eth_typing.evm import ChecksumAddress
|
|
11
11
|
from hexbytes import HexBytes
|
|
12
|
+
from web3.constants import CHECKSUM_ADDRESSS_ZERO
|
|
12
13
|
from web3.types import TxParams
|
|
13
14
|
|
|
14
15
|
|
|
@@ -20,6 +21,19 @@ class Authenticator(Protocol):
|
|
|
20
21
|
def sign_transaction(self, params: TxParams) -> SignedTransaction: ...
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
class NullAuthenticator(Authenticator):
|
|
25
|
+
"""Authenticator stub as placeholder for testing."""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
self.address = CHECKSUM_ADDRESSS_ZERO
|
|
29
|
+
|
|
30
|
+
def sign_message(self, message: bytes) -> HexBytes:
|
|
31
|
+
raise NotImplementedError()
|
|
32
|
+
|
|
33
|
+
def sign_transaction(self, params: TxParams) -> SignedTransaction:
|
|
34
|
+
raise NotImplementedError()
|
|
35
|
+
|
|
36
|
+
|
|
23
37
|
class PrivateKeyAuthenticator(Authenticator):
|
|
24
38
|
"""Authenticates with a private key specified in a constructor argument.
|
|
25
39
|
|