afp-sdk 0.3.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.
@@ -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 config, signing, validators
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 Builder(ClearingSystemAPI):
25
- """API for building and submitting new products.
26
+ class Product(ClearingSystemAPI):
27
+ """API for managing products."""
26
28
 
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
- """
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,
@@ -50,10 +45,10 @@ class Builder(ClearingSystemAPI):
50
45
  unit_value: Decimal,
51
46
  initial_margin_requirement: Decimal,
52
47
  maintenance_margin_requirement: Decimal,
53
- offer_price_buffer: Decimal,
54
48
  auction_bounty: Decimal,
55
49
  tradeout_interval: int,
56
50
  extended_metadata: str,
51
+ oracle_address: str | None = None,
57
52
  ) -> ProductSpecification:
58
53
  """Creates a product specification with the given product data.
59
54
 
@@ -64,7 +59,6 @@ class Builder(ClearingSystemAPI):
64
59
  ----------
65
60
  symbol : str
66
61
  description : str
67
- oracle_address: str
68
62
  fsv_decimals: int
69
63
  fsp_alpha: Decimal
70
64
  fsp_beta: int
@@ -76,29 +70,33 @@ class Builder(ClearingSystemAPI):
76
70
  unit_value : Decimal
77
71
  initial_margin_requirement : Decimal
78
72
  maintenance_margin_requirement : Decimal
79
- offer_price_buffer : Decimal
80
73
  auction_bounty : Decimal
81
74
  tradeout_interval : int
82
75
  extended_metadata : str
76
+ oracle_address: str, optional
83
77
 
84
78
  Returns
85
79
  -------
86
80
  afp.schemas.ProductSpecification
87
81
  """
88
- product_id = Web3.to_hex(signing.generate_product_id(self._account, symbol))
82
+ if oracle_address is None:
83
+ oracle_address = self._config.oracle_provider_address
89
84
 
90
- erc20_contract = ERC20(self._w3, Web3.to_checksum_address(collateral_asset))
91
- price_quotation = erc20_contract.symbol()
92
-
93
- if not price_quotation:
85
+ if len(self._w3.eth.get_code(Web3.to_checksum_address(collateral_asset))) == 0:
94
86
  raise NotFoundError(f"No ERC20 token found at address {collateral_asset}")
95
-
96
87
  if len(self._w3.eth.get_code(Web3.to_checksum_address(oracle_address))) == 0:
97
88
  raise NotFoundError(f"No contract found at oracle address {oracle_address}")
98
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
+
99
97
  return ProductSpecification(
100
98
  id=product_id,
101
- builder_id=self._account.address,
99
+ builder_id=self._authenticator.address,
102
100
  symbol=symbol,
103
101
  description=description,
104
102
  oracle_address=oracle_address,
@@ -114,14 +112,15 @@ class Builder(ClearingSystemAPI):
114
112
  unit_value=unit_value,
115
113
  initial_margin_requirement=initial_margin_requirement,
116
114
  maintenance_margin_requirement=maintenance_margin_requirement,
117
- offer_price_buffer=offer_price_buffer,
118
115
  auction_bounty=auction_bounty,
119
116
  tradeout_interval=tradeout_interval,
120
117
  extended_metadata=extended_metadata,
121
118
  )
122
119
 
120
+ ### Transactions ###
121
+
123
122
  @convert_web3_error(PRODUCT_REGISTRY_ABI)
124
- def register_product(self, product: ProductSpecification) -> str:
123
+ def register_product(self, product: ProductSpecification) -> Transaction:
125
124
  """Submits a product specification to the clearing system.
126
125
 
127
126
  Parameters
@@ -130,20 +129,56 @@ class Builder(ClearingSystemAPI):
130
129
 
131
130
  Returns
132
131
  -------
133
- str
134
- The hash of the transaction.
132
+ afp.schemas.Transaction
133
+ Transaction parameters.
135
134
  """
136
135
  erc20_contract = ERC20(
137
136
  self._w3, cast(ChecksumAddress, product.collateral_asset)
138
137
  )
139
138
  decimals = erc20_contract.decimals()
140
139
 
