dkg 0.1.0b5__py3-none-any.whl → 1.0.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 (43) hide show
  1. dkg/asset.py +182 -70
  2. dkg/constants.py +39 -6
  3. dkg/data/interfaces/ContentAsset.json +133 -3
  4. dkg/data/interfaces/Paranet.json +821 -0
  5. dkg/data/interfaces/{Identity.json → ParanetIncentivesPoolFactory.json} +67 -86
  6. dkg/data/interfaces/ParanetKnowledgeMinersRegistry.json +919 -0
  7. dkg/data/interfaces/ParanetNeurowebIncentivesPool.json +1102 -0
  8. dkg/data/interfaces/{ServiceAgreementStorageV1.json → ParanetsRegistry.json} +331 -360
  9. dkg/dataclasses.py +28 -8
  10. dkg/main.py +6 -3
  11. dkg/method.py +55 -39
  12. dkg/module.py +1 -0
  13. dkg/network.py +20 -10
  14. dkg/paranet.py +477 -0
  15. dkg/providers/blockchain.py +83 -60
  16. dkg/types/__init__.py +1 -0
  17. dkg/types/general.py +44 -0
  18. dkg/utils/blockchain_request.py +149 -4
  19. dkg/utils/node_request.py +77 -80
  20. {dkg-0.1.0b5.dist-info → dkg-1.0.0.dist-info}/METADATA +6 -144
  21. dkg-1.0.0.dist-info/NOTICE +9 -0
  22. dkg-1.0.0.dist-info/RECORD +52 -0
  23. {dkg-0.1.0b5.dist-info → dkg-1.0.0.dist-info}/WHEEL +1 -1
  24. dkg/data/interfaces/Assertion.json +0 -157
  25. dkg/data/interfaces/CommitManagerV1.json +0 -549
  26. dkg/data/interfaces/CommitManagerV1U1.json +0 -735
  27. dkg/data/interfaces/HashingProxy.json +0 -253
  28. dkg/data/interfaces/IdentityStorage.json +0 -342
  29. dkg/data/interfaces/ParametersStorage.json +0 -487
  30. dkg/data/interfaces/Profile.json +0 -318
  31. dkg/data/interfaces/ProfileStorage.json +0 -596
  32. dkg/data/interfaces/ProofManagerV1.json +0 -540
  33. dkg/data/interfaces/ProofManagerV1U1.json +0 -561
  34. dkg/data/interfaces/ScoringProxy.json +0 -268
  35. dkg/data/interfaces/ServiceAgreementStorageV1U1.json +0 -1097
  36. dkg/data/interfaces/ServiceAgreementV1.json +0 -745
  37. dkg/data/interfaces/ShardingTable.json +0 -294
  38. dkg/data/interfaces/ShardingTableStorage.json +0 -317
  39. dkg/data/interfaces/Staking.json +0 -482
  40. dkg/data/interfaces/StakingStorage.json +0 -407
  41. dkg/data/interfaces/WhitelistStorage.json +0 -124
  42. dkg-0.1.0b5.dist-info/RECORD +0 -64
  43. {dkg-0.1.0b5.dist-info → dkg-1.0.0.dist-info}/LICENSE +0 -0
