mech-client 0.8.0__py3-none-any.whl → 0.8.2__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.
- mech_client/__init__.py +1 -1
- {mech_client-0.8.0.dist-info → mech_client-0.8.2.dist-info}/METADATA +1 -1
- {mech_client-0.8.0.dist-info → mech_client-0.8.2.dist-info}/RECORD +28 -6
- scripts/__init__.py +1 -0
- scripts/benchmark.sh +32 -0
- scripts/bump.py +316 -0
- scripts/deposit_native.py +118 -0
- scripts/deposit_token.py +187 -0
- scripts/nvm_subscribe.py +51 -0
- scripts/nvm_subscription/contracts/agreement_manager.py +44 -0
- scripts/nvm_subscription/contracts/base_contract.py +75 -0
- scripts/nvm_subscription/contracts/did_registry.py +91 -0
- scripts/nvm_subscription/contracts/escrow_payment.py +85 -0
- scripts/nvm_subscription/contracts/lock_payment.py +76 -0
- scripts/nvm_subscription/contracts/nft.py +47 -0
- scripts/nvm_subscription/contracts/nft_sales.py +119 -0
- scripts/nvm_subscription/contracts/subscription_provider.py +107 -0
- scripts/nvm_subscription/contracts/token.py +87 -0
- scripts/nvm_subscription/contracts/transfer_nft.py +81 -0
- scripts/nvm_subscription/envs/base.env +10 -0
- scripts/nvm_subscription/envs/gnosis.env +10 -0
- scripts/nvm_subscription/manager.py +265 -0
- scripts/nvm_subscription/resources/networks.json +56 -0
- scripts/utils.py +127 -0
- scripts/whitelist.py +5 -0
- {mech_client-0.8.0.dist-info → mech_client-0.8.2.dist-info}/LICENSE +0 -0
- {mech_client-0.8.0.dist-info → mech_client-0.8.2.dist-info}/WHEEL +0 -0
- {mech_client-0.8.0.dist-info → mech_client-0.8.2.dist-info}/entry_points.txt +0 -0
scripts/deposit_token.py
ADDED
|
@@ -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")
|
scripts/nvm_subscribe.py
ADDED
|
@@ -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
|
|
@@ -0,0 +1,76 @@
|
|
|
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 LockPaymentConditionContract(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 LockPaymentConditionContract")
|
|
27
|
+
super().__init__(w3, name="LockPaymentCondition")
|
|
28
|
+
logger.info("LockPaymentConditionContract initialized")
|
|
29
|
+
|
|
30
|
+
def hash_values(
|
|
31
|
+
self,
|
|
32
|
+
did: str,
|
|
33
|
+
reward_address: Union[ChecksumAddress, ENS],
|
|
34
|
+
token_address: Union[ChecksumAddress, ENS],
|
|
35
|
+
amounts: List[int],
|
|
36
|
+
receivers: List[Union[ChecksumAddress, ENS]]
|
|
37
|
+
) -> bytes:
|
|
38
|
+
"""
|
|
39
|
+
Compute the hash of the condition parameters.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
did (str): The decentralized identifier.
|
|
43
|
+
reward_address (ChecksumAddress | ENS): Address to receive the reward.
|
|
44
|
+
token_address (ChecksumAddress | ENS): ERC20 token address.
|
|
45
|
+
amounts (List[int]): List of amounts for each receiver.
|
|
46
|
+
receivers (List[ChecksumAddress | ENS]): List of receiver addresses.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bytes: The keccak256 hash of the encoded values.
|
|
50
|
+
"""
|
|
51
|
+
logger.debug("Computing hash for lock payment condition")
|
|
52
|
+
hash_ = self.functions().hashValues(
|
|
53
|
+
did,
|
|
54
|
+
reward_address,
|
|
55
|
+
token_address,
|
|
56
|
+
amounts,
|
|
57
|
+
receivers
|
|
58
|
+
).call()
|
|
59
|
+
logger.debug(f"Computed hash: {hash_.hex()}")
|
|
60
|
+
return hash_
|
|
61
|
+
|
|
62
|
+
def generate_id(self, agreement_id: bytes, hash_value: bytes) -> bytes:
|
|
63
|
+
"""
|
|
64
|
+
Generate a condition ID for a given agreement ID and hash.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
agreement_id (str): ID of the agreement.
|
|
68
|
+
hash_value (bytes): Hashed condition parameters.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
bytes: The condition ID.
|
|
72
|
+
"""
|
|
73
|
+
logger.debug("Generating condition ID from agreement ID and hash")
|
|
74
|
+
condition_id = self.functions().generateId(agreement_id, hash_value).call()
|
|
75
|
+
logger.info(f"Generated condition ID: {condition_id.hex()}")
|
|
76
|
+
return condition_id
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# subscription/contracts/transfer_nft.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import 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 SubscriptionNFT(BaseContract):
|
|
14
|
+
"""
|
|
15
|
+
Wrapper for the Token smart contract. Supports token balance
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, w3: Web3):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the Subscription NFT instance.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
w3 (Web3): A connected Web3 instance.
|
|
24
|
+
"""
|
|
25
|
+
logger.debug("Initializing Subscription NFT")
|
|
26
|
+
super().__init__(w3, name="SubscriptionNFT")
|
|
27
|
+
logger.info("Subscription NFT initialized")
|
|
28
|
+
|
|
29
|
+
def get_balance(
|
|
30
|
+
self, sender: Union[ChecksumAddress, ENS], subscription_id: str
|
|
31
|
+
) -> int:
|
|
32
|
+
"""
|
|
33
|
+
Gets the user subscription credit balance.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
sender (ChecksumAddress | ENS): User address.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
int: The user's credit balance.
|
|
40
|
+
"""
|
|
41
|
+
sender_address: ChecksumAddress = self.w3.to_checksum_address(sender)
|
|
42
|
+
|
|
43
|
+
balance = (
|
|
44
|
+
self.functions().balanceOf(sender_address, int(subscription_id)).call()
|
|
45
|
+
)
|
|
46
|
+
logger.debug(f"Fetched Balance: {balance}")
|
|
47
|
+
return balance
|