olas-operate-middleware 0.10.20__py3-none-any.whl → 0.11.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.
operate/constants.py CHANGED
@@ -33,20 +33,37 @@ DEPLOYMENT_JSON = "deployment.json"
33
33
  CONFIG_JSON = "config.json"
34
34
  USER_JSON = "user.json"
35
35
  HEALTHCHECK_JSON = "healthcheck.json"
36
+ VERSION_FILE = "operate.version"
37
+ SETTINGS_JSON = "settings.json"
38
+ FUNDING_REQUIREMENTS_JSON = "funding_requirements.json"
39
+ DEFAULT_TOPUP_THRESHOLD = 0.5
40
+
41
+ MASTER_EOA_PLACEHOLDER = "master_eoa"
42
+ MASTER_SAFE_PLACEHOLDER = "master_safe"
43
+ AGENT_EOA_PLACEHOLDER = "agent_eoa"
44
+ SERVICE_SAFE_PLACEHOLDER = "service_safe"
45
+
46
+ FERNET_KEY_LENGTH = 32
36
47
 
37
48
  AGENT_PERSISTENT_STORAGE_DIR = "persistent_data"
38
49
  AGENT_PERSISTENT_STORAGE_ENV_VAR = "STORE_PATH"
39
50
  AGENT_LOG_DIR = "benchmarks"
40
51
  AGENT_LOG_ENV_VAR = "LOG_DIR"
52
+ AGENT_RUNNER_PREFIX = "agent_runner"
41
53
 
42
54
  ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
43
55
 
56
+ MIN_AGENT_BOND = 1
57
+ MIN_SECURITY_DEPOSIT = 1
58
+
44
59
  ON_CHAIN_INTERACT_TIMEOUT = 120.0
45
60
  ON_CHAIN_INTERACT_RETRIES = 12
46
61
  ON_CHAIN_INTERACT_SLEEP = 5.0
47
62
  MIN_PASSWORD_LENGTH = 8
63
+ DEFAULT_FUNDING_REQUESTS_COOLDOWN_SECONDS = 300 # Seconds to wait after an agent has been funded during which it will not be asked for fund requirements again
48
64
 
49
65
  HEALTH_CHECK_URL = "http://127.0.0.1:8716/healthcheck" # possible DNS issues on windows so use IP address
66
+ AGENT_FUNDS_STATUS_URL = "http://127.0.0.1:8716/funds-status"
50
67
  SAFE_WEBAPP_URL = "https://app.safe.global/home?safe=gno:"
51
68
  TM_CONTROL_URL = "http://localhost:8080"
52
69
  IPFS_ADDRESS = "https://gateway.autonolas.tech/ipfs/f01701220{hash}"
@@ -63,3 +80,6 @@ NO_STAKING_PROGRAM_ID = "no_staking"
63
80
 
64
81
  DEPLOYMENT_START_TRIES_NUM = 3
65
82
  IPFS_CHECK_URL = "https://gateway.autonolas.tech/ipfs/bafybeigcllaxn4ycjjvika3zd6eicksuriez2wtg67gx7pamhcazl3tv54/echo/README.md"
83
+ MSG_NEW_PASSWORD_MISSING = "'new_password' is required." # nosec
84
+ MSG_INVALID_PASSWORD = "Password is not valid." # nosec
85
+ MSG_INVALID_MNEMONIC = "Seed phrase is not valid." # nosec
@@ -21,9 +21,13 @@
21
21
 
22
22
  import os
23
23
  import typing as t
24
+ from copy import deepcopy
24
25
  from math import ceil
25
26
 
26
27
  from aea.crypto.base import LedgerApi
28
+ from aea.crypto.registries import make_ledger_api
29
+ from aea_ledger_ethereum import DEFAULT_GAS_PRICE_STRATEGIES, EIP1559, GWEI, to_wei
30
+ from web3.middleware import geth_poa_middleware
27
31
 