dkg/paranet.py ADDED
@@ -0,0 +1,477 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ import json
19
+
20
+ from dataclasses import dataclass
21
+ from web3 import Web3
22
+ from web3.contract import Contract
23
+ from web3.types import TxReceipt
24
+
25
+ from dkg.dataclasses import BaseIncentivesPoolParams, ParanetIncentivizationType
26
+ from dkg.manager import DefaultRequestManager
27
+ from dkg.method import Method
28
+ from dkg.module import Module
29
+ from dkg.types import Address, UAL, HexStr
30
+ from dkg.utils.blockchain_request import BlockchainRequest
31
+ from dkg.utils.ual import parse_ual
32
+
33
+
34
+ class Paranet(Module):
35
+ @dataclass
36
+ class NeuroWebIncentivesPoolParams(BaseIncentivesPoolParams):
37
+ neuro_emission_multiplier: float
38
+ operator_percentage: float
39
+ voters_percentage: float
40
+
41
+ def to_contract_args(self) -> dict:
42
+ return {
43
+ "tracToNeuroEmissionMultiplier": int(
44
+ self.neuro_emission_multiplier * (10**12)
45
+ ),
46
+ "paranetOperatorRewardPercentage": int(self.operator_percentage * 100),
47
+ "paranetIncentivizationProposalVotersRewardPercentage": int(
48
+ self.voters_percentage * 100
49
+ ),
50
+ }
51
+
52
+ def __init__(self, manager: DefaultRequestManager):
53
+ self.manager = manager
54
+ self.incentives_pools_deployment_functions = {
55
+ ParanetIncentivizationType.NEUROWEB: self._deploy_neuro_incentives_pool,
56
+ }
57
+
58
+ _register_paranet = Method(BlockchainRequest.register_paranet)
59
+
60
+ def create(
61
+ self, ual: UAL, name: str, description: str
62
+ ) -> dict[str, str | HexStr | TxReceipt]:
63
+ parsed_ual = parse_ual(ual)
64
+ knowledge_asset_storage, knowledge_asset_token_id = (
65
+ parsed_ual["contract_address"],
66
+ parsed_ual["token_id"],
67
+ )
68
+
69
+ receipt: TxReceipt = self._register_paranet(
70
+ knowledge_asset_storage,
71
+ knowledge_asset_token_id,
72
+ name,
73
+ description,
74
+ )
75
+
76
+ return {
77
+ "paranetUAL": ual,
78
+ "paranetId": Web3.to_hex(
79
+ Web3.solidity_keccak(
80
+ ["address", "uint256"],
81
+ [knowledge_asset_storage, knowledge_asset_token_id],
82
+ )
83
+ ),
84
+ "operation": json.loads(Web3.to_json(receipt)),
85
+ }
86
+
87
+ _deploy_neuro_incentives_pool = Method(
88
+ BlockchainRequest.deploy_neuro_incentives_pool
89
+ )
90
+
91
+ def deploy_incentives_contract(
92
+ self,
93
+ ual: UAL,
94
+ incentives_pool_parameters: NeuroWebIncentivesPoolParams,
95
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
96
+ ) -> dict[str, str | HexStr | TxReceipt]:
97
+ deploy_incentives_pool_fn = self.incentives_pools_deployment_functions.get(
98
+ incentives_type,
99
+ None,
100
+ )
101
+
102
+ if deploy_incentives_pool_fn is None:
103
+ raise ValueError(
104
+ f"{incentives_type} Incentive Type isn't supported. Supported "
105
+ f"Incentive Types: {self.incentives_pools_deployment_functions.keys()}"
106
+ )
107
+
108
+ parsed_ual = parse_ual(ual)
109
+ knowledge_asset_storage, knowledge_asset_token_id = (
110
+ parsed_ual["contract_address"],
111
+ parsed_ual["token_id"],
112
+ )
113
+
114
+ receipt: TxReceipt = deploy_incentives_pool_fn(
115
+ knowledge_asset_storage,
116
+ knowledge_asset_token_id,
117
+ **incentives_pool_parameters.to_contract_args(),
118
+ )
119
+
120
+ events = self.manager.blockchain_provider.decode_logs_event(
121
+ receipt,
122
+ "ParanetIncentivesPoolFactory",
123
+ "ParanetIncetivesPoolDeployed",
124
+ )
125
+
126
+ return {
127
+ "paranetUAL": ual,
128
+ "paranetId": Web3.to_hex(
129
+ Web3.solidity_keccak(
130
+ ["address", "uint256"],
131
+ [knowledge_asset_storage, knowledge_asset_token_id],
132
+ )
133
+ ),
134
+ "incentivesPoolAddress": events[0].args["incentivesPool"]["addr"],
135
+ "operation": json.loads(Web3.to_json(receipt)),
136
+ }
137
+
138
+ _get_incentives_pool_address = Method(BlockchainRequest.get_incentives_pool_address)
139
+
140
+ def get_incentives_pool_address(
141
+ self,
142
+ ual: UAL,
143
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
144
+ ) -> Address:
145
+ parsed_ual = parse_ual(ual)
146
+ knowledge_asset_storage, knowledge_asset_token_id = (
147
+ parsed_ual["contract_address"],
148
+ parsed_ual["token_id"],
149
+ )
150
+ paranet_id = Web3.solidity_keccak(
151
+ ["address", "uint256"], [knowledge_asset_storage, knowledge_asset_token_id]
152
+ )
153
+
154
+ return self._get_incentives_pool_address(paranet_id, incentives_type)
155
+
156
+ _register_paranet_service = Method(BlockchainRequest.register_paranet_service)
157
+
158
+ def create_service(
159
+ self, ual: UAL, name: str, description: str, addresses: list[Address]
160
+ ) -> dict[str, str | HexStr | TxReceipt]:
161
+ parsed_ual = parse_ual(ual)
162
+ knowledge_asset_storage, knowledge_asset_token_id = (
163
+ parsed_ual["contract_address"],
164
+ parsed_ual["token_id"],
165
+ )
166
+
167
+ receipt: TxReceipt = self._register_paranet_service(
168
+ knowledge_asset_storage,
169
+ knowledge_asset_token_id,
170
+ name,
171
+ description,
172
+ addresses,
173
+ )
174
+
175
+ return {
176
+ "paranetServiceUAL": ual,
177
+ "paranetServiceId": Web3.to_hex(
178
+ Web3.solidity_keccak(
179
+ ["address", "uint256"],
180
+ [knowledge_asset_storage, knowledge_asset_token_id],
181
+ )
182
+ ),
183
+ "operation": json.loads(Web3.to_json(receipt)),
184
+ }
185
+
186
+ _add_paranet_services = Method(BlockchainRequest.add_paranet_services)
187
+
188
+ def add_services(
189
+ self, ual: UAL, services_uals: list[UAL]
190
+ ) -> dict[str, str | HexStr | TxReceipt]:
191
+ parsed_paranet_ual = parse_ual(ual)
192
+ paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id = (
193
+ parsed_paranet_ual["contract_address"],
194
+ parsed_paranet_ual["token_id"],
195
+ )
196
+
197
+ parsed_service_uals = []
198
+ for service_ual in services_uals:
199
+ parsed_service_ual = parse_ual(service_ual)
200
+ (service_knowledge_asset_storage, service_knowledge_asset_token_id) = (
201
+ parsed_service_ual["contract_address"],
202
+ parsed_service_ual["token_id"],
203
+ )
204
+
205
+ parsed_service_uals.append(
206
+ {
207
+ "knowledgeAssetStorageContract": service_knowledge_asset_storage,
208
+ "tokenId": service_knowledge_asset_token_id,
209
+ }
210
+ )
211
+
212
+ receipt: TxReceipt = self._add_paranet_services(
213
+ paranet_knowledge_asset_storage,
214
+ paranet_knowledge_asset_token_id,
215
+ parsed_service_uals,
216
+ )
217
+
218
+ return {
219
+ "paranetUAL": ual,
220
+ "paranetId": Web3.to_hex(
221
+ Web3.solidity_keccak(
222
+ ["address", "uint256"],
223
+ [paranet_knowledge_asset_storage, paranet_knowledge_asset_token_id],
224
+ )
225
+ ),
226
+ "operation": json.loads(Web3.to_json(receipt)),
227
+ }
228
+
229
+ _is_knowledge_miner_registered = Method(
230
+ BlockchainRequest.is_knowledge_miner_registered
231
+ )
232
+
233
+ def is_knowledge_miner(self, ual: UAL, address: Address | None = None) -> bool:
234
+ parsed_ual = parse_ual(ual)
235
+ knowledge_asset_storage, knowledge_asset_token_id = (
236
+ parsed_ual["contract_address"],
237
+ parsed_ual["token_id"],
238
+ )
239
+
240
+ paranet_id = Web3.solidity_keccak(
241
+ ["address", "uint256"], [knowledge_asset_storage, knowledge_asset_token_id]
242
+ )
243
+
244
+ return self._is_knowledge_miner_registered(
245
+ paranet_id, address or self.manager.blockchain_provider.account.address
246
+ )
247
+
248
+ _owner_of = Method(BlockchainRequest.owner_of)
249
+
250
+ def is_operator(self, ual: UAL, address: Address | None = None) -> bool:
251
+ knowledge_asset_token_id = parse_ual(ual)["token_id"]
252
+
253
+ return self._owner_of(knowledge_asset_token_id) == (
254
+ address or self.manager.blockchain_provider.account.address
255
+ )
256
+
257
+ _is_proposal_voter = Method(BlockchainRequest.is_proposal_voter)
258
+
259
+ def is_voter(
260
+ self,
261
+ ual: UAL,
262
+ address: Address | None = None,
263
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
264
+ ) -> bool:
265
+ return self._is_proposal_voter(
266
+ contract=self._get_incentives_pool_contract(ual, incentives_type),
267
+ addr=address or self.manager.blockchain_provider.account.address,
268
+ )
269
+
270
+ _get_claimable_knowledge_miner_reward_amount = Method(
271
+ BlockchainRequest.get_claimable_knowledge_miner_reward_amount
272
+ )
273
+
274
+ def calculate_claimable_miner_reward_amount(
275
+ self,
276
+ ual: UAL,
277
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
278
+ ) -> int:
279
+ return self._get_claimable_knowledge_miner_reward_amount(
280
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
281
+ )
282
+
283
+ _get_claimable_all_knowledge_miners_reward_amount = Method(
284
+ BlockchainRequest.get_claimable_all_knowledge_miners_reward_amount
285
+ )
286
+
287
+ def calculate_all_claimable_miner_rewards_amount(
288
+ self,
289
+ ual: UAL,
290
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
291
+ ) -> int:
292
+ return self._get_claimable_all_knowledge_miners_reward_amount(
293
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
294
+ )
295
+
296
+ _claim_knowledge_miner_reward = Method(
297
+ BlockchainRequest.claim_knowledge_miner_reward
298
+ )
299
+
300
+ def claim_miner_reward(
301
+ self,
302
+ ual: UAL,
303
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
304
+ ) -> dict[str, str | HexStr | TxReceipt]:
305
+ receipt: TxReceipt = self._claim_knowledge_miner_reward(
306
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
307
+ )
308
+
309
+ parsed_ual = parse_ual(ual)
310
+ knowledge_asset_storage, knowledge_asset_token_id = (
311
+ parsed_ual["contract_address"],
312
+ parsed_ual["token_id"],
313
+ )
314
+
315
+ return {
316
+ "paranetUAL": ual,
317
+ "paranetId": Web3.to_hex(
318
+ Web3.solidity_keccak(
319
+ ["address", "uint256"],
320
+ [knowledge_asset_storage, knowledge_asset_token_id],
321
+ )
322
+ ),
323
+ "operation": json.loads(Web3.to_json(receipt)),
324
+ }
325
+
326
+ _get_claimable_paranet_operator_reward_amount = Method(
327
+ BlockchainRequest.get_claimable_paranet_operator_reward_amount
328
+ )
329
+
330
+ def calculate_claimable_operator_reward_amount(
331
+ self,
332
+ ual: UAL,
333
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
334
+ ) -> int:
335
+ return self._get_claimable_paranet_operator_reward_amount(
336
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
337
+ )
338
+
339
+ _claim_paranet_operator_reward = Method(
340
+ BlockchainRequest.claim_paranet_operator_reward
341
+ )
342
+
343
+ def claim_operator_reward(
344
+ self,
345
+ ual: UAL,
346
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
347
+ ) -> dict[str, str | HexStr | TxReceipt]:
348
+ receipt: TxReceipt = self._claim_paranet_operator_reward(
349
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
350
+ )
351
+
352
+ parsed_ual = parse_ual(ual)
353
+ knowledge_asset_storage, knowledge_asset_token_id = (
354
+ parsed_ual["contract_address"],
355
+ parsed_ual["token_id"],
356
+ )
357
+
358
+ return {
359
+ "paranetUAL": ual,
360
+ "paranetId": Web3.to_hex(
361
+ Web3.solidity_keccak(
362
+ ["address", "uint256"],
363
+ [knowledge_asset_storage, knowledge_asset_token_id],
364
+ )
365
+ ),
366
+ "operation": json.loads(Web3.to_json(receipt)),
367
+ }
368
+
369
+ _get_claimable_proposal_voter_reward_amount = Method(
370
+ BlockchainRequest.get_claimable_proposal_voter_reward_amount
371
+ )
372
+
373
+ def calculate_claimable_voter_reward_amount(
374
+ self,
375
+ ual: UAL,
376
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
377
+ ) -> int:
378
+ return self._get_claimable_proposal_voter_reward_amount(
379
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
380
+ )
381
+
382
+ _get_claimable_all_proposal_voters_reward_amount = Method(
383
+ BlockchainRequest.get_claimable_all_proposal_voters_reward_amount
384
+ )
385
+
386
+ def calculate_all_claimable_voters_reward_amount(
387
+ self,
388
+ ual: UAL,
389
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
390
+ ) -> int:
391
+ return self._get_claimable_all_proposal_voters_reward_amount(
392
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
393
+ )
394
+
395
+ _claim_incentivization_proposal_voter_reward = Method(
396
+ BlockchainRequest.claim_incentivization_proposal_voter_reward
397
+ )
398
+
399
+ def claim_voter_reward(
400
+ self,
401
+ ual: UAL,
402
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
403
+ ) -> dict[str, str | HexStr | TxReceipt]:
404
+ receipt: TxReceipt = self._claim_incentivization_proposal_voter_reward(
405
+ contract=self._get_incentives_pool_contract(ual, incentives_type)
406
+ )
407
+
408
+ parsed_ual = parse_ual(ual)
409
+ knowledge_asset_storage, knowledge_asset_token_id = (
410
+ parsed_ual["contract_address"],
411
+ parsed_ual["token_id"],
412
+ )
413
+
414
+ return {
415
+ "paranetUAL": ual,
416
+ "paranetId": Web3.to_hex(
417
+ Web3.solidity_keccak(
418
+ ["address", "uint256"],
419
+ [knowledge_asset_storage, knowledge_asset_token_id],
420
+ )
421
+ ),
422
+ "operation": json.loads(Web3.to_json(receipt)),
423
+ }
424
+
425
+ _get_updating_knowledge_asset_states = Method(
426
+ BlockchainRequest.get_updating_knowledge_asset_states
427
+ )
428
+ _process_updated_knowledge_asset_states_metadata = Method(
429
+ BlockchainRequest.process_updated_knowledge_asset_states_metadata
430
+ )
431
+
432
+ def update_claimable_rewards(self, ual: UAL) -> dict[str, str | HexStr | TxReceipt]:
433
+ parsed_ual = parse_ual(ual)
434
+ knowledge_asset_storage, knowledge_asset_token_id = (
435
+ parsed_ual["contract_address"],
436
+ parsed_ual["token_id"],
437
+ )
438
+
439
+ paranet_id = Web3.solidity_keccak(
440
+ ["address", "uint256"], [knowledge_asset_storage, knowledge_asset_token_id]
441
+ )
442
+
443
+ updating_states = self._get_updating_knowledge_asset_states(
444
+ self.manager.blockchain_provider.account.address,
445
+ paranet_id,
446
+ )
447
+ receipt: TxReceipt = self._process_updated_knowledge_asset_states_metadata(
448
+ knowledge_asset_storage,
449
+ knowledge_asset_token_id,
450
+ 0,
451
+ len(updating_states),
452
+ )
453
+
454
+ return {
455
+ "paranetUAL": ual,
456
+ "paranetId": paranet_id,
457
+ "operation": json.loads(Web3.to_json(receipt)),
458
+ }
459
+
460
+ def _get_incentives_pool_contract(
461
+ self,
462
+ ual: UAL,
463
+ incentives_type: ParanetIncentivizationType = ParanetIncentivizationType.NEUROWEB,
464
+ ) -> str | dict[str, str]:
465
+ incentives_pool_name = f"Paranet{str(incentives_type)}IncentivesPool"
466
+ is_incentives_pool_cached = (
467
+ incentives_pool_name in self.manager.blockchain_provider.contracts.keys()
468
+ )
469
+
470
+ return (
471
+ incentives_pool_name
472
+ if is_incentives_pool_cached
473
+ else {
474
+ "name": incentives_pool_name,
475
+ "address": self.get_incentives_pool_address(ual, incentives_type),
476
+ }
477
+ )
@@ -24,11 +24,14 @@ from typing import Any, Type
24
24
 
