olas-operate-middleware 0.10.6__py3-none-any.whl → 0.10.8__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.
@@ -0,0 +1,61 @@
1
+ # -*- coding: utf-8 -*-
2
+ # ------------------------------------------------------------------------------
3
+ #
4
+ # Copyright 2025 Valory AG
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # ------------------------------------------------------------------------------
19
+
20
+ """This module contains the class to connect to the `RecoveryModule` contract."""
21
+
22
+
23
+ from typing import Any, Dict
24
+
25
+ from aea.configurations.base import PublicId
26
+ from aea.contracts.base import Contract
27
+ from aea.crypto.base import LedgerApi
28
+
29
+
30
+ PUBLIC_ID = PublicId.from_str("valory/recovery_module:0.1.0")
31
+
32
+
33
+ class RecoveryModule(Contract):
34
+ """The RecoveryModule contract"""
35
+
36
+ contract_id = PUBLIC_ID
37
+
38
+ @classmethod
39
+ def get_recover_access_transaction(
40
+ cls,
41
+ ledger_api: LedgerApi,
42
+ contract_address: str,
43
+ owner: str,
44
+ service_id: int,
45
+ raise_on_try: bool = False,
46
+ ) -> Dict[str, Any]:
47
+ """Get the recover access transaction."""
48
+
49
+ tx_params = ledger_api.build_transaction(
50
+ contract_instance=cls.get_instance(
51
+ ledger_api=ledger_api, contract_address=contract_address
52
+ ),
53
+ method_name="recoverAccess",
54
+ method_args={
55
+ "serviceId": service_id,
56
+ },
57
+ tx_args={"sender_address": owner},
58
+ raise_on_try=raise_on_try,
59
+ )
60
+
61
+ return tx_params
@@ -0,0 +1,23 @@
1
+ name: recovery_module
2
+ author: valory
3
+ version: 0.1.0
4
+ type: contract
5
+ description: Recovery module
6
+ license: Apache-2.0
7
+ aea_version: '>=1.0.0, <2.0.0'
8
+ fingerprint:
9
+ __init__.py: bafybeicjlkmxs5ikpgdtgndifstpmmpaixwcbgouvmt6gowuhf5dy3dpgu
10
+ build/RecoveryModule.json: bafybeifsyjdbprcp4kxpijlpqfovxcf7cv2ujapt3d5zn34wby26ldv4ky
11
+ contract.py: bafybeifihpwb3etbrnn2hqdizsp5zolis3ur23slt3q5zctcykm5nlvv7q
12
+ fingerprint_ignore_patterns: []
13
+ contracts: []
14
+ class_name: RecoveryModule
15
+ contract_interface_paths:
16
+ ethereum: build/RecoveryModule.json
17
+ dependencies:
18
+ open-aea-ledger-ethereum:
19
+ version: ==1.60.0
20
+ open-aea-test-autonomy:
21
+ version: ==0.18.3
22
+ web3:
23
+ version: <7,>=6.0.0
operate/keys.py CHANGED
@@ -92,16 +92,22 @@ class KeysManager(metaclass=SingletonMeta):
92
92
  suffix=".txt",
93
93
  delete=False, # Handle cleanup manually
94
94
  ) as temp_file:
95
+ temp_file_name = temp_file.name
95
96
  temp_file.write(key.private_key)
96
97
  temp_file.flush()
97
98
  temp_file.close() # Close the file before reading
98
99
 
99
100
  # Set proper file permissions (readable by owner only)
100
- os.chmod(temp_file.name, 0o600)
101
- crypto = EthereumCrypto(private_key_path=temp_file.name)
101
+ os.chmod(temp_file_name, 0o600)
102
+ crypto = EthereumCrypto(private_key_path=temp_file_name)
102
103
 
103
104
  try:
104
- os.unlink(temp_file.name) # Clean up the temporary file
105
+ with open(temp_file_name, "r+", encoding="utf-8") as f:
106
+ f.seek(0)
107
+ f.write("\0" * len(key.private_key))
108
+ f.flush()
109
+ f.close()
110
+ os.unlink(temp_file_name) # Clean up the temporary file
105
111
  except OSError as e:
106
112
  self.logger.error(f"Failed to delete temp file {temp_file.name}: {e}")
107
113
 
