afp-sdk 0.4.0__py3-none-any.whl → 0.5.1__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.
@@ -1,44 +1,40 @@
1
+ import warnings
1
2
  from datetime import datetime
2
3
  from decimal import Decimal
3
- from typing import cast
4
+ from typing import Any, cast
4
5
 
5
6
  from eth_typing.evm import ChecksumAddress
6
7
  from hexbytes import HexBytes
7
8
  from web3 import Web3
8
9
 
9
- from .. import config, signing, validators
10
+ from .. import constants, hashing, validators
10
11
  from ..bindings import (
12
+ ClearingDiamond,
11
13
  OracleSpecification,
12
- Product,
14
+ Product as OnChainProduct,
13
15
  ProductMetadata,
14
16
  ProductRegistry,
15
17
  )
16
18
  from ..bindings.erc20 import ERC20
19
+ from ..bindings.facade import CLEARING_DIAMOND_ABI
17
20
  from ..bindings.product_registry import ABI as PRODUCT_REGISTRY_ABI
18
21
  from ..decorators import convert_web3_error
19
22
  from ..exceptions import NotFoundError
20
- from ..schemas import ProductSpecification
23
+ from ..schemas import ProductSpecification, Transaction
21
24
  from .base import ClearingSystemAPI
22
25
 
23
26
 
24
- class Builder(ClearingSystemAPI):
25
- """API for building and submitting new products.
27
+ class Product(ClearingSystemAPI):
28
+ """API for managing products."""
26
29
 
27
- Parameters
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
- """
30
+ ### Factories ###
34
31
 
35
32
  @convert_web3_error()
36
- def create_product(
33
+ def create(
37
34
  self,
38
35
  *,
39
36
  symbol: str,
40
37
  description: str,
41
- oracle_address: str,
42
38
  fsv_decimals: int,
43
39
  fsp_alpha: Decimal,
44
40
  fsp_beta: Decimal,
@@ -53,6 +49,7 @@ class Builder(ClearingSystemAPI):
53
49
  auction_bounty: Decimal,
54
50
  tradeout_interval: int,
55
51
  extended_metadata: str,
52
+ oracle_address: str | None = None,
56
53
  ) -> ProductSpecification:
57
54
  """Creates a product specification with the given product data.
58
55
 
@@ -63,7 +60,6 @@ class Builder(ClearingSystemAPI):
63
60
  ----------
64
61
  symbol : str
65
62
  description : str
66
- oracle_address: str
67
63
  fsv_decimals: int
68
64
  fsp_alpha: Decimal
69
65
  fsp_beta: int
@@ -78,25 +74,30 @@ class Builder(ClearingSystemAPI):
78
74
  auction_bounty : Decimal
79
75
  tradeout_interval : int
80
76
  extended_metadata : str
77
+ oracle_address: str, optional
81
78
 
82
79
  Returns
83
80
  -------
84
81
  afp.schemas.ProductSpecification
85
82
  """
86
- product_id = Web3.to_hex(signing.generate_product_id(self._account, symbol))
83
+ if oracle_address is None:
84
+ oracle_address = self._config.oracle_provider_address
87
85
 
88
- erc20_contract = ERC20(self._w3, Web3.to_checksum_address(collateral_asset))
89
- price_quotation = erc20_contract.symbol()
90
-
91
- if not price_quotation:
86
+ if len(self._w3.eth.get_code(Web3.to_checksum_address(collateral_asset))) == 0:
92
87
  raise NotFoundError(f"No ERC20 token found at address {collateral_asset}")
93
-
94
88
  if len(self._w3.eth.get_code(Web3.to_checksum_address(oracle_address))) == 0:
95
89
  raise NotFoundError(f"No contract found at oracle address {oracle_address}")
96
90
 
91
+ erc20_contract = ERC20(self._w3, Web3.to_checksum_address(collateral_asset))
92
+ price_quotation = erc20_contract.symbol()
93
+
94
+ product_id = Web3.to_hex(
95
+ hashing.generate_product_id(self._authenticator.address, symbol)
96
+ )
97
+
97
98
  return ProductSpecification(
98
99
  id=product_id,
99
- builder_id=self._account.address,
100
+ builder_id=self._authenticator.address,
100
101
  symbol=symbol,
101
102
  description=description,
102
103
  oracle_address=oracle_address,
@@ -117,33 +118,89 @@ class Builder(ClearingSystemAPI):
117
118
  extended_metadata=extended_metadata,
118
119
  )
119
120
 
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,
127
+ )
128
+ return self.create(**kwargs)
129
+
130
+ ### Transactions ###
131
+
120
132
  @convert_web3_error(PRODUCT_REGISTRY_ABI)
