olas-operate-middleware 0.12.2__py3-none-any.whl → 0.13.1__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.4
2
2
  Name: olas-operate-middleware
3
- Version: 0.12.2
3
+ Version: 0.13.1
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: David Vilela
@@ -6,7 +6,7 @@ operate/bridge/providers/lifi_provider.py,sha256=UzAeEnX9FGpnCYYml5lcICeEZeHHqNR
6
6
  operate/bridge/providers/native_bridge_provider.py,sha256=vAx0MtVPIAxIdQ5OKSUDhnGurYVkC8tKVJRFK9NkIdk,25088
7
7
  operate/bridge/providers/provider.py,sha256=KXp5CITCQ-fSOv2iFMOt8Wer1QGhBvJG0HN5Tnh5Qns,17287
8
8
  operate/bridge/providers/relay_provider.py,sha256=4D2U8jrugh2DJZeSoxLCTVSZe8xMEwdCimqFDtfwWwc,17422
9
- operate/cli.py,sha256=9IuYdcSv4fnLsvGBxwCav2A7pGXUncvT9InGzMchudM,68025
9
+ operate/cli.py,sha256=REYysEoQ_gBIGS_s3JBIQMe1ZHVy84TS6pIQmyi3H84,70116
10
10
  operate/constants.py,sha256=FxQL9MmGHD-nNr7UBPb6ItltWtrCLB2fnT6fb4XNlKg,3849
11
11
  operate/data/README.md,sha256=jGPyZTvg2LCGdllvmYxmFMkkkiXb6YWatbqIkcX3kv4,879
12
12
  operate/data/__init__.py,sha256=ttC51Yqk9c4ehpIgs1Qbe7aJvzkrbbdZ1ClaCxJYByE,864
@@ -57,32 +57,32 @@ 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=ufnH4655vj0h7WGYMjPo-nwvVhZWRHdjyw4UYL-McIk,5959
60
+ operate/keys.py,sha256=DqwEnHwGx6XfQKRS86znJ_-Q63AbIYyAx5AT2pSieUk,6139
61
61
  operate/ledger/__init__.py,sha256=G0iWcA0Rc-Um8NwpwPJWsQNUQb2tWKyDjzirxPhGc98,6517
62
- operate/ledger/profiles.py,sha256=7dxUFzjlT-VNpu9z45qeqPGyz5fDz0bM3LUK9VLcgJk,14272
63
- operate/migration.py,sha256=hjTKumZYWEW3G03Cd_bCV1JITn4JGgexR1sPbsEoAm0,19885
62
+ operate/ledger/profiles.py,sha256=ldmpzmvhic6MOvDqslbqbNnLqct-P4EGhym7vbGCvU4,14761
63
+ operate/migration.py,sha256=hdZlhhdkoPPzkOD0CFyNYAp-eqUrVu_PJnw8_PoxpWk,21015
64
64
  operate/operate_http/__init__.py,sha256=dxCIVSUos23M4R-PFZZG6k5QrOlEiK0SxhCYSFNxh7U,4711
65
65
  operate/operate_http/exceptions.py,sha256=4UFzrn-GyDD71RhkaOyFPBynL6TrrtP3eywaaU3o4fc,1339
66
- operate/operate_types.py,sha256=hGzYW47MPxOQuvDQAD_TqyIHxNReTeDYQI_uKrfJ1Jc,15570
66
+ operate/operate_types.py,sha256=lO9mOT76CHIxt9UjcMq-wd2ZMCVtPxSLOAWMWSF1QZU,15843
67
67
  operate/pearl.py,sha256=yrTpSXLu_ML3qT-uNxq3kScOyo31JyxBujiSMfMUbcg,1690
68
68
  operate/quickstart/analyse_logs.py,sha256=cAeAL2iUy0Po8Eor70tq54-Ibg-Dn8rkuaS167yjE_I,4198
69
69
  operate/quickstart/claim_staking_rewards.py,sha256=K7X1Yq0mxe3qWmFLb1Xu9-Jghhml95lS_LpM_BXii0o,3533
70
70
  operate/quickstart/reset_configs.py,sha256=DVPM4mh6Djunwq16hf8lD9-nGkkm7wVtwr2JUXr1if8,3380
71
- operate/quickstart/reset_password.py,sha256=ZlsRUiISQo4GVnIqB0-WmkvLeCW_ck2cRdEjIw5xMNI,2424
71
+ operate/quickstart/reset_password.py,sha256=jEBk2ROR1q8PkTIHlqum7E8PRQtXHwrauiy0_bik3RQ,2394
72
72
  operate/quickstart/reset_staking.py,sha256=SB5LZq9EctG4SYn2M6oPZ7R7ARHSFLRGzAqfKkpRcy0,5111