28
32
  from operate.operate_types import Chain
29
33
 
@@ -89,15 +93,17 @@ CURRENCY_SMALLEST_UNITS = {
89
93
  Chain.SOLANA: "Lamport",
90
94
  }
91
95
 
96
+ NATIVE_CURRENCY_DECIMALS = 18
97
+
92
98
 
93
99
  def get_default_rpc(chain: Chain) -> str:
94
100
  """Get default RPC chain type."""
95
- return DEFAULT_RPCS.get(chain, ETHEREUM_RPC)
101
+ return DEFAULT_RPCS[chain]
96
102
 
97
103
 
98
104
  def get_currency_denom(chain: Chain) -> str:
99
105
  """Get currency denom by chain type."""
100
- return CURRENCY_DENOMS.get(chain, "ETH")
106
+ return CURRENCY_DENOMS.get(chain, "NATIVE")
101
107
 
102
108
 
103
109
  def get_currency_smallest_unit(chain: Chain) -> str:
@@ -105,6 +111,50 @@ def get_currency_smallest_unit(chain: Chain) -> str:
105
111
  return CURRENCY_SMALLEST_UNITS.get(chain, "Wei")
106
112
 
107
113
 
114
+ DEFAULT_LEDGER_APIS: t.Dict[Chain, LedgerApi] = {}
115
+
116
+
117
+ def make_chain_ledger_api(
118
+ chain: Chain,
119
+ rpc: t.Optional[str] = None,
120
+ ) -> LedgerApi:
121
+ """Get default RPC chain type."""
122
+
123
+ if chain not in DEFAULT_LEDGER_APIS:
124
+ if chain == Chain.SOLANA: # TODO: Complete when Solana is supported
125
+ raise NotImplementedError("Solana not yet supported.")
126
+
127
+ gas_price_strategies = deepcopy(DEFAULT_GAS_PRICE_STRATEGIES)
128
+ if chain in (Chain.BASE, Chain.MODE, Chain.OPTIMISM):
129
+ gas_price_strategies[EIP1559]["fallback_estimate"]["maxFeePerGas"] = to_wei(
130
+ 5, GWEI
131
+ )
132
+
133
+ ledger_api = make_ledger_api(
134
+ chain.ledger_type.name.lower(),
135
+ address=rpc or get_default_rpc(chain=chain),
136
+ chain_id=chain.id,
137
+ gas_price_strategies=gas_price_strategies,
138
+ poa_chain=chain == Chain.POLYGON,
139
+ )
140
+
141
+ if chain == Chain.OPTIMISM:
142
+ ledger_api.api.middleware_onion.inject(geth_poa_middleware, layer=0)
143
+
144
+ DEFAULT_LEDGER_APIS[chain] = ledger_api
145
+
146
+ return DEFAULT_LEDGER_APIS[chain]
147
+
148
+
149
+ def get_default_ledger_api(chain: Chain) -> LedgerApi:
150
+ """Get default RPC chain type."""
151
+ if chain not in DEFAULT_LEDGER_APIS:
152
+ DEFAULT_LEDGER_APIS[chain] = make_chain_ledger_api(
153
+ chain=chain, rpc=get_default_rpc(chain=chain)
154
+ )
155
+ return DEFAULT_LEDGER_APIS[chain]
156
+
157
+
108
158
  GAS_ESTIMATE_FALLBACK_ADDRESSES = [
109
159
  "0x000000000000000000000000000000000000dEaD",
110
160
  "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", # nosec
@@ -137,7 +187,7 @@ def update_tx_with_gas_pricing(tx: t.Dict, ledger_api: LedgerApi) -> None:
137
187
  # TODO This gas management should be done at a lower level in the library
138
188
  def update_tx_with_gas_estimate(tx: t.Dict, ledger_api: LedgerApi) -> None:
139
189
  """Update transaction with gas estimate."""
140
- print(f"[PROVIDER] Trying to update transaction gas {tx['from']=} {tx['gas']=}.")
190
+ print(f"[LEDGER] Trying to update transaction gas {tx['from']=} {tx['gas']=}.")
141
191
  original_from = tx["from"]
142
192
  original_gas = tx.get("gas", 1)
143
193
 
@@ -146,11 +196,11 @@ def update_tx_with_gas_estimate(tx: t.Dict, ledger_api: LedgerApi) -> None:
146
196
  tx["gas"] = 1
147
197
  ledger_api.update_with_gas_estimate(tx)
148
198
  if tx["gas"] > 1:
149
- print(f"[PROVIDER] Gas estimated successfully {tx['from']=} {tx['gas']=}.")
199
+ print(f"[LEDGER] Gas estimated successfully {tx['from']=} {tx['gas']=}.")
150
200
  break
151
201
 
152
202
  tx["from"] = original_from
153
203
  if tx["gas"] == 1:
154
204
  tx["gas"] = original_gas
155
- print(f"[PROVIDER] Unable to estimate gas. Restored {tx['gas']=}.")
205
+ print(f"[LEDGER] Unable to estimate gas. Restored {tx['gas']=}.")
156
206
  tx["gas"] = ceil(tx["gas"] * GAS_ESTIMATE_BUFFER)
@@ -20,11 +20,18 @@
20
20
  """Chain profiles."""
21
21
 
22
22
  import typing as t
23
+ from functools import cache
23
24
 
25
+ from autonomy.chain.base import registry_contracts
24
26
  from autonomy.chain.constants import CHAIN_PROFILES, DEFAULT_MULTISEND
25
27
 
26
28
  from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
27
- from operate.ledger import CHAINS
29
+ from operate.ledger import (
30
+ CHAINS,
31
+ NATIVE_CURRENCY_DECIMALS,
32
+ get_currency_denom,
33
+ get_default_ledger_api,
34
+ )
28
35
  from operate.operate_types import Chain, ContractAddresses
29
36
 
30
37
 
@@ -177,7 +184,22 @@ WRAPPED_NATIVE_ASSET = {
177
184
  Chain.POLYGON: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
178
185
  }
179
186
 
180
- ERC20_TOKENS = [OLAS, USDC, WRAPPED_NATIVE_ASSET]
187
+ ERC20_TOKENS = {
188
+ "OLAS": OLAS,
189
+ "USDC": USDC,
190
+ "WRAPPED_NATIVE": WRAPPED_NATIVE_ASSET,
191
+ }
192
+
193
+ DUST = {
194
+ Chain.ARBITRUM_ONE: int(1e14),
195
+ Chain.BASE: int(1e14),
196
+ Chain.CELO: int(1e15),
197
+ Chain.ETHEREUM: int(1e14),
198
+ Chain.GNOSIS: int(1e15),
199
+ Chain.MODE: int(1e14),
200
+ Chain.OPTIMISM: int(1e14),
201
+ Chain.POLYGON: int(1e14),
202
+ }
181
203
 
182
204
  DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
183
205
  Chain.ARBITRUM_ONE: {
@@ -206,17 +228,24 @@ DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
206
228
  },
207
229
  }
