afp-sdk 0.5.4__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.
Files changed (49) hide show
  1. afp/__init__.py +4 -1
  2. afp/afp.py +11 -0
  3. afp/api/admin.py +1 -1
  4. afp/api/base.py +11 -2
  5. afp/api/margin_account.py +12 -148
  6. afp/api/product.py +302 -148
  7. afp/api/trading.py +10 -19
  8. afp/auth.py +14 -0
  9. afp/bindings/__init__.py +30 -16
  10. afp/bindings/admin_facet.py +890 -0
  11. afp/bindings/clearing_facet.py +356 -773
  12. afp/bindings/facade.py +9 -6
  13. afp/bindings/final_settlement_facet.py +258 -99
  14. afp/bindings/margin_account.py +524 -839
  15. afp/bindings/margin_account_facet.py +722 -0
  16. afp/bindings/margin_account_registry.py +184 -310
  17. afp/bindings/mark_price_tracker_facet.py +74 -16
  18. afp/bindings/product_registry.py +1577 -541
  19. afp/bindings/product_registry_facet.py +1467 -0
  20. afp/bindings/system_viewer.py +592 -369
  21. afp/bindings/types.py +223 -0
  22. afp/config.py +4 -0
  23. afp/constants.py +49 -6
  24. afp/decorators.py +25 -3
  25. afp/dtos.py +142 -0
  26. afp/exceptions.py +10 -0
  27. afp/exchange.py +10 -8
  28. afp/hashing.py +7 -5
  29. afp/ipfs.py +245 -0
  30. afp/json-schemas/bafyreiaw34o6l3rmatabzbds2i2myazdw2yolevcpsoyd2i2g3ms7wa2eq.json +1 -0
  31. afp/json-schemas/bafyreibnfg6nq74dvpkre5rakkccij7iadp5rxpim7omsatjnrpmj3y7v4.json +1 -0
  32. afp/json-schemas/bafyreicgr6dfo5yduixjkcifghiulskfegwojvuwodtouvivl362zndhxe.json +1 -0
  33. afp/json-schemas/bafyreicheoypx6synljushh7mq2572iyhlolf4nake2p5dwobgnj3r5eua.json +1 -0
  34. afp/json-schemas/bafyreid35a67db4sqh4fs6boddyt2xvscbqy6nqvsp5jjur56qhkw4ixre.json +1 -0
  35. afp/json-schemas/bafyreidzs7okcpqiss6ztftltyptqwnw5e5opsy5yntospekjha4kpykaa.json +1 -0
  36. afp/json-schemas/bafyreifcec2km7hxwq6oqzjlspni2mgipetjb7pqtaewh2efislzoctboi.json +1 -0
  37. afp/json-schemas/bafyreihn3oiaxffe4e2w7pwtreadpw3obfd7gqlogbcxm56jc2hzfvco74.json +1 -0
  38. afp/json-schemas/bafyreihur3dzwhja6uxsbcw6eeoj3xmmc4e3zkmyzpot5v5dleevxe5zam.json +1 -0
  39. afp/schemas.py +227 -177
  40. afp/types.py +169 -0
  41. afp/validators.py +218 -8
  42. {afp_sdk-0.5.4.dist-info → afp_sdk-0.6.0.dist-info}/METADATA +73 -10
  43. afp_sdk-0.6.0.dist-info/RECORD +50 -0
  44. afp/bindings/auctioneer_facet.py +0 -752
  45. afp/bindings/bankruptcy_facet.py +0 -391
  46. afp/bindings/trading_protocol.py +0 -1158
  47. afp_sdk-0.5.4.dist-info/RECORD +0 -37
  48. {afp_sdk-0.5.4.dist-info → afp_sdk-0.6.0.dist-info}/WHEEL +0 -0
  49. {afp_sdk-0.5.4.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
- OracleSpecification,
14
- Product as OnChainProduct,
15
- ProductMetadata,
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 ..exceptions import NotFoundError
23
- from ..schemas import ProductSpecification, Transaction
24
- from .base import ClearingSystemAPI
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
- ### Factories ###
31
-
32
- @convert_web3_error()
33
- def create(
34
- self,
35
- *,
36
- symbol: str,
37
- description: str,
38
- fsv_decimals: int,
39
- fsp_alpha: Decimal,
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
- symbol : str
62
- description : str
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.ProductSpecification
60
+ afp.schemas.PredictionProduct
61
+
62
+ Raises
63
+ ------
64
+ ValueError, afp.exceptions.ValidationError
65
+ If the product specification fails validation.
82
66
  """
83
- if oracle_address is None:
84
- oracle_address = self._config.oracle_provider_address
67
+ return self._verify_product_spec(PredictionProduct.model_validate(product_dict))
85
68
 
86
- if len(self._w3.eth.get_code(Web3.to_checksum_address(collateral_asset))) == 0:
87
- raise NotFoundError(f"No ERC20 token found at address {collateral_asset}")
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
- erc20_contract = ERC20(self._w3, Web3.to_checksum_address(collateral_asset))
92
- price_quotation = erc20_contract.symbol()
72
+ The dictionary must follow the schema of the afp.schemas.ProductSpec model.
93
73
 
94
- product_id = Web3.to_hex(
95
- hashing.generate_product_id(self._authenticator.address, symbol)
96
- )
74
+ Parameters
75
+ ----------
76
+ product_json : str
77
+ A JSON string that follows the PredictionProduct schema.
97
78
 
98
- return ProductSpecification(
99
- id=product_id,
100
- builder_id=self._authenticator.address,
101
- symbol=symbol,
102
- description=description,
103
- oracle_address=oracle_address,
104
- fsv_decimals=fsv_decimals,
105
- fsp_alpha=fsp_alpha,
106
- fsp_beta=fsp_beta,
107
- fsv_calldata=fsv_calldata,
108
- price_quotation=price_quotation,
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 create_product(self, **kwargs: Any) -> ProductSpecification:
122
- """Deprecated alias of `Product.create`."""
123
- warnings.warn(
124
- "Product.create_product() is deprecated. Use Product.create() instead.",
125
- DeprecationWarning,
126
- stacklevel=2,
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
- @convert_web3_error(PRODUCT_REGISTRY_ABI)
133
- def register(self, product_specification: ProductSpecification) -> Transaction:
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
- product_specification : afp.schemas.ProductSpecification
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, product_specification.collateral_asset)
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.register(
155
- self._convert_product_specification(product_specification, decimals)
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
- def product_state(self, product_id: str) -> str:
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 _convert_product_specification(
259
- product: ProductSpecification, decimals: int
260
- ) -> OnChainProduct:
261
- return OnChainProduct(
262
- metadata=ProductMetadata(
263
- builder=cast(ChecksumAddress, product.builder_id),
264
- symbol=product.symbol,
265
- description=product.description,
266
- ),
267
- oracle_spec=OracleSpecification(
268
- oracle_address=cast(ChecksumAddress, product.oracle_address),
269
- fsv_decimals=product.fsv_decimals,
270
- fsp_alpha=int(product.fsp_alpha * constants.FULL_PRECISION_MULTIPLIER),
271
- fsp_beta=int(product.fsp_beta * 10**product.fsv_decimals),
272
- fsv_calldata=HexBytes(product.fsv_calldata),
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
- price_quotation=product.price_quotation,
275
- collateral_asset=cast(ChecksumAddress, product.collateral_asset),
276
- start_time=int(product.start_time.timestamp()),
277
- earliest_fsp_submission_time=int(
278
- product.earliest_fsp_submission_time.timestamp()
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
- tick_size=product.tick_size,
281
- unit_value=int(product.unit_value * 10**decimals),
282
- initial_margin_requirement=int(
283
- product.initial_margin_requirement * constants.RATE_MULTIPLIER
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
- maintenance_margin_requirement=int(
286
- product.maintenance_margin_requirement * constants.RATE_MULTIPLIER
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
- auction_bounty=int(product.auction_bounty * constants.RATE_MULTIPLIER),
289
- tradeout_interval=product.tradeout_interval,
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), product.tick_size, rounding
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,
@@ -301,19 +305,6 @@ class Trading(ExchangeAPI):
301
305
  )
302
306
  return self._exchange.get_orders(filter)
303
307
 
304
- @refresh_token_on_expiry
305
- def open_orders(self, product_id: str | None = None) -> list[Order]:
306
- """Deprecated alias of Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL"))."""
307
- warnings.warn(
308
- "Trading.open_orders() is deprecated. Use "
309
- 'Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL")) instead.',
310
- DeprecationWarning,
311
- stacklevel=2,
312
- )
313
- return self.orders(
314
- type_="limit_order", states=("open", "partial"), product_id=product_id
315
- )
316
-
317
308
  @refresh_token_on_expiry
318
309
  def order_fills(
319
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