25
25
  import requests
26
26
  from dkg.constants import BLOCKCHAINS, DEFAULT_GAS_PRICE_GWEI
27
- from dkg.exceptions import (AccountMissing, EnvironmentNotSupported,
28
- NetworkNotSupported, RPCURINotDefined)
27
+ from dkg.exceptions import (
28
+ AccountMissing,
29
+ EnvironmentNotSupported,
30
+ NetworkNotSupported,
31
+ RPCURINotDefined,
32
+ )
29
33
  from dkg.types import URI, Address, DataHexStr, Environment, Wei
30
34
  from eth_account.signers.local import LocalAccount
31
- from requests.exceptions import ConnectionError, HTTPError, RequestException, Timeout
32
35
  from web3 import Web3
33
36
  from web3.contract import Contract
34
37
  from web3.contract.contract import ContractFunction
@@ -97,13 +100,14 @@ class BlockchainProvider:
97
100
  "Hub": self.w3.eth.contract(
98
101
  address=hub_address,
99
102
  abi=self.abi["Hub"],
103
+ decode_tuples=True,
100
104
  )
101
105
  }
102
106
  self._init_contracts()
103
107
 
104
108
  if (
105
- private_key is not None or
106
- (private_key_env := os.environ.get("PRIVATE_KEY", None)) is not None
109
+ private_key is not None
110
+ or (private_key_env := os.environ.get("PRIVATE_KEY", None)) is not None
107
111
  ):