141
- product_registry_contract = ProductRegistry(self._w3)
142
- tx_hash = product_registry_contract.register(
143
- self._convert_product_specification(product, decimals)
144
- ).transact()
145
- self._w3.eth.wait_for_transaction_receipt(tx_hash)
146
- return Web3.to_hex(tx_hash)
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 ###
147
182
 
148
183
  @convert_web3_error(PRODUCT_REGISTRY_ABI)
149
184
  def product_state(self, product_id: str) -> str:
@@ -159,15 +194,43 @@ class Builder(ClearingSystemAPI):
159
194
  str
160
195
  """
161
196
  product_id = validators.validate_hexstr32(product_id)
162
- product_registry_contract = ProductRegistry(self._w3)
197
+ product_registry_contract = ProductRegistry(
198
+ self._w3, self._config.product_registry_address
199
+ )
163
200
  state = product_registry_contract.state(HexBytes(product_id))
164
201
  return state.name
165
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
+
166
229
  @staticmethod
167
230
  def _convert_product_specification(
168
231
  product: ProductSpecification, decimals: int
169
- ) -> Product:
170
- return Product(
232
+ ) -> OnChainProduct:
233
+ return OnChainProduct(
171
234
  metadata=ProductMetadata(
172
235
  builder=cast(ChecksumAddress, product.builder_id),
173
236
  symbol=product.symbol,
@@ -176,7 +239,7 @@ class Builder(ClearingSystemAPI):
176
239
  oracle_spec=OracleSpecification(
177
240
  oracle_address=cast(ChecksumAddress, product.oracle_address),
178
241
  fsv_decimals=product.fsv_decimals,
179
- fsp_alpha=int(product.fsp_alpha * config.FULL_PRECISION_MULTIPLIER),
242
+ fsp_alpha=int(product.fsp_alpha * constants.FULL_PRECISION_MULTIPLIER),
180
243
  fsp_beta=int(product.fsp_beta * 10**product.fsv_decimals),
181
244
  fsv_calldata=HexBytes(product.fsv_calldata),
182
245
  ),
@@ -189,13 +252,12 @@ class Builder(ClearingSystemAPI):
189
252
  tick_size=product.tick_size,
190
253
  unit_value=int(product.unit_value * 10**decimals),
191
254
  initial_margin_requirement=int(
192
- product.initial_margin_requirement * config.RATE_MULTIPLIER
255
+ product.initial_margin_requirement * constants.RATE_MULTIPLIER
193
256
  ),
194
257
  maintenance_margin_requirement=int(
195
- product.maintenance_margin_requirement * config.RATE_MULTIPLIER
258
+ product.maintenance_margin_requirement * constants.RATE_MULTIPLIER
196
259
  ),
197
- offer_price_buffer=int(product.offer_price_buffer * config.RATE_MULTIPLIER),
198
- auction_bounty=int(product.auction_bounty * config.RATE_MULTIPLIER),
260
+ auction_bounty=int(product.auction_bounty * constants.RATE_MULTIPLIER),
199
261
  tradeout_interval=product.tradeout_interval,
200
262
  extended_metadata=product.extended_metadata,
201
263
  )
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
 
@@ -226,15 +211,19 @@ class Trading(ExchangeAPI):
226
211
  return self._exchange.get_order_by_id(value)
227
212
 
228
213
  @refresh_token_on_expiry
229
- def open_orders(self) -> list[Order]:
214
+ def open_orders(self, product_id: str | None = None) -> list[Order]:
230
215
  """Retrieves all open and partially filled limit orders that have been submitted
231
216
  by the authenticated account.
232
217
 
218
+ Parameters
219
+ ----------
220
+ product_id : str, optional
221
+
233
222
  Returns
234
223
  -------
235
224
  list of afp.schemas.Order
236
225
  """
237
- return self._exchange.get_open_orders()
226
+ return self._exchange.get_open_orders(product_id)
238
227
 
239
228
  @refresh_token_on_expiry
240
229
  def order_fills(
@@ -262,7 +251,7 @@ class Trading(ExchangeAPI):
262
251
  list of afp.schemas.OrderFill
263
252
  """