208
230
 
209
- DEFAULT_MASTER_EOA_FUNDS = {
210
- Chain.ARBITRUM_ONE: {ZERO_ADDRESS: 5_000_000_000_000_000},
211
- Chain.BASE: {ZERO_ADDRESS: 5_000_000_000_000_000},
212
- Chain.CELO: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
213
- Chain.ETHEREUM: {ZERO_ADDRESS: 20_000_000_000_000_000},
214
- Chain.GNOSIS: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
215
- Chain.MODE: {ZERO_ADDRESS: 500_000_000_000_000},
216
- Chain.OPTIMISM: {ZERO_ADDRESS: 5_000_000_000_000_000},
217
- Chain.POLYGON: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
231
+ DEFAULT_EOA_TOPUPS = {
232
+ Chain.ARBITRUM_ONE: {ZERO_ADDRESS: 2_500_000_000_000_000},
233
+ Chain.BASE: {ZERO_ADDRESS: 2_500_000_000_000_000},
234
+ Chain.CELO: {ZERO_ADDRESS: 750_000_000_000_000_000},
235
+ Chain.ETHEREUM: {ZERO_ADDRESS: 10_000_000_000_000_000},
236
+ Chain.GNOSIS: {ZERO_ADDRESS: 750_000_000_000_000_000},
237
+ Chain.MODE: {ZERO_ADDRESS: 250_000_000_000_000},
238
+ Chain.OPTIMISM: {ZERO_ADDRESS: 2_500_000_000_000_000},
239
+ Chain.POLYGON: {ZERO_ADDRESS: 750_000_000_000_000_000},
240
+ }
241
+
242
+ DEFAULT_EOA_TOPUPS_WITHOUT_SAFE = {
243
+ chain: {asset: amount * 2 for asset, amount in amounts.items()}
244
+ for chain, amounts in DEFAULT_EOA_TOPUPS.items()
218
245
  }
219
246
 
247
+ DEFAULT_EOA_THRESHOLD = 0.5
248
+
220
249
  EXPLORER_URL = {
221
250
  Chain.ARBITRUM_ONE: {
222
251
  "tx": "https://arbiscan.io/tx/{tx_hash}",
@@ -253,6 +282,45 @@ EXPLORER_URL = {
253
282
  }
254
283
 
255
284
 
285
+ @cache
286
+ def get_asset_name(chain: Chain, asset_address: str) -> str:
287
+ """Get token name."""
288
+ if asset_address == ZERO_ADDRESS:
289
+ return get_currency_denom(chain)
290
+
291
+ if WRAPPED_NATIVE_ASSET.get(chain) == asset_address:
292
+ return f"W{get_currency_denom(chain)}"
293
+
294
+ for symbol, tokens in ERC20_TOKENS.items():
295
+ if tokens.get(chain) == asset_address:
296
+ return symbol
297
+
298
+ return asset_address
299
+
300
+
301
+ @cache
302
+ def get_asset_decimals(chain: Chain, asset_address: str) -> int:
303
+ """Get token decimals."""
304
+ if asset_address == ZERO_ADDRESS:
305
+ return NATIVE_CURRENCY_DECIMALS
306
+ erc20_token = registry_contracts.erc20.get_instance(
307
+ ledger_api=get_default_ledger_api(chain),
308
+ contract_address=asset_address,
309
+ )
310
+ return erc20_token.functions.decimals().call()
311
+
312
+
313
+ def format_asset_amount(
314
+ chain: Chain, asset_address: str, amount: int, precision: int = 4
315
+ ) -> str:
316
+ """Convert smallest unit to human-readable string with token symbol."""
317
+ decimals = get_asset_decimals(chain, asset_address)
318
+ symbol = get_asset_name(chain, asset_address)
319
+ value = amount / (10**decimals)
320
+ return f"{value:.{precision}f} {symbol}"
321
+
322
+
323
+ # TODO: Deprecate in favour of StakingManager method
256
324
  def get_staking_contract(
257
325
  chain: str, staking_program_id: t.Optional[str]
258
326
  ) -> t.Optional[str]:
operate/operate_types.py CHANGED
@@ -19,16 +19,22 @@
19
19
 
20
20
  """Types module."""
21
21
 
22
+ import base64
23
+ import copy
22
24
  import enum
23
25
  import os
24
26
  import typing as t
25
- from dataclasses import dataclass
27
+ from dataclasses import dataclass, field
28
+ from pathlib import Path
26
29
 
30
+ import argon2
31
+ from aea_ledger_ethereum import cast
27
32
  from autonomy.chain.config import ChainType
28
33
  from autonomy.chain.constants import CHAIN_NAME_TO_CHAIN_ID
34
+ from cryptography.fernet import Fernet
29
35
  from typing_extensions import TypedDict
30
36
 
31
- from operate.constants import NO_STAKING_PROGRAM_ID
37
+ from operate.constants import FERNET_KEY_LENGTH, NO_STAKING_PROGRAM_ID
32
38
  from operate.resource import LocalResource
33
39
 
34
40
 
@@ -55,6 +61,11 @@ class LedgerType(str, enum.Enum):
55
61
  """Key filename."""
56
62
  return f"{self.name.lower()}.txt"
57
63
 
64
+ @property
65
+ def mnemonic_file(self) -> str:
66
+ """Mnemonic filename."""
67
+ return f"{self.name.lower()}.mnemonic.json"
68
+
58
69
  @classmethod
59
70
  def from_id(cls, chain_id: int) -> "LedgerType":
60
71
  """Load from chain ID."""
@@ -323,3 +334,195 @@ class MechMarketplaceConfig:
323
334
  mech_marketplace_address: str
324
335
  priority_mech_address: str
325
336
  priority_mech_service_id: int
337
+
338
+
339
+ class Version:
340
+ """Version class."""
341
+
342
+ def __init__(self, version: str) -> None:
343
+ """Initialize the version."""
344
+ version = version.strip()
345
+ version_parts = version.split(".") if version else []
346
+ self.major = int(version_parts[0]) if len(version_parts) > 0 else 0
347
+ self.minor = int(version_parts[1]) if len(version_parts) > 1 else 0
348
+ self.patch = int(version_parts[2]) if len(version_parts) > 2 else 0
349
+
350
+ def __str__(self) -> str:
351
+ """String representation of the version."""
352
+ return f"{self.major}.{self.minor}.{self.patch}"
353
+
354
+ def __eq__(self, other: object) -> bool:
355
+ """Equality comparison."""
356
+ if not isinstance(other, Version):
357
+ return NotImplemented
358
+ return (
359
+ self.major == other.major
360
+ and self.minor == other.minor
361
+ and self.patch == other.patch
362
+ )
363
+
364
+ def __lt__(self, other: "Version") -> bool:
365
+ """Less than comparison."""
366
+ if self.major != other.major:
367
+ return self.major < other.major
368
+ if self.minor != other.minor:
369
+ return self.minor < other.minor
370
+ return self.patch < other.patch
371
+
372
+
373
+ class ChainAmounts(dict[str, dict[str, dict[str, int]]]):
374
+ """
375
+ Class that represents chain amounts as a dictionary
376
+
377
+ The standard format follows the convention {chain: {address: {token: amount}}}
378
+ """
379
+
380
+ @classmethod
381
+ def shortfalls(
382
+ cls, requirements: "ChainAmounts", balances: "ChainAmounts"
383
+ ) -> "ChainAmounts":
384
+ """Return the shortfalls between requirements and balances."""
385
+ result: dict[str, dict[str, dict[str, int]]] = {}
386
+
387
+ for chain, addresses in requirements.items():
388
+ for address, assets in addresses.items():
389
+ for asset, required_amount in assets.items():
390
+ available = balances.get(chain, {}).get(address, {}).get(asset, 0)
391
+ shortfall = max(required_amount - available, 0)
392
+ result.setdefault(chain, {}).setdefault(address, {})[
393
+ asset
394
+ ] = shortfall
395
+
396
+ return cls(result)
397
+
398
+ @classmethod
399
+ def add(cls, *chainamounts: "ChainAmounts") -> "ChainAmounts":
400
+ """Add multiple ChainAmounts"""
401
+ result: dict[str, dict[str, dict[str, int]]] = {}
402
+
403
+ for ca in chainamounts:
404
+ for chain, addresses in ca.items():
405
+ result_addresses = result.setdefault(chain, {})
406
+ for address, assets in addresses.items():
407
+ result_assets = result_addresses.setdefault(address, {})
408
+ for asset, amount in assets.items():
409
+ result_assets[asset] = result_assets.get(asset, 0) + amount
410
+
411
+ return cls(result)
412
+
413
+ def __add__(self, other: "ChainAmounts") -> "ChainAmounts":
414
+ """Add two ChainAmounts"""
415
+ return ChainAmounts.add(self, other)
416
+
417
+ def __mul__(self, multiplier: float) -> "ChainAmounts":
418
+ """Multiply all amounts by the specified multiplier"""
419
+ output = copy.deepcopy(self)
420
+ for _, addresses in output.items():
421
+ for _, balances in addresses.items():
422
+ for asset, amount in balances.items():
423
+ balances[asset] = int(amount * multiplier)
424
+ return output
425
+
426
+ def __sub__(self, other: "ChainAmounts") -> "ChainAmounts":
427
+ """Subtract two ChainAmounts"""
428
+ return self + (other * -1)
429
+
430
+ def __floordiv__(self, divisor: float) -> "ChainAmounts":
431
+ """Divide all amounts by the specified divisor"""
432
+ if divisor == 0:
433
+ raise ValueError("Cannot divide by zero")
434
+
435
+ output = copy.deepcopy(self)
436
+ for _, addresses in output.items():
437
+ for _, balances in addresses.items():
438
+ for asset, amount in balances.items():
439
+ balances[asset] = int(amount // divisor)
440
+ return output
441
+
442
+ def __lt__(self, other: "ChainAmounts") -> bool:
443
+ """Return True if all amounts in self are strictly less than the corresponding amounts in other."""
444
+ for chain, addresses in self.items():
445
+ for address, assets in addresses.items():
446
+ for asset, amount in assets.items():
447
+ if amount >= other.get(chain, {}).get(address, {}).get(asset, 0):
448
+ return False
449
+ return True
450
+
451
+
452
+ @dataclass
453
+ class EncryptedData(LocalResource):
454
+ """EncryptedData type."""
455
+
456
+ path: Path
457
+ version: int
458
+ cipher: str
459
+ cipherparams: t.Dict[str, t.Union[int, str]] = field(repr=False)
460
+ ciphertext: str = field(repr=False)
461
+ kdf: str
462
+ kdfparams: t.Dict[str, t.Union[int, str]] = field(repr=False)
463
+
464
+ @classmethod
465
+ def new(cls, path: Path, password: str, plaintext_bytes: bytes) -> "EncryptedData":
466
+ """Creates a new EncryptedData"""
467
+ ph = argon2.PasswordHasher()
468
+ salt = os.urandom(ph.salt_len)
469
+ time_cost = ph.time_cost
470
+ memory_cost = ph.memory_cost
471
+ parallelism = ph.parallelism
472
+ hash_len = FERNET_KEY_LENGTH
473
+ argon2_type = argon2.Type.ID
474
+ key = argon2.low_level.hash_secret_raw(
475
+ secret=password.encode(),
476
+ salt=salt,
477
+ time_cost=time_cost,
478
+ memory_cost=memory_cost,
479
+ parallelism=parallelism,
480
+ hash_len=hash_len,
481
+ type=argon2_type,
482
+ )
483
+
484
+ fernet_key = base64.urlsafe_b64encode(key)
485
+ fernet = Fernet(fernet_key)
486
+ ciphertext_bytes = fernet.encrypt(plaintext_bytes)
487
+
488
+ return cls(
489
+ path=path,
490
+ version=1,
491
+ cipher=f"{fernet.__class__.__module__}.{fernet.__class__.__qualname__}",
492
+ cipherparams={ # Fernet token (ciphertext variable) already stores them
493
+ "version": ciphertext_bytes[0]
494
+ },
495
+ ciphertext=ciphertext_bytes.hex(),
496
+ kdf=f"{ph.__class__.__module__}.{ph.__class__.__qualname__}",
497
+ kdfparams={
498
+ "salt": salt.hex(),
499
+ "time_cost": time_cost,
500
+ "memory_cost": memory_cost,
501
+ "parallelism": parallelism,
502
+ "hash_len": hash_len,
503
+ "type": argon2_type.name,
504
+ },
505
+ )
506
+
507
+ def decrypt(self, password: str) -> bytes:
508
+ """Decrypts the EncryptedData"""
509
+ kdfparams = self.kdfparams
510
+ key = argon2.low_level.hash_secret_raw(
511
+ secret=password.encode(),
512
+ salt=bytes.fromhex(kdfparams["salt"]),
513
+ time_cost=kdfparams["time_cost"],
514
+ memory_cost=kdfparams["memory_cost"],
515
+ parallelism=kdfparams["parallelism"],
516
+ hash_len=kdfparams["hash_len"],
517
+ type=argon2.Type[kdfparams["type"]],
518
+ )
519
+ fernet_key = base64.urlsafe_b64encode(key)
520
+ fernet = Fernet(fernet_key)
521
+ ciphertext_bytes = bytes.fromhex(self.ciphertext)
522
+ plaintext_bytes = fernet.decrypt(ciphertext_bytes)
523
+ return plaintext_bytes
524
+
525
+ @classmethod
526
+ def load(cls, path: Path) -> "EncryptedData":
527
+ """Load EncryptedData."""
528
+ return cast(EncryptedData, super().load(path))
@@ -690,7 +690,7 @@ def run_service(
690
690
  )
691
691
 
692
692
  print_section("Funding the service")
693
- manager.fund_service(service_config_id=service.service_config_id)
693
+ manager.funding_manager.fund_service_initial(service=service)
694
694
 
695
695
  print_section("Deploying the service")
696
696
  manager.deploy_service_locally(
@@ -32,6 +32,8 @@ import requests
32
32
  from aea.configurations.data_types import PublicId
33
33
  from aea.helpers.logging import setup_logger
34
34
 
35
+ from operate.constants import AGENT_RUNNER_PREFIX
36
+
35
37
 
36
38
  @dataclass
37
39
  class AgentRelease:
@@ -65,10 +67,10 @@ class AgentRelease:
65
67
  # list of agents releases supported
66
68
  AGENTS_SUPPORTED = {
67
69
  "valory/trader": AgentRelease(
68
- owner="valory-xyz", repo="trader", release="v0.27.2-rc.1"
70
+ owner="valory-xyz", repo="trader", release="v0.27.2-1-rc.2"
69
71
  ),
70
72
  "valory/optimus": AgentRelease(
71
- owner="valory-xyz", repo="optimus", release="v0.0.1051"
73
+ owner="valory-xyz", repo="optimus", release="v0.6.0-rc.1"
72
74
  ),
73
75
  "dvilela/memeooorr": AgentRelease(
74
76
  owner="valory-xyz", repo="meme-ooorr", release="v0.0.101"
@@ -101,7 +103,7 @@ class AgentRunnerManager:
101
103
  else:
102
104
  raise ValueError(f"unsupported arch: {platform.machine()}")
103
105
 
104
- exec_name = f"agent_runner_{os_name}_{arch}"
106
+ exec_name = f"{AGENT_RUNNER_PREFIX}_{os_name}_{arch}"
105
107
  if platform.system() == "Windows":
106
108
  exec_name += ".exe"
107
109
  return exec_name
@@ -224,6 +224,9 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
224
224
  )
225
225
 
226
226
  self._run_aea_command("-s", "add-key", "ethereum", cwd=working_dir / "agent")
227
+ self._run_aea_command(
228
+ "-s", "add-key", "ethereum", "--connection", cwd=working_dir / "agent"
229
+ )
227
230
 
228
231
  self._run_aea_command("-s", "issue-certificates", cwd=working_dir / "agent")
229
232