olas-operate-middleware 0.10.8__py3-none-any.whl → 0.10.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: olas-operate-middleware
3
- Version: 0.10.8
3
+ Version: 0.10.9
4
4
  Summary:
5
5
  Author: David Vilela
6
6
  Author-email: dvilelaf@gmail.com
@@ -6,7 +6,7 @@ operate/bridge/providers/lifi_provider.py,sha256=FpAlBAA_gOt-oOHKhGaOQhhTZIL-hgY
6
6
  operate/bridge/providers/native_bridge_provider.py,sha256=gG8bSyxUoAVEF6_J9tn1qKRv1PnXuMJdMwUAVJ4GJz8,24647
7
7
  operate/bridge/providers/provider.py,sha256=i54RL7m4wMSADM_D_V_quQump_ipPTmByUc-c-AOLPQ,19687
8
8
  operate/bridge/providers/relay_provider.py,sha256=L7D-PKepsuBadarJmrNneZk0bsrh88u05uD6_2MSss4,17341
9
- operate/cli.py,sha256=UZmmQOmod4ytFsMl65MH5rtc881EEE3wQgYIYr-BGW0,55788
9
+ operate/cli.py,sha256=REHgtFvaSs7jm7B0K1tPkEKco7Bmd06KQvYAQstFPyw,55783
10
10
  operate/constants.py,sha256=ilmLiv0H_xKZyfqmfEdqMci-TlLQyNPp9rrxxqtkqFI,2992
11
11
  operate/data/README.md,sha256=jGPyZTvg2LCGdllvmYxmFMkkkiXb6YWatbqIkcX3kv4,879
12
12
  operate/data/__init__.py,sha256=ttC51Yqk9c4ehpIgs1Qbe7aJvzkrbbdZ1ClaCxJYByE,864
@@ -57,16 +57,16 @@ operate/data/contracts/uniswap_v2_erc20/contract.py,sha256=MwBks4QmZ3XouMT_TqWLn
57
57
  operate/data/contracts/uniswap_v2_erc20/contract.yaml,sha256=XUdz-XtKtmZgLfItbO8usP-QPbtUkAxKGn0hL7OftAg,741
58
58
  operate/data/contracts/uniswap_v2_erc20/tests/__init__.py,sha256=3Arw8dsCsJz6hVOl0t9UjFASHXbV9yp3hw6x4HqgXpU,847
59
59
  operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py,sha256=FzZbw9OTcr_yvjOXpk9YcO-K40eyDARyybcfSHDg2Ps,13392
60
- operate/keys.py,sha256=_TAS7x9TyU9a8AMnw2FacAHUeqLB0kBSJECc3Msxv54,5207
61
- operate/ledger/__init__.py,sha256=TCZxTdUTCHjhYErVAjsZgSbv7YUdAzYKOe-vJDvCkGk,3963
62
- operate/ledger/profiles.py,sha256=7aLLf0pJTIHerpXbwpjeKE9inuwiw0FG0GgTWU_0vGE,11977
60
+ operate/keys.py,sha256=mPZ2KZujPjHZ1RNVD2vkLzK_3q6J32o9q0WCB6Raxs4,5237
61
+ operate/ledger/__init__.py,sha256=s8GEcV-VtKdBQb9DqW7uf-tj_NpFNn13aNHCh0eqfaQ,3037
62
+ operate/ledger/profiles.py,sha256=LaYPyz-1Cy-KRRuXk60W0ZXQJ2cwoWmDxYrL_psIdKo,11993
63
63
  operate/migration.py,sha256=mh921s8FG48XMfDu63ojrdhU7j_RZsdl5E2BvxsbkWE,16937
64
64
  operate/operate_http/__init__.py,sha256=dxCIVSUos23M4R-PFZZG6k5QrOlEiK0SxhCYSFNxh7U,4711
65
65
  operate/operate_http/exceptions.py,sha256=4UFzrn-GyDD71RhkaOyFPBynL6TrrtP3eywaaU3o4fc,1339