108
112
  self.set_account(private_key or private_key_env)
109
113
 
@@ -126,33 +130,48 @@ class BlockchainProvider:
126
130
  except Exception as err:
127
131
  if (
128
132
  contract_name
133
+ and isinstance(contract_name, str)
129
134
  and any(msg in str(err) for msg in ["revert", "VM Exception"])
130
135
  and not self._check_contract_status(contract_name)
131
136
  ):
132
- self._update_contract_instance(contract_name)
133
- return func(self, *args, **kwargs)
134
- raise
137
+ is_updated = self._update_contract_instance(contract_name)
138
+ if is_updated:
139
+ return func(self, *args, **kwargs)
140
+ raise err
135
141
 
136
142
  return wrapper
137
143
 
138
144
  @handle_updated_contract
139
145
  def call_function(
140
146
  self,
141
- contract: str,
147
+ contract: str | dict[str, str],
142
148
  function: str,
143
149
  args: dict[str, Any] = {},
144
150
  state_changing: bool = False,
145
151
  gas_price: Wei | None = None,
146
152
  gas_limit: Wei | None = None,
147
153
  ) -> TxReceipt | Any:
148
- contract_instance = self.contracts[contract]
154
+ if isinstance(contract, str):
155
+ contract_name = contract
156
+ contract_instance = self.contracts[contract_name]
157
+ else:
158
+ contract_name = contract["name"]
159
+ contract_instance = self.w3.eth.contract(
160
+ address=contract["address"],
161
+ abi=self.abi[contract_name],
162
+ decode_tuples=True,
163
+ )
164
+ self.contracts[contract_name] = contract_instance
165
+
149
166
  contract_function: ContractFunction = getattr(
150
167
  contract_instance.functions, function
151
168
  )
