olas-operate-middleware 0.10.7__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.
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/METADATA +1 -1
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/RECORD +20 -19
- operate/__init__.py +12 -0
- operate/cli.py +145 -23
- operate/constants.py +9 -0
- operate/keys.py +11 -4
- operate/ledger/__init__.py +36 -33
- operate/ledger/profiles.py +21 -28
- operate/migration.py +73 -14
- operate/quickstart/claim_staking_rewards.py +11 -22
- operate/quickstart/reset_password.py +3 -2
- operate/quickstart/run_service.py +7 -2
- operate/services/manage.py +104 -63
- operate/services/protocol.py +7 -9
- operate/services/service.py +40 -70
- operate/wallet/master.py +7 -4
- operate/wallet/wallet_recovery_manager.py +210 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/LICENSE +0 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.10.7.dist-info → olas_operate_middleware-0.10.9.dist-info}/entry_points.txt +0 -0
operate/migration.py
CHANGED
|
@@ -29,11 +29,10 @@ from time import time
|
|
|
29
29
|
|
|
30
30
|
from aea_cli_ipfs.ipfs_utils import IPFSTool
|
|
31
31
|
|
|
32
|
-
from operate.constants import ZERO_ADDRESS
|
|
32
|
+
from operate.constants import USER_JSON, ZERO_ADDRESS
|
|
33
33
|
from operate.operate_types import Chain, LedgerType
|
|
34
34
|
from operate.services.manage import ServiceManager
|
|
35
35
|
from operate.services.service import (
|
|
36
|
-
DEFAULT_TRADER_ENV_VARS,
|
|
37
36
|
NON_EXISTENT_MULTISIG,
|
|
38
37
|
SERVICE_CONFIG_PREFIX,
|
|
39
38
|
SERVICE_CONFIG_VERSION,
|
|
@@ -43,11 +42,67 @@ from operate.utils import create_backup
|
|
|
43
42
|
from operate.wallet.master import LEDGER_TYPE_TO_WALLET_CLASS, MasterWalletManager
|
|
44
43
|
|
|
45
44
|
|
|
45
|
+
DEFAULT_TRADER_ENV_VARS = {
|
|
46
|
+
"GNOSIS_LEDGER_RPC": {
|
|
47
|
+
"name": "Gnosis ledger RPC",
|
|
48
|
+
"description": "",
|
|
49
|
+
"value": "",
|
|
50
|
+
"provision_type": "computed",
|
|
51
|
+
},
|
|
52
|
+
"STAKING_CONTRACT_ADDRESS": {
|
|
53
|
+
"name": "Staking contract address",
|
|
54
|
+
"description": "",
|
|
55
|
+
"value": "",
|
|
56
|
+
"provision_type": "computed",
|
|
57
|
+
},
|
|
58
|
+
"MECH_MARKETPLACE_CONFIG": {
|
|
59
|
+
"name": "Mech marketplace configuration",
|
|
60
|
+
"description": "",
|
|
61
|
+
"value": "",
|
|
62
|
+
"provision_type": "computed",
|
|
63
|
+
},
|
|
64
|
+
"MECH_ACTIVITY_CHECKER_CONTRACT": {
|
|
65
|
+
"name": "Mech activity checker contract",
|
|
66
|
+
"description": "",
|
|
67
|
+
"value": "",
|
|
68
|
+
"provision_type": "computed",
|
|
69
|
+
},
|
|
70
|
+
"MECH_CONTRACT_ADDRESS": {
|
|
71
|
+
"name": "Mech contract address",
|
|
72
|
+
"description": "",
|
|
73
|
+
"value": "",
|
|
74
|
+
"provision_type": "computed",
|
|
75
|
+
},
|
|
76
|
+
"MECH_REQUEST_PRICE": {
|
|
77
|
+
"name": "Mech request price",
|
|
78
|
+
"description": "",
|
|
79
|
+
"value": "10000000000000000",
|
|
80
|
+
"provision_type": "computed",
|
|
81
|
+
},
|
|
82
|
+
"USE_MECH_MARKETPLACE": {
|
|
83
|
+
"name": "Use Mech marketplace",
|
|
84
|
+
"description": "",
|
|
85
|
+
"value": "False",
|
|
86
|
+
"provision_type": "computed",
|
|
87
|
+
},
|
|
88
|
+
"REQUESTER_STAKING_INSTANCE_ADDRESS": {
|
|
89
|
+
"name": "Requester staking instance address",
|
|
90
|
+
"description": "",
|
|
91
|
+
"value": "",
|
|
92
|
+
"provision_type": "computed",
|
|
93
|
+
},
|
|
94
|
+
"PRIORITY_MECH_ADDRESS": {
|
|
95
|
+
"name": "Priority Mech address",
|
|
96
|
+
"description": "",
|
|
97
|
+
"value": "",
|
|
98
|
+
"provision_type": "computed",
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
46
103
|
class MigrationManager:
|
|
47
104
|
"""MigrationManager"""
|
|
48
105
|
|
|
49
|
-
# TODO Backport here migration for services/config.json, etc.
|
|
50
|
-
|
|
51
106
|
def __init__(
|
|
52
107
|
self,
|
|
53
108
|
home: Path,
|
|
@@ -62,12 +117,12 @@ class MigrationManager:
|
|
|
62
117
|
"""Log directories present in `path`."""
|
|
63
118
|
directories = [f" - {str(p)}" for p in path.iterdir() if p.is_dir()]
|
|
64
119
|
directories_str = "\n".join(directories)
|
|
65
|
-
self.logger.info(f"Directories in {path}
|
|
120
|
+
self.logger.info(f"Directories in {path}:\n{directories_str}")
|
|
66
121
|
|
|
67
122
|
def migrate_user_account(self) -> None:
|
|
68
123
|
"""Migrates user.json"""
|
|
69
124
|
|
|
70
|
-
path = self._path /
|
|
125
|
+
path = self._path / USER_JSON
|
|
71
126
|
if not path.exists():
|
|
72
127
|
return
|
|
73
128
|
|
|
@@ -102,18 +157,22 @@ class MigrationManager:
|
|
|
102
157
|
|
|
103
158
|
self.logger.info("Migrating wallet configs done.")
|
|
104
159
|
|
|
105
|
-
@staticmethod
|
|
106
160
|
def _migrate_service( # pylint: disable=too-many-statements,too-many-locals
|
|
161
|
+
self,
|
|
107
162
|
path: Path,
|
|
108
163
|
) -> bool:
|
|
109
164
|
"""Migrate the JSON file format if needed."""
|
|
110
165
|
|
|
111
166
|
if not path.is_dir():
|
|
167
|
+
self.logger.warning(f"Service config path {path} is not a directory.")
|
|
112
168
|
return False
|
|
113
169
|
|
|
114
170
|
if not path.name.startswith(SERVICE_CONFIG_PREFIX) and not path.name.startswith(
|
|
115
171
|
"bafybei"
|
|
116
172
|
):
|
|
173
|
+
self.logger.warning(
|
|
174
|
+
f"Service config path {path} is not a valid service config."
|
|
175
|
+
)
|
|
117
176
|
return False
|
|
118
177
|
|
|
119
178
|
if path.name.startswith("bafybei"):
|
|
@@ -155,6 +214,10 @@ class MigrationManager:
|
|
|
155
214
|
if version == SERVICE_CONFIG_VERSION:
|
|
156
215
|
return False
|
|
157
216
|
|
|
217
|
+
self.logger.info(
|
|
218
|
+
f"Migrating service config in {path} from version {version} to {SERVICE_CONFIG_VERSION}..."
|
|
219
|
+
)
|
|
220
|
+
|
|
158
221
|
# Migration steps for older versions
|
|
159
222
|
if version == 0:
|
|
160
223
|
new_data = {
|
|
@@ -344,13 +407,9 @@ class MigrationManager:
|
|
|
344
407
|
paths = list(service_manager.path.iterdir())
|
|
345
408
|
for path in paths:
|
|
346
409
|
try:
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
self.logger.info(f"migrate_service_configs {str(path)}")
|
|
351
|
-
migrated = self._migrate_service(path)
|
|
352
|
-
if migrated:
|
|
353
|
-
self.logger.info(f"Folder {str(path)} has been migrated.")
|
|
410
|
+
migrated = self._migrate_service(path)
|
|
411
|
+
if migrated:
|
|
412
|
+
self.logger.info(f"Folder {str(path)} has been migrated.")
|
|
354
413
|
except Exception as e: # pylint: disable=broad-except
|
|
355
414
|
self.logger.error(
|
|
356
415
|
f"Failed to migrate service: {path.name}. Exception {e}: {traceback.format_exc()}"
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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}")
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
from typing import TYPE_CHECKING
|
|
22
22
|
|
|
23
23
|
from operate.account.user import UserAccount
|
|
24
|
+
from operate.constants import USER_JSON
|
|
24
25
|
from operate.operate_types import LedgerType
|
|
25
26
|
from operate.quickstart.run_service import ask_confirm_password
|
|
26
27
|
from operate.quickstart.utils import ask_or_get_from_env, print_section, print_title
|
|
@@ -36,7 +37,7 @@ def reset_password(operate: "OperateApp") -> None:
|
|
|
36
37
|
print_title("Reset your password")
|
|
37
38
|
|
|
38
39
|
# check if agent was started before
|
|
39
|
-
if not (operate._path /
|
|
40
|
+
if not (operate._path / USER_JSON).exists():
|
|
40
41
|
print("No previous agent setup found. Exiting.")
|
|
41
42
|
return
|
|
42
43
|
|
|
@@ -57,7 +58,7 @@ def reset_password(operate: "OperateApp") -> None:
|
|
|
57
58
|
print("Resetting password of user account...")
|
|
58
59
|
UserAccount.new(
|
|
59
60
|
password=old_password,
|
|
60
|
-
path=operate._path /
|
|
61
|
+
path=operate._path / USER_JSON,
|
|
61
62
|
).update(
|
|
62
63
|
old_password=old_password,
|
|
63
64
|
new_password=new_password,
|
|
@@ -34,7 +34,12 @@ from halo import Halo # type: ignore[import]
|
|
|
34
34
|
from web3.exceptions import Web3Exception
|
|
35
35
|
|
|
36
36
|
from operate.account.user import UserAccount
|
|
37
|
-
from operate.constants import
|
|
37
|
+
from operate.constants import (
|
|
38
|
+
IPFS_ADDRESS,
|
|
39
|
+
NO_STAKING_PROGRAM_ID,
|
|
40
|
+
OPERATE_HOME,
|
|
41
|
+
USER_JSON,
|
|
42
|
+
)
|
|
38
43
|
from operate.data import DATA_DIR
|
|
39
44
|
from operate.data.contracts.staking_token.contract import StakingTokenContract
|
|
40
45
|
from operate.ledger.profiles import STAKING, get_staking_contract
|
|
@@ -453,7 +458,7 @@ def ask_password_if_needed(operate: "OperateApp") -> None:
|
|
|
453
458
|
password = ask_confirm_password()
|
|
454
459
|
UserAccount.new(
|
|
455
460
|
password=password,
|
|
456
|
-
path=operate._path /
|
|
461
|
+
path=operate._path / USER_JSON,
|
|
457
462
|
)
|
|
458
463
|
else:
|
|
459
464
|
_password = None
|
operate/services/manage.py
CHANGED
|
@@ -23,7 +23,6 @@ import asyncio
|
|
|
23
23
|
import json
|
|
24
24
|
import logging
|
|
25
25
|
import os
|
|
26
|
-
import tempfile
|
|
27
26
|
import traceback
|
|
28
27
|
import typing as t
|
|
29
28
|
from collections import Counter, defaultdict
|
|
@@ -31,16 +30,24 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
31
30
|
from contextlib import suppress
|
|
32
31
|
from http import HTTPStatus
|
|
33
32
|
from pathlib import Path
|
|
33
|
+
from time import time
|
|
34
34
|
|
|
35
35
|
import requests
|
|
36
36
|
from aea.helpers.base import IPFSHash
|
|
37
|
-
from aea_ledger_ethereum import
|
|
37
|
+
from aea_ledger_ethereum import LedgerApi
|
|
38
38
|
from autonomy.chain.base import registry_contracts
|
|
39
39
|
from autonomy.chain.config import CHAIN_PROFILES, ChainType
|
|
40
40
|
from autonomy.chain.metadata import IPFS_URI_PREFIX
|
|
41
41
|
from web3 import Web3
|
|
42
42
|
|
|
43
|
-
from operate.constants import
|
|
43
|
+
from operate.constants import (
|
|
44
|
+
AGENT_LOG_DIR,
|
|
45
|
+
AGENT_LOG_ENV_VAR,
|
|
46
|
+
AGENT_PERSISTENT_STORAGE_DIR,
|
|
47
|
+
AGENT_PERSISTENT_STORAGE_ENV_VAR,
|
|
48
|
+
IPFS_ADDRESS,
|
|
49
|
+
ZERO_ADDRESS,
|
|
50
|
+
)
|
|
44
51
|
from operate.data import DATA_DIR
|
|
45
52
|
from operate.data.contracts.mech_activity.contract import MechActivityContract
|
|
46
53
|
from operate.data.contracts.requester_activity_checker.contract import (
|
|
@@ -48,7 +55,7 @@ from operate.data.contracts.requester_activity_checker.contract import (
|
|
|
48
55
|
)
|
|
49
56
|
from operate.data.contracts.staking_token.contract import StakingTokenContract
|
|
50
57
|
from operate.keys import KeysManager
|
|
51
|
-
from operate.ledger import
|
|
58
|
+
from operate.ledger import get_currency_denom, get_default_rpc
|
|
52
59
|
from operate.ledger.profiles import (
|
|
53
60
|
CONTRACTS,
|
|
54
61
|
DEFAULT_MASTER_EOA_FUNDS,
|
|
@@ -695,12 +702,15 @@ class ServiceManager:
|
|
|
695
702
|
|
|
696
703
|
env_var_to_value.update(
|
|
697
704
|
{
|
|
698
|
-
"
|
|
699
|
-
"
|
|
700
|
-
"
|
|
701
|
-
"
|
|
702
|
-
"
|
|
703
|
-
"MODE_LEDGER_RPC":
|
|
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),
|
|
704
714
|
f"{chain.upper()}_LEDGER_RPC": ledger_config.rpc,
|
|
705
715
|
"STAKING_CONTRACT_ADDRESS": target_staking_params.get(
|
|
706
716
|
"staking_contract"
|
|
@@ -730,8 +740,8 @@ class ServiceManager:
|
|
|
730
740
|
|
|
731
741
|
# Set environment variables for the service
|
|
732
742
|
for dir_name, env_var_name in (
|
|
733
|
-
(
|
|
734
|
-
(
|
|
743
|
+
(AGENT_PERSISTENT_STORAGE_DIR, AGENT_PERSISTENT_STORAGE_ENV_VAR),
|
|
744
|
+
(AGENT_LOG_DIR, AGENT_LOG_ENV_VAR),
|
|
735
745
|
):
|
|
736
746
|
dir_path = service.path / dir_name
|
|
737
747
|
dir_path.mkdir(parents=True, exist_ok=True)
|
|
@@ -1257,6 +1267,12 @@ class ServiceManager:
|
|
|
1257
1267
|
chain=chain,
|
|
1258
1268
|
staking_program_id=current_staking_program,
|
|
1259
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
|
+
)
|
|
1260
1276
|
|
|
1261
1277
|
if self._get_on_chain_state(service=service, chain=chain) in (
|
|
1262
1278
|
OnChainState.ACTIVE_REGISTRATION,
|
|
@@ -1318,18 +1334,17 @@ class ServiceManager:
|
|
|
1318
1334
|
service_config_id=service_config_id, chain=chain
|
|
1319
1335
|
)
|
|
1320
1336
|
self.logger.info("Swapping Safe owners")
|
|
1321
|
-
|
|
1322
|
-
|
|
1337
|
+
owner_crypto = self.keys_manager.get_crypto_instance(
|
|
1338
|
+
address=current_safe_owners[0]
|
|
1339
|
+
)
|
|
1340
|
+
sftxb.swap(
|
|
1341
|
+
service_id=chain_data.token,
|
|
1323
1342
|
multisig=chain_data.multisig, # TODO this can be read from the registry
|
|
1324
|
-
|
|
1325
|
-
self.keys_manager.get(
|
|
1326
|
-
key=current_safe_owners[0]
|
|
1327
|
-
).private_key # TODO allow multiple owners
|
|
1328
|
-
), # noqa: E800
|
|
1343
|
+
owner_cryptos=[owner_crypto], # TODO allow multiple owners
|
|
1329
1344
|
new_owner_address=(
|
|
1330
1345
|
safe if safe else wallet.crypto.address
|
|
1331
1346
|
), # TODO it should always be safe address
|
|
1332
|
-
)
|
|
1347
|
+
)
|
|
1333
1348
|
|
|
1334
1349
|
if withdrawal_address is not None:
|
|
1335
1350
|
ethereum_crypto = KeysManager().get_crypto_instance(
|
|
@@ -1457,26 +1472,18 @@ class ServiceManager:
|
|
|
1457
1472
|
if agent_is_service_safe_owner:
|
|
1458
1473
|
self.logger.info("(Agent) Enabling recovery module in service Safe.")
|
|
1459
1474
|
try:
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
)
|
|
1471
|
-
|
|
1472
|
-
module_address=recovery_module_address,
|
|
1473
|
-
safe_address=service_safe_address,
|
|
1474
|
-
)
|
|
1475
|
-
).settle()
|
|
1476
|
-
tmp_file.seek(0)
|
|
1477
|
-
tmp_file.write("\0" * len(private_key))
|
|
1478
|
-
tmp_file.flush()
|
|
1479
|
-
|
|
1475
|
+
crypto = self.keys_manager.get_crypto_instance(address=agent_address)
|
|
1476
|
+
EthSafeTxBuilder._new_tx( # pylint: disable=protected-access
|
|
1477
|
+
ledger_api=sftxb.ledger_api,
|
|
1478
|
+
crypto=crypto,
|
|
1479
|
+
chain_type=ChainType(chain),
|
|
1480
|
+
safe=service_safe_address,
|
|
1481
|
+
).add(
|
|
1482
|
+
sftxb.get_enable_module_data(
|
|
1483
|
+
module_address=recovery_module_address,
|
|
1484
|
+
safe_address=service_safe_address,
|
|
1485
|
+
)
|
|
1486
|
+
).settle()
|
|
1480
1487
|
self.logger.info(
|
|
1481
1488
|
"(Agent) Recovery module enabled successfully in service Safe."
|
|
1482
1489
|
)
|
|
@@ -1834,7 +1841,12 @@ class ServiceManager:
|
|
|
1834
1841
|
staking_program_id: t.Optional[str] = None,
|
|
1835
1842
|
force: bool = False,
|
|
1836
1843
|
) -> None:
|
|
1837
|
-
"""
|
|
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
|
+
)
|
|
1838
1850
|
|
|
1839
1851
|
self.logger.info("unstake_service_on_chain_from_safe")
|
|
1840
1852
|
service = self.load(service_config_id=service_config_id)
|
|
@@ -1877,38 +1889,42 @@ class ServiceManager:
|
|
|
1877
1889
|
self,
|
|
1878
1890
|
service_config_id: str,
|
|
1879
1891
|
chain: str,
|
|
1880
|
-
) ->
|
|
1881
|
-
"""Claim rewards from
|
|
1892
|
+
) -> int:
|
|
1893
|
+
"""Claim rewards from staking and returns the claimed amount"""
|
|
1882
1894
|
self.logger.info("claim_on_chain_from_safe")
|
|
1883
1895
|
service = self.load(service_config_id=service_config_id)
|
|
1884
1896
|
chain_config = service.chain_configs[chain]
|
|
1885
1897
|
ledger_config = chain_config.ledger_config
|
|
1886
|
-
|
|
1887
|
-
staking_program_id = chain_data.user_params.staking_program_id
|
|
1898
|
+
staking_program_id = chain_config.chain_data.user_params.staking_program_id
|
|
1888
1899
|
wallet = self.wallet_manager.load(ledger_config.chain.ledger_type)
|
|
1889
1900
|
ledger_api = wallet.ledger_api(chain=ledger_config.chain, rpc=ledger_config.rpc)
|
|
1890
|
-
|
|
1891
|
-
f"OLAS Balance on service Safe {chain_data.multisig}: "
|
|
1892
|
-
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)}"
|
|
1893
1904
|
)
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
is None
|
|
1900
|
-
):
|
|
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:
|
|
1901
1910
|
raise RuntimeError(
|
|
1902
1911
|
"No staking contract found for the current staking_program_id: "
|
|
1903
1912
|
f"{staking_program_id}. Not claiming the rewards."
|
|
1904
1913
|
)
|
|
1905
1914
|
|
|
1906
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
|
+
|
|
1907
1923
|
receipt = (
|
|
1908
1924
|
sftxb.new_tx()
|
|
1909
1925
|
.add(
|
|
1910
1926
|
sftxb.get_claiming_data(
|
|
1911
|
-
service_id=chain_data.token,
|
|
1927
|
+
service_id=chain_config.chain_data.token,
|
|
1912
1928
|
staking_contract=get_staking_contract(
|
|
1913
1929
|
chain=ledger_config.chain,
|
|
1914
1930
|
staking_program_id=staking_program_id,
|
|
@@ -1917,7 +1933,21 @@ class ServiceManager:
|
|
|
1917
1933
|
)
|
|
1918
1934
|
.settle()
|
|
1919
1935
|
)
|
|
1920
|
-
|
|
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
|
|
1921
1951
|
|
|
1922
1952
|
def fund_service( # pylint: disable=too-many-arguments,too-many-locals
|
|
1923
1953
|
self,
|
|
@@ -2254,6 +2284,7 @@ class ServiceManager:
|
|
|
2254
2284
|
task = asyncio.current_task()
|
|
2255
2285
|
task_id = id(task) if task else "Unknown task_id"
|
|
2256
2286
|
with ThreadPoolExecutor() as executor:
|
|
2287
|
+
last_claim = 0
|
|
2257
2288
|
while True:
|
|
2258
2289
|
try:
|
|
2259
2290
|
await loop.run_in_executor(
|
|
@@ -2285,6 +2316,22 @@ class ServiceManager:
|
|
|
2285
2316
|
logging.info(
|
|
2286
2317
|
f"Error occured while funding the service\n{traceback.format_exc()}"
|
|
2287
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
|
+
|
|
2288
2335
|
await asyncio.sleep(60)
|
|
2289
2336
|
|
|
2290
2337
|
def deploy_service_locally(
|
|
@@ -2341,12 +2388,6 @@ class ServiceManager:
|
|
|
2341
2388
|
deployment.delete()
|
|
2342
2389
|
return deployment
|
|
2343
2390
|
|
|
2344
|
-
def log_directories(self) -> None:
|
|
2345
|
-
"""Log directories."""
|
|
2346
|
-
directories = [f" - {str(p)}" for p in self.path.iterdir() if p.is_dir()]
|
|
2347
|
-
directories_str = "\n".join(directories)
|
|
2348
|
-
self.logger.info(f"Directories in {self.path}\n: {directories_str}")
|
|
2349
|
-
|
|
2350
2391
|
def update(
|
|
2351
2392
|
self,
|
|
2352
2393
|
service_config_id: str,
|
operate/services/protocol.py
CHANGED
|
@@ -34,7 +34,6 @@ from typing import Optional, Union, cast
|
|
|
34
34
|
from aea.configurations.data_types import PackageType
|
|
35
35
|
from aea.crypto.base import Crypto, LedgerApi
|
|
36
36
|
from aea.helpers.base import IPFSHash, cd
|
|
37
|
-
from aea_ledger_ethereum.ethereum import EthereumCrypto
|
|
38
37
|
from autonomy.chain.base import registry_contracts
|
|
39
38
|
from autonomy.chain.config import ChainConfigs, ChainType, ContractConfigs
|
|
40
39
|
from autonomy.chain.constants import (
|
|
@@ -722,7 +721,11 @@ class _ChainUtil:
|
|
|
722
721
|
).get("owners", [])
|
|
723
722
|
|
|
724
723
|
def swap( # pylint: disable=too-many-arguments,too-many-locals
|
|
725
|
-
self,
|
|
724
|
+
self,
|
|
725
|
+
service_id: int,
|
|
726
|
+
multisig: str,
|
|
727
|
+
owner_cryptos: t.List[Crypto],
|
|
728
|
+
new_owner_address: str,
|
|
726
729
|
) -> None:
|
|
727
730
|
"""Swap safe owner."""
|
|
728
731
|
logging.info(f"Swapping safe for service {service_id} [{multisig}]...")
|
|
@@ -736,11 +739,6 @@ class _ChainUtil:
|
|
|
736
739
|
retries=ON_CHAIN_INTERACT_RETRIES,
|
|
737
740
|
sleep=ON_CHAIN_INTERACT_SLEEP,
|
|
738
741
|
)
|
|
739
|
-
with tempfile.TemporaryDirectory() as temp_dir:
|
|
740
|
-
key_file = Path(temp_dir, "key.txt")
|
|
741
|
-
key_file.write_text(owner_key, encoding="utf-8")
|
|
742
|
-
owner_crypto = EthereumCrypto(private_key_path=str(key_file))
|
|
743
|
-
owner_cryptos: t.List[EthereumCrypto] = [owner_crypto]
|
|
744
742
|
owners = [
|
|
745
743
|
manager.ledger_api.api.to_checksum_address(owner_crypto.address)
|
|
746
744
|
for owner_crypto in owner_cryptos
|
|
@@ -797,7 +795,7 @@ class _ChainUtil:
|
|
|
797
795
|
tx = registry_contracts.gnosis_safe.get_raw_safe_transaction(
|
|
798
796
|
ledger_api=manager.ledger_api,
|
|
799
797
|
contract_address=multisig,
|
|
800
|
-
sender_address=
|
|
798
|
+
sender_address=owner_cryptos[0].address,
|
|
801
799
|
owners=tuple(owners), # type: ignore
|
|
802
800
|
to_address=tx_params["to_address"],
|
|
803
801
|
value=tx_params["ether_value"],
|
|
@@ -806,7 +804,7 @@ class _ChainUtil:
|
|
|
806
804
|
signatures_by_owner=owner_to_signature,
|
|
807
805
|
operation=SafeOperation.DELEGATE_CALL.value,
|
|
808
806
|
)
|
|
809
|
-
stx =
|
|
807
|
+
stx = owner_cryptos[0].sign_transaction(tx)
|
|
810
808
|
tx_digest = manager.ledger_api.send_signed_transaction(stx)
|
|
811
809
|
receipt = manager.ledger_api.api.eth.wait_for_transaction_receipt(tx_digest)
|
|
812
810
|
if receipt["status"] != 1:
|