66
66
  operate/operate_types.py,sha256=oVOzd6K_CTbmAe1pbSFr4LCajNJ_n0y6J5Wwdf_6ev8,8102
67
67
  operate/pearl.py,sha256=yrTpSXLu_ML3qT-uNxq3kScOyo31JyxBujiSMfMUbcg,1690
68
68
  operate/quickstart/analyse_logs.py,sha256=K11AWWevkddUIUzTe75J3fYVS6aLfi6kT_dAI9OjrX8,4195
69
- operate/quickstart/claim_staking_rewards.py,sha256=AqfLMRef2YijQtWPaTuGwX2sOItNEkoyoi6Q9SICp_I,4026
69
+ operate/quickstart/claim_staking_rewards.py,sha256=K7X1Yq0mxe3qWmFLb1Xu9-Jghhml95lS_LpM_BXii0o,3533
70
70
  operate/quickstart/reset_configs.py,sha256=ipPpbYyB9gQ4KOKS-xBrRi8fT5LvwctSkQi-8XiUMig,3341
71
71
  operate/quickstart/reset_password.py,sha256=78riP7zyFM2JMa1H8Dh6pg-PtLJLQS7PFzx9SECPACQ,2571
72
72
  operate/quickstart/reset_staking.py,sha256=SB5LZq9EctG4SYn2M6oPZ7R7ARHSFLRGzAqfKkpRcy0,5111
@@ -79,7 +79,7 @@ operate/services/__init__.py,sha256=isrThS-Ccu5Sc15JZgkN4uTAVaSg-NwUUSDeTyJEqLk,
79
79
  operate/services/agent_runner.py,sha256=6tJePUJmlRxlIugT2fDaCJHSrQlDnl1t9pbg3-7EmCQ,7560
80
80
  operate/services/deployment_runner.py,sha256=Su73o7cdH6fkQfj468K77J04a_TWiokJwbMyVQ25xko,27067
81
81
  operate/services/health_checker.py,sha256=2KSEDxG3YmGolUDU--648ny0UJpTAAKvxkcr_VZQv-I,9654
82
- operate/services/manage.py,sha256=AoJsa4vB3C9cyMLgKBQxsUBv9okyDqxtLjCpsG3UzdQ,115769
82
+ operate/services/manage.py,sha256=cmZ2pb3P1pHZrOdLAYkM0Azxv81dZdNQwnanM5ag7Zo,118090
83
83
  operate/services/protocol.py,sha256=FoJmHz5nukI01-VYtReMsIl3JT5RgZZSLX-mf9EXQ90,62903
84
84
  operate/services/service.py,sha256=ZbkTryuK3kBkdvhxd9QDyXRxRFsGGG9WZqbLGOUPloc,39301
85
85
  operate/services/utils/__init__.py,sha256=TvioaZ1mfTRUSCtrQoLNAp4WMVXyqEJqFJM4PxSQCRU,24
@@ -91,8 +91,8 @@ operate/utils/ssl.py,sha256=O5DrDoZD4T4qQuHP8GLwWUVxQ-1qXeefGp6uDJiF2lM,4308
91
91
  operate/wallet/__init__.py,sha256=NGiozD3XhvkBi7_FaOWQ8x1thZPK4uGpokJaeDY_o2w,813
92
92
  operate/wallet/master.py,sha256=jLFLcRgO6ADok2DigpK59PFJ4mnc_0H9a4RRioYzlvw,30811
93
93
  operate/wallet/wallet_recovery_manager.py,sha256=sXEZyvFMePxQKf9NJg4HT90mPg4-7ZcTbvggMnKKzhA,7795