73
- operate/quickstart/run_service.py,sha256=34T4TUijyzLFQim4NEJcdq5OyqbRxxE_E1IW_vXSo2A,27760
74
- operate/quickstart/stop_service.py,sha256=CNcCucI4sqfZG0wTxxh-k14xGcbOh50UGyXqTZVQJP0,2048
73
+ operate/quickstart/run_service.py,sha256=eyMrUK5pL1uPilRHE_IfK6Uhrx1qNyslU2ubDAErpL0,28123
74
+ operate/quickstart/stop_service.py,sha256=a3-1vVyZma2UtFUPKMvVrOso1Iwpz5Rzpus9VAI4qOc,2169
75
75
  operate/quickstart/terminate_on_chain_service.py,sha256=5ENU8_mkj06i80lKUX-v1QbLU0YzKeOZDUL1e_jzySE,2914
76
76
  operate/quickstart/utils.py,sha256=jvi7IgPtJEWf7-ciZFyEh_jgNthhv3Pus4VZa_Ha_ms,9221
77
77
  operate/resource.py,sha256=MnLdoEV68vQkaoClMFEJqkuxvqBQSIySuKym6h61Pk4,5741
78
78
  operate/services/__init__.py,sha256=isrThS-Ccu5Sc15JZgkN4uTAVaSg-NwUUSDeTyJEqLk,855
79
- operate/services/agent_runner.py,sha256=IIN77vD_2lq_hCkai-3dJQ3Ae64GUOQncvv0wxb3qUE,7629
80
- operate/services/deployment_runner.py,sha256=DHtp-DLOKNCT-8WR76cnuVKVxeCd1ZPY8smCNLaz9Xc,28116
81
- operate/services/funding_manager.py,sha256=v7ypERcYhzts2ESK6Wuvy5pckwlyZQ8pd4rfB4ogcC0,38533
79
+ operate/services/agent_runner.py,sha256=IQ9DAirYZAqWIk30CdU9mO0tVMymdj5LW5O3l8Uhj2w,7433
80
+ operate/services/deployment_runner.py,sha256=bJr0u9yW8pku_46q9bUFFLnM9b9SKliDi0BzKr_gd3U,29336
81
+ operate/services/funding_manager.py,sha256=S9jYnRQe2m6QDVrkvGS11KFYkbTPrZc0zNygahukHVs,38621
82
82
  operate/services/health_checker.py,sha256=r_lMlRZu-UNfqaM-Zo5_cWjsigdqYTAty4h-ISpM7RE,9859
83
- operate/services/manage.py,sha256=ybpQ0Wqd9SESdhb3S-eqFs-Df7I4shfRcPO3pMHe0FI,112800
83
+ operate/services/manage.py,sha256=EFZn_sUGn_WMvDecT6VW7yl-N-Q3nLooWXAaldipEfg,113703
84
84
  operate/services/protocol.py,sha256=DHu3TzaDuwTtidoEp7kGlbTAWw8pPbLjbyvanuyyjJs,72285
85
- operate/services/service.py,sha256=XojE2r-GaatAl-vmAKWC3qj7YyrfYjmJ6kLCv4JbNwY,44376
85
+ operate/services/service.py,sha256=GqeS8Fm-iA8UPb4QdDWSHa5uZ-Or0G7po7qT0WfUJWI,45348
86
86
  operate/services/utils/__init__.py,sha256=TvioaZ1mfTRUSCtrQoLNAp4WMVXyqEJqFJM4PxSQCRU,24
87
87
  operate/services/utils/mech.py,sha256=W2x4dqodivNKXjWU-Brp40QhoUHsIMyNAO7-caMoR0Q,3821
88
88
  operate/services/utils/tendermint.py,sha256=3h9nDb2Z89T0RwUr_AaVjqtymQmsu3u6DAVCfL_k1U0,25591
@@ -92,10 +92,10 @@ operate/utils/gnosis.py,sha256=iyaFw3ZMlNnd1lDulhXfcYxQunPL4Zfhnk1fy20ga7g,19843
92
92
  operate/utils/single_instance.py,sha256=pmtumg0fFDWWcGzXFXQdLXSW54Zq9qBKgJTEPF6pVW8,9092
93
93
  operate/utils/ssl.py,sha256=O5DrDoZD4T4qQuHP8GLwWUVxQ-1qXeefGp6uDJiF2lM,4308
94
94
  operate/wallet/__init__.py,sha256=NGiozD3XhvkBi7_FaOWQ8x1thZPK4uGpokJaeDY_o2w,813