@@ -24,62 +24,76 @@ import os
24
24
  from operate.operate_types import Chain
25
25
 
26
26
 
27
- ETHEREUM_PUBLIC_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
28
- GNOSIS_PUBLIC_RPC = os.environ.get("GNOSIS_RPC", "https://gnosis-rpc.publicnode.com")
29
- SOLANA_PUBLIC_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
27
+ ARBITRUM_ONE_PUBLIC_RPC = os.environ.get(
28
+ "ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc"
29
+ )
30
30
  BASE_PUBLIC_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
31
31
  CELO_PUBLIC_RPC = os.environ.get("CELO_RPC", "https://forno.celo.org")
32
- OPTIMISM_PUBLIC_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
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")
33
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")
34
38
 
35
- ETHEREUM_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
36
- GNOSIS_RPC = os.environ.get("GNOSIS_RPC", "https://rpc-gate.autonolas.tech/gnosis-rpc/")
37
- SOLANA_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
39
+ ARBITRUM_ONE_RPC = os.environ.get("ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc")
38
40
  BASE_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
39
41
  CELO_RPC = os.environ.get("CELO_RPC", "https://forno.celo.org")
40
- OPTIMISM_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
42
+ ETHEREUM_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
43
+ GNOSIS_RPC = os.environ.get("GNOSIS_RPC", "https://rpc-gate.autonolas.tech/gnosis-rpc/")
41
44
  MODE_RPC = os.environ.get("MODE_RPC", "https://mainnet.mode.network/")
45
+ OPTIMISM_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
46
+ POLYGON_RPC = os.environ.get("POLYGON_RPC", "https://polygon-rpc.com")
47
+ SOLANA_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
42
48
 
43
49
  PUBLIC_RPCS = {
44
- Chain.ETHEREUM: ETHEREUM_PUBLIC_RPC,
45
- Chain.GNOSIS: GNOSIS_PUBLIC_RPC,
46
- Chain.SOLANA: SOLANA_PUBLIC_RPC,
50
+ Chain.ARBITRUM_ONE: ARBITRUM_ONE_PUBLIC_RPC,
47
51
  Chain.BASE: BASE_PUBLIC_RPC,
48
52
  Chain.CELO: CELO_PUBLIC_RPC,
49
- Chain.OPTIMISM: OPTIMISM_PUBLIC_RPC,
53
+ Chain.ETHEREUM: ETHEREUM_PUBLIC_RPC,
54
+ Chain.GNOSIS: GNOSIS_PUBLIC_RPC,
50
55
  Chain.MODE: MODE_PUBLIC_RPC,
56
+ Chain.OPTIMISM: OPTIMISM_PUBLIC_RPC,
57
+ Chain.POLYGON: POLYGON_PUBLIC_RPC,
58
+ Chain.SOLANA: SOLANA_PUBLIC_RPC,
51
59
  }
52
60
 
53
61
  DEFAULT_RPCS = {
54
- Chain.ETHEREUM: ETHEREUM_RPC,
55
- Chain.GNOSIS: GNOSIS_RPC,
56
- Chain.SOLANA: SOLANA_RPC,
62
+ Chain.ARBITRUM_ONE: ARBITRUM_ONE_RPC,
57
63
  Chain.BASE: BASE_RPC,
58
64
  Chain.CELO: CELO_RPC,
59
- Chain.OPTIMISM: OPTIMISM_RPC,
65
+ Chain.ETHEREUM: ETHEREUM_RPC,
66
+ Chain.GNOSIS: GNOSIS_RPC,
60
67
  Chain.MODE: MODE_RPC,
68
+ Chain.OPTIMISM: OPTIMISM_RPC,
69
+ Chain.POLYGON: POLYGON_RPC,
70
+ Chain.SOLANA: SOLANA_RPC,
61
71
  }
62
72
 
63
73
  # Base currency for each chain
64
74
  CURRENCY_DENOMS = {
65
- Chain.ETHEREUM: "ETH",
66
- Chain.GNOSIS: "xDAI",
67
- Chain.SOLANA: "SOL",
75
+ Chain.ARBITRUM_ONE: "ETH",
68
76
  Chain.BASE: "ETH",
69
77
  Chain.CELO: "CELO",
70
- Chain.OPTIMISM: "ETH",
78
+ Chain.ETHEREUM: "ETH",
79
+ Chain.GNOSIS: "xDAI",
71
80
  Chain.MODE: "ETH",
81
+ Chain.OPTIMISM: "ETH",
82
+ Chain.POLYGON: "POL",
83
+ Chain.SOLANA: "SOL",
72
84
  }