152
169
 
153
170
  if not state_changing:
154
171
  result = contract_function(**args).call()
155
- if function in (output_named_tuples := self.output_named_tuples[contract]):
172
+ if function in (
173
+ output_named_tuples := self.output_named_tuples[contract_name]
174
+ ):
156
175
  result = output_named_tuples[function](*result)
157
176
  return result
158
177
  else:
@@ -162,11 +181,9 @@ class BlockchainProvider:
162
181
  "account."
163
182
  )
164
183
 
165
- nonce = self.w3.eth.get_transaction_count(self.w3.eth.default_account)
166
184
  gas_price = self.gas_price or gas_price or self._get_network_gas_price()
167
185
 
168
186
  options = {
169
- "nonce": nonce,
170
187
  "gasPrice": gas_price,
171
188
  "gas": gas_limit or contract_function(**args).estimate_gas(),
172
189
  }
@@ -192,45 +209,42 @@ class BlockchainProvider:
192
209
  )
193
210
  self.w3.eth.default_account = self.account.address
194
211
 
195
- def _get_network_gas_price(self) -> int | None:
196
- blockchain_name, chain_id = self.blockchain_id.split(":")
197
-
198
- default_gas_price = self.w3.to_wei(DEFAULT_GAS_PRICE_GWEI, "gwei")
199
-
200
- match blockchain_name:
201
- case "otp":
202
- return self.w3.eth.gas_price
203
- case "gnosis":
204
- if self.gas_price_oracle is None:
205
- return default_gas_price
206
-
207
- try:
208
- response = requests.get(self.gas_price_oracle)
209
-
210
- response.raise_for_status()
211
-
212
- try:
213
- response_json: dict = response.json()
214
- except ValueError:
215
- return default_gas_price
212
+ def _get_network_gas_price(self) -> Wei | None:
213
+ if self.environment == "development":
214
+ return None
216
215
 