95
- operate/wallet/master.py,sha256=gcax6M8_KT2IkL0kGZP4lF4f2updoP5r6ix5e72ZrL4,33674
96
- operate/wallet/wallet_recovery_manager.py,sha256=ZOLq0B9irux_og8pLBzaxlRA1r6RqJfZA4j4RX0kzoU,7775
97
- olas_operate_middleware-0.12.2.dist-info/METADATA,sha256=G-t5M3ar2O1zW4YKuP_DsUSVpPXrt7OJQrN-4wb-fck,2139
98
- olas_operate_middleware-0.12.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
99
- olas_operate_middleware-0.12.2.dist-info/entry_points.txt,sha256=dM1g2I7ODApKQFcgl5J4NGA7pfBTo6qsUTXM-j2OLlw,44
100
- olas_operate_middleware-0.12.2.dist-info/licenses/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
101
- olas_operate_middleware-0.12.2.dist-info/RECORD,,
95
+ operate/wallet/master.py,sha256=Wr6TsEqG3hQBDNeubGHZ2tZvaqFCfJ-nn8mTl7yQaXs,33698
96
+ operate/wallet/wallet_recovery_manager.py,sha256=hG8qjWOqHuVaXn1puGQ3BpdUBMVx3LZtDzsPK-OeRqY,17515
97
+ olas_operate_middleware-0.13.1.dist-info/METADATA,sha256=z63DL8AN11C-qH7Xi7hmyH00NKJXnRyodjnJa_4WBw0,2139
98
+ olas_operate_middleware-0.13.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
99
+ olas_operate_middleware-0.13.1.dist-info/entry_points.txt,sha256=dM1g2I7ODApKQFcgl5J4NGA7pfBTo6qsUTXM-j2OLlw,44
100
+ olas_operate_middleware-0.13.1.dist-info/licenses/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
101
+ olas_operate_middleware-0.13.1.dist-info/RECORD,,
operate/cli.py CHANGED
@@ -159,7 +159,6 @@ class OperateApp: # pylint: disable=too-many-instance-attributes
159
159
  self._backup_operate_if_new_version()
160
160
 
161
161
  self._password: t.Optional[str] = os.environ.get("OPERATE_USER_PASSWORD")
162
- KeysManager._instances.clear() # reset singleton instance
163
162
  self._keys_manager: KeysManager = KeysManager(
164
163
  path=self._keys,
165
164
  logger=logger,
@@ -173,6 +172,7 @@ class OperateApp: # pylint: disable=too-many-instance-attributes
173
172
  )
174
173
  self._wallet_manager.setup()
175
174
  self._funding_manager = FundingManager(
175
+ keys_manager=self._keys_manager,
176
176
  wallet_manager=self._wallet_manager,
177
177
  logger=logger,
178
178
  )
@@ -284,6 +284,7 @@ class OperateApp: # pylint: disable=too-many-instance-attributes
284
284
  """Load service manager."""
285
285
  return services.manage.ServiceManager(
286
286
  path=self._services,
287
+ keys_manager=self.keys_manager,
287
288
  wallet_manager=self.wallet_manager,
288
289
  funding_manager=self.funding_manager,
289
290
  logger=logger,
@@ -302,17 +303,23 @@ class OperateApp: # pylint: disable=too-many-instance-attributes
302
303
  return UserAccount.load(self._path / USER_JSON)
303
304
  return None
304
305
 
306
+ @property
307
+ def keys_manager(self) -> KeysManager:
308
+ """Load keys manager."""
309
+ return self._keys_manager
310
+
305
311
  @property
306
312
  def wallet_manager(self) -> MasterWalletManager:
307
313
  """Load wallet manager."""
308
314
  return self._wallet_manager
309
315
 
310
316
  @property
311
- def wallet_recoverey_manager(self) -> WalletRecoveryManager:
317
+ def wallet_recovery_manager(self) -> WalletRecoveryManager:
312
318
  """Load wallet recovery manager."""
313
319
  manager = WalletRecoveryManager(
314
320
  path=self._path / WALLET_RECOVERY_DIR,
315
321
  wallet_manager=self.wallet_manager,
322
+ service_manager=self.service_manager(),
316
323
  logger=logger,
317
324
  )
318
325
  return manager
@@ -730,7 +737,7 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
730
737
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
731
738
  )
732
739
 
733
- @app.get("/api/extended/wallet")
740
+ @app.get("/api/wallet/extended")
734
741
  async def _get_wallet_safe(request: Request) -> t.List[t.Dict]:
735
742
  """Get wallets."""
736
743
  wallets = []
@@ -1512,9 +1519,9 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
1512
1519
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
1513
1520
  )