73
85
 
74
86
  # Smallest denomination for each chain
75
87
  CURRENCY_SMALLEST_UNITS = {
76
- Chain.ETHEREUM: "Wei",
77
- Chain.GNOSIS: "Wei",
78
- Chain.SOLANA: "Lamport",
88
+ Chain.ARBITRUM_ONE: "Wei",
79
89
  Chain.BASE: "Wei",
80
90
  Chain.CELO: "Wei",
81
- Chain.OPTIMISM: "Wei",
91
+ Chain.ETHEREUM: "Wei",
92
+ Chain.GNOSIS: "Wei",
82
93
  Chain.MODE: "Wei",
94
+ Chain.OPTIMISM: "Wei",
95
+ Chain.POLYGON: "Wei",
96
+ Chain.SOLANA: "Lamport",
83
97
  }
84
98
 
85
99
 
@@ -21,64 +21,46 @@
21
21
 
22
22
  import typing as t
23
23
 
24
+ from autonomy.chain.constants import CHAIN_PROFILES, DEFAULT_MULTISEND
25
+
24
26
  from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
25
27
  from operate.operate_types import Chain, ContractAddresses
26
28
 
27
29
 
28
- CONTRACTS: t.Dict[Chain, ContractAddresses] = {
29
- Chain.GNOSIS: ContractAddresses(
30
- {
31
- "service_manager": "0x04b0007b2aFb398015B76e5f22993a1fddF83644",
32
- "service_registry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD",
33
- "service_registry_token_utility": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8",
34
- "gnosis_safe_proxy_factory": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
35
- "gnosis_safe_same_address_multisig": "0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06",
36
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
37
- }
38
- ),
39
- Chain.OPTIMISM: ContractAddresses(
40
- {
41
- "service_manager": "0xFbBEc0C8b13B38a9aC0499694A69a10204c5E2aB",
42
- "service_registry": "0x3d77596beb0f130a4415df3D2D8232B3d3D31e44",
43
- "service_registry_token_utility": "0xBb7e1D6Cb6F243D6bdE81CE92a9f2aFF7Fbe7eac",
44
- "gnosis_safe_proxy_factory": "0x5953f21495BD9aF1D78e87bb42AcCAA55C1e896C",
45
- "gnosis_safe_same_address_multisig": "0xb09CcF0Dbf0C178806Aaee28956c74bd66d21f73",
46
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
47
- }
48
- ),
49
- Chain.ETHEREUM: ContractAddresses(
50
- {
51
- "service_manager": "0x2EA682121f815FBcF86EA3F3CaFdd5d67F2dB143",
52
- "service_registry": "0x48b6af7B12C71f09e2fC8aF4855De4Ff54e775cA",
53
- "service_registry_token_utility": "0x3Fb926116D454b95c669B6Bf2E7c3bad8d19affA",
54
- "gnosis_safe_proxy_factory": "0x46C0D07F55d4F9B5Eed2Fc9680B5953e5fd7b461",
55
- "gnosis_safe_same_address_multisig": "0xfa517d01DaA100cB1932FA4345F68874f7E7eF46",
56
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
57
- }
58
- ),
59
- Chain.BASE: ContractAddresses(
60
- {
61
- "service_manager": "0x63e66d7ad413C01A7b49C7FF4e3Bb765C4E4bd1b",
62
- "service_registry": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
63
- "service_registry_token_utility": "0x34C895f302D0b5cf52ec0Edd3945321EB0f83dd5",
64
- "gnosis_safe_proxy_factory": "0x22bE6fDcd3e29851B29b512F714C328A00A96B83",
65
- "gnosis_safe_same_address_multisig": "0xFbBEc0C8b13B38a9aC0499694A69a10204c5E2aB",
66
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
67
- }
68
- ),
69
- Chain.MODE: ContractAddresses(
30
+ # 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
+ CONTRACTS: t.Dict[Chain, ContractAddresses] = {}
43
+ for _chain in CHAINS:
44
+ profile = CHAIN_PROFILES[_chain.value]
45
+ CONTRACTS[_chain] = ContractAddresses(
70
46
  {
71
- "service_manager": "0x63e66d7ad413C01A7b49C7FF4e3Bb765C4E4bd1b",
72
- "service_registry": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
73
- "service_registry_token_utility": "0x34C895f302D0b5cf52ec0Edd3945321EB0f83dd5",
74
- "gnosis_safe_proxy_factory": "0xBb7e1D6Cb6F243D6bdE81CE92a9f2aFF7Fbe7eac",
75
- "gnosis_safe_same_address_multisig": "0xFbBEc0C8b13B38a9aC0499694A69a10204c5E2aB",
76
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
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,
77
59
  }
78
- ),
79
- }
60
+ )
80
61
 
