iwa 0.0.0__py3-none-any.whl → 0.0.1a2__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.
- conftest.py +22 -0
- iwa/__init__.py +1 -0
- iwa/__main__.py +6 -0
- iwa/core/__init__.py +1 -0
- iwa/core/chain/__init__.py +68 -0
- iwa/core/chain/errors.py +47 -0
- iwa/core/chain/interface.py +514 -0
- iwa/core/chain/manager.py +38 -0
- iwa/core/chain/models.py +128 -0
- iwa/core/chain/rate_limiter.py +193 -0
- iwa/core/cli.py +210 -0
- iwa/core/constants.py +28 -0
- iwa/core/contracts/__init__.py +1 -0
- iwa/core/contracts/contract.py +297 -0
- iwa/core/contracts/erc20.py +79 -0
- iwa/core/contracts/multisend.py +71 -0
- iwa/core/db.py +317 -0
- iwa/core/keys.py +361 -0
- iwa/core/mnemonic.py +385 -0
- iwa/core/models.py +344 -0
- iwa/core/monitor.py +209 -0
- iwa/core/plugins.py +45 -0
- iwa/core/pricing.py +91 -0
- iwa/core/services/__init__.py +17 -0
- iwa/core/services/account.py +57 -0
- iwa/core/services/balance.py +113 -0
- iwa/core/services/plugin.py +88 -0
- iwa/core/services/safe.py +392 -0
- iwa/core/services/transaction.py +172 -0
- iwa/core/services/transfer/__init__.py +166 -0
- iwa/core/services/transfer/base.py +260 -0
- iwa/core/services/transfer/erc20.py +247 -0
- iwa/core/services/transfer/multisend.py +386 -0
- iwa/core/services/transfer/native.py +262 -0
- iwa/core/services/transfer/swap.py +326 -0
- iwa/core/settings.py +95 -0
- iwa/core/tables.py +60 -0
- iwa/core/test.py +27 -0
- iwa/core/tests/test_wallet.py +255 -0
- iwa/core/types.py +59 -0
- iwa/core/ui.py +99 -0
- iwa/core/utils.py +59 -0
- iwa/core/wallet.py +380 -0
- iwa/plugins/__init__.py +1 -0
- iwa/plugins/gnosis/__init__.py +5 -0
- iwa/plugins/gnosis/cow/__init__.py +6 -0
- iwa/plugins/gnosis/cow/quotes.py +148 -0
- iwa/plugins/gnosis/cow/swap.py +403 -0
- iwa/plugins/gnosis/cow/types.py +20 -0
- iwa/plugins/gnosis/cow_utils.py +44 -0
- iwa/plugins/gnosis/plugin.py +68 -0
- iwa/plugins/gnosis/safe.py +157 -0
- iwa/plugins/gnosis/tests/test_cow.py +227 -0
- iwa/plugins/gnosis/tests/test_safe.py +100 -0
- iwa/plugins/olas/__init__.py +5 -0
- iwa/plugins/olas/constants.py +106 -0
- iwa/plugins/olas/contracts/activity_checker.py +93 -0
- iwa/plugins/olas/contracts/base.py +10 -0
- iwa/plugins/olas/contracts/mech.py +49 -0
- iwa/plugins/olas/contracts/mech_marketplace.py +43 -0
- iwa/plugins/olas/contracts/service.py +215 -0
- iwa/plugins/olas/contracts/staking.py +403 -0
- iwa/plugins/olas/importer.py +736 -0
- iwa/plugins/olas/mech_reference.py +135 -0
- iwa/plugins/olas/models.py +110 -0
- iwa/plugins/olas/plugin.py +243 -0
- iwa/plugins/olas/scripts/test_full_mech_flow.py +259 -0
- iwa/plugins/olas/scripts/test_simple_lifecycle.py +74 -0
- iwa/plugins/olas/service_manager/__init__.py +60 -0
- iwa/plugins/olas/service_manager/base.py +113 -0
- iwa/plugins/olas/service_manager/drain.py +336 -0
- iwa/plugins/olas/service_manager/lifecycle.py +839 -0
- iwa/plugins/olas/service_manager/mech.py +322 -0
- iwa/plugins/olas/service_manager/staking.py +530 -0
- iwa/plugins/olas/tests/conftest.py +30 -0
- iwa/plugins/olas/tests/test_importer.py +128 -0
- iwa/plugins/olas/tests/test_importer_error_handling.py +349 -0
- iwa/plugins/olas/tests/test_mech_contracts.py +85 -0
- iwa/plugins/olas/tests/test_olas_contracts.py +249 -0
- iwa/plugins/olas/tests/test_olas_integration.py +561 -0
- iwa/plugins/olas/tests/test_olas_models.py +144 -0
- iwa/plugins/olas/tests/test_olas_view.py +258 -0
- iwa/plugins/olas/tests/test_olas_view_actions.py +137 -0
- iwa/plugins/olas/tests/test_olas_view_modals.py +120 -0
- iwa/plugins/olas/tests/test_plugin.py +70 -0
- iwa/plugins/olas/tests/test_plugin_full.py +212 -0
- iwa/plugins/olas/tests/test_service_lifecycle.py +150 -0
- iwa/plugins/olas/tests/test_service_manager.py +1065 -0
- iwa/plugins/olas/tests/test_service_manager_errors.py +208 -0
- iwa/plugins/olas/tests/test_service_manager_flows.py +497 -0
- iwa/plugins/olas/tests/test_service_manager_mech.py +135 -0
- iwa/plugins/olas/tests/test_service_manager_rewards.py +360 -0
- iwa/plugins/olas/tests/test_service_manager_validation.py +145 -0
- iwa/plugins/olas/tests/test_service_staking.py +342 -0
- iwa/plugins/olas/tests/test_staking_integration.py +269 -0
- iwa/plugins/olas/tests/test_staking_validation.py +109 -0
- iwa/plugins/olas/tui/__init__.py +1 -0
- iwa/plugins/olas/tui/olas_view.py +952 -0
- iwa/tools/check_profile.py +67 -0
- iwa/tools/release.py +111 -0
- iwa/tools/reset_env.py +111 -0
- iwa/tools/reset_tenderly.py +362 -0
- iwa/tools/restore_backup.py +82 -0
- iwa/tui/__init__.py +1 -0
- iwa/tui/app.py +174 -0
- iwa/tui/modals/__init__.py +5 -0
- iwa/tui/modals/base.py +406 -0
- iwa/tui/rpc.py +63 -0
- iwa/tui/screens/__init__.py +1 -0
- iwa/tui/screens/wallets.py +749 -0
- iwa/tui/tests/test_app.py +125 -0
- iwa/tui/tests/test_rpc.py +139 -0
- iwa/tui/tests/test_wallets_refactor.py +30 -0
- iwa/tui/tests/test_widgets.py +123 -0
- iwa/tui/widgets/__init__.py +5 -0
- iwa/tui/widgets/base.py +100 -0
- iwa/tui/workers.py +42 -0
- iwa/web/dependencies.py +76 -0
- iwa/web/models.py +76 -0
- iwa/web/routers/accounts.py +115 -0
- iwa/web/routers/olas/__init__.py +24 -0
- iwa/web/routers/olas/admin.py +169 -0
- iwa/web/routers/olas/funding.py +135 -0
- iwa/web/routers/olas/general.py +29 -0
- iwa/web/routers/olas/services.py +378 -0
- iwa/web/routers/olas/staking.py +341 -0
- iwa/web/routers/state.py +65 -0
- iwa/web/routers/swap.py +617 -0
- iwa/web/routers/transactions.py +153 -0
- iwa/web/server.py +155 -0
- iwa/web/tests/test_web_endpoints.py +713 -0
- iwa/web/tests/test_web_olas.py +430 -0
- iwa/web/tests/test_web_swap.py +103 -0
- iwa-0.0.1a2.dist-info/METADATA +234 -0
- iwa-0.0.1a2.dist-info/RECORD +186 -0
- iwa-0.0.1a2.dist-info/entry_points.txt +2 -0
- iwa-0.0.1a2.dist-info/licenses/LICENSE +21 -0
- iwa-0.0.1a2.dist-info/top_level.txt +4 -0
- tests/legacy_cow.py +248 -0
- tests/legacy_safe.py +93 -0
- tests/legacy_transaction_retry_logic.py +51 -0
- tests/legacy_tui.py +440 -0
- tests/legacy_wallets_screen.py +554 -0
- tests/legacy_web.py +243 -0
- tests/test_account_service.py +120 -0
- tests/test_balance_service.py +186 -0
- tests/test_chain.py +490 -0
- tests/test_chain_interface.py +210 -0
- tests/test_cli.py +139 -0
- tests/test_contract.py +195 -0
- tests/test_db.py +180 -0
- tests/test_drain_coverage.py +174 -0
- tests/test_erc20.py +95 -0
- tests/test_gnosis_plugin.py +111 -0
- tests/test_keys.py +449 -0
- tests/test_legacy_wallet.py +1285 -0
- tests/test_main.py +13 -0
- tests/test_mnemonic.py +217 -0
- tests/test_modals.py +109 -0
- tests/test_models.py +213 -0
- tests/test_monitor.py +202 -0
- tests/test_multisend.py +84 -0
- tests/test_plugin_service.py +119 -0
- tests/test_pricing.py +143 -0
- tests/test_rate_limiter.py +199 -0
- tests/test_reset_tenderly.py +202 -0
- tests/test_rpc_view.py +73 -0
- tests/test_safe_coverage.py +139 -0
- tests/test_safe_service.py +168 -0
- tests/test_service_manager_integration.py +61 -0
- tests/test_service_manager_structure.py +31 -0
- tests/test_service_transaction.py +176 -0
- tests/test_staking_router.py +71 -0
- tests/test_staking_simple.py +31 -0
- tests/test_tables.py +76 -0
- tests/test_transaction_service.py +161 -0
- tests/test_transfer_multisend.py +179 -0
- tests/test_transfer_native.py +220 -0
- tests/test_transfer_security.py +93 -0
- tests/test_transfer_structure.py +37 -0
- tests/test_transfer_swap_unit.py +155 -0
- tests/test_ui_coverage.py +66 -0
- tests/test_utils.py +53 -0
- tests/test_workers.py +91 -0
- tools/verify_drain.py +183 -0
- __init__.py +0 -2
- hello.py +0 -6
- iwa-0.0.0.dist-info/METADATA +0 -10
- iwa-0.0.0.dist-info/RECORD +0 -6
- iwa-0.0.0.dist-info/top_level.txt +0 -2
- {iwa-0.0.0.dist-info → iwa-0.0.1a2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""Mech manager mixin."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from web3 import Web3
|
|
7
|
+
|
|
8
|
+
from iwa.core.constants import ZERO_ADDRESS
|
|
9
|
+
from iwa.plugins.olas.constants import (
|
|
10
|
+
OLAS_CONTRACTS,
|
|
11
|
+
PAYMENT_TYPE_NATIVE,
|
|
12
|
+
)
|
|
13
|
+
from iwa.plugins.olas.contracts.mech import MechContract
|
|
14
|
+
from iwa.plugins.olas.contracts.mech_marketplace import MechMarketplaceContract
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MechManagerMixin:
|
|
18
|
+
"""Mixin for Mech interactions."""
|
|
19
|
+
|
|
20
|
+
def send_mech_request(
|
|
21
|
+
self,
|
|
22
|
+
data: bytes,
|
|
23
|
+
value: Optional[int] = None,
|
|
24
|
+
mech_address: Optional[str] = None,
|
|
25
|
+
use_marketplace: bool = False,
|
|
26
|
+
use_new_abi: bool = False,
|
|
27
|
+
priority_mech: Optional[str] = None,
|
|
28
|
+
max_delivery_rate: Optional[int] = None,
|
|
29
|
+
payment_type: Optional[bytes] = None,
|
|
30
|
+
payment_data: bytes = b"",
|
|
31
|
+
response_timeout: int = 300,
|
|
32
|
+
) -> Optional[str]:
|
|
33
|
+
"""Send a Mech request from the service multisig.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
data: The request data (IPFS hash bytes).
|
|
37
|
+
value: Payment value in wei. For marketplace, should match mech's maxDeliveryRate.
|
|
38
|
+
mech_address: Address of the Mech contract (for legacy/direct flow).
|
|
39
|
+
use_marketplace: Whether to use the Mech Marketplace flow.
|
|
40
|
+
use_new_abi: Whether to use new ABI for legacy flow.
|
|
41
|
+
priority_mech: Priority mech address (required for marketplace).
|
|
42
|
+
max_delivery_rate: Max delivery rate in wei (for marketplace). If None, uses value.
|
|
43
|
+
payment_type: Payment type bytes32 (for marketplace). Defaults to NATIVE.
|
|
44
|
+
payment_data: Payment data (for marketplace).
|
|
45
|
+
response_timeout: Timeout in seconds for marketplace request (60-300).
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
The transaction hash if successful, None otherwise.
|
|
49
|
+
|
|
50
|
+
"""
|
|
51
|
+
if not self.service:
|
|
52
|
+
logger.error("No active service loaded")
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
service_id = self.service.service_id
|
|
56
|
+
multisig_address = self.service.multisig_address
|
|
57
|
+
|
|
58
|
+
if not multisig_address:
|
|
59
|
+
logger.error(f"Service {service_id} has no multisig address")
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
if use_marketplace:
|
|
63
|
+
return self._send_marketplace_mech_request(
|
|
64
|
+
data=data,
|
|
65
|
+
value=value,
|
|
66
|
+
priority_mech=priority_mech,
|
|
67
|
+
max_delivery_rate=max_delivery_rate,
|
|
68
|
+
payment_type=payment_type,
|
|
69
|
+
payment_data=payment_data,
|
|
70
|
+
response_timeout=response_timeout,
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
return self._send_legacy_mech_request(
|
|
74
|
+
data=data,
|
|
75
|
+
value=value,
|
|
76
|
+
mech_address=mech_address,
|
|
77
|
+
use_new_abi=use_new_abi,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _send_legacy_mech_request(
|
|
81
|
+
self,
|
|
82
|
+
data: bytes,
|
|
83
|
+
value: Optional[int] = None,
|
|
84
|
+
mech_address: Optional[str] = None,
|
|
85
|
+
use_new_abi: bool = False,
|
|
86
|
+
) -> Optional[str]:
|
|
87
|
+
"""Send a legacy (direct) mech request."""
|
|
88
|
+
if not self.service:
|
|
89
|
+
logger.error("No active service")
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
multisig_address = self.service.multisig_address
|
|
93
|
+
protocol_contracts = OLAS_CONTRACTS.get(self.chain_name, {})
|
|
94
|
+
mech_address = mech_address or protocol_contracts.get("OLAS_MECH")
|
|
95
|
+
|
|
96
|
+
if not mech_address:
|
|
97
|
+
logger.error(f"Legacy mech address not found for chain {self.chain_name}")
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
mech = MechContract(str(mech_address), chain_name=self.chain_name, use_new_abi=use_new_abi)
|
|
101
|
+
|
|
102
|
+
# Get mech price if value not provided
|
|
103
|
+
if value is None:
|
|
104
|
+
value = mech.get_price()
|
|
105
|
+
logger.info(f"Using mech price: {value} wei")
|
|
106
|
+
|
|
107
|
+
tx_data = mech.prepare_request_tx(
|
|
108
|
+
from_address=multisig_address,
|
|
109
|
+
data=data,
|
|
110
|
+
value=value,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if not tx_data:
|
|
114
|
+
logger.error("Failed to prepare legacy mech request transaction")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
return self._execute_mech_tx(
|
|
118
|
+
tx_data=tx_data,
|
|
119
|
+
to_address=str(mech_address),
|
|
120
|
+
contract_instance=mech,
|
|
121
|
+
expected_event="Request",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def _validate_priority_mech(self, marketplace, priority_mech: str) -> bool:
|
|
125
|
+
"""Validate priority mech is registered on marketplace."""
|
|
126
|
+
try:
|
|
127
|
+
mech_multisig = marketplace.call("checkMech", priority_mech)
|
|
128
|
+
if mech_multisig == ZERO_ADDRESS:
|
|
129
|
+
logger.error(f"Priority mech {priority_mech} is NOT registered on marketplace")
|
|
130
|
+
return False
|
|
131
|
+
logger.debug(f"Priority mech {priority_mech} -> multisig {mech_multisig}")
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Failed to verify priority mech registration: {e}")
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
# Log mech factory info (optional validation)
|
|
137
|
+
try:
|
|
138
|
+
mech_factory = marketplace.call("mapAgentMechFactories", priority_mech)
|
|
139
|
+
if mech_factory == ZERO_ADDRESS:
|
|
140
|
+
logger.warning(
|
|
141
|
+
f"Priority mech {priority_mech} has no factory (may be unregistered)"
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
logger.debug(f"Priority mech factory: {mech_factory}")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(f"Could not fetch mech factory: {e}")
|
|
147
|
+
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
def _validate_marketplace_params(
|
|
151
|
+
self, marketplace, response_timeout: int, payment_type: bytes
|
|
152
|
+
) -> bool:
|
|
153
|
+
"""Validate marketplace parameters."""
|
|
154
|
+
# Validate response_timeout bounds
|
|
155
|
+
try:
|
|
156
|
+
min_timeout = marketplace.call("minResponseTimeout")
|
|
157
|
+
max_timeout = marketplace.call("maxResponseTimeout")
|
|
158
|
+
if response_timeout < min_timeout or response_timeout > max_timeout:
|
|
159
|
+
logger.error(
|
|
160
|
+
f"response_timeout {response_timeout} out of bounds [{min_timeout}, {max_timeout}]"
|
|
161
|
+
)
|
|
162
|
+
return False
|
|
163
|
+
logger.debug(
|
|
164
|
+
f"Response timeout {response_timeout}s within bounds [{min_timeout}, {max_timeout}]"
|
|
165
|
+
)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.warning(f"Could not validate response_timeout bounds: {e}")
|
|
168
|
+
|
|
169
|
+
# Validate payment type has balance tracker
|
|
170
|
+
try:
|
|
171
|
+
balance_tracker = marketplace.call("mapPaymentTypeBalanceTrackers", payment_type)
|
|
172
|
+
if balance_tracker == ZERO_ADDRESS:
|
|
173
|
+
logger.error(f"No balance tracker for payment type 0x{payment_type.hex()}")
|
|
174
|
+
return False
|
|
175
|
+
logger.debug(f"Payment type balance tracker: {balance_tracker}")
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.warning(f"Could not validate payment type: {e}")
|
|
178
|
+
|
|
179
|
+
return True
|
|
180
|
+
|
|
181
|
+
def _send_marketplace_mech_request(
|
|
182
|
+
self,
|
|
183
|
+
data: bytes,
|
|
184
|
+
value: Optional[int] = None,
|
|
185
|
+
priority_mech: Optional[str] = None,
|
|
186
|
+
max_delivery_rate: Optional[int] = None,
|
|
187
|
+
payment_type: Optional[bytes] = None,
|
|
188
|
+
payment_data: bytes = b"",
|
|
189
|
+
response_timeout: int = 300,
|
|
190
|
+
) -> Optional[str]:
|
|
191
|
+
"""Send a marketplace mech request with validation."""
|
|
192
|
+
if not self.service:
|
|
193
|
+
logger.error("No active service")
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
multisig_address = self.service.multisig_address
|
|
197
|
+
chain_name = self.chain_name if self.service else getattr(self, "chain_name", "gnosis")
|
|
198
|
+
protocol_contracts = OLAS_CONTRACTS.get(chain_name, {})
|
|
199
|
+
marketplace_address = protocol_contracts.get("OLAS_MECH_MARKETPLACE")
|
|
200
|
+
|
|
201
|
+
if not marketplace_address:
|
|
202
|
+
logger.error(f"Mech Marketplace address not found for chain {chain_name}")
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
if not priority_mech:
|
|
206
|
+
logger.error("priority_mech is required for marketplace requests")
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
priority_mech = Web3.to_checksum_address(priority_mech)
|
|
210
|
+
marketplace = MechMarketplaceContract(str(marketplace_address), chain_name=chain_name)
|
|
211
|
+
|
|
212
|
+
if not self._validate_priority_mech(marketplace, priority_mech):
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
# Set defaults for payment
|
|
216
|
+
if payment_type is None:
|
|
217
|
+
payment_type = bytes.fromhex(PAYMENT_TYPE_NATIVE)
|
|
218
|
+
|
|
219
|
+
if value is None:
|
|
220
|
+
value = 10_000_000_000_000_000
|
|
221
|
+
logger.info(f"Using default value: {value} wei (0.01 xDAI)")
|
|
222
|
+
|
|
223
|
+
if max_delivery_rate is None:
|
|
224
|
+
max_delivery_rate = value
|
|
225
|
+
logger.info(f"Using value as max_delivery_rate: {max_delivery_rate}")
|
|
226
|
+
|
|
227
|
+
if not self._validate_marketplace_params(marketplace, response_timeout, payment_type):
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
# Prepare transaction
|
|
231
|
+
tx_data = marketplace.prepare_request_tx(
|
|
232
|
+
from_address=multisig_address,
|
|
233
|
+
request_data=data,
|
|
234
|
+
priority_mech=priority_mech,
|
|
235
|
+
response_timeout=response_timeout,
|
|
236
|
+
max_delivery_rate=max_delivery_rate,
|
|
237
|
+
payment_type=payment_type,
|
|
238
|
+
payment_data=payment_data,
|
|
239
|
+
value=value,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if not tx_data:
|
|
243
|
+
logger.error("Failed to prepare marketplace request transaction")
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
return self._execute_mech_tx(
|
|
247
|
+
tx_data=tx_data,
|
|
248
|
+
to_address=str(marketplace_address),
|
|
249
|
+
contract_instance=marketplace,
|
|
250
|
+
expected_event="MarketplaceRequest",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def _execute_mech_tx(
|
|
254
|
+
self,
|
|
255
|
+
tx_data: dict,
|
|
256
|
+
to_address: str,
|
|
257
|
+
contract_instance,
|
|
258
|
+
expected_event: str,
|
|
259
|
+
) -> Optional[str]:
|
|
260
|
+
"""Execute a mech transaction and verify the event."""
|
|
261
|
+
if not self.service:
|
|
262
|
+
logger.error("No active service")
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
multisig_address = self.service.multisig_address
|
|
266
|
+
tx_value = int(tx_data.get("value", 0))
|
|
267
|
+
|
|
268
|
+
from iwa.core.models import StoredSafeAccount
|
|
269
|
+
|
|
270
|
+
sender_account = self.wallet.account_service.resolve_account(str(multisig_address))
|
|
271
|
+
is_safe = isinstance(sender_account, StoredSafeAccount)
|
|
272
|
+
|
|
273
|
+
if is_safe:
|
|
274
|
+
logger.info(f"Sending mech request via Safe {multisig_address} (value: {tx_value} wei)")
|
|
275
|
+
try:
|
|
276
|
+
tx_hash = self.wallet.safe_service.execute_safe_transaction(
|
|
277
|
+
safe_address_or_tag=str(multisig_address),
|
|
278
|
+
to=to_address,
|
|
279
|
+
value=tx_value,
|
|
280
|
+
chain_name=self.chain_name,
|
|
281
|
+
data=tx_data["data"],
|
|
282
|
+
)
|
|
283
|
+
except Exception as e:
|
|
284
|
+
logger.error(f"Safe transaction failed: {e}")
|
|
285
|
+
return None
|
|
286
|
+
else:
|
|
287
|
+
logger.info(f"Sending mech request via EOA {multisig_address} (value: {tx_value} wei)")
|
|
288
|
+
tx = {
|
|
289
|
+
"to": to_address,
|
|
290
|
+
"value": tx_value,
|
|
291
|
+
"data": tx_data["data"],
|
|
292
|
+
}
|
|
293
|
+
success, receipt = self.wallet.sign_and_send_transaction(
|
|
294
|
+
transaction=tx,
|
|
295
|
+
signer_address_or_tag=str(multisig_address),
|
|
296
|
+
chain_name=self.chain_name,
|
|
297
|
+
tags=["olas_mech_request"],
|
|
298
|
+
)
|
|
299
|
+
tx_hash = receipt.get("transactionHash").hex() if success else None
|
|
300
|
+
|
|
301
|
+
if not tx_hash:
|
|
302
|
+
logger.error("Failed to send mech request transaction")
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
logger.info(f"Mech request transaction sent: {tx_hash}")
|
|
306
|
+
|
|
307
|
+
# Verify event emission
|
|
308
|
+
try:
|
|
309
|
+
receipt = self.registry.chain_interface.web3.eth.wait_for_transaction_receipt(tx_hash)
|
|
310
|
+
events = contract_instance.extract_events(receipt)
|
|
311
|
+
event_found = next((e for e in events if e["name"] == expected_event), None)
|
|
312
|
+
|
|
313
|
+
if event_found:
|
|
314
|
+
logger.info(f"Event '{expected_event}' verified successfully")
|
|
315
|
+
return tx_hash
|
|
316
|
+
else:
|
|
317
|
+
logger.error(f"Event '{expected_event}' NOT found in transaction logs")
|
|
318
|
+
logger.debug(f"Found events: {[e['name'] for e in events]}")
|
|
319
|
+
return None
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.error(f"Error verifying event emission: {e}")
|
|
322
|
+
return None
|