1514
1521
 
1515
- @app.post("/api/wallet/recovery/initiate")
1516
- async def _wallet_recovery_initiate(request: Request) -> JSONResponse:
1517
- """Initiate wallet recovery."""
1522
+ @app.post("/api/wallet/recovery/prepare")
1523
+ async def _wallet_recovery_prepare(request: Request) -> JSONResponse:
1524
+ """Prepare wallet recovery."""
1518
1525
  if operate.user_account is None:
1519
1526
  return ACCOUNT_NOT_FOUND_ERROR
1520
1527
 
@@ -1533,7 +1540,7 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
1533
1540
  )
1534
1541
 
1535
1542
  try:
1536
- output = operate.wallet_recoverey_manager.initiate_recovery(
1543
+ output = operate.wallet_recovery_manager.prepare_recovery(
1537
1544
  new_password=new_password
1538
1545
  )
1539
1546
  return JSONResponse(
@@ -1541,16 +1548,54 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
1541
1548
  status_code=HTTPStatus.OK,
1542
1549
  )
1543
1550
  except (ValueError, WalletRecoveryError) as e:
1544
- logger.error(f"_recovery_initiate error: {e}")
1551
+ logger.error(f"_recovery_prepare error: {e}")
1545
1552
  return JSONResponse(
1546
- content={"error": f"Failed to initiate recovery: {e}"},
1553
+ content={"error": f"Failed to prepare recovery: {e}"},
1547
1554
  status_code=HTTPStatus.BAD_REQUEST,
1548
1555
  )
1549
1556
  except Exception as e: # pylint: disable=broad-except
1550
- logger.error(f"_recovery_initiate error: {e}\n{traceback.format_exc()}")
1557
+ logger.error(f"_recovery_prepare error: {e}\n{traceback.format_exc()}")
1558
+ return JSONResponse(
1559
+ content={"error": "Failed to prepare recovery. Please check the logs."},
1560
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
1561
+ )
1562
+
1563
+ @app.get("/api/wallet/recovery/funding_requirements")
1564
+ async def _get_recovery_funding_requirements(request: Request) -> JSONResponse:
1565
+ """Get recovery funding requirements."""
1566
+
1567
+ try:
1568
+ output = operate.wallet_recovery_manager.recovery_requirements()
1569
+ return JSONResponse(
1570
+ content=output,
1571
+ status_code=HTTPStatus.OK,
1572
+ )
1573
+ except Exception as e: # pylint: disable=broad-except
1574
+ logger.error(
1575
+ f"_recovery_funding_requirements error: {e}\n{traceback.format_exc()}"
1576
+ )
1551
1577
  return JSONResponse(
1552
1578
  content={
1553
- "error": "Failed to initiate recovery. Please check the logs."
1579
+ "error": "Failed to retrieve recovery funding requirements. Please check the logs."
1580
+ },
1581
+ status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
1582
+ )
1583
+
1584
+ @app.get("/api/wallet/recovery/status")
1585
+ async def _get_recovery_status(request: Request) -> JSONResponse:
1586
+ """Get recovery status."""
1587
+
1588
+ try:
1589
+ output = operate.wallet_recovery_manager.status()
1590
+ return JSONResponse(
1591
+ content=output,
1592
+ status_code=HTTPStatus.OK,
1593
+ )
1594
+ except Exception as e: # pylint: disable=broad-except
1595
+ logger.error(f"_recovery_status error: {e}\n{traceback.format_exc()}")
1596
+ return JSONResponse(
1597
+ content={
1598
+ "error": "Failed to retrieve recovery status. Please check the logs."
1554
1599
  },
1555
1600
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
1556
1601
  )
@@ -1564,15 +1609,16 @@ def create_app( # pylint: disable=too-many-locals, unused-argument, too-many-st
1564
1609
  if operate.password:
1565
1610
  return USER_LOGGED_IN_ERROR
1566
1611
 
1567
- data = await request.json()
1568
- bundle_id = data.get("id")
1569
- password = data.get("password")
1612
+ data = {}
1613
+ if request.headers.get("content-type", "").startswith("application/json"):
1614
+ body = await request.body()
1615
+ if body:
1616
+ data = await request.json()
1617
+
1570
1618
  raise_if_inconsistent_owners = data.get("require_consistent_owners", True)
1571
1619
 
1572
1620
  try:
1573
- operate.wallet_recoverey_manager.complete_recovery(
1574
- bundle_id=bundle_id,
1575
- password=password,
1621
+ operate.wallet_recovery_manager.complete_recovery(
1576
1622
  raise_if_inconsistent_owners=raise_if_inconsistent_owners,
1577
1623
  )
1578
1624
  return JSONResponse(
@@ -1664,6 +1710,10 @@ def qs_start(
1664
1710
  bool,
1665
1711
  params.Boolean(help="Will skip the dependencies check for minting the service"),
1666
1712
  ] = False,
1713
+ use_binary: Annotated[
1714
+ bool,
1715
+ params.Boolean(help="Will use the released binary to run the service"),
1716
+ ] = False,
1667
1717
  ) -> None:
1668
1718
  """Quickstart."""
1669
1719
  os.environ["ATTENDED"] = attended.lower()
@@ -1674,12 +1724,17 @@ def qs_start(
1674
1724
  config_path=config,
1675
1725
  build_only=build_only,
1676
1726
  skip_dependency_check=skip_dependency_check,
1727
+ use_binary=use_binary,
1677
1728
  )
1678
1729
 
1679
1730
 
1680
1731
  @_operate.command(name="quickstop")
1681
1732
  def qs_stop(
1682
1733
  config: Annotated[str, params.String(help="Quickstart config file path")],
1734
+ use_binary: Annotated[
1735
+ bool,
1736
+ params.Boolean(help="Will use the released binary to run the service"),
1737
+ ] = False,
1683
1738
  attended: Annotated[
1684
1739
  str, params.String(help="Run in attended/unattended mode (default: true")
1685
1740
  ] = "true",
@@ -1688,7 +1743,7 @@ def qs_stop(
1688
1743
  os.environ["ATTENDED"] = attended.lower()
1689
1744
  operate = OperateApp()
1690
1745
  operate.setup()
1691
- stop_service(operate=operate, config_path=config)
1746
+ stop_service(operate=operate, config_path=config, use_binary=use_binary)
1692
1747
 
1693
1748
 
1694
1749
  @_operate.command(name="terminate")
operate/keys.py CHANGED
@@ -31,7 +31,7 @@ from aea_ledger_ethereum.ethereum import EthereumCrypto
31
31
 
32
32
  from operate.operate_types import LedgerType
33
33
  from operate.resource import LocalResource
34
- from operate.utils import SingletonMeta, unrecoverable_delete
34
+ from operate.utils import unrecoverable_delete
35
35
 
36
36
 
37
37
  @dataclass
@@ -42,7 +42,7 @@ class Key(LocalResource):
42
42
  address: str
43
43
  private_key: str
44
44
 
45
- def get_decrypted(self, password: str) -> dict:
45
+ def get_decrypted_json(self, password: str) -> dict:
46
46
  """Get decrypted key json."""
47
47
  return {
48
48
  "ledger": self.ledger.value,
@@ -57,7 +57,7 @@ class Key(LocalResource):
57
57
  return super().load(path) # type: ignore
58
58
 
59
59
 
60
- class KeysManager(metaclass=SingletonMeta):
60
+ class KeysManager:
61
61
  """Keys manager."""
62
62
 
63
63
  def __init__(self, **kwargs: Any) -> None:
@@ -113,6 +113,12 @@ class KeysManager(metaclass=SingletonMeta):
113
113
  )
114
114
  )
115
115
 
116
+ def get_decrypted(self, key: str) -> dict:
117
+ """Get key json."""
118
+ if self.password is not None:
119
+ return self.get(key).get_decrypted_json(self.password)
120
+ return self.get(key).json
121
+
116
122
  def get_private_key_file(self, address: str) -> Path:
117
123
  """Get the path to the private key file for the given address."""
118
124
  path = self.path / f"{address}_private_key"
@@ -246,6 +246,17 @@ DEFAULT_EOA_TOPUPS_WITHOUT_SAFE = {
246
246
  for chain, amounts in DEFAULT_EOA_TOPUPS.items()
247
247
  }
248
248
 
249
+ DEFAULT_RECOVERY_TOPUPS = {
250
+ Chain.ARBITRUM_ONE: {ZERO_ADDRESS: 625_000_000_000_000},
251
+ Chain.BASE: {ZERO_ADDRESS: 625_000_000_000_000},
252
+ Chain.CELO: {ZERO_ADDRESS: 187_500_000_000_000_000},
253
+ Chain.ETHEREUM: {ZERO_ADDRESS: 2_500_000_000_000_000},
254
+ Chain.GNOSIS: {ZERO_ADDRESS: 187_500_000_000_000_000},
255
+ Chain.MODE: {ZERO_ADDRESS: 62_500_000_000_000},
256
+ Chain.OPTIMISM: {ZERO_ADDRESS: 625_000_000_000_000},
257
+ Chain.POLYGON: {ZERO_ADDRESS: 187_500_000_000_000_000},
258
+ }
259
+
249
260
  DEFAULT_EOA_THRESHOLD = 0.5
250
261
 
251
262
  EXPLORER_URL = {
operate/migration.py CHANGED
@@ -33,7 +33,9 @@ from web3 import Web3
33
33
 
34
34
  from operate.constants import USER_JSON, ZERO_ADDRESS
35
35
  from operate.keys import KeysManager
36
- from operate.operate_types import Chain, LedgerType
36
+ from operate.operate_types import AgentRelease as AgentReleaseType
37
+ from operate.operate_types import AgentReleaseRepo, Chain, LedgerType
38
+ from operate.services.agent_runner import AgentRelease
37
39
  from operate.services.manage import ServiceManager
38
40
  from operate.services.service import (
39
41
  NON_EXISTENT_MULTISIG,
@@ -160,7 +162,7 @@ class MigrationManager:
160
162
 
161
163
  self.logger.info("Migrating wallet configs done.")
162
164
 
163
- def _migrate_service( # pylint: disable=too-many-statements,too-many-locals
165
+ def _migrate_service( # pylint: disable=too-many-statements,too-many-locals,too-many-branches
164
166
  self,
165
167
  path: Path,
166
168
  ) -> bool:
@@ -250,12 +252,6 @@ class MigrationManager:
250
252
  "nft": data.get("chain_data", {})
251
253
  .get("user_params", {})
252
254
  .get("nft"),
253
- "threshold": data.get("chain_data", {})
254
- .get("user_params", {})
255
- .get("threshold"),
256
- "use_staking": data.get("chain_data", {})
257
- .get("user_params", {})
258
- .get("use_staking"),
259
255
  "cost_of_bond": data.get("chain_data", {})
260
256
  .get("user_params", {})
261
257
  .get("cost_of_bond"),
@@ -277,9 +273,6 @@ class MigrationManager:
277
273
  if version < 4:
278
274
  # Add missing fields introduced in later versions, if necessary.
279
275
  for _, chain_data in data.get("chain_configs", {}).items():
280
- chain_data.setdefault("chain_data", {}).setdefault(
281
- "user_params", {}
282
- ).setdefault("use_mech_marketplace", False)
283
276
  service_name = data.get("name", "")
284
277
  agent_id = Service.determine_agent_id(service_name)
285
278
  chain_data.setdefault("chain_data", {}).setdefault("user_params", {})[
@@ -342,6 +335,12 @@ class MigrationManager:
342
335
  new_chain_configs[chain] = chain_data # type: ignore
343
336
  data["chain_configs"] = new_chain_configs
344
337
 
338
+ if version < 6 and "service_path" in data:
339
+ # Redownload service path
340
+ package_absolute_path = path / Path(data["service_path"]).name
341
+ data.pop("service_path")
342
+ data["package_path"] = str(package_absolute_path.name)
343
+
345
344
  if version < 7:
346
345
  for _, chain_data in data.get("chain_configs", {}).items():
347
346
  if chain_data["chain_data"]["multisig"] == "0xm":
@@ -371,6 +370,39 @@ class MigrationManager:
371
370
  if chain_config["ledger_config"]["chain"] == "optimistic":
372
371
  chain_config["ledger_config"]["chain"] = Chain.OPTIMISM.value
373
372
 
373
+ if version < 9:
374
+ agents_supported = {
375
+ "trader_pearl": AgentRelease(
376
+ is_aea=True, owner="valory-xyz", repo="trader", release="v0.0.101"
377
+ ),
378
+ "optimus": AgentRelease(
379
+ is_aea=True, owner="valory-xyz", repo="optimus", release="v0.0.103"
380
+ ),
381
+ "memeooorr": AgentRelease(
382
+ is_aea=True,
383
+ owner="valory-xyz",
384
+ repo="meme-ooorr",
385
+ release="v0.0.101",
386
+ ),
387
+ }
388
+ package_path = data["package_path"]
389
+ try:
390
+ release_data = agents_supported[package_path]
391
+ except KeyError as e:
392
+ raise RuntimeError(f"Found unsupported {package_path=}") from e
393
+
394
+ data["agent_release"] = AgentReleaseType(
395
+ is_aea=release_data.is_aea,
396
+ repository=AgentReleaseRepo(
397
+ owner=release_data.owner,
398
+ name=release_data.repo,
399
+ version=release_data.release,
400
+ ),
401
+ )
402
+
403
+ if data["name"] is None:
404
+ data["name"] = release_data.repo
405
+
374
406
  data["version"] = SERVICE_CONFIG_VERSION
375
407
 
376
408
  # Redownload service path
operate/operate_types.py CHANGED
@@ -223,6 +223,21 @@ class EnvVariableAttributes(TypedDict):
223
223
  provision_type: ServiceEnvProvisionType
224
224
 
225
225
 
226
+ class AgentReleaseRepo(TypedDict):
227
+ """Agent release repo template."""
228
+
229
+ owner: str
230
+ name: str
231
+ version: str
232
+
233
+
234
+ class AgentRelease(TypedDict):
235
+ """Agent release template."""
236
+
237
+ is_aea: bool
238
+ repository: AgentReleaseRepo
239
+
240
+
226
241
  ConfigurationTemplates = t.Dict[str, ConfigurationTemplate]
227
242
  EnvVariables = t.Dict[str, EnvVariableAttributes]
228
243
 
@@ -235,6 +250,7 @@ class ServiceTemplate(TypedDict, total=False):
235
250
  image: str
236
251
  description: str
237
252
  service_version: str
253
+ agent_release: AgentRelease
238
254
  home_chain: str
239
255
  configurations: ConfigurationTemplates
240
256
  env_variables: EnvVariables
@@ -22,7 +22,6 @@ from typing import TYPE_CHECKING
22
22
 
23
23
  from operate.account.user import UserAccount
24
24
  from operate.constants import USER_JSON
25
- from operate.keys import KeysManager
26
25
  from operate.quickstart.run_service import ask_confirm_password
27
26
  from operate.quickstart.utils import ask_or_get_from_env, print_section, print_title
28
27
 
@@ -66,6 +65,6 @@ def reset_password(operate: "OperateApp") -> None:
66
65
  print('Resetting password of "ethereum" wallet...')
67
66
  operate.password = old_password
68
67
  operate.wallet_manager.update_password(new_password=new_password)
69
- KeysManager().update_password(new_password=new_password)
68
+ operate.keys_manager.update_password(new_password=new_password)
70
69
 
71
70
  print_section("Password reset done!")
@@ -485,7 +485,11 @@ def get_service(manager: ServiceManager, template: ServiceTemplate) -> Service:
485
485
  for service in manager.json:
486
486
  if service["name"] == template["name"]:
487
487
  old_hash = service["hash"]
488
- if old_hash == template["hash"]:
488
+ old_version = service["agent_release"]["repository"]["version"]
489
+ if (
490
+ old_hash == template["hash"]
491
+ and old_version == template["agent_release"]["repository"]["version"]
492
+ ):
489
493
  print(f'Loading service {template["hash"]}')
490
494
  service = manager.load(
491
495
  service_config_id=service["service_config_id"],
@@ -668,6 +672,7 @@ def run_service(
668
672
  config_path: str,
669
673
  build_only: bool = False,
670
674
  skip_dependency_check: bool = False,
675
+ use_binary: bool = False,
671
676
  ) -> None:
672
677
  """Run service."""
673
678
 
@@ -702,10 +707,17 @@ def run_service(
702
707
  manager.funding_manager.topup_service_initial(service=service)
703
708
 
704
709
  print_section("Deploying the service")
710
+ if use_binary:
711
+ use_docker = False
712
+ use_k8s = False
713
+ else:
714
+ use_docker = True
715
+ use_k8s = True
716
+
705
717
  manager.deploy_service_locally(
706
718
  service_config_id=service.service_config_id,
707
- use_docker=True,
708
- use_kubernetes=True,
719
+ use_docker=use_docker,
720
+ use_kubernetes=use_k8s,
709
721
  build_only=build_only,
710
722
  )
711
723
  if build_only:
@@ -37,7 +37,9 @@ if TYPE_CHECKING:
37
37
  warnings.filterwarnings("ignore", category=UserWarning)
38
38
 
39
39
 
40
- def stop_service(operate: "OperateApp", config_path: str) -> None:
40
+ def stop_service(
41
+ operate: "OperateApp", config_path: str, use_binary: bool = False
42
+ ) -> None:
41
43
  """Stop service."""
42
44
 
43
45
  with open(config_path, "r") as config_file:
@@ -57,8 +59,13 @@ def stop_service(operate: "OperateApp", config_path: str) -> None:
57
59
  configure_local_config(template, operate)
58
60
  manager = operate.service_manager()
59
61
  service = get_service(manager, template)
62
+ if use_binary:
63
+ use_docker = False
64
+ else:
65
+ use_docker = True
66
+
60
67
  manager.stop_service_locally(
61
- service_config_id=service.service_config_id, use_docker=True, force=True
68
+ service_config_id=service.service_config_id, use_docker=use_docker, force=True
62
69
  )
63
70
 
64
71
  print()
@@ -19,6 +19,7 @@
19
19
  # -------------------------------------------------------------
20
20
  """Source dode to download and run agent from the repos."""
21
21
  import hashlib
22
+ import json
22
23
  import os
23
24
  import platform
24
25
  import shutil
@@ -32,7 +33,7 @@ import requests
32
33
  from aea.configurations.data_types import PublicId
33
34
  from aea.helpers.logging import setup_logger
34
35
 
35
- from operate.constants import AGENT_RUNNER_PREFIX
36
+ from operate.constants import AGENT_RUNNER_PREFIX, CONFIG_JSON
36
37
 
37
38
 
38
39
  @dataclass
@@ -42,6 +43,7 @@ class AgentRelease:
42
43
  owner: str
43
44
  repo: str
44
45
  release: str
46
+ is_aea: bool
45
47
 
46
48
  @property
47
49
  def release_url(self) -> str:
@@ -64,25 +66,10 @@ class AgentRelease:
64
66
  return file_url, file_hash
65
67
 
66
68
 
67
- # list of agents releases supported
68
- AGENTS_SUPPORTED = {
69
- "valory/trader": AgentRelease(
70
- owner="valory-xyz", repo="trader", release="v0.27.2-1-rc.2"
71
- ),
72
- "valory/optimus": AgentRelease(
73
- owner="valory-xyz", repo="optimus", release="v0.6.0-rc.1"
74
- ),
75
- "dvilela/memeooorr": AgentRelease(
76
- owner="valory-xyz", repo="meme-ooorr", release="v0.0.101"
77
- ),
78
- }
79
-
80
-
81
69
  class AgentRunnerManager:
82
70
  """Agent Runner Manager."""
83
71
 
84
72
  logger = setup_logger(name="operate.agent_runner_manager")
85
- AGENTS = AGENTS_SUPPORTED
86
73
 
87
74
  @staticmethod
88
75
  def get_agent_runner_executable_name() -> str:
@@ -133,14 +120,19 @@ class AgentRunnerManager:
133
120
  raise
134
121
 
135
122
  @classmethod
136
- def get_agent_release_by_public_id(cls, agent_public_id_str: str) -> AgentRelease:
123
+ def get_agent_release_from_service_dir(cls, service_dir: Path) -> AgentRelease:
137
124
  """Get agent release object according to public id."""
138
- agent_author, agent_name = cls.parse_agent(public_id_str=agent_public_id_str)
139
-
140
- agent_name = f"{agent_author}/{agent_name}"
141
- agent_release = cls.AGENTS.get(agent_name, None)
142
- if agent_release is None:
143
- raise ValueError(f"{agent_name} is not supported!")
125
+ service_config_file = service_dir / CONFIG_JSON
126
+ service_config = json.loads(service_config_file.read_text())
127
+ if "agent_release" not in service_config:
128
+ raise ValueError(f"Agent release details are not found in {service_config}")
129
+ agent_release_data = service_config["agent_release"]
130
+ agent_release = AgentRelease(
131
+ is_aea=agent_release_data["is_aea"],
132
+ owner=agent_release_data["repository"]["owner"],
133
+ repo=agent_release_data["repository"]["name"],
134
+ release=agent_release_data["repository"]["version"],
135
+ )
144
136
  return agent_release
145
137
 
146
138
  @staticmethod
@@ -189,13 +181,11 @@ class AgentRunnerManager:
189
181
  raise
190
182
 
191
183
  @classmethod
192
- def get_agent_runner_path(cls, service_dir: Path, agent_public_id_str: str) -> str:
184
+ def get_agent_runner_path(cls, service_dir: Path) -> str:
193
185
  """Get path to the agent runner bin palced."""
194
186
  agent_runner_name = cls.get_agent_runner_executable_name()
195
187
  agent_runner_path: Path = service_dir / agent_runner_name
196
- agent_release = cls.get_agent_release_by_public_id(
197
- agent_public_id_str=agent_public_id_str
198
- )
188
+ agent_release = cls.get_agent_release_from_service_dir(service_dir=service_dir)
199
189
 
200
190
  cls.update_agent_runner(
201
191
  target_path=agent_runner_path,
@@ -205,8 +195,8 @@ class AgentRunnerManager:
205
195
  return str(agent_runner_path)
206
196
 
207
197
 
208
- def get_agent_runner_path(service_dir: Path, agent_public_id_str: str) -> str:
198
+ def get_agent_runner_path(service_dir: Path) -> str:
209
199
  """Get path to the agent runner bin placed."""
210
200
  return AgentRunnerManager.get_agent_runner_path(
211
- service_dir=service_dir, agent_public_id_str=agent_public_id_str
201
+ service_dir=service_dir,
212
202
  )