81
62
  STAKING: t.Dict[Chain, t.Dict[str, str]] = {
63
+ Chain.ARBITRUM_ONE: {},
82
64
  Chain.GNOSIS: {
83
65
  "pearl_alpha": "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A",
84
66
  "pearl_beta": "0xeF44Fb0842DDeF59D37f85D61A1eF492bbA6135d",
@@ -150,6 +132,7 @@ STAKING: t.Dict[Chain, t.Dict[str, str]] = {
150
132
  "modius_alpha_3": "0x9034D0413D122015710f1744A19eFb1d7c2CEB13",
151
133
  "modius_alpha_4": "0x8BcAdb2c291C159F9385964e5eD95a9887302862",
152
134
  },
135
+ Chain.POLYGON: {},
153
136
  }
154
137
 
155
138
 
@@ -167,37 +150,50 @@ DEFAULT_PRIORITY_MECH = { # maps mech marketplace address to its default priori
167
150
 
168
151
  # ERC20 token addresses
169
152
  OLAS: t.Dict[Chain, str] = {
170
- Chain.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
171
- Chain.OPTIMISM: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
153
+ Chain.ARBITRUM_ONE: "0x064F8B858C2A603e1b106a2039f5446D32dc81c1",
172
154
  Chain.BASE: "0x54330d28ca3357F294334BDC454a032e7f353416",
155
+ Chain.CELO: "0xaCFfAe8e57Ec6E394Eb1b41939A8CF7892DbDc51",
173
156
  Chain.ETHEREUM: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0",
157
+ Chain.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
174
158
  Chain.MODE: "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9",
159
+ Chain.OPTIMISM: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
160
+ Chain.POLYGON: "0xFEF5d947472e72Efbb2E388c730B7428406F2F95",
175
161
  }
176
162
 
177
163
  USDC: t.Dict[Chain, str] = {
178
- Chain.GNOSIS: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
179
- Chain.OPTIMISM: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
164
+ Chain.ARBITRUM_ONE: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
180
165
  Chain.BASE: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
166
+ Chain.CELO: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
181
167
  Chain.ETHEREUM: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
168
+ Chain.GNOSIS: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
182
169
  Chain.MODE: "0xd988097fb8612cc24eeC14542bC03424c656005f",
170
+ Chain.OPTIMISM: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
171
+ Chain.POLYGON: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
183
172
  }
184
173
 
185
174
  WRAPPED_NATIVE_ASSET = {
186
- Chain.GNOSIS: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
187
- Chain.OPTIMISM: "0x4200000000000000000000000000000000000006",
175
+ Chain.ARBITRUM_ONE: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
188
176
  Chain.BASE: "0x4200000000000000000000000000000000000006",
177
+ Chain.CELO: "0x471EcE3750Da237f93B8E339c536989b8978a438", # Dual token
189
178
  Chain.ETHEREUM: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
179
+ Chain.GNOSIS: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
190
180
  Chain.MODE: "0x4200000000000000000000000000000000000006",
181
+ Chain.OPTIMISM: "0x4200000000000000000000000000000000000006",
191
182
  Chain.POLYGON: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
192
- Chain.ARBITRUM_ONE: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
193
183
  }
194
184
 
195
185
  ERC20_TOKENS = [OLAS, USDC, WRAPPED_NATIVE_ASSET]
196
186
 
197
187
  DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
188
+ Chain.ARBITRUM_ONE: {
189
+ ZERO_ADDRESS: int(1e15 / 4),
190
+ },
198
191
  Chain.BASE: {
199
192
  ZERO_ADDRESS: int(1e15 / 4),
200
193
  },
194
+ Chain.CELO: {
195
+ ZERO_ADDRESS: int(1e18),
196
+ },
201
197
  Chain.ETHEREUM: {
202
198
  ZERO_ADDRESS: int(1e15 / 4),
203
199
  },
@@ -210,21 +206,35 @@ DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
210
206
  Chain.OPTIMISM: {
211
207
  ZERO_ADDRESS: int(1e15 / 4),
212
208
  },
209
+ Chain.POLYGON: {
210
+ ZERO_ADDRESS: int(1e18),
211
+ },
213
212
  }
214
213
 
215
214
  DEFAULT_MASTER_EOA_FUNDS = {
215
+ Chain.ARBITRUM_ONE: {ZERO_ADDRESS: 5_000_000_000_000_000},
216
216
  Chain.BASE: {ZERO_ADDRESS: 5_000_000_000_000_000},
217
+ Chain.CELO: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
217
218
  Chain.ETHEREUM: {ZERO_ADDRESS: 20_000_000_000_000_000},
218
219
  Chain.GNOSIS: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
219
220
  Chain.MODE: {ZERO_ADDRESS: 500_000_000_000_000},
220
221
  Chain.OPTIMISM: {ZERO_ADDRESS: 5_000_000_000_000_000},
222
+ Chain.POLYGON: {ZERO_ADDRESS: 1_500_000_000_000_000_000},
221
223
  }
222
224
 
223
225
  EXPLORER_URL = {
226
+ Chain.ARBITRUM_ONE: {
227
+ "tx": "https://arbiscan.io/tx/{tx_hash}",
228
+ "address": "https://arbiscan.io/address/{address}",
229
+ },
224
230
  Chain.BASE: {
225
231
  "tx": "https://basescan.org/tx/{tx_hash}",
226
232
  "address": "https://basescan.org/address/{address}",
227
233
  },
234
+ Chain.CELO: {
235
+ "tx": "https://celoscan.io/tx/{tx_hash}",
236
+ "address": "https://celoscan.io/address/{address}",
237
+ },
228
238
  Chain.ETHEREUM: {
229
239
  "tx": "https://etherscan.io/tx/{tx_hash}",
230
240
  "address": "https://etherscan.io/address/{address}",
@@ -234,13 +244,17 @@ EXPLORER_URL = {
234
244
  "address": "https://gnosisscan.io/address/{address}",
235
245
  },
236
246
  Chain.MODE: {
237
- "tx": "https://modescan.io/tx/{tx_hash}",
238
- "address": "https://modescan.io/address/{address}",
247
+ "tx": "https://explorer.mode.network/tx/{tx_hash}",
248
+ "address": "https://explorer.mode.network/address/{address}",
239
249
  },
240
250
  Chain.OPTIMISM: {
241
251
  "tx": "https://optimistic.etherscan.io/tx/{tx_hash}",
242
252
  "address": "https://optimistic.etherscan.io/address/{address}",
243
253
  },
254
+ Chain.POLYGON: {
255
+ "tx": "https://polygonscan.com/tx/{tx_hash}",
256
+ "address": "https://polygonscan.com/address/{address}",
257
+ },
244
258
  }
245
259
 
246
260
 
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}\n: {directories_str}")
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 / "user.json"
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
- if path.name.startswith(SERVICE_CONFIG_PREFIX) or path.name.startswith(
348
- "bafybei"
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()}"
operate/operate_types.py CHANGED
@@ -154,6 +154,8 @@ class ContractAddresses(TypedDict):
154
154
  service_registry_token_utility: str
155
155
  gnosis_safe_proxy_factory: str
156
156
  gnosis_safe_same_address_multisig: str
157
+ safe_multisig_with_recovery_module: str
158
+ recovery_module: str
157
159
  multisend: str
158
160
 
159
161
 
@@ -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 / "user.json").exists():
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 / "user.json",
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 IPFS_ADDRESS, NO_STAKING_PROGRAM_ID, OPERATE_HOME
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 / "user.json",
461
+ path=operate._path / USER_JSON,
457
462
  )
458
463
  else:
459
464
  _password = None