217
- except (HTTPError, ConnectionError, Timeout, RequestException):
218
- return default_gas_price
216
+ blockchain_name, _ = self.blockchain_id.split(":")
219
217
 
220
- gas_price = None
221
- match chain_id:
222
- case "100":
223
- gas_price_hex = response_json.get("result")
224
- if gas_price_hex:
225
- gas_price = int(gas_price_hex, 16)
226
- case "10200":
227
- gas_price_avg = response_json.get("average")
228
- if gas_price_avg:
229
- gas_price = self.w3.to_wei(gas_price_avg, "gwei")
218
+ default_gas_price = self.w3.to_wei(
219
+ DEFAULT_GAS_PRICE_GWEI[blockchain_name], "gwei"
220
+ )
230
221
 
231
- return gas_price if gas_price is not None else default_gas_price
232
- case _:
233
- return default_gas_price
222
+ def fetch_gas_price(oracle_url: str) -> Wei | None:
223
+ try:
224
+ response = requests.get(oracle_url)
225
+ response.raise_for_status()
226
+ data: dict = response.json()
227
+
228
+ if "result" in data:
229
+ return int(data["result"], 16)
230
+ elif "average" in data:
231
+ return self.w3.to_wei(data["average"], "gwei")
232
+ else:
233
+ return None
234
+ except Exception:
235
+ return None
236
+
237
+ oracles = self.gas_price_oracle
238
+ if oracles is not None:
239
+ if isinstance(oracles, str):
240
+ oracles = [oracles]
241
+
242
+ for oracle_url in oracles:
243
+ gas_price = fetch_gas_price(oracle_url)
244
+ if gas_price is not None:
245
+ return gas_price
246
+
247
+ return default_gas_price
234
248
 