264
253
  filter = OrderFillFilter(
265
- intent_account_id=self._account.address,
254
+ intent_account_id=self._authenticator.address,
266
255
  product_id=product_id,
267
256
  margin_account_id=margin_account_id,
268
257
  intent_hash=intent_hash,
@@ -298,7 +287,7 @@ class Trading(ExchangeAPI):
298
287
  afp.schemas.OrderFill
299
288
  """
300
289
  filter = OrderFillFilter(
301
- intent_account_id=self._account.address,
290
+ intent_account_id=self._authenticator.address,
302
291
  product_id=product_id,
303
292
  margin_account_id=margin_account_id,
304
293
  intent_hash=intent_hash,
@@ -308,7 +297,6 @@ class Trading(ExchangeAPI):
308
297
  )
309
298
  yield from self._exchange.iter_order_fills(filter)
310
299
 
311
- @refresh_token_on_expiry
312
300
  def market_depth(self, product_id: str) -> MarketDepthData:
313
301
  """Retrieves the depth of market for the given product.
314
302
 
@@ -328,7 +316,6 @@ class Trading(ExchangeAPI):
328
316
  value = validators.validate_hexstr32(product_id)
329
317
  return self._exchange.get_market_depth_data(value)
330
318
 
331
- @refresh_token_on_expiry
332
319
  def iter_market_depth(
333
320
  self, product_id: str
334
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
+ keyfile : 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())
@@ -56,6 +56,11 @@ class BankruptcyFacet:
56
56
  abi=ABI,
57
57
  )
58
58
 
59
+ @property
60
+ def LossMutualized(self) -> contract.ContractEvent:
61
+ """Binding for `event LossMutualized` on the BankruptcyFacet contract."""
62
+ return self._contract.events.LossMutualized
63
+
59
64
  def last_traded_timestamp(
60
65
  self,
61
66
  product_id: hexbytes.HexBytes,
@@ -219,6 +224,37 @@ ABI = typing.cast(
219
224
  "type": "error",
220
225
  },
221
226
  {"inputs": [], "name": "QueueIsEmpty", "type": "error"},
227
+ {
228
+ "anonymous": False,
229
+ "inputs": [
230
+ {
231
+ "indexed": True,
232
+ "internalType": "address",
233
+ "name": "bankruptAccount",
234
+ "type": "address",
235
+ },
236
+ {
237
+ "indexed": True,
238
+ "internalType": "address",
239
+ "name": "collateralToken",
240
+ "type": "address",
241
+ },
242
+ {
243
+ "indexed": False,
244
+ "internalType": "address",
245
+ "name": "caller",
246
+ "type": "address",
247
+ },
248
+ {
249
+ "indexed": False,
250
+ "internalType": "int256",
251
+ "name": "lossAmount",
252
+ "type": "int256",
253
+ },
254
+ ],
255
+ "name": "LossMutualized",
256
+ "type": "event",
257
+ },
222
258
  {
223
259
  "inputs": [
224
260
  {"internalType": "bytes32", "name": "productId", "type": "bytes32"},
@@ -423,6 +423,25 @@ class ClearingFacet:
423
423
  ),
424
424
  )
425
425
 
426
+ def set_treasury(
427
+ self,
428
+ new_treasury: eth_typing.ChecksumAddress,
429
+ ) -> contract.ContractFunction:
430
+ """Binding for `setTreasury` on the ClearingFacet contract.
431
+
432
+ Parameters
433
+ ----------
434
+ new_treasury : eth_typing.ChecksumAddress
435
+
436
+ Returns
437
+ -------
438
+ web3.contract.contract.ContractFunction
439
+ A contract function instance to be sent in a transaction.
440
+ """
441
+ return self._contract.functions.setTreasury(
442
+ new_treasury,
443
+ )
444
+
426
445
  def version(
427
446
  self,
428
447
  ) -> str:
@@ -1003,6 +1022,15 @@ ABI = typing.cast(
1003
1022
  "stateMutability": "nonpayable",
1004
1023
  "type": "function",
1005
1024
  },
1025
+ {
1026
+ "inputs": [
1027
+ {"internalType": "address", "name": "newTreasury", "type": "address"}
1028
+ ],
1029
+ "name": "setTreasury",
1030
+ "outputs": [],
1031
+ "stateMutability": "nonpayable",
1032
+ "type": "function",
1033
+ },
1006
1034
  {
1007
1035
  "inputs": [],
1008
1036
  "name": "version",
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)