mech-client 0.8.1__py3-none-any.whl → 0.9.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 (30) hide show
  1. mech_client/__init__.py +1 -1
  2. mech_client/cli.py +8 -0
  3. mech_client/marketplace_interact.py +18 -6
  4. {mech_client-0.8.1.dist-info → mech_client-0.9.0.dist-info}/METADATA +11 -1
  5. {mech_client-0.8.1.dist-info → mech_client-0.9.0.dist-info}/RECORD +30 -8
  6. scripts/__init__.py +1 -0
  7. scripts/benchmark.sh +32 -0
  8. scripts/bump.py +316 -0
  9. scripts/deposit_native.py +118 -0
  10. scripts/deposit_token.py +187 -0
  11. scripts/nvm_subscribe.py +51 -0
  12. scripts/nvm_subscription/contracts/agreement_manager.py +44 -0
  13. scripts/nvm_subscription/contracts/base_contract.py +75 -0
  14. scripts/nvm_subscription/contracts/did_registry.py +91 -0
  15. scripts/nvm_subscription/contracts/escrow_payment.py +85 -0
  16. scripts/nvm_subscription/contracts/lock_payment.py +76 -0
  17. scripts/nvm_subscription/contracts/nft.py +47 -0
  18. scripts/nvm_subscription/contracts/nft_sales.py +119 -0
  19. scripts/nvm_subscription/contracts/subscription_provider.py +107 -0
  20. scripts/nvm_subscription/contracts/token.py +87 -0
  21. scripts/nvm_subscription/contracts/transfer_nft.py +81 -0
  22. scripts/nvm_subscription/envs/base.env +10 -0
  23. scripts/nvm_subscription/envs/gnosis.env +10 -0
  24. scripts/nvm_subscription/manager.py +265 -0
  25. scripts/nvm_subscription/resources/networks.json +56 -0
  26. scripts/utils.py +127 -0
  27. scripts/whitelist.py +5 -0
  28. {mech_client-0.8.1.dist-info → mech_client-0.9.0.dist-info}/LICENSE +0 -0
  29. {mech_client-0.8.1.dist-info → mech_client-0.9.0.dist-info}/WHEEL +0 -0
  30. {mech_client-0.8.1.dist-info → mech_client-0.9.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,118 @@
1
+ # -*- coding: utf-8 -*-
2
+ # ------------------------------------------------------------------------------
3
+ #
4
+ # Copyright 2025 Valory AG
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # ------------------------------------------------------------------------------
19
+
20
+
21
+ import sys
22
+ from pathlib import Path
23
+ from typing import Optional
24
+ from aea_ledger_ethereum import EthereumApi, EthereumCrypto
25
+ from dataclasses import asdict
26
+
27
+ from mech_client.interact import get_mech_config, PRIVATE_KEY_FILE_PATH
28
+ from .utils import (
29
+ print_title,
30
+ CHAIN_TO_NATIVE_BALANCE_TRACKER,
31
+ )
32
+ from mech_client.wss import wait_for_receipt
33
+
34
+
35
+ def deposit(
36
+ ledger_api: EthereumApi,
37
+ crypto: EthereumCrypto,
38
+ to: str,
39
+ amount: int,
40
+ ) -> str:
41
+ sender = crypto.address
42
+
43
+ try:
44
+ print("Fetching user balance")
45
+ user_balance = ledger_api.get_balance(address=sender)
46
+ if user_balance < amount:
47
+ formatted_user_balance = user_balance / 1e18
48
+ formatted_amount = amount / 1e18
49
+ print("User balance low!!")
50
+ print(f"Balance: {formatted_user_balance}")
51
+ print(f"Want to Deposit: {formatted_amount}")
52
+ sys.exit(1)
53
+ except Exception as e:
54
+ print(f"Error occured while fetching user balance: {e}")
55
+ return str(e)
56
+
57
+ try:
58
+ print("Sending deposit tx")
59
+ raw_transaction = ledger_api.get_transfer_transaction(
60
+ sender_address=sender,
61
+ destination_address=to,
62
+ amount=amount,
63
+ tx_fee=50000,
64
+ tx_nonce="0x",
65
+ )
66
+ signed_transaction = crypto.sign_transaction(raw_transaction)
67
+ transaction_digest = ledger_api.send_signed_transaction(
68
+ signed_transaction,
69
+ raise_on_try=True,
70
+ )
71
+ return transaction_digest
72
+ except Exception as e: # pylint: disable=broad-except
73
+ print(f"Error occured while sending the transaction: {e}")
74
+ return str(e)
75
+
76
+
77
+ def main(
78
+ amount: str,
79
+ private_key_path: Optional[str] = None,
80
+ chain_config: Optional[str] = None,
81
+ ) -> None:
82
+ """Runs the deposit functionality for the native mech type"""
83
+ print_title("Native Deposit")
84
+ print("This script will assist you in depositing native balance for mech requests.")
85
+ print()
86
+
87
+ amount_to_deposit = int(float(amount) * 10**18)
88
+ private_key_path = private_key_path or PRIVATE_KEY_FILE_PATH
89
+
90
+ mech_config = get_mech_config(chain_config)
91
+ ledger_config = mech_config.ledger_config
92
+ ledger_api = EthereumApi(**asdict(ledger_config))
93
+
94
+ if not Path(private_key_path).exists():
95
+ raise FileNotFoundError(
96
+ f"Private key file `{private_key_path}` does not exist!"
97
+ )
98
+ crypto = EthereumCrypto(private_key_path=private_key_path)
99
+
100
+ print(f"Sender address: {crypto.address}")
101
+
102
+ chain_id = mech_config.ledger_config.chain_id
103
+ to = CHAIN_TO_NATIVE_BALANCE_TRACKER[chain_id]
104
+
105
+ deposit_tx = deposit(ledger_api, crypto, to, amount_to_deposit)
106
+ if not deposit_tx:
107
+ print("Unable to deposit")
108
+ sys.exit(1)
109
+
110
+ transaction_url_formatted = mech_config.transaction_url.format(
111
+ transaction_digest=deposit_tx
112
+ )
113
+ print(f" - Transaction sent: {transaction_url_formatted}")
114
+ print(" - Waiting for transaction receipt...")
115
+ wait_for_receipt(deposit_tx, ledger_api)
116
+
117
+ print("")
118
+ print("Deposit Successful")
@@ -0,0 +1,187 @@
1
+ # -*- coding: utf-8 -*-
2
+ # ------------------------------------------------------------------------------
3
+ #
4
+ # Copyright 2025 Valory AG
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # ------------------------------------------------------------------------------
19
+
20
+
21
+ import sys
22
+ from pathlib import Path
23
+ from typing import Optional
24
+ from aea_ledger_ethereum import EthereumApi, EthereumCrypto
25
+ from dataclasses import asdict
26
+ from web3.contract import Contract as Web3Contract
27
+
28
+
29
+ from mech_client.interact import (
30
+ get_mech_config,
31
+ PRIVATE_KEY_FILE_PATH,
32
+ )
33
+ from .utils import (
34
+ print_title,
35
+ get_token_contract,
36
+ get_token_balance_tracker_contract,
37
+ )
38
+ from mech_client.wss import wait_for_receipt
39
+
40
+
41
+ def check_token_balance(token_contract: Web3Contract, sender: str, amount: int) -> None:
42
+ try:
43
+ print("Fetching user balance")
44
+ user_token_balance = token_contract.functions.balanceOf(sender).call()
45
+ if user_token_balance < amount:
46
+ formatted_user_balance = user_token_balance / 1e18
47
+ formatted_amount = amount / 1e18
48
+ print("User balance low!!")
49
+ print(f"Balance: {formatted_user_balance}")
50
+ print(f"Want to Deposit: {formatted_amount}")
51
+ sys.exit(1)
52
+ except Exception as e:
53
+ print(f"Error occured while fetching user balance: {e}")
54
+
55
+
56
+ def approve(
57
+ crypto: EthereumCrypto,
58
+ ledger_api: EthereumApi,
59
+ token_contract: Web3Contract,
60
+ token_balance_tracker_contract: Web3Contract,
61
+ amount: int,
62
+ ) -> str:
63
+ sender = crypto.address
64
+
65
+ print("Sending approve tx")
66
+ try:
67
+ tx_args = {"sender_address": sender, "value": 0, "gas": 60000}
68
+ raw_transaction = ledger_api.build_transaction(
69
+ contract_instance=token_contract,
70
+ method_name="approve",
71
+ method_args={
72
+ "_to": token_balance_tracker_contract.address,
73
+ "_value": amount,
74
+ },
75
+ tx_args=tx_args,
76
+ raise_on_try=True,
77
+ )
78
+ signed_transaction = crypto.sign_transaction(raw_transaction)
79
+ transaction_digest = ledger_api.send_signed_transaction(
80
+ signed_transaction,
81
+ raise_on_try=True,
82
+ )
83
+ return transaction_digest
84
+ except Exception as e: # pylint: disable=broad-except
85
+ print(f"Error occured while sending the transaction: {e}")
86
+ return str(e)
87
+
88
+
89
+ def deposit(
90
+ ledger_api: EthereumApi,
91
+ crypto: EthereumCrypto,
92
+ token_balance_tracker_contract: Web3Contract,
93
+ amount: int,
94
+ ) -> str:
95
+ sender = crypto.address
96
+
97
+ print("Sending deposit tx")
98
+ try:
99
+ tx_args = {"sender_address": sender, "value": 0, "gas": 100000}
100
+ raw_transaction = ledger_api.build_transaction(
101
+ contract_instance=token_balance_tracker_contract,
102
+ method_name="deposit",
103
+ method_args={"amount": amount},
104
+ tx_args=tx_args,
105
+ raise_on_try=True,
106
+ )
107
+ signed_transaction = crypto.sign_transaction(raw_transaction)
108
+ transaction_digest = ledger_api.send_signed_transaction(
109
+ signed_transaction,
110
+ raise_on_try=True,
111
+ )
112
+ return transaction_digest
113
+ except Exception as e: # pylint: disable=broad-except
114
+ print(f"Error occured while sending the transaction: {e}")
115
+ return str(e)
116
+
117
+
118
+ def main(
119
+ amount: str,
120
+ private_key_path: Optional[str] = None,
121
+ chain_config: Optional[str] = None,
122
+ ) -> None:
123
+ """Runs the deposit functionality for the token mech type"""
124
+ print_title("Token Deposit")
125
+ print("This script will assist you in depositing token balance for mech requests.")
126
+ print()
127
+
128
+ amount_to_deposit = int(float(amount) * 10**18)
129
+ private_key_path = private_key_path or PRIVATE_KEY_FILE_PATH
130
+
131
+ mech_config = get_mech_config(chain_config)
132
+ ledger_config = mech_config.ledger_config
133
+ ledger_api = EthereumApi(**asdict(ledger_config))
134
+
135
+ if not Path(private_key_path).exists():
136
+ raise FileNotFoundError(
137
+ f"Private key file `{private_key_path}` does not exist!"
138
+ )
139
+ crypto = EthereumCrypto(private_key_path=private_key_path)
140
+
141
+ print(f"Sender address: {crypto.address}")
142
+
143
+ chain_id = mech_config.ledger_config.chain_id
144
+ token_balance_tracker_contract = get_token_balance_tracker_contract(
145
+ ledger_api, chain_id
146
+ )
147
+ token_contract = get_token_contract(ledger_api, chain_id)
148
+
149
+ check_token_balance(token_contract, crypto.address, amount_to_deposit)
150
+
151
+ approve_tx = approve(
152
+ crypto,
153
+ ledger_api,
154
+ token_contract,
155
+ token_balance_tracker_contract,
156
+ amount_to_deposit,
157
+ )
158
+ if not approve_tx:
159
+ print("Unable to approve")
160
+ sys.exit(1)
161
+
162
+ transaction_url_formatted = mech_config.transaction_url.format(
163
+ transaction_digest=approve_tx
164
+ )
165
+ print(f" - Transaction sent: {transaction_url_formatted}")
166
+ print(" - Waiting for transaction receipt...")
167
+ wait_for_receipt(approve_tx, ledger_api)
168
+
169
+ deposit_tx = deposit(
170
+ ledger_api,
171
+ crypto,
172
+ token_balance_tracker_contract,
173
+ amount_to_deposit,
174
+ )
175
+ if not deposit_tx:
176
+ print("Unable to deposit")
177
+ sys.exit(1)
178
+
179
+ transaction_url_formatted = mech_config.transaction_url.format(
180
+ transaction_digest=deposit_tx
181
+ )
182
+ print(f" - Transaction sent: {transaction_url_formatted}")
183
+ print(" - Waiting for transaction receipt...")
184
+ wait_for_receipt(deposit_tx, ledger_api)
185
+
186
+ print("")
187
+ print("Deposit Successful")
@@ -0,0 +1,51 @@
1
+ import os
2
+ import sys
3
+ from dotenv import load_dotenv
4
+ from typing import Optional, Dict
5
+ from pathlib import Path
6
+ from mech_client.interact import PRIVATE_KEY_FILE_PATH
7
+ from web3 import Web3
8
+ from scripts.nvm_subscription.manager import NVMSubscriptionManager
9
+
10
+ BASE_ENV_PATH = Path(__file__).parent / "nvm_subscription" / "envs"
11
+
12
+ CHAIN_TO_ENVS: Dict[str, Path] = {
13
+ "gnosis": BASE_ENV_PATH / "gnosis.env",
14
+ "base": BASE_ENV_PATH / "base.env",
15
+ }
16
+
17
+
18
+ def main(
19
+ private_key_path: str,
20
+ chain_config: str,
21
+ ) -> None:
22
+
23
+ chain_env = CHAIN_TO_ENVS.get(chain_config)
24
+ if chain_env:
25
+ load_dotenv(chain_env)
26
+ else:
27
+ print(f"{chain_config} network not supported")
28
+ sys.exit(1)
29
+
30
+ private_key_path = private_key_path or PRIVATE_KEY_FILE_PATH
31
+ if not Path(private_key_path).exists():
32
+ raise FileNotFoundError(
33
+ f"Private key file `{private_key_path}` does not exist!"
34
+ )
35
+
36
+ with open(private_key_path, "r") as file:
37
+ content = file.read()
38
+
39
+ WALLET_PVT_KEY = content
40
+ PLAN_DID = os.environ["PLAN_DID"]
41
+ NETWORK = os.environ["NETWORK_NAME"]
42
+ CHAIN_ID = int(os.environ["CHAIN_ID"])
43
+ SENDER = Web3().eth.account.from_key(WALLET_PVT_KEY).address
44
+
45
+ print(f"Sender address: {SENDER}")
46
+
47
+ manager = NVMSubscriptionManager(NETWORK, WALLET_PVT_KEY)
48
+ tx_receipt = manager.create_subscription(PLAN_DID, WALLET_PVT_KEY, CHAIN_ID)
49
+
50
+ print("Subscription created successfully")
51
+ print(tx_receipt)
@@ -0,0 +1,44 @@
1
+ # subscription/contracts/lock_payment.py
2
+ import logging
3
+ from typing import List, Union
4
+ from web3 import Web3
5
+ from eth_typing import ChecksumAddress
6
+ from web3.types import ENS
7
+
8
+ from .base_contract import BaseContract
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class AgreementStorageManagerContract(BaseContract):
14
+ """
15
+ Wrapper class for the LockPaymentCondition smart contract.
16
+ Provides methods for computing hash and generating IDs.
17
+ """
18
+
19
+ def __init__(self, w3: Web3):
20
+ """
21
+ Initialize the LockPaymentConditionContract.
22
+
23
+ Args:
24
+ w3 (Web3): An instance of Web3 connected to the target Ethereum network.
25
+ """
26
+ logger.debug("Initializing AgreementStoreManager")
27
+ super().__init__(w3, name="AgreementStoreManager")
28
+ logger.info("AgreementStoreManagerContract initialized")
29
+
30
+ def agreement_id(self, agreement_id_seed: str, subscriber: str) -> bytes:
31
+ """
32
+ Generate a condition ID for a given agreement ID and hash.
33
+
34
+ Args:
35
+ agreement_id_seed (str): Seed for the agreement ID.
36
+ subscriber (str): Address of the subscriber.
37
+
38
+ Returns:
39
+ bytes: The condition ID.
40
+ """
41
+ logger.debug("Generating condition ID from agreement ID and hash")
42
+ agreement_id = self.functions().agreementId(agreement_id_seed, subscriber).call()
43
+ logger.info(f"Generated condition ID: {agreement_id.hex()}")
44
+ return agreement_id
@@ -0,0 +1,75 @@
1
+ # subscription/contracts/base_contract.py
2
+ import os
3
+ import json
4
+ import logging
5
+ from typing import Any, Dict
6
+ from web3 import Web3
7
+ from web3.contract import Contract
8
+
9
+ # Configure module-level logger
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class BaseContract:
14
+ """
15
+ Base class to interact with Ethereum smart contracts. Handles loading of contract
16
+ ABI and instantiating a Web3 contract instance.
17
+ """
18
+
19
+ def __init__(self, w3: Web3, name: str):
20
+ """
21
+ Initialize the base contract wrapper.
22
+
23
+ Args:
24
+ w3 (Web3): An instance of Web3 connected to the desired network.
25
+ name (str): The name of the contract artifact file (without extension).
26
+ """
27
+ self.w3 = w3
28
+ self.name = name
29
+ self.chain_id = self.w3.eth.chain_id
30
+ self.chain_name = "gnosis" if self.chain_id == 100 else "base"
31
+ logger.debug(f"Initializing contract wrapper for '{self.name}'")
32
+ self.contract = self._load_contract() # Load contract from artifact
33
+
34
+ self.address = self.contract.address
35
+ logger.info(f"Contract '{self.name}' loaded successfully")
36
+
37
+ def _load_contract_info(self) -> Dict[str, Any]:
38
+ """
39
+ Load contract metadata (ABI and address) from the artifacts directory.
40
+
41
+ Returns:
42
+ dict: A dictionary containing the contract address and ABI.
43
+ """
44
+ current_dir = os.path.dirname(os.path.abspath(__file__))
45
+ root_dir = os.path.abspath(os.path.join(current_dir, '..', '..', '..'))
46
+ path = os.path.join(root_dir, 'mech_client', 'abis', f'{self.name}.{self.chain_name}.json')
47
+ logger.debug(f"Loading contract info from: {path}")
48
+ with open(path, 'r', encoding='utf-8') as f:
49
+ info = json.load(f) # Parse JSON containing ABI and address
50
+ logger.debug(f"Loaded contract address: {info.get('address')}")
51
+ return info
52
+
53
+ def _load_contract(self) -> Contract:
54
+ """
55
+ Instantiate a Web3 contract object using the loaded contract info.
56
+
57
+ Returns:
58
+ Contract: A Web3 contract instance bound to the deployed address.
59
+ """
60
+ info = self._load_contract_info()
61
+ address = self.w3.to_checksum_address(info['address']) # Ensure correct checksum
62
+ logger.debug(f"Creating contract instance at address: {address}")
63
+ contract = self.w3.eth.contract(address=address, abi=info['abi'])
64
+ logger.info("Contract instance created successfully")
65
+ return contract
66
+
67
+ def functions(self) -> Any:
68
+ """
69
+ Access the functions of the loaded contract. Acts as a proxy to contract.functions.
70
+
71
+ Returns:
72
+ Any: The contract.functions interface for calling or building transactions.
73
+ """
74
+ logger.debug(f"Accessing contract functions for '{self.name}'")
75
+ return self.contract.functions
@@ -0,0 +1,91 @@
1
+ # subscription/contracts/did_registry.py
2
+ import logging
3
+ from typing import Dict, Any
4
+ from web3 import Web3
5
+ from web3.constants import ADDRESS_ZERO
6
+
7
+ from .base_contract import BaseContract
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class DIDRegistryContract(BaseContract):
13
+ """
14
+ Wrapper class for the DIDRegistry smart contract.
15
+ Provides methods to interact with DID-related functionality.
16
+ """
17
+
18
+ def __init__(self, w3: Web3):
19
+ """
20
+ Initialize the DIDRegistryContract with a Web3 instance.
21
+
22
+ Args:
23
+ w3 (Web3): Web3 instance connected to the desired Ethereum network.
24
+ """
25
+ super().__init__(w3, name="DIDRegistry")
26
+
27
+ def get_ddo(self, did: str) -> Dict[str, Any]:
28
+ """
29
+ Retrieve the DDO (Decentralized Document Object) for a given DID.
30
+
31
+ Args:
32
+ did (str): Decentralized identifier (DID) to look up.
33
+
34
+ Returns:
35
+ Dict[str, Any]: Parsed DDO object.
36
+ """
37
+ logger.debug(f"Fetching DDO for DID: {did}")
38
+ registered_values = self.functions().getDIDRegister(did).call()
39
+ service_endpoint = registered_values[2]
40
+
41
+ logger.debug(f"Resolved service endpoint: {service_endpoint}")
42
+ response = self._fetch_ddo_from_endpoint(service_endpoint)
43
+
44
+ ddo = {
45
+ "did": f"did:nv:{did}",
46
+ "serviceEndpoint": registered_values[2],
47
+ "checksum": registered_values[1],
48
+ "owner": registered_values[0],
49
+ "providers": registered_values[5],
50
+ "royalties": registered_values[6],
51
+ "immutableUrl": registered_values[7],
52
+ "nftInitialized": registered_values[8],
53
+ "service": response.get("service", []),
54
+ "proof": response.get("proof", [])
55
+ }
56
+
57
+ non_zero_providers = [
58
+ addr
59
+ for addr in ddo["providers"]
60
+ if addr.lower() != ADDRESS_ZERO
61
+ ]
62
+ print("================ SUBSCRIBGING TO NVM OLAS PLAN =======================")
63
+ print(f"PLAN : {ddo['did']}")
64
+ print(f"OWNER: {ddo['owner']}")
65
+ print(f"PROVIDERS: {non_zero_providers}")
66
+ logger.info(f"ROYALTIES: {ddo['royalties']}")
67
+ logger.info(f"IMMUTABLE URL: {ddo['immutableUrl']}")
68
+ logger.info(f"NFT INITIALIZED: {ddo['nftInitialized']}")
69
+ logger.info(f"DDO fetched successfully for DID: {did}")
70
+ return ddo
71
+
72
+ def _fetch_ddo_from_endpoint(self, endpoint: str) -> Dict[str, Any]:
73
+ """
74
+ Helper method to fetch the DDO JSON from a service endpoint.
75
+
76
+ Args:
77
+ endpoint (str): URL to fetch DDO data from.
78
+
79
+ Returns:
80
+ Dict[str, Any]: Parsed JSON response.
81
+ """
82
+ import requests
83
+
84
+ try:
85
+ response = requests.get(endpoint, timeout=10)
86
+ response.raise_for_status()
87
+ logger.debug(f"Received response from DDO endpoint: {response.status_code}")
88
+ return response.json()
89
+ except requests.RequestException as e:
90
+ logger.error(f"Failed to fetch DDO from {endpoint}: {e}")
91
+ return {}
@@ -0,0 +1,85 @@
1
+ # subscription/contracts/escrow_payment.py
2
+ import logging
3
+ from typing import List, Union
4
+ from web3 import Web3
5
+ from eth_typing import ChecksumAddress
6
+ from web3.types import ENS
7
+
8
+ from .base_contract import BaseContract
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class EscrowPaymentConditionContract(BaseContract):
14
+ """
15
+ Wrapper for the EscrowPaymentCondition smart contract.
16
+ Provides methods to hash values and generate condition IDs.
17
+ """
18
+
19
+ def __init__(self, w3: Web3):
20
+ """
21
+ Initialize the EscrowPaymentConditionContract.
22
+
23
+ Args:
24
+ w3 (Web3): A connected Web3 instance.
25
+ """
26
+ logger.debug("Initializing EscrowPaymentConditionContract")
27
+ super().__init__(w3, name="EscrowPaymentCondition")
28
+ logger.info("EscrowPaymentConditionContract initialized")
29
+
30
+ def hash_values(
31
+ self,
32
+ did: str,
33
+ amounts: List[int],
34
+ receivers: List[Union[ChecksumAddress, ENS]],
35
+ sender: Union[ChecksumAddress, ENS],
36
+ receiver: Union[ChecksumAddress, ENS],
37
+ token_address: Union[ChecksumAddress, ENS],
38
+ lock_condition_id: bytes,
39
+ release_condition_id: bytes
40
+ ) -> bytes:
41
+ """
42
+ Compute the hash of parameters for escrow condition.
43
+
44
+ Args:
45
+ did (str): Decentralized identifier.
46
+ amounts (List[int]): Payment amounts.
47
+ receivers (List[ChecksumAddress | ENS]): Receiver addresses.
48
+ sender (ChecksumAddress | ENS): Sender address.
49
+ receiver (ChecksumAddress | ENS): Receiver address.
50
+ token_address (ChecksumAddress | ENS): Token contract address.
51
+ lock_condition_id (bytes): ID of the lock payment condition.
52
+ release_condition_id (bytes): ID of the release condition.
53
+
54
+ Returns:
55
+ bytes: Hashed values.
56
+ """
57
+ logger.debug("Computing hash for escrow payment condition")
58
+ hash_ = self.functions().hashValues(
59
+ did,
60
+ amounts,
61
+ receivers,
62
+ sender,
63
+ receiver,
64
+ token_address,
65
+ lock_condition_id,
66
+ release_condition_id
67
+ ).call()
68
+ logger.debug(f"Escrow payment hash: {hash_.hex()}")
69
+ return hash_
70
+
71
+ def generate_id(self, agreement_id: bytes, hash_value: bytes) -> bytes:
72
+ """
73
+ Generate the condition ID for the escrow payment.
74
+
75
+ Args:
76
+ agreement_id (str): Agreement identifier.
77
+ hash_value (bytes): Hash of condition inputs.
78
+
79
+ Returns:
80
+ bytes: Generated condition ID.
81
+ """
82
+ logger.debug("Generating escrow payment condition ID")
83
+ condition_id = self.functions().generateId(agreement_id, hash_value).call()
84
+ logger.info(f"Escrow payment condition ID: {condition_id.hex()}")
85
+ return condition_id