mech-client 0.14.0__py3-none-any.whl → 0.15.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 (49) hide show
  1. mech_client/__init__.py +1 -1
  2. mech_client/cli.py +257 -11
  3. mech_client/configs/mechs.json +110 -110
  4. mech_client/interact.py +6 -1
  5. mech_client/marketplace_interact.py +161 -43
  6. mech_client/safe.py +73 -0
  7. mech_client/subgraph.py +0 -11
  8. {mech_client-0.14.0.dist-info → mech_client-0.15.0.dist-info}/METADATA +264 -219
  9. {mech_client-0.14.0.dist-info → mech_client-0.15.0.dist-info}/RECORD +22 -48
  10. scripts/deposit_native.py +48 -16
  11. scripts/deposit_token.py +107 -31
  12. scripts/nvm_subscribe.py +14 -6
  13. scripts/nvm_subscription/contracts/base_contract.py +9 -1
  14. scripts/nvm_subscription/contracts/nft_sales.py +1 -3
  15. scripts/nvm_subscription/contracts/subscription_provider.py +2 -4
  16. scripts/nvm_subscription/contracts/token.py +23 -5
  17. scripts/nvm_subscription/manager.py +109 -16
  18. scripts/utils.py +2 -2
  19. scripts/whitelist.py +9 -1
  20. mech_client/helpers/acn/README.md +0 -76
  21. mech_client/helpers/acn/__init__.py +0 -30
  22. mech_client/helpers/acn/acn.proto +0 -71
  23. mech_client/helpers/acn/acn_pb2.py +0 -42
  24. mech_client/helpers/acn/custom_types.py +0 -224
  25. mech_client/helpers/acn/dialogues.py +0 -126
  26. mech_client/helpers/acn/message.py +0 -274
  27. mech_client/helpers/acn/protocol.yaml +0 -24
  28. mech_client/helpers/acn/serialization.py +0 -149
  29. mech_client/helpers/acn/tests/__init__.py +0 -20
  30. mech_client/helpers/acn/tests/test_acn.py +0 -256
  31. mech_client/helpers/acn/tests/test_acn_dialogues.py +0 -53
  32. mech_client/helpers/acn/tests/test_acn_messages.py +0 -117
  33. mech_client/helpers/acn_data_share/README.md +0 -32
  34. mech_client/helpers/acn_data_share/__init__.py +0 -32
  35. mech_client/helpers/acn_data_share/acn_data_share.proto +0 -17
  36. mech_client/helpers/acn_data_share/acn_data_share_pb2.py +0 -29
  37. mech_client/helpers/acn_data_share/dialogues.py +0 -115
  38. mech_client/helpers/acn_data_share/message.py +0 -213
  39. mech_client/helpers/acn_data_share/protocol.yaml +0 -21
  40. mech_client/helpers/acn_data_share/serialization.py +0 -111
  41. mech_client/helpers/acn_data_share/tests/test_acn_data_share_dialogues.py +0 -49
  42. mech_client/helpers/acn_data_share/tests/test_acn_data_share_messages.py +0 -53
  43. mech_client/helpers/p2p_libp2p_client/README.md +0 -15
  44. mech_client/helpers/p2p_libp2p_client/__init__.py +0 -21
  45. mech_client/helpers/p2p_libp2p_client/connection.py +0 -703
  46. mech_client/helpers/p2p_libp2p_client/connection.yaml +0 -52
  47. {mech_client-0.14.0.dist-info → mech_client-0.15.0.dist-info}/LICENSE +0 -0
  48. {mech_client-0.14.0.dist-info → mech_client-0.15.0.dist-info}/WHEEL +0 -0
  49. {mech_client-0.14.0.dist-info → mech_client-0.15.0.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # ------------------------------------------------------------------------------
3
3
  #
4
- # Copyright 2024 Valory AG
4
+ # Copyright 2024-2025 Valory AG
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ from mech_client.interact import (
52
52
  )
53
53
  from mech_client.mech_marketplace_tool_management import get_mech_tools
54
54
  from mech_client.prompt_to_ipfs import push_metadata_to_ipfs
55
+ from mech_client.safe import EthereumClient, get_safe_nonce, send_safe_tx
55
56
  from mech_client.wss import wait_for_receipt, watch_for_marketplace_request_ids
56
57
 
57
58
 
@@ -61,13 +62,26 @@ class PaymentType(Enum):
61
62
 
62
63
  NATIVE = "ba699a34be8fe0e7725e93dcbce1701b0211a8ca61330aaeb8a05bf2ec7abed1" # nosec
63
64
  TOKEN = "3679d66ef546e66ce9057c4a052f317b135bc8e8c509638f7966edfd4fcf45e9" # nosec
65
+ USDC_TOKEN = (
66
+ "6406bb5f31a732f898e1ce9fdd988a80a808d36ab5d9a4a4805a8be8d197d5e3" # nosec
67
+ )
64
68
  NATIVE_NVM = (
65
69
  "803dd08fe79d91027fc9024e254a0942372b92f3ccabc1bd19f4a5c2b251c316" # nosec
66
70
  )
67
- TOKEN_NVM = (
71
+ TOKEN_NVM_USDC = (
68
72
  "0d6fd99afa9c4c580fab5e341922c2a5c4b61d880da60506193d7bf88944dd14" # nosec
69
73
  )
70
74
 
75
+ @classmethod
76
+ def get_token_payment_types(cls) -> List[str]:
77
+ """Get all token-based payment types that require ERC20 approval."""
78
+ return [cls.TOKEN.value, cls.USDC_TOKEN.value]
79
+
80
+ @classmethod
81
+ def get_prepaid_supported_types(cls) -> List[str]:
82
+ """Get payment types that support prepaid balances."""
83
+ return [cls.NATIVE.value, cls.TOKEN.value, cls.USDC_TOKEN.value]
84
+
71
85
 
72
86
  IPFS_URL_TEMPLATE = "https://gateway.autonolas.tech/ipfs/f01701220{}"
73
87
  MECH_OFFCHAIN_REQUEST_ENDPOINT = "send_signed_requests"
@@ -91,17 +105,18 @@ BALANCE_TRACKER_NVM_TOKEN_ABI_PATH = (
91
105
  PAYMENT_TYPE_TO_ABI_PATH: Dict[str, Path] = {
92
106
  PaymentType.NATIVE.value: BALANCE_TRACKER_NATIVE_ABI_PATH,
93
107
  PaymentType.TOKEN.value: BALANCE_TRACKER_TOKEN_ABI_PATH,
108
+ PaymentType.USDC_TOKEN.value: BALANCE_TRACKER_TOKEN_ABI_PATH,
94
109
  PaymentType.NATIVE_NVM.value: BALANCE_TRACKER_NVM_NATIVE_ABI_PATH,
95
- PaymentType.TOKEN_NVM.value: BALANCE_TRACKER_NVM_TOKEN_ABI_PATH,
110
+ PaymentType.TOKEN_NVM_USDC.value: BALANCE_TRACKER_NVM_TOKEN_ABI_PATH,
96
111
  }
97
112
 
98
113
  CHAIN_TO_PRICE_TOKEN = {
99
114
  1: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0",
100
115
  10: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
101
116
  100: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
102
- 137: "0xFEF5d947472e72Efbb2E388c730B7428406F2F95",
117
+ 137: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
103
118
  8453: "0x54330d28ca3357F294334BDC454a032e7f353416",
104
- 42220: "0xFEF5d947472e72Efbb2E388c730B7428406F2F95",
119
+ 42220: "0x96ffa56a963EC33e5bC7057B9002722D1884fc01",
105
120
  }
106
121
 
107
122
 
@@ -114,6 +129,10 @@ CHAIN_TO_DEFAULT_MECH_MARKETPLACE_REQUEST_CONFIG = {
114
129
  "response_timeout": 300,
115
130
  "payment_data": "0x",
116
131
  },
132
+ 137: {
133
+ "response_timeout": 300,
134
+ "payment_data": "0x",
135
+ },
117
136
  }
118
137
 
119
138
 
@@ -176,7 +195,6 @@ def fetch_mech_info(
176
195
  payment_type_bytes
177
196
  ).call()
178
197
  )
179
-
180
198
  if payment_type not in PaymentType._value2member_map_: # pylint: disable=W0212
181
199
  print(" - Invalid mech type detected.")
182
200
  sys.exit(1)
@@ -189,9 +207,12 @@ def fetch_mech_info(
189
207
  )
190
208
 
191
209
 
192
- def approve_price_tokens(
210
+ def approve_price_tokens( # pylint: disable=too-many-arguments, too-many-locals
193
211
  crypto: EthereumCrypto,
194
212
  ledger_api: EthereumApi,
213
+ ethereum_client: EthereumClient,
214
+ agent_mode: bool,
215
+ safe_address: str,
195
216
  wrapped_token: str,
196
217
  mech_payment_balance_tracker: str,
197
218
  price: int,
@@ -203,6 +224,12 @@ def approve_price_tokens(
203
224
  :type crypto: EthereumCrypto
204
225
  :param ledger_api: The Ethereum API used for interacting with the ledger.
205
226
  :type ledger_api: EthereumApi
227
+ :param ethereum_client: The Ethereum Client used for interacting with the safe.
228
+ :type ethereum_client: EthereumClient
229
+ :param agent_mode: Specifies whether agent mode is active or not.
230
+ :type agent_mode: bool
231
+ :param safe_address: Specifies the safe address related to the configured service, empty is client mode.
232
+ :type safe_address: str
206
233
  :param wrapped_token: The wrapped token contract address.
207
234
  :type wrapped_token: str
208
235
  :param mech_payment_balance_tracker: Requested mech's balance tracker contract address
@@ -212,7 +239,9 @@ def approve_price_tokens(
212
239
  :return: The transaction digest.
213
240
  :rtype: str
214
241
  """
215
- sender = crypto.address
242
+ # Tokens will be on the safe and EOA pays for gas
243
+ # so for agent mode, sender has to be safe
244
+ sender = safe_address or crypto.address
216
245
 
217
246
  with open(ITOKEN_ABI_PATH, encoding="utf-8") as f:
218
247
  abi = json.load(f)
@@ -230,19 +259,41 @@ def approve_price_tokens(
230
259
  sys.exit(1)
231
260
 
232
261
  tx_args = {"sender_address": sender, "value": 0, "gas": 60000}
233
- raw_transaction = ledger_api.build_transaction(
234
- contract_instance=token_contract,
235
- method_name="approve",
236
- method_args={"_to": mech_payment_balance_tracker, "_value": price},
237
- tx_args=tx_args,
238
- raise_on_try=True,
262
+ method_name = "approve"
263
+ method_args = {"_to": mech_payment_balance_tracker, "_value": price}
264
+
265
+ if not agent_mode:
266
+ raw_transaction = ledger_api.build_transaction(
267
+ contract_instance=token_contract,
268
+ method_name=method_name,
269
+ method_args=method_args,
270
+ tx_args=tx_args,
271
+ raise_on_try=True,
272
+ )
273
+ signed_transaction = crypto.sign_transaction(raw_transaction)
274
+ transaction_digest = ledger_api.send_signed_transaction(
275
+ signed_transaction,
276
+ raise_on_try=True,
277
+ )
278
+ return transaction_digest
279
+
280
+ function = token_contract.functions[method_name](**method_args)
281
+ transaction = function.build_transaction(
282
+ {
283
+ "chainId": int(ledger_api._chain_id), # pylint: disable=protected-access
284
+ "gas": 0,
285
+ "nonce": get_safe_nonce(ethereum_client, safe_address),
286
+ }
239
287
  )
240
- signed_transaction = crypto.sign_transaction(raw_transaction)
241
- transaction_digest = ledger_api.send_signed_transaction(
242
- signed_transaction,
243
- raise_on_try=True,
288
+ transaction_digest = send_safe_tx(
289
+ ethereum_client=ethereum_client,
290
+ tx_data=transaction["data"],
291
+ to_adress=token_contract.address,
292
+ safe_address=safe_address,
293
+ signer_pkey=crypto.private_key,
294
+ value=0,
244
295
  )
245
- return transaction_digest
296
+ return transaction_digest.hex()
246
297
 
247
298
 
248
299
  def fetch_requester_nvm_subscription_balance(
@@ -300,10 +351,13 @@ def fetch_requester_nvm_subscription_balance(
300
351
  def send_marketplace_request( # pylint: disable=too-many-arguments,too-many-locals
301
352
  crypto: EthereumCrypto,
302
353
  ledger_api: EthereumApi,
354
+ ethereum_client: EthereumClient,
303
355
  marketplace_contract: Web3Contract,
304
356
  gas_limit: int,
305
357
  prompts: tuple,
306
358
  tools: tuple,
359
+ agent_mode: bool,
360
+ safe_address: str,
307
361
  method_args_data: MechMarketplaceRequestConfig,
308
362
  extra_attributes: Optional[Dict[str, Any]] = None,
309
363
  price: int = 10_000_000_000_000_000,
@@ -318,6 +372,8 @@ def send_marketplace_request( # pylint: disable=too-many-arguments,too-many-loc
318
372
  :type crypto: EthereumCrypto
319
373
  :param ledger_api: The Ethereum API used for interacting with the ledger.
320
374
  :type ledger_api: EthereumApi
375
+ :param ethereum_client: The Ethereum Client used for interacting with the safe.
376
+ :type ethereum_client: EthereumClient
321
377
  :param marketplace_contract: The mech marketplace contract instance.
322
378
  :type marketplace_contract: Web3Contract
323
379
  :param gas_limit: Gas limit.
@@ -326,6 +382,10 @@ def send_marketplace_request( # pylint: disable=too-many-arguments,too-many-loc
326
382
  :type prompts: tuple
327
383
  :param tools: The requested tools.
328
384
  :type tools: tuple
385
+ :param agent_mode: Specifies whether agent mode is active or not.
386
+ :type agent_mode: bool
387
+ :param safe_address: Specifies the safe address related to the configured service, empty is client mode.
388
+ :type safe_address: str
329
389
  :param method_args_data: Method data to use to call the marketplace contract request
330
390
  :type method_args_data: MechMarketplaceRequestConfig
331
391
  :param extra_attributes: Extra attributes to be included in the request metadata.
@@ -390,19 +450,40 @@ def send_marketplace_request( # pylint: disable=too-many-arguments,too-many-loc
390
450
  while tries < retries and datetime.now().timestamp() < deadline:
391
451
  tries += 1
392
452
  try:
393
- raw_transaction = ledger_api.build_transaction(
394
- contract_instance=marketplace_contract,
395
- method_name=method_name,
396
- method_args=method_args,
397
- tx_args=tx_args,
398
- raise_on_try=True,
453
+ if not agent_mode:
454
+ raw_transaction = ledger_api.build_transaction(
455
+ contract_instance=marketplace_contract,
456
+ method_name=method_name,
457
+ method_args=method_args,
458
+ tx_args=tx_args,
459
+ raise_on_try=True,
460
+ )
461
+ signed_transaction = crypto.sign_transaction(raw_transaction)
462
+ transaction_digest = ledger_api.send_signed_transaction(
463
+ signed_transaction,
464
+ raise_on_try=True,
465
+ )
466
+ return transaction_digest
467
+
468
+ function = marketplace_contract.functions[method_name](**method_args)
469
+ transaction = function.build_transaction(
470
+ {
471
+ "chainId": int(
472
+ ledger_api._chain_id # pylint: disable=protected-access
473
+ ),
474
+ "gas": 0,
475
+ "nonce": get_safe_nonce(ethereum_client, safe_address),
476
+ }
399
477
  )
400
- signed_transaction = crypto.sign_transaction(raw_transaction)
401
- transaction_digest = ledger_api.send_signed_transaction(
402
- signed_transaction,
403
- raise_on_try=True,
478
+ transaction_digest = send_safe_tx(
479
+ ethereum_client=ethereum_client,
480
+ tx_data=transaction["data"],
481
+ to_adress=marketplace_contract.address,
482
+ safe_address=safe_address,
483
+ signer_pkey=crypto.private_key,
484
+ value=price,
404
485
  )
405
- return transaction_digest
486
+ return transaction_digest.hex()
406
487
  except Exception as e: # pylint: disable=broad-except
407
488
  print(
408
489
  f"Error occured while sending the transaction: {e}; Retrying in {sleep}"
@@ -616,9 +697,10 @@ def wait_for_offchain_marketplace_data(mech_offchain_url: str, request_id: str)
616
697
  time.sleep(WAIT_SLEEP)
617
698
 
618
699
 
619
- def check_prepaid_balances(
700
+ def check_prepaid_balances( # pylint: disable=too-many-arguments
620
701
  crypto: Crypto,
621
702
  ledger_api: EthereumApi,
703
+ safe_address: str,
622
704
  mech_payment_balance_tracker: str,
623
705
  payment_type: str,
624
706
  max_delivery_rate: int,
@@ -630,6 +712,8 @@ def check_prepaid_balances(
630
712
  :type crypto: Crypto
631
713
  :param ledger_api: The Ethereum API used for interacting with the ledger.
632
714
  :type ledger_api: EthereumApi
715
+ :param safe_address: Specifies the safe address related to the configured service, empty is client mode.
716
+ :type safe_address: str
633
717
  :param mech_payment_balance_tracker: The mech's balance tracker contract address.
634
718
  :type mech_payment_balance_tracker: str
635
719
  :param payment_type: The payment type of the mech.
@@ -637,9 +721,9 @@ def check_prepaid_balances(
637
721
  :param max_delivery_rate: The max_delivery_rate of the mech
638
722
  :type max_delivery_rate: int
639
723
  """
640
- requester = crypto.address
724
+ requester = safe_address or crypto.address
641
725
 
642
- if payment_type in [PaymentType.NATIVE.value, PaymentType.TOKEN.value]:
726
+ if payment_type in PaymentType.get_prepaid_supported_types():
643
727
  payment_type_name = PaymentType(payment_type).name.lower()
644
728
  payment_type_abi_path = PAYMENT_TYPE_TO_ABI_PATH[payment_type]
645
729
 
@@ -680,7 +764,6 @@ def verify_tools(tools: tuple, service_id: int, chain_config: Optional[str]) ->
680
764
  mech_tools_data = get_mech_tools(service_id=service_id, chain_config=chain_config)
681
765
  if not mech_tools_data:
682
766
  raise ValueError("Error while fetching mech tools data")
683
-
684
767
  mech_tools = mech_tools_data.get("tools", [])
685
768
  invalid_tools = [tool for tool in tools if tool not in mech_tools]
686
769
  if invalid_tools:
@@ -692,12 +775,15 @@ def verify_tools(tools: tuple, service_id: int, chain_config: Optional[str]) ->
692
775
  def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals, too-many-statements, too-many-return-statements
693
776
  prompts: tuple,
694
777
  priority_mech: str,
778
+ agent_mode: bool,
779
+ safe_address: str,
695
780
  use_prepaid: bool = False,
696
781
  use_offchain: bool = False,
697
782
  mech_offchain_url: str = "",
698
783
  tools: tuple = (),
699
784
  extra_attributes: Optional[Dict[str, Any]] = None,
700
785
  private_key_path: Optional[str] = None,
786
+ private_key_password: Optional[str] = None,
701
787
  retries: Optional[int] = None,
702
788
  timeout: Optional[float] = None,
703
789
  sleep: Optional[float] = None,
@@ -708,8 +794,12 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
708
794
 
709
795
  :param prompts: The interaction prompts.
710
796
  :type prompts: tuple
711
- :param priority_mech: Priority mech address to use (Optional)
797
+ :param priority_mech: Priority mech address to use
712
798
  :type priority_mech: str
799
+ :param agent_mode: Specifies whether agent mode is active or not.
800
+ :type agent_mode: bool
801
+ :param safe_address: Specifies the safe address related to the configured service, empty is client mode.
802
+ :type safe_address: str
713
803
  :param use_prepaid: Whether to use prepaid model or not.
714
804
  :type use_prepaid: bool
715
805
  :param use_offchain: Whether to use offchain model or not.
@@ -722,6 +812,8 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
722
812
  :type extra_attributes: Optional[Dict[str, Any]]
723
813
  :param private_key_path: The path to the private key file (optional).
724
814
  :type private_key_path: Optional[str]
815
+ :param private_key_password: Password to decrypt the keystore (if encrypted).
816
+ :type private_key_password: Optional[str]
725
817
  :return: The data received from on-chain/off-chain.
726
818
  :param retries: Number of retries for sending a transaction
727
819
  :type retries: int
@@ -735,6 +827,8 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
735
827
  """
736
828
 
737
829
  mech_config = get_mech_config(chain_config)
830
+ ledger_rpc = mech_config.ledger_config.address
831
+ ethereum_client = EthereumClient(ledger_rpc)
738
832
  ledger_config = mech_config.ledger_config
739
833
  priority_mech_address = priority_mech
740
834
  mech_marketplace_contract = mech_config.mech_marketplace_contract
@@ -770,8 +864,9 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
770
864
  raise FileNotFoundError(
771
865
  f"Private key file `{private_key_path}` does not exist!"
772
866
  )
773
-
774
- crypto = EthereumCrypto(private_key_path=private_key_path)
867
+ crypto = EthereumCrypto(
868
+ private_key_path=private_key_path, password=private_key_password
869
+ )
775
870
  ledger_api = EthereumApi(**asdict(ledger_config))
776
871
 
777
872
  with open(MARKETPLACE_ABI_PATH, encoding="utf-8") as f:
@@ -801,16 +896,35 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
801
896
  mech_deliver_event_signature = fetch_mech_deliver_event_signature(
802
897
  ledger_api, priority_mech_address
803
898
  )
804
-
805
899
  verify_tools(tools, service_id, chain_config)
806
-
807
900
  if not use_prepaid:
808
901
  price = max_delivery_rate * num_requests
809
- if payment_type == PaymentType.TOKEN.value:
810
- print("Token Mech detected, approving wrapped token for price payment...")
902
+ if payment_type == PaymentType.NATIVE.value:
903
+ print(
904
+ "Native Mech detected, fetching user native balance for price payment..."
905
+ )
906
+ sender = safe_address or crypto.address
907
+ balance = ledger_api.get_balance(sender)
908
+ if balance < price:
909
+ print(
910
+ f" - Sender Native balance low. Needed: {price}, Actual: {balance}"
911
+ )
912
+ print(f" - Sender Address: {sender}")
913
+ sys.exit(1)
914
+ if payment_type in PaymentType.get_token_payment_types():
811
915
  price_token = CHAIN_TO_PRICE_TOKEN[chain_id]
916
+ print(
917
+ f"Token Mech detected, approving token {price_token} for price payment..."
918
+ )
812
919
  approve_tx = approve_price_tokens(
813
- crypto, ledger_api, price_token, mech_payment_balance_tracker, price
920
+ crypto,
921
+ ledger_api,
922
+ ethereum_client,
923
+ agent_mode,
924
+ safe_address,
925
+ price_token,
926
+ mech_payment_balance_tracker,
927
+ price,
814
928
  )
815
929
  if not approve_tx:
816
930
  print("Unable to approve allowance")
@@ -832,6 +946,7 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
832
946
  check_prepaid_balances(
833
947
  crypto,
834
948
  ledger_api,
949
+ safe_address,
835
950
  mech_payment_balance_tracker,
836
951
  payment_type,
837
952
  max_delivery_rate,
@@ -839,7 +954,7 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
839
954
 
840
955
  is_nvm_mech = payment_type in [
841
956
  PaymentType.NATIVE_NVM.value,
842
- PaymentType.TOKEN_NVM.value,
957
+ PaymentType.TOKEN_NVM_USDC.value,
843
958
  ]
844
959
  if is_nvm_mech:
845
960
  nvm_mech_type = PaymentType(payment_type).name.lower()
@@ -874,11 +989,14 @@ def marketplace_interact( # pylint: disable=too-many-arguments, too-many-locals
874
989
  transaction_digest = send_marketplace_request(
875
990
  crypto=crypto,
876
991
  ledger_api=ledger_api,
992
+ ethereum_client=ethereum_client,
877
993
  marketplace_contract=mech_marketplace_contract,
878
994
  gas_limit=mech_config.gas_limit,
879
995
  price=price,
880
996
  prompts=prompts,
881
997
  tools=tools,
998
+ agent_mode=agent_mode,
999
+ safe_address=safe_address,
882
1000
  method_args_data=mech_marketplace_request_config,
883
1001
  extra_attributes=extra_attributes,
884
1002
  retries=retries,
mech_client/safe.py ADDED
@@ -0,0 +1,73 @@
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
+ """Mech client Safe Module."""
21
+ from typing import Optional
22
+
23
+ from hexbytes import HexBytes
24
+ from safe_eth.eth import EthereumClient # pylint:disable=import-error
25
+ from safe_eth.safe import Safe # pylint:disable=import-error
26
+ from web3.constants import ADDRESS_ZERO
27
+
28
+
29
+ def send_safe_tx( # pylint: disable=too-many-arguments
30
+ ethereum_client: EthereumClient,
31
+ tx_data: str,
32
+ to_adress: str,
33
+ safe_address: str,
34
+ signer_pkey: str,
35
+ value: int = 0,
36
+ ) -> Optional[HexBytes]:
37
+ """Send a Safe transaction"""
38
+ # Get the safe
39
+ safe = Safe( # pylint:disable=abstract-class-instantiated
40
+ safe_address, ethereum_client
41
+ )
42
+
43
+ estimated_gas = safe.estimate_tx_gas_with_safe(
44
+ to=to_adress, value=value, data=bytes.fromhex(tx_data[2:]), operation=0
45
+ )
46
+
47
+ # Build, sign and send the safe transaction
48
+ safe_tx = safe.build_multisig_tx(
49
+ to=to_adress,
50
+ value=value,
51
+ data=bytes.fromhex(tx_data[2:]),
52
+ operation=0,
53
+ safe_tx_gas=estimated_gas,
54
+ base_gas=0,
55
+ gas_price=0,
56
+ gas_token=ADDRESS_ZERO,
57
+ refund_receiver=ADDRESS_ZERO,
58
+ )
59
+ safe_tx.sign(signer_pkey)
60
+ try:
61
+ tx_hash, _ = safe_tx.execute(signer_pkey)
62
+ return tx_hash
63
+ except Exception as e: # pylint: disable=broad-except
64
+ print(f"Exception while sending a safe transaction: {e}")
65
+ return None
66
+
67
+
68
+ def get_safe_nonce(ethereum_client: EthereumClient, safe_address: str) -> int:
69
+ """Get the Safe nonce"""
70
+ safe = Safe( # pylint:disable=abstract-class-instantiated
71
+ safe_address, ethereum_client
72
+ )
73
+ return safe.retrieve_nonce()
mech_client/subgraph.py CHANGED
@@ -35,21 +35,10 @@ AGENT_QUERY_TEMPLATE = Template(
35
35
  DEFAULT_TIMEOUT = 600.0
36
36
  CHAIN_TO_ADDRESSES = {
37
37
  "gnosis": {
38
- 3: "0xFf82123dFB52ab75C417195c5fDB87630145ae81",
39
38
  6: "0x77af31De935740567Cf4fF1986D04B2c964A786a",
40
39
  9: "0x552cea7bc33cbbeb9f1d90c1d11d2c6daeffd053",
41
- 11: "0x9aDe7A78A39B39a44b7a084923E93AA0B19Fd690",
42
40
  19: "0x45b73d649c7b982548d5a6dd3d35e1c5c48997d0",
43
41
  },
44
- "base": {
45
- 1: "0x37C484cc34408d0F827DB4d7B6e54b8837Bf8BDA",
46
- 2: "0x111D7DB1B752AB4D2cC0286983D9bd73a49bac6c",
47
- 3: "0x111D7DB1B752AB4D2cC0286983D9bd73a49bac6c",
48
- },
49
- "arbitrum": {2: "0x1FDAD3a5af5E96e5a64Fc0662B1814458F114597"},
50
- "polygon": {2: "0xbF92568718982bf65ee4af4F7020205dE2331a8a"},
51
- "celo": {2: "0x230eD015735c0D01EA0AaD2786Ed6Bd3C6e75912"},
52
- "optimism": {2: "0xDd40E7D93c37eFD860Bd53Ab90b2b0a8D05cf71a"},
53
42
  }
54
43
 
55
44