94
- olas_operate_middleware-0.10.8.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
95
- olas_operate_middleware-0.10.8.dist-info/METADATA,sha256=1kSqMoV3eXtdFxGckk49yl2kpcubr6PAub2RJ2nZipQ,2035
96
- olas_operate_middleware-0.10.8.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
97
- olas_operate_middleware-0.10.8.dist-info/entry_points.txt,sha256=dM1g2I7ODApKQFcgl5J4NGA7pfBTo6qsUTXM-j2OLlw,44
98
- olas_operate_middleware-0.10.8.dist-info/RECORD,,
94
+ olas_operate_middleware-0.10.9.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
95
+ olas_operate_middleware-0.10.9.dist-info/METADATA,sha256=ljzrFEYIuZlVQMFuMZtspZeau_-1Yp0SvIeyPJiONP8,2035
96
+ olas_operate_middleware-0.10.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
97
+ olas_operate_middleware-0.10.9.dist-info/entry_points.txt,sha256=dM1g2I7ODApKQFcgl5J4NGA7pfBTo6qsUTXM-j2OLlw,44
98
+ olas_operate_middleware-0.10.9.dist-info/RECORD,,
operate/cli.py CHANGED
@@ -209,7 +209,7 @@ class OperateApp:
209
209
  manager = WalletRecoveryManager(
210
210
  path=self._path / WALLET_RECOVERY_DIR,
211
211
  wallet_manager=self.wallet_manager,
212
- logger=self.logger,
212
+ logger=logger,
213
213
  )
214
214
  return manager
215
215
 
operate/keys.py CHANGED
@@ -85,6 +85,7 @@ class KeysManager(metaclass=SingletonMeta):
85
85
  )
86
86
  )
87
87
  )
88
+ private_key = key.private_key
88
89
  # Create temporary file with delete=False to handle it manually
89
90
  with tempfile.NamedTemporaryFile(
90
91
  dir=self.path,
@@ -93,7 +94,7 @@ class KeysManager(metaclass=SingletonMeta):
93
94
  delete=False, # Handle cleanup manually
94
95
  ) as temp_file:
95
96
  temp_file_name = temp_file.name
96
- temp_file.write(key.private_key)
97
+ temp_file.write(private_key)
97
98
  temp_file.flush()
98
99
  temp_file.close() # Close the file before reading
99
100
 
@@ -104,7 +105,7 @@ class KeysManager(metaclass=SingletonMeta):
104
105
  try:
105
106
  with open(temp_file_name, "r+", encoding="utf-8") as f:
106
107
  f.seek(0)
107
- f.write("\0" * len(key.private_key))
108
+ f.write("\0" * len(private_key))
108
109
  f.flush()
109
110
  f.close()
110
111
  os.unlink(temp_file_name) # Clean up the temporary file
@@ -24,17 +24,17 @@ import os
24
24
  from operate.operate_types import Chain
25
25
 
26
26
 
27
- ARBITRUM_ONE_PUBLIC_RPC = os.environ.get(
28
- "ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc"
29
- )
30
- BASE_PUBLIC_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
31
- CELO_PUBLIC_RPC = os.environ.get("CELO_RPC", "https://forno.celo.org")
32
- ETHEREUM_PUBLIC_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
33
- GNOSIS_PUBLIC_RPC = os.environ.get("GNOSIS_RPC", "https://gnosis-rpc.publicnode.com")
34
- MODE_PUBLIC_RPC = os.environ.get("MODE_RPC", "https://mainnet.mode.network/")
35
- OPTIMISM_PUBLIC_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
36
- POLYGON_PUBLIC_RPC = os.environ.get("POLYGON_RPC", "https://polygon-rpc.com")
37
- SOLANA_PUBLIC_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
27
+ CHAINS = [
28
+ Chain.ARBITRUM_ONE,
29
+ Chain.BASE,
30
+ Chain.CELO,
31
+ Chain.ETHEREUM,
32
+ Chain.GNOSIS,
33
+ Chain.MODE,
34
+ Chain.OPTIMISM,
35
+ Chain.POLYGON,
36
+ Chain.SOLANA,
37
+ ]
38
38
 
39
39
  ARBITRUM_ONE_RPC = os.environ.get("ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc")
40
40
  BASE_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
@@ -46,17 +46,6 @@ OPTIMISM_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
46
46
  POLYGON_RPC = os.environ.get("POLYGON_RPC", "https://polygon-rpc.com")