235
249
  def _init_contracts(self):
236
250
  for contract in self.abi.keys():
@@ -239,17 +253,26 @@ class BlockchainProvider:
239
253
 
240
254
  self._update_contract_instance(contract)
241
255
 
242
- def _update_contract_instance(self, contract: str):
243
- self.contracts[contract] = self.w3.eth.contract(
244
- address=(
245
- self.contracts["Hub"]
246
- .functions.getContractAddress(contract).call()
247
- if not contract.endswith("AssetStorage")
248
- else self.contracts["Hub"]
249
- .functions.getAssetStorageAddress(contract).call()
250
- ),
251
- abi=self.abi[contract],
252
- )
256
+ def _update_contract_instance(self, contract: str) -> bool:
257
+ if (
258
+ self.contracts["Hub"].functions.isContract(contractName=contract).call()
259
+ or self.contracts["Hub"]
260
+ .functions.isAssetStorage(assetStorageName=contract)
261
+ .call()
262
+ ):
263
+ self.contracts[contract] = self.w3.eth.contract(
264
+ address=(
265
+ self.contracts["Hub"].functions.getContractAddress(contract).call()
266
+ if not contract.endswith("AssetStorage")
267
+ else self.contracts["Hub"]
268
+ .functions.getAssetStorageAddress(contract)
269
+ .call()
270
+ ),
271
+ abi=self.abi[contract],
272
+ decode_tuples=True,
273
+ )
274
+ return True
275
+ return False
253
276
 
254
277
  def _check_contract_status(self, contract: str) -> bool:
255
278
  try:
dkg/types/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ from .general import AutoStrEnum, AutoStrEnumCapitalize, AutoStrEnumUpperCase # NOQA: F401
1
2
  from .blockchain import (ABI, ABIElement, ABIError, ABIEvent, # NOQA: F401
2
3
  ABIFunction, ABIParameter, AgreementData, Environment)
3
4
  from .dkg_node import UAL # NOQA: F401