121
- def register_product(self, product: ProductSpecification) -> str:
133
+ def register(self, product_specification: ProductSpecification) -> Transaction:
122
134
  """Submits a product specification to the clearing system.
123
135
 
124
136
  Parameters
125
137
  ----------
126
- product : afp.schemas.ProductSpecification
138
+ product_specification : afp.schemas.ProductSpecification
127
139
 
128
140
  Returns
129
141
  -------
130
- str
131
- The hash of the transaction.
142
+ afp.schemas.Transaction
143
+ Transaction parameters.
132
144
  """
133
145
  erc20_contract = ERC20(
134
- self._w3, cast(ChecksumAddress, product.collateral_asset)
146
+ self._w3, cast(ChecksumAddress, product_specification.collateral_asset)
135
147
  )
136
148
  decimals = erc20_contract.decimals()
137
149
 
138
- product_registry_contract = ProductRegistry(self._w3)
139
- tx_hash = product_registry_contract.register(
140
- self._convert_product_specification(product, decimals)
141
- ).transact()
142
- self._w3.eth.wait_for_transaction_receipt(tx_hash)
143
- return Web3.to_hex(tx_hash)
150
+ product_registry_contract = ProductRegistry(
151
+ self._w3, self._config.product_registry_address
152
+ )
153
+ return self._transact(
154
+ product_registry_contract.register(
155
+ self._convert_product_specification(product_specification, decimals)
156
+ )
157
+ )
158
+
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
+ @convert_web3_error(CLEARING_DIAMOND_ABI)
169
+ def initiate_final_settlement(
170
+ self, product_id: str, accounts: list[str]
171
+ ) -> Transaction:
172
+ """Initiate final settlement (closeout) process for the specified accounts.
173
+
174
+ The product must be in Final Settlement state. The accounts must hold non-zero
175
+ positions in the product that offset each other (i.e. the sum of their position
176
+ sizes is 0.)
177
+
178
+ Parameters
179
+ ----------
180
+ product_id : str
181
+ The ID of the product.
182
+ accounts : list of str
183
+ List of margin account IDs to initiate settlement for.
184
+
185
+ Returns
186
+ -------
187
+ afp.schemas.Transaction
188
+ Transaction parameters.
189
+ """
190
+ product_id = validators.validate_hexstr32(product_id)
191
+ addresses = [validators.validate_address(account) for account in accounts]
192
+
193
+ clearing_contract = ClearingDiamond(
194
+ self._w3, self._config.clearing_diamond_address
195
+ )
196
+ return self._transact(
197
+ clearing_contract.initiate_final_settlement(HexBytes(product_id), addresses)
198
+ )
199
+
200
+ ### Views ###
144
201
 
145
202
  @convert_web3_error(PRODUCT_REGISTRY_ABI)
146
- def product_state(self, product_id: str) -> str:
203
+ def state(self, product_id: str) -> str:
147
204
  """Returns the current state of a product.
148
205
 
149
206
  Parameters
@@ -156,15 +213,52 @@ class Builder(ClearingSystemAPI):
156
213
  str
157
214
  """
158
215
  product_id = validators.validate_hexstr32(product_id)
159
- product_registry_contract = ProductRegistry(self._w3)
216
+ product_registry_contract = ProductRegistry(
217
+ self._w3, self._config.product_registry_address
218
+ )
160
219
  state = product_registry_contract.state(HexBytes(product_id))
161
220
  return state.name
162
221
 
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)
232
+ def collateral_asset(self, product_id: str) -> str:
233
+ """Returns the collateral asset of a product.
234
+
235
+ Parameters
236
+ ----------
237
+ product_id : str
238
+ The ID of the product.
239
+
240
+ Returns
241
+ -------
242
+ str
243
+ """
244
+ product_id = validators.validate_hexstr32(product_id)
245
+ product_registry_contract = ProductRegistry(
246
+ self._w3, self._config.product_registry_address
247
+ )
248
+ collateral_asset = product_registry_contract.collateral_asset(
249
+ HexBytes(product_id)
250
+ )
251
+ if Web3.to_int(hexstr=collateral_asset) == 0:
252
+ raise NotFoundError("Product not found in the product registry")
253
+ return collateral_asset
254
+
255
+ ### Internal helpers ###
256
+
163
257
  @staticmethod
164
258
  def _convert_product_specification(
165
259
  product: ProductSpecification, decimals: int
166
- ) -> Product:
167
- return Product(
260
+ ) -> OnChainProduct:
261
+ return OnChainProduct(
168
262
  metadata=ProductMetadata(
169
263
  builder=cast(ChecksumAddress, product.builder_id),
170
264
  symbol=product.symbol,
@@ -173,7 +267,7 @@ class Builder(ClearingSystemAPI):
173
267
  oracle_spec=OracleSpecification(
174
268
  oracle_address=cast(ChecksumAddress, product.oracle_address),
175
269
  fsv_decimals=product.fsv_decimals,
176
- fsp_alpha=int(product.fsp_alpha * config.FULL_PRECISION_MULTIPLIER),
270
+ fsp_alpha=int(product.fsp_alpha * constants.FULL_PRECISION_MULTIPLIER),
177
271
  fsp_beta=int(product.fsp_beta * 10**product.fsv_decimals),
178
272
  fsv_calldata=HexBytes(product.fsv_calldata),
179
273
  ),
@@ -186,12 +280,12 @@ class Builder(ClearingSystemAPI):
186
280
  tick_size=product.tick_size,
187
281
  unit_value=int(product.unit_value * 10**decimals),
188
282
  initial_margin_requirement=int(
189
- product.initial_margin_requirement * config.RATE_MULTIPLIER
283
+ product.initial_margin_requirement * constants.RATE_MULTIPLIER
190
284
  ),
191
285
  maintenance_margin_requirement=int(
192
- product.maintenance_margin_requirement * config.RATE_MULTIPLIER
286
+ product.maintenance_margin_requirement * constants.RATE_MULTIPLIER
193
287
  ),
194
- auction_bounty=int(product.auction_bounty * config.RATE_MULTIPLIER),
288
+ auction_bounty=int(product.auction_bounty * constants.RATE_MULTIPLIER),
195
289
  tradeout_interval=product.tradeout_interval,
196
290
  extended_metadata=product.extended_metadata,
197
291
  )
afp/api/trading.py CHANGED
@@ -5,7 +5,7 @@ from typing import Generator
5
5
 
6
6
  from web3 import Web3
7
7
 
8
- from .. import signing, validators
8
+ from .. import hashing, validators
9
9
  from ..decorators import refresh_token_on_expiry
10
10
  from ..enums import OrderType
11
11
  from ..schemas import (
@@ -25,20 +25,7 @@ from .base import ExchangeAPI
25
25
 
26
26
 
27
27
  class Trading(ExchangeAPI):
28
- """API for trading in the AutEx exchange.
29
-
30
- Authenticates with the exchange on creation.
31
-
32
- Parameters
33
- ----------
34
- private_key : str
35
- The private key of the account that submits intents to the exchange.
36
-
37
- Raises
38
- ------
39
- afp.exceptions.AuthenticationError
40
- If the exchange rejects the login attempt.
41
- """
28
+ """API for trading in the AutEx exchange."""
42
29
 
43
30
  @staticmethod
44
31
  def _generate_nonce() -> int:
@@ -89,7 +76,7 @@ class Trading(ExchangeAPI):
89
76
  margin_account_id = (
90
77
  validators.validate_address(margin_account_id)
91
78
  if margin_account_id is not None
92
- else self._account.address
79
+ else self._authenticator.address
93
80
  )
94
81
 
95
82
  intent_data = IntentData(
@@ -104,17 +91,17 @@ class Trading(ExchangeAPI):
104
91
  good_until_time=good_until_time,
105
92
  nonce=self._generate_nonce(),
106
93
  )
107
- intent_hash = signing.generate_intent_hash(
94
+ intent_hash = hashing.generate_intent_hash(
108
95
  intent_data=intent_data,
109
96
  margin_account_id=margin_account_id,
110
- intent_account_id=self._account.address,
97
+ intent_account_id=self._authenticator.address,
111
98
  tick_size=product.tick_size,
112
99
  )
113
- signature = signing.sign_message(self._account, intent_hash)
100
+ signature = self._authenticator.sign_message(intent_hash)
114
101
  return Intent(
115
102
  hash=Web3.to_hex(intent_hash),
116
103
  margin_account_id=margin_account_id,
117
- intent_account_id=self._account.address,
104
+ intent_account_id=self._authenticator.address,
118
105
  signature=Web3.to_hex(signature),
119
106
  data=intent_data,
120
107
  )
@@ -160,12 +147,12 @@ class Trading(ExchangeAPI):
160
147
  If the exchange rejects the cancellation.
161
148
  """
162
149
  nonce = self._generate_nonce()
163
- cancellation_hash = signing.generate_order_cancellation_hash(nonce, intent_hash)
164
- signature = signing.sign_message(self._account, cancellation_hash)
150
+ cancellation_hash = hashing.generate_order_cancellation_hash(nonce, intent_hash)
151
+ signature = self._authenticator.sign_message(cancellation_hash)
165
152
  cancellation_data = OrderCancellationData(
166
153
  intent_hash=intent_hash,
167
154
  nonce=nonce,
168
- intent_account_id=self._account.address,
155
+ intent_account_id=self._authenticator.address,
169
156
  signature=Web3.to_hex(signature),
170
157
  )
171
158
  submission = OrderSubmission(
@@ -174,7 +161,6 @@ class Trading(ExchangeAPI):
174
161
  )
175
162
  return self._exchange.submit_order(submission)
176
163
 
177
- @refresh_token_on_expiry
178
164
  def products(self) -> list[ExchangeProduct]:
179
165
  """Retrieves the products approved for trading on the exchange.
180
166
 
@@ -184,7 +170,6 @@ class Trading(ExchangeAPI):
184
170
  """
185
171
  return self._exchange.get_approved_products()
186
172
 
187
- @refresh_token_on_expiry
188
173
  def product(self, product_id: str) -> ExchangeProduct:
189
174
  """Retrieves a product for trading by its ID.
190
175
 
@@ -266,7 +251,7 @@ class Trading(ExchangeAPI):
266
251
  list of afp.schemas.OrderFill
267
252
  """
268
253
  filter = OrderFillFilter(
269
- intent_account_id=self._account.address,
254
+ intent_account_id=self._authenticator.address,
270
255
  product_id=product_id,
271
256
  margin_account_id=margin_account_id,
272
257
  intent_hash=intent_hash,
@@ -302,7 +287,7 @@ class Trading(ExchangeAPI):
302
287
  afp.schemas.OrderFill
303
288
  """
304
289
  filter = OrderFillFilter(
305
- intent_account_id=self._account.address,
290
+ intent_account_id=self._authenticator.address,
306
291
  product_id=product_id,
307
292
  margin_account_id=margin_account_id,
308
293
  intent_hash=intent_hash,
@@ -312,7 +297,6 @@ class Trading(ExchangeAPI):
312
297
  )
313
298
  yield from self._exchange.iter_order_fills(filter)
314
299
 
315
- @refresh_token_on_expiry
316
300
  def market_depth(self, product_id: str) -> MarketDepthData:
317
301
  """Retrieves the depth of market for the given product.
318
302
 
@@ -332,7 +316,6 @@ class Trading(ExchangeAPI):
332
316
  value = validators.validate_hexstr32(product_id)
333
317
  return self._exchange.get_market_depth_data(value)
334
318
 
335
- @refresh_token_on_expiry
336
319
  def iter_market_depth(
337
320
  self, product_id: str
338
321
  ) -> Generator[MarketDepthData, None, None]:
afp/auth.py ADDED
@@ -0,0 +1,66 @@
1
+ import json
2
+ import os
3
+ from typing import Protocol, cast
4
+
5
+ from eth_account.account import Account
6
+ from eth_account.datastructures import SignedTransaction
7
+ from eth_account.messages import encode_defunct
8
+ from eth_account.signers.local import LocalAccount
9
+ from eth_account.types import TransactionDictType
10
+ from eth_typing.evm import ChecksumAddress
11
+ from hexbytes import HexBytes
12
+ from web3.types import TxParams
13
+
14
+
15
+ class Authenticator(Protocol):
16
+ address: ChecksumAddress
17
+
18
+ def sign_message(self, message: bytes) -> HexBytes: ...
19
+
20
+ def sign_transaction(self, params: TxParams) -> SignedTransaction: ...
21
+
22
+
23
+ class PrivateKeyAuthenticator(Authenticator):
24
+ """Authenticates with a private key specified in a constructor argument.
25
+
26
+ Parameters
27
+ ----------
28
+ private_key: str
29
+ The private key of a blockchain account.
30
+ """
31
+
32
+ _account: LocalAccount
33
+
34
+ def __init__(self, private_key: str) -> None:
35
+ self._account = Account.from_key(private_key)
36
+ self.address = self._account.address
37
+
38
+ def sign_message(self, message: bytes) -> HexBytes:
39
+ eip191_message = encode_defunct(message)
40
+ signed_message = self._account.sign_message(eip191_message)
41
+ return signed_message.signature
42
+
43
+ def sign_transaction(self, params: TxParams) -> SignedTransaction:
44
+ return self._account.sign_transaction(cast(TransactionDictType, params))
45
+
46
+ def __repr__(self):
47
+ return f"{self.__class__.__name__}(address='{self.address}')"
48
+
49
+
50
+ class KeyfileAuthenticator(PrivateKeyAuthenticator):
51
+ """Authenticates with a private key read from an encrypted keyfile.
52
+
53
+ Parameters
54
+ ----------
55
+ key_file : str
56
+ The path to the keyfile.
57
+ password : str
58
+ The password for decrypting the keyfile.
59
+ """
60
+
61
+ def __init__(self, key_file: str, password: str) -> None:
62
+ with open(os.path.expanduser(key_file), encoding="utf8") as f:
63
+ key_data = json.load(f)
64
+
65
+ private_key = Account.decrypt(key_data, password=password)
66
+ super().__init__(private_key.to_0x_hex())
afp/bindings/facade.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from itertools import chain
2
2
 
3
+ from eth_typing.evm import ChecksumAddress
4
+
3
5
  from web3 import Web3
4
6
 
5
- from .. import config
6
7
  from . import (
7
8
  auctioneer_facet,
8
9
  bankruptcy_facet,
@@ -14,6 +15,7 @@ from . import (
14
15
  product_registry,
15
16
  system_viewer,
16
17
  )
18
+ from ..constants import defaults
17
19
 
18
20
  # In order to include a facet in the ClearingDiamond facade:
19
21
  # 1. Add its ABI to CLEARING_DIAMOND_ABI
@@ -46,10 +48,10 @@ class ClearingDiamond(
46
48
  w3 : web3.Web3
47
49
  """
48
50
 
49
- def __init__(self, w3: Web3):
50
- self._contract = w3.eth.contract(
51
- address=config.CLEARING_DIAMOND_ADDRESS, abi=CLEARING_DIAMOND_ABI
52
- )
51
+ def __init__(
52
+ self, w3: Web3, address: ChecksumAddress = defaults.CLEARING_DIAMOND_ADDRESS
53
+ ):
54
+ self._contract = w3.eth.contract(address=address, abi=CLEARING_DIAMOND_ABI)
53
55
 
54
56
 
55
57
  class MarginAccountRegistry(margin_account_registry.MarginAccountRegistry):
@@ -60,8 +62,12 @@ class MarginAccountRegistry(margin_account_registry.MarginAccountRegistry):
60
62
  w3 : web3.Web3
61
63
  """
62
64
 
63
- def __init__(self, w3: Web3):
64
- super().__init__(w3, config.MARGIN_ACCOUNT_REGISTRY_ADDRESS)
65
+ def __init__(
66
+ self,
67
+ w3: Web3,
68
+ address: ChecksumAddress = defaults.MARGIN_ACCOUNT_REGISTRY_ADDRESS,
69
+ ):
70
+ super().__init__(w3, address)
65
71
 
66
72
 
67
73
  class OracleProvider(oracle_provider.OracleProvider):
@@ -72,8 +78,10 @@ class OracleProvider(oracle_provider.OracleProvider):
72
78
  w3 : web3.Web3
73
79
  """
74
80
 
75
- def __init__(self, w3: Web3):
76
- super().__init__(w3, config.ORACLE_PROVIDER_ADDRESS)
81
+ def __init__(
82
+ self, w3: Web3, address: ChecksumAddress = defaults.ORACLE_PROVIDER_ADDRESS
83
+ ):
84
+ super().__init__(w3, address)
77
85
 
78
86
 
79
87
  class ProductRegistry(product_registry.ProductRegistry):
@@ -84,8 +92,10 @@ class ProductRegistry(product_registry.ProductRegistry):
84
92
  w3 : web3.Web3
85
93
  """
86
94
 
87
- def __init__(self, w3: Web3):
88
- super().__init__(w3, config.PRODUCT_REGISTRY_ADDRESS)
95
+ def __init__(
96
+ self, w3: Web3, address: ChecksumAddress = defaults.PRODUCT_REGISTRY_ADDRESS
97
+ ):
98
+ super().__init__(w3, address)
89
99
 
90
100
 
91
101
  class SystemViewer(system_viewer.SystemViewer):
@@ -96,5 +106,7 @@ class SystemViewer(system_viewer.SystemViewer):
96
106
  w3 : web3.Web3
97
107
  """
98
108
 
99
- def __init__(self, w3: Web3):
100
- super().__init__(w3, config.SYSTEM_VIEWER_ADDRESS)
109
+ def __init__(
110
+ self, w3: Web3, address: ChecksumAddress = defaults.SYSTEM_VIEWER_ADDRESS
111
+ ):
112
+ super().__init__(w3, address)
afp/config.py CHANGED
@@ -1,42 +1,28 @@
1
- import os
1
+ from dataclasses import dataclass
2
2
 
3
- from web3 import Web3
3
+ from eth_typing.evm import ChecksumAddress
4
4
 
5
+ from .auth import Authenticator
5
6
 
6
- # Constants from clearing/contracts/lib/constants.sol
7
- RATE_MULTIPLIER = 10**4
8
- FEE_RATE_MULTIPLIER = 10**6
9
- FULL_PRECISION_MULTIPLIER = 10**18
10
7
 
11
- USER_AGENT = "afp-sdk"
12
- DEFAULT_EXCHANGE_API_VERSION = 1
13
- EXCHANGE_URL = os.getenv(
14
- "AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app"
15
- )
8
+ @dataclass(frozen=True)
9
+ class Config:
10
+ authenticator: Authenticator | None
16
11
 
17
- CHAIN_ID = int(os.getenv("AFP_CHAIN_ID", 65000000))
12
+ # Venue parameters
13
+ exchange_url: str
18
14
 
19
- CLEARING_DIAMOND_ADDRESS = Web3.to_checksum_address(
20
- os.getenv(
21
- "AFP_CLEARING_DIAMOND_ADDRESS", "0x5B5411F1548254d25360d71FE40cFc1cC983B2A2"
22
- )
23
- )
24
- MARGIN_ACCOUNT_REGISTRY_ADDRESS = Web3.to_checksum_address(
25
- os.getenv(
26
- "AFP_MARGIN_ACCOUNT_REGISTRY_ADDRESS",
27
- "0x99f4FA9Cdce7AD227eB84907936a8FeF2095D846",
28
- )
29
- )
30
- ORACLE_PROVIDER_ADDRESS = Web3.to_checksum_address(
31
- os.getenv(
32
- "AFP_ORACLE_PROVIDER_ADDRESS", "0xF2A2A27da33D30B4BF38D7e186E7B0b1e964e55c"
33
- )
34
- )
35
- PRODUCT_REGISTRY_ADDRESS = Web3.to_checksum_address(
36
- os.getenv(
37
- "AFP_PRODUCT_REGISTRY_ADDRESS", "0x86B3829471929B115367DA0958f56A6AB844b08e"
38
- )
39
- )
40
- SYSTEM_VIEWER_ADDRESS = Web3.to_checksum_address(
41
- os.getenv("AFP_SYSTEM_VIEWER_ADDRESS", "0xfF2DFcC44a95cce96E03EfC33C65c8Be671Bae5B")
42
- )
15
+ # Blockchain parameters
16
+ rpc_url: str | None
17
+ chain_id: int
18
+ gas_limit: int | None
19
+ max_fee_per_gas: int | None
20
+ max_priority_fee_per_gas: int | None
21
+ timeout_seconds: int
22
+
23
+ # Clearing System parameters
24
+ clearing_diamond_address: ChecksumAddress
25
+ margin_account_registry_address: ChecksumAddress
26
+ oracle_provider_address: ChecksumAddress
27
+ product_registry_address: ChecksumAddress
28
+ system_viewer_address: ChecksumAddress
afp/constants.py ADDED
@@ -0,0 +1,52 @@
1
+ import os
2
+ from types import SimpleNamespace
3
+
4
+
5
+ def _int_or_none(value: str | None) -> int | None:
6
+ return int(value) if value is not None else None
7
+
8
+
9
+ USER_AGENT = "afp-sdk"
10
+ DEFAULT_EXCHANGE_API_VERSION = 1
11
+
12
+ # Constants from clearing/contracts/lib/constants.sol
13
+ RATE_MULTIPLIER = 10**4
14
+ FEE_RATE_MULTIPLIER = 10**6
15
+ FULL_PRECISION_MULTIPLIER = 10**18
16
+
17
+ defaults = SimpleNamespace(
18
+ # Authentication parameters
19
+ KEYFILE=os.getenv("AFP_KEYFILE", None),
20
+ KEYFILE_PASSWORD=os.getenv("AFP_KEYFILE_PASSWORD", ""),
21
+ PRIVATE_KEY=os.getenv("AFP_PRIVATE_KEY", None),
22
+ # Venue parameters
23
+ EXCHANGE_URL=os.getenv(
24
+ "AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app"
25
+ ),
26
+ # Blockchain parameters
27
+ RPC_URL=os.getenv("AFP_RPC_URL", None),
28
+ CHAIN_ID=int(os.getenv("AFP_CHAIN_ID", 65000000)),
29
+ GAS_LIMIT=_int_or_none(os.getenv("AFP_GAS_LIMIT", None)),
30
+ MAX_FEE_PER_GAS=_int_or_none(os.getenv("AFP_MAX_FEE_PER_GAS", None)),
31
+ MAX_PRIORITY_FEE_PER_GAS=_int_or_none(
32
+ os.getenv("AFP_MAX_PRIORITY_FEE_PER_GAS", None)
33
+ ),
34
+ TIMEOUT_SECONDS=int(os.getenv("AFP_TIMEOUT_SECONDS", 10)),
35
+ # Clearing System parameters
36
+ CLEARING_DIAMOND_ADDRESS=os.getenv(
37
+ "AFP_CLEARING_DIAMOND_ADDRESS", "0x5B5411F1548254d25360d71FE40cFc1cC983B2A2"
38
+ ),
39
+ MARGIN_ACCOUNT_REGISTRY_ADDRESS=os.getenv(
40
+ "AFP_MARGIN_ACCOUNT_REGISTRY_ADDRESS",
41
+ "0x99f4FA9Cdce7AD227eB84907936a8FeF2095D846",
42
+ ),
43
+ ORACLE_PROVIDER_ADDRESS=os.getenv(
44
+ "AFP_ORACLE_PROVIDER_ADDRESS", "0xF2A2A27da33D30B4BF38D7e186E7B0b1e964e55c"
45
+ ),
46
+ PRODUCT_REGISTRY_ADDRESS=os.getenv(
47
+ "AFP_PRODUCT_REGISTRY_ADDRESS", "0x86B3829471929B115367DA0958f56A6AB844b08e"
48
+ ),
49
+ SYSTEM_VIEWER_ADDRESS=os.getenv(
50
+ "AFP_SYSTEM_VIEWER_ADDRESS", "0xfF2DFcC44a95cce96E03EfC33C65c8Be671Bae5B"
51
+ ),
52
+ )