47
47
  SOLANA_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
48
48
 
49
- PUBLIC_RPCS = {
50
- Chain.ARBITRUM_ONE: ARBITRUM_ONE_PUBLIC_RPC,
51
- Chain.BASE: BASE_PUBLIC_RPC,
52
- Chain.CELO: CELO_PUBLIC_RPC,
53
- Chain.ETHEREUM: ETHEREUM_PUBLIC_RPC,
54
- Chain.GNOSIS: GNOSIS_PUBLIC_RPC,
55
- Chain.MODE: MODE_PUBLIC_RPC,
56
- Chain.OPTIMISM: OPTIMISM_PUBLIC_RPC,
57
- Chain.POLYGON: POLYGON_PUBLIC_RPC,
58
- Chain.SOLANA: SOLANA_PUBLIC_RPC,
59
- }
60
49
 
61
50
  DEFAULT_RPCS = {
62
51
  Chain.ARBITRUM_ONE: ARBITRUM_ONE_RPC,
@@ -24,40 +24,33 @@ import typing as t
24
24
  from autonomy.chain.constants import CHAIN_PROFILES, DEFAULT_MULTISEND
25
25
 
26
26
  from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
27
+ from operate.ledger import CHAINS
27
28
  from operate.operate_types import Chain, ContractAddresses
28
29
 
29
30
 
30
31
  # TODO: Refactor, remove the usage of CONTRACTS and use CHAIN_PROFILES from Open Autonomy instead.
31
- CHAINS = [
32
- Chain.ARBITRUM_ONE,
33
- Chain.BASE,
34
- Chain.CELO,
35
- Chain.ETHEREUM,
36
- Chain.GNOSIS,
37
- Chain.MODE,
38
- Chain.OPTIMISM,
39
- Chain.POLYGON,
40
- ]
41
-
42
32
  CONTRACTS: t.Dict[Chain, ContractAddresses] = {}
43
33
  for _chain in CHAINS:
44
- profile = CHAIN_PROFILES[_chain.value]
45
- CONTRACTS[_chain] = ContractAddresses(
46
- {
47
- "service_registry": profile["service_registry"],
48
- "service_registry_token_utility": profile["service_registry_token_utility"],
49
- "service_manager": profile["service_manager_token"],
50
- "gnosis_safe_proxy_factory": profile["gnosis_safe_proxy_factory"],
51
- "gnosis_safe_same_address_multisig": profile[
52
- "gnosis_safe_same_address_multisig"
53
- ],
54
- "safe_multisig_with_recovery_module": profile[
55
- "safe_multisig_with_recovery_module"
56
- ],
57
- "recovery_module": profile["recovery_module"],
58
- "multisend": DEFAULT_MULTISEND,
59
- }
60
- )
34
+ if _chain.value in CHAIN_PROFILES:
35
+ profile = CHAIN_PROFILES[_chain.value]
36
+ CONTRACTS[_chain] = ContractAddresses(
37
+ {
38
+ "service_registry": profile["service_registry"],
39
+ "service_registry_token_utility": profile[
40
+ "service_registry_token_utility"
41
+ ],
42
+ "service_manager": profile["service_manager_token"],
43
+ "gnosis_safe_proxy_factory": profile["gnosis_safe_proxy_factory"],
44
+ "gnosis_safe_same_address_multisig": profile[
45
+ "gnosis_safe_same_address_multisig"
46
+ ],
47
+ "safe_multisig_with_recovery_module": profile[
48
+ "safe_multisig_with_recovery_module"
49
+ ],
50
+ "recovery_module": profile["recovery_module"],
51
+ "multisend": DEFAULT_MULTISEND,
52
+ }
53
+ )
61
54
 
62
55
  STAKING: t.Dict[Chain, t.Dict[str, str]] = {
63
56
  Chain.ARBITRUM_ONE: {},
@@ -25,14 +25,13 @@ import warnings
25
25
  from typing import TYPE_CHECKING, cast
26
26
 
27
27
  from operate.constants import SAFE_WEBAPP_URL
28
- from operate.ledger.profiles import get_staking_contract
29
28
  from operate.quickstart.run_service import (
30
29
  ask_password_if_needed,
31
30
  configure_local_config,
32
31
  get_service,
33
32
  load_local_config,
34
33
  )
35
- from operate.quickstart.utils import ask_yes_or_no, print_section, print_title
34
+ from operate.quickstart.utils import ask_yes_or_no, print_section
36
35
 
37
36
 
38
37
  if TYPE_CHECKING:
@@ -74,6 +73,10 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
74
73
 
75
74
  # reload manger and config after setting operate.password
76
75
  manager = operate.service_manager()
76
+ chain_config = service.chain_configs[config.principal_chain]
77
+ wallet = operate.wallet_manager.load(
78
+ ledger_type=chain_config.ledger_config.chain.ledger_type
79
+ )
77
80
  config = load_local_config(operate=operate, service_name=cast(str, service.name))
78
81
  assert ( # nosec
79
82
  config.principal_chain is not None
@@ -81,23 +84,8 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
81
84
  assert config.rpc is not None, "RPC not set in quickstart config" # nosec
82
85
  os.environ["CUSTOM_CHAIN_RPC"] = config.rpc[config.principal_chain]
83
86
 
84
- chain_config = service.chain_configs[config.principal_chain]
85
- sftxb = manager.get_eth_safe_tx_builder(
86
- ledger_config=chain_config.ledger_config,
87
- )
88
- staking_contract = get_staking_contract(
89
- chain=config.principal_chain,
90
- staking_program_id=config.staking_program_id,
91
- )
92
- if not staking_contract or not sftxb.staking_rewards_claimable(
93
- service_id=chain_config.chain_data.token,
94
- staking_contract=staking_contract,
95
- ):
96
- print("No rewards to claim. Exiting.")
97
- return
98
-
99
87
  try:
100
- tx_hash = manager.claim_on_chain_from_safe(
88
+ manager.claim_on_chain_from_safe(
101
89
  service_config_id=service.service_config_id,
102
90
  chain=config.principal_chain,
103
91
  )
@@ -109,7 +97,8 @@ def claim_staking_rewards(operate: "OperateApp", config_path: str) -> None:
109
97
  logging.error(e)
110
98
  return
111
99
 
112
- service_safe_address = chain_config.chain_data.multisig
113
- print_title(f"Claim transaction done. Hash: {tx_hash.hex()}")
114
- print(f"Claimed staking transferred to your service Safe {service_safe_address}.\n")
115
- print(f"You may connect to service Safe at {SAFE_WEBAPP_URL}{service_safe_address}")
100
+ master_safe = wallet.safes[chain_config.ledger_config.chain]
101
+ print(
102
+ f"Claimed staking rewards are transferred to your Master Safe {master_safe}.\n"
103
+ )
104
+ print(f"You may connect to the Master Safe at {SAFE_WEBAPP_URL}{master_safe}")
@@ -30,6 +30,7 @@ from concurrent.futures import ThreadPoolExecutor
30
30
  from contextlib import suppress
31
31
  from http import HTTPStatus
32
32
  from pathlib import Path
33
+ from time import time
33
34
 
34
35
  import requests
35
36
  from aea.helpers.base import IPFSHash
@@ -54,7 +55,7 @@ from operate.data.contracts.requester_activity_checker.contract import (
54
55
  )
55
56
  from operate.data.contracts.staking_token.contract import StakingTokenContract
56
57
  from operate.keys import KeysManager
57
- from operate.ledger import PUBLIC_RPCS, get_currency_denom
58
+ from operate.ledger import get_currency_denom, get_default_rpc
58
59
  from operate.ledger.profiles import (
59
60
  CONTRACTS,
60
61
  DEFAULT_MASTER_EOA_FUNDS,
@@ -701,12 +702,15 @@ class ServiceManager:
701
702
 
702
703
  env_var_to_value.update(
703
704
  {
704
- "ETHEREUM_LEDGER_RPC": PUBLIC_RPCS[Chain.ETHEREUM],
705
- "GNOSIS_LEDGER_RPC": PUBLIC_RPCS[Chain.GNOSIS],
706
- "BASE_LEDGER_RPC": PUBLIC_RPCS[Chain.BASE],
707
- "CELO_LEDGER_RPC": PUBLIC_RPCS[Chain.CELO],
708
- "OPTIMISM_LEDGER_RPC": PUBLIC_RPCS[Chain.OPTIMISM],
709
- "MODE_LEDGER_RPC": PUBLIC_RPCS[Chain.MODE],
705
+ "ARBITRUM_ONE_LEDGER_RPC": get_default_rpc(Chain.ARBITRUM_ONE),
706
+ "BASE_LEDGER_RPC": get_default_rpc(Chain.BASE),
707
+ "CELO_LEDGER_RPC": get_default_rpc(Chain.CELO),
708
+ "ETHEREUM_LEDGER_RPC": get_default_rpc(Chain.ETHEREUM),
709
+ "GNOSIS_LEDGER_RPC": get_default_rpc(Chain.GNOSIS),
710
+ "MODE_LEDGER_RPC": get_default_rpc(Chain.MODE),
711
+ "OPTIMISM_LEDGER_RPC": get_default_rpc(Chain.OPTIMISM),
712
+ "POLYGON_LEDGER_RPC": get_default_rpc(Chain.POLYGON),
713
+ "SOLANA_LEDGER_RPC": get_default_rpc(Chain.SOLANA),
710
714
  f"{chain.upper()}_LEDGER_RPC": ledger_config.rpc,
711
715
  "STAKING_CONTRACT_ADDRESS": target_staking_params.get(
712
716
  "staking_contract"
@@ -1263,6 +1267,12 @@ class ServiceManager:
1263
1267
  chain=chain,
1264
1268
  staking_program_id=current_staking_program,
1265
1269
  )
1270
+ else:
1271
+ # at least claim the rewards if we cannot unstake yet
1272
+ self.claim_on_chain_from_safe(
1273
+ service_config_id=service_config_id,
1274
+ chain=chain,
1275
+ )
1266
1276
 
1267
1277
  if self._get_on_chain_state(service=service, chain=chain) in (
1268
1278
  OnChainState.ACTIVE_REGISTRATION,
@@ -1831,7 +1841,12 @@ class ServiceManager:
1831
1841
  staking_program_id: t.Optional[str] = None,
1832
1842
  force: bool = False,
1833
1843
  ) -> None:
1834
- """Unbond service on-chain"""
1844
+ """Unstake service on-chain"""
1845
+ # Claim the rewards first so that they are moved to the Master Safe
1846
+ self.claim_on_chain_from_safe(
1847
+ service_config_id=service_config_id,
1848
+ chain=chain,
1849
+ )
1835
1850
 
1836
1851
  self.logger.info("unstake_service_on_chain_from_safe")
1837
1852
  service = self.load(service_config_id=service_config_id)
@@ -1874,38 +1889,42 @@ class ServiceManager:
1874
1889
  self,
1875
1890
  service_config_id: str,
1876
1891
  chain: str,
1877
- ) -> str:
1878
- """Claim rewards from Safe and returns transaction hash"""
1892
+ ) -> int:
1893
+ """Claim rewards from staking and returns the claimed amount"""
1879
1894
  self.logger.info("claim_on_chain_from_safe")
1880
1895
  service = self.load(service_config_id=service_config_id)
1881
1896
  chain_config = service.chain_configs[chain]
1882
1897
  ledger_config = chain_config.ledger_config
1883
- chain_data = chain_config.chain_data
1884
- staking_program_id = chain_data.user_params.staking_program_id
1898
+ staking_program_id = chain_config.chain_data.user_params.staking_program_id
1885
1899
  wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
1886
1900
  ledger_api = wallet.ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc)
1887
- print(
1888
- f"OLAS Balance on service Safe {chain_data.multisig}: "
1889
- f"{get_asset_balance(ledger_api, OLAS[Chain(chain)], chain_data.multisig)}"
1901
+ self.logger.info(
1902
+ f"OLAS Balance on service Safe {chain_config.chain_data.multisig}: "
1903
+ f"{get_asset_balance(ledger_api, OLAS[Chain(chain)], chain_config.chain_data.multisig)}"
1890
1904
  )
1891
- if (
1892
- get_staking_contract(
1893
- chain=ledger_config.chain,
1894
- staking_program_id=staking_program_id,
1895
- )
1896
- is None
1897
- ):
1905
+ staking_contract = get_staking_contract(
1906
+ chain=ledger_config.chain,
1907
+ staking_program_id=staking_program_id,
1908
+ )
1909
+ if staking_contract is None:
1898
1910
  raise RuntimeError(
1899
1911
  "No staking contract found for the current staking_program_id: "
1900
1912
  f"{staking_program_id}. Not claiming the rewards."
1901
1913
  )
1902
1914
 
1903
1915
  sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
1916
+ if not sftxb.staking_rewards_claimable(
1917
+ service_id=chain_config.chain_data.token,
1918
+ staking_contract=staking_contract,
1919
+ ):
1920
+ self.logger.info("No staking rewards claimable")
1921
+ return 0
1922
+
1904
1923
  receipt = (
1905
1924
  sftxb.new_tx()
1906
1925
  .add(
1907
1926
  sftxb.get_claiming_data(
1908
- service_id=chain_data.token,
1927
+ service_id=chain_config.chain_data.token,
1909
1928
  staking_contract=get_staking_contract(
1910
1929
  chain=ledger_config.chain,
1911
1930
  staking_program_id=staking_program_id,
@@ -1914,7 +1933,21 @@ class ServiceManager:
1914
1933
  )
1915
1934
  .settle()
1916
1935
  )
1917
- return receipt["transactionHash"]
1936
+
1937
+ # transfer claimed amount from agents safe to master safe
1938
+ # TODO: remove after staking contract directly starts sending the rewards to master safe
1939
+ amount_claimed = receipt["logs"][0]["data"]
1940
+ self.logger.info(f"Claimed amount: {amount_claimed}")
1941
+ ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
1942
+ transfer_erc20_from_safe(
1943
+ ledger_api=ledger_api,
1944
+ crypto=ethereum_crypto,
1945
+ safe=chain_config.chain_data.multisig,
1946
+ token=receipt["logs"][0]["address"],
1947
+ to=wallet.safes[Chain(chain)],
1948
+ amount=amount_claimed,
1949
+ )
1950
+ return amount_claimed
1918
1951
 
1919
1952
  def fund_service( # pylint: disable=too-many-arguments,too-many-locals
1920
1953
  self,
@@ -2251,6 +2284,7 @@ class ServiceManager:
2251
2284
  task = asyncio.current_task()
2252
2285
  task_id = id(task) if task else "Unknown task_id"
2253
2286
  with ThreadPoolExecutor() as executor:
2287
+ last_claim = 0
2254
2288
  while True:
2255
2289
  try:
2256
2290
  await loop.run_in_executor(
@@ -2282,6 +2316,22 @@ class ServiceManager:
2282
2316
  logging.info(
2283
2317
  f"Error occured while funding the service\n{traceback.format_exc()}"
2284
2318
  )
2319
+
2320
+ # try claiming rewards every hour
2321
+ if last_claim + 3600 < time():
2322
+ try:
2323
+ await loop.run_in_executor(
2324
+ executor,
2325
+ self.claim_on_chain_from_safe,
2326
+ service_config_id=service_config_id,
2327
+ chain=service.home_chain,
2328
+ )
2329
+ except Exception: # pylint: disable=broad-except
2330
+ logging.info(
2331
+ f"Error occured while claiming rewards\n{traceback.format_exc()}"
2332
+ )
2333
+ last_claim = time()
2334
+
2285
2335
  await asyncio.sleep(60)
2286
2336
 
2287
2337
  def deploy_service_locally(