olas-operate-middleware 0.11.5__tar.gz → 0.12.1__tar.gz
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.11.5 → olas_operate_middleware-0.12.1}/PKG-INFO +1 -1
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/bridge_manager.py +52 -42
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/providers/native_bridge_provider.py +26 -13
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/cli.py +14 -8
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l1_standard_bridge/contract.py +20 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l1_standard_bridge/contract.yaml +1 -1
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/keys.py +75 -70
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/migration.py +66 -1
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/reset_password.py +3 -7
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/resource.py +9 -25
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/deployment_runner.py +61 -27
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/manage.py +2 -1
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/protocol.py +24 -13
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/service.py +25 -7
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/utils/__init__.py +46 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/pyproject.toml +1 -1
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/LICENSE +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/README.md +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/account/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/account/user.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/providers/lifi_provider.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/providers/provider.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/providers/relay_provider.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/constants.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/README.md +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/dual_staking_token/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/dual_staking_token/build/DualStakingToken.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/dual_staking_token/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/dual_staking_token/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/foreign_omnibridge/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/foreign_omnibridge/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/foreign_omnibridge/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/home_omnibridge/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/home_omnibridge/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/home_omnibridge/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l1_standard_bridge/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l2_standard_bridge/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l2_standard_bridge/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/l2_standard_bridge/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/mech_activity/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/mech_activity/build/MechActivity.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/mech_activity/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/mech_activity/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/optimism_mintable_erc20/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/optimism_mintable_erc20/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/optimism_mintable_erc20/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/recovery_module/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/recovery_module/build/RecoveryModule.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/recovery_module/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/recovery_module/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/requester_activity_checker/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/requester_activity_checker/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/requester_activity_checker/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/staking_token/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/staking_token/build/StakingToken.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/staking_token/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/staking_token/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/build/IUniswapV2ERC20.json +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/contract.yaml +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/ledger/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/ledger/profiles.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/operate_http/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/operate_http/exceptions.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/operate_types.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/pearl.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/analyse_logs.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/claim_staking_rewards.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/reset_configs.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/reset_staking.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/run_service.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/stop_service.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/terminate_on_chain_service.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/quickstart/utils.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/agent_runner.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/funding_manager.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/health_checker.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/utils/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/utils/mech.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/services/utils/tendermint.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/settings.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/utils/gnosis.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/utils/single_instance.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/utils/ssl.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/wallet/__init__.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/wallet/master.py +0 -0
- {olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/wallet/wallet_recovery_manager.py +0 -0
{olas_operate_middleware-0.11.5 → olas_operate_middleware-0.12.1}/operate/bridge/bridge_manager.py
RENAMED
|
@@ -57,52 +57,60 @@ RELAY_PROVIDER_ID = "relay-provider"
|
|
|
57
57
|
|
|
58
58
|
NATIVE_BRIDGE_PROVIDER_CONFIGS: t.Dict[str, t.Any] = {
|
|
59
59
|
"native-ethereum-to-base": {
|
|
60
|
-
"from_chain":
|
|
60
|
+
"from_chain": Chain.ETHEREUM.value,
|
|
61
61
|
"from_bridge": "0x3154Cf16ccdb4C6d922629664174b904d80F2C35",
|
|
62
|
-
"to_chain":
|
|
62
|
+
"to_chain": Chain.BASE.value,
|
|
63
63
|
"to_bridge": "0x4200000000000000000000000000000000000010",
|
|
64
64
|
"bridge_eta": 300,
|
|
65
65
|
"bridge_contract_adaptor_class": OptimismContractAdaptor,
|
|
66
66
|
},
|
|
67
|
+
"native-ethereum-to-celo": {
|
|
68
|
+
"from_chain": Chain.ETHEREUM.value,
|
|
69
|
+
"from_bridge": "0x9C4955b92F34148dbcfDCD82e9c9eCe5CF2badfe",
|
|
70
|
+
"to_chain": Chain.CELO.value,
|
|
71
|
+
"to_bridge": "0x4200000000000000000000000000000000000010",
|
|
72
|
+
"bridge_eta": 300,
|
|
73
|
+
"bridge_contract_adaptor_class": OptimismContractAdaptor,
|
|
74
|
+
},
|
|
75
|
+
"native-ethereum-to-gnosis": {
|
|
76
|
+
"from_chain": Chain.ETHEREUM.value,
|
|
77
|
+
"from_bridge": "0x88ad09518695c6c3712AC10a214bE5109a655671",
|
|
78
|
+
"to_chain": Chain.GNOSIS.value,
|
|
79
|
+
"to_bridge": "0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d",
|
|
80
|
+
"bridge_eta": 1800,
|
|
81
|
+
"bridge_contract_adaptor_class": OmnibridgeContractAdaptor,
|
|
82
|
+
},
|
|
67
83
|
"native-ethereum-to-mode": {
|
|
68
|
-
"from_chain":
|
|
84
|
+
"from_chain": Chain.ETHEREUM.value,
|
|
69
85
|
"from_bridge": "0x735aDBbE72226BD52e818E7181953f42E3b0FF21",
|
|
70
|
-
"to_chain":
|
|
86
|
+
"to_chain": Chain.MODE.value,
|
|
71
87
|
"to_bridge": "0x4200000000000000000000000000000000000010",
|
|
72
88
|
"bridge_eta": 300,
|
|
73
89
|
"bridge_contract_adaptor_class": OptimismContractAdaptor,
|
|
74
90
|
},
|
|
75
91
|
"native-ethereum-to-optimism": {
|
|
76
|
-
"from_chain":
|
|
92
|
+
"from_chain": Chain.ETHEREUM.value,
|
|
77
93
|
"from_bridge": "0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1",
|
|
78
|
-
"to_chain":
|
|
94
|
+
"to_chain": Chain.OPTIMISM.value,
|
|
79
95
|
"to_bridge": "0x4200000000000000000000000000000000000010",
|
|
80
96
|
"bridge_eta": 300,
|
|
81
97
|
"bridge_contract_adaptor_class": OptimismContractAdaptor,
|
|
82
98
|
},
|
|
83
|
-
"native-ethereum-to-gnosis": {
|
|
84
|
-
"from_chain": "ethereum",
|
|
85
|
-
"from_bridge": "0x88ad09518695c6c3712AC10a214bE5109a655671",
|
|
86
|
-
"to_chain": "gnosis",
|
|
87
|
-
"to_bridge": "0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d",
|
|
88
|
-
"bridge_eta": 1800,
|
|
89
|
-
"bridge_contract_adaptor_class": OmnibridgeContractAdaptor,
|
|
90
|
-
},
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
# Routes are defined as the tuples (from_chain, from_token, to_chain, to_token)
|
|
102
|
+
PREFERRED_ROUTES = {
|
|
95
103
|
(
|
|
96
|
-
Chain.ETHEREUM,
|
|
97
|
-
USDC[Chain.ETHEREUM],
|
|
98
|
-
Chain.OPTIMISM,
|
|
99
|
-
USDC[Chain.OPTIMISM],
|
|
104
|
+
Chain.ETHEREUM,
|
|
105
|
+
USDC[Chain.ETHEREUM],
|
|
106
|
+
Chain.OPTIMISM,
|
|
107
|
+
USDC[Chain.OPTIMISM],
|
|
100
108
|
): LIFI_PROVIDER_ID,
|
|
101
109
|
(
|
|
102
|
-
Chain.ETHEREUM,
|
|
103
|
-
USDC[Chain.ETHEREUM],
|
|
104
|
-
Chain.BASE,
|
|
105
|
-
USDC[Chain.BASE],
|
|
110
|
+
Chain.ETHEREUM,
|
|
111
|
+
USDC[Chain.ETHEREUM],
|
|
112
|
+
Chain.BASE,
|
|
113
|
+
USDC[Chain.BASE],
|
|
106
114
|
): LIFI_PROVIDER_ID,
|
|
107
115
|
(Chain.ETHEREUM, ZERO_ADDRESS, Chain.GNOSIS, ZERO_ADDRESS): RELAY_PROVIDER_ID,
|
|
108
116
|
}
|
|
@@ -260,24 +268,26 @@ class BridgeManager:
|
|
|
260
268
|
|
|
261
269
|
provider_requests = []
|
|
262
270
|
for params in requests_params:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
271
|
+
route = (
|
|
272
|
+
Chain(params["from"]["chain"]),
|
|
273
|
+
params["from"]["token"],
|
|
274
|
+
Chain(params["to"]["chain"]),
|
|
275
|
+
params["to"]["token"],
|
|
276
|
+
)
|
|
277
|
+
provider_id = PREFERRED_ROUTES.get(route)
|
|
278
|
+
|
|
279
|
+
if not provider_id:
|
|
280
|
+
for provider in self._native_bridge_providers.values():
|
|
281
|
+
if provider.can_handle_request(params):
|
|
282
|
+
provider_id = provider.provider_id
|
|
283
|
+
break
|
|
284
|
+
|
|
285
|
+
if not provider_id:
|
|
286
|
+
provider_id = RELAY_PROVIDER_ID
|
|
287
|
+
|
|
288
|
+
provider_requests.append(
|
|
289
|
+
self._providers[provider_id].create_request(params=params)
|
|
290
|
+
)
|
|
281
291
|
|
|
282
292
|
bundle = ProviderRequestBundle(
|
|
283
293
|
id=f"{BRIDGE_REQUEST_BUNDLE_PREFIX}{uuid.uuid4()}",
|
|
@@ -53,7 +53,11 @@ from operate.data.contracts.l2_standard_bridge.contract import L2StandardBridge
|
|
|
53
53
|
from operate.data.contracts.optimism_mintable_erc20.contract import (
|
|
54
54
|
OptimismMintableERC20,
|
|
55
55
|
)
|
|
56
|
-
from operate.ledger import
|
|
56
|
+
from operate.ledger import (
|
|
57
|
+
get_default_ledger_api,
|
|
58
|
+
update_tx_with_gas_estimate,
|
|
59
|
+
update_tx_with_gas_pricing,
|
|
60
|
+
)
|
|
57
61
|
from operate.ledger.profiles import ERC20_TOKENS, EXPLORER_URL
|
|
58
62
|
from operate.operate_types import Chain
|
|
59
63
|
from operate.wallet.master import MasterWalletManager
|
|
@@ -75,13 +79,15 @@ class BridgeContractAdaptor(ABC):
|
|
|
75
79
|
) -> None:
|
|
76
80
|
"""Initialize the bridge contract adaptor."""
|
|
77
81
|
super().__init__()
|
|
82
|
+
if from_chain == to_chain:
|
|
83
|
+
raise ValueError("from_chain and to_chain cannot be the same.")
|
|
78
84
|
self.from_chain = from_chain
|
|
79
85
|
self.from_bridge = from_bridge
|
|
80
86
|
self.to_chain = to_chain
|
|
81
87
|
self.to_bridge = to_bridge
|
|
82
88
|
self.bridge_eta = bridge_eta
|
|
83
89
|
|
|
84
|
-
def can_handle_request(self,
|
|
90
|
+
def can_handle_request(self, params: t.Dict) -> bool:
|
|
85
91
|
"""Returns 'true' if the contract adaptor can handle a request for 'params'."""
|
|
86
92
|
from_chain = params["from"]["chain"]
|
|
87
93
|
from_token = Web3.to_checksum_address(params["from"]["token"])
|
|
@@ -158,11 +164,24 @@ class OptimismContractAdaptor(BridgeContractAdaptor):
|
|
|
158
164
|
),
|
|
159
165
|
)
|
|
160
166
|
|
|
161
|
-
def can_handle_request(self,
|
|
167
|
+
def can_handle_request(self, params: t.Dict) -> bool:
|
|
162
168
|
"""Returns 'true' if the contract adaptor can handle a request for 'params'."""
|
|
163
169
|
|
|
170
|
+
if not super().can_handle_request(params):
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
from_chain = params["from"]["chain"]
|
|
164
174
|
from_token = Web3.to_checksum_address(params["from"]["token"])
|
|
175
|
+
from_ledger_api = get_default_ledger_api(Chain(from_chain))
|
|
176
|
+
to_chain = params["to"]["chain"]
|
|
165
177
|
to_token = Web3.to_checksum_address(params["to"]["token"])
|
|
178
|
+
to_ledger_api = get_default_ledger_api(Chain(to_chain))
|
|
179
|
+
|
|
180
|
+
if from_token == ZERO_ADDRESS and to_token == ZERO_ADDRESS:
|
|
181
|
+
if not self._l1_standard_bridge_contract.supports_bridge_eth_to(
|
|
182
|
+
ledger_api=from_ledger_api, contract_address=self.from_bridge
|
|
183
|
+
):
|
|
184
|
+
return False
|
|
166
185
|
|
|
167
186
|
if to_token != ZERO_ADDRESS:
|
|
168
187
|
try:
|
|
@@ -176,7 +195,7 @@ class OptimismContractAdaptor(BridgeContractAdaptor):
|
|
|
176
195
|
except Exception: # pylint: disable=broad-except
|
|
177
196
|
return False
|
|
178
197
|
|
|
179
|
-
return
|
|
198
|
+
return True
|
|
180
199
|
|
|
181
200
|
def build_bridge_tx(
|
|
182
201
|
self, from_ledger_api: LedgerApi, provider_request: ProviderRequest
|
|
@@ -288,13 +307,13 @@ class OmnibridgeContractAdaptor(BridgeContractAdaptor):
|
|
|
288
307
|
),
|
|
289
308
|
)
|
|
290
309
|
|
|
291
|
-
def can_handle_request(self,
|
|
310
|
+
def can_handle_request(self, params: t.Dict) -> bool:
|
|
292
311
|
"""Returns 'true' if the contract adaptor can handle a request for 'params'."""
|
|
293
312
|
from_token = Web3.to_checksum_address(params["from"]["token"])
|
|
294
313
|
if from_token == ZERO_ADDRESS:
|
|
295
314
|
return False
|
|
296
315
|
|
|
297
|
-
return super().can_handle_request(
|
|
316
|
+
return super().can_handle_request(params)
|
|
298
317
|
|
|
299
318
|
def build_bridge_tx(
|
|
300
319
|
self, from_ledger_api: LedgerApi, provider_request: ProviderRequest
|
|
@@ -428,16 +447,10 @@ class NativeBridgeProvider(Provider):
|
|
|
428
447
|
|
|
429
448
|
def can_handle_request(self, params: t.Dict) -> bool:
|
|
430
449
|
"""Returns 'true' if the provider can handle a request for 'params'."""
|
|
431
|
-
|
|
432
450
|
if not super().can_handle_request(params):
|
|
433
451
|
return False
|
|
434
452
|
|
|
435
|
-
|
|
436
|
-
chain = Chain(to_chain)
|
|
437
|
-
wallet = self.wallet_manager.load(chain.ledger_type)
|
|
438
|
-
to_ledger_api = wallet.ledger_api(chain)
|
|
439
|
-
|
|
440
|
-
if not self.bridge_contract_adaptor.can_handle_request(to_ledger_api, params):
|
|
453
|
+
if not self.bridge_contract_adaptor.can_handle_request(params):
|
|
441
454
|
return False
|
|
442
455
|
|
|
443
456
|
return True
|
|
@@ -64,6 +64,7 @@ from operate.constants import (
|
|
|
64
64
|
WALLET_RECOVERY_DIR,
|
|
65
65
|
ZERO_ADDRESS,
|
|
66
66
|
)
|
|
67
|
+
from operate.keys import KeysManager
|
|
67
68
|
from operate.ledger.profiles import (
|
|
68
69
|
DEFAULT_EOA_TOPUPS,
|
|
69
70
|
DEFAULT_NEW_SAFE_FUNDS,
|
|
@@ -143,7 +144,7 @@ def service_not_found_error(service_config_id: str) -> JSONResponse:
|
|
|
143
144
|
)
|
|
144
145
|
|
|
145
146
|
|
|
146
|
-
class OperateApp:
|
|
147
|
+
class OperateApp: # pylint: disable=too-many-instance-attributes
|
|
147
148
|
"""Operate app."""
|
|
148
149
|
|
|
149
150
|
def __init__(
|
|
@@ -157,11 +158,13 @@ class OperateApp:
|
|
|
157
158
|
self.setup()
|
|
158
159
|
self._backup_operate_if_new_version()
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
self._password: t.Optional[str] = os.environ.get("OPERATE_USER_PASSWORD")
|
|
162
|
+
KeysManager._instances.clear() # reset singleton instance
|
|
163
|
+
self._keys_manager: KeysManager = KeysManager(
|
|
161
164
|
path=self._keys,
|
|
162
165
|
logger=logger,
|
|
166
|
+
password=self._password,
|
|
163
167
|
)
|
|
164
|
-
self._password: t.Optional[str] = os.environ.get("OPERATE_USER_PASSWORD")
|
|
165
168
|
self.settings = Settings(path=self._path)
|
|
166
169
|
|
|
167
170
|
self._wallet_manager = MasterWalletManager(
|
|
@@ -174,11 +177,11 @@ class OperateApp:
|
|
|
174
177
|
logger=logger,
|
|
175
178
|
)
|
|
176
179
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
self._migration_manager = MigrationManager(self._path, logger)
|
|
181
|
+
self._migration_manager.migrate_user_account()
|
|
182
|
+
self._migration_manager.migrate_services(self.service_manager())
|
|
183
|
+
self._migration_manager.migrate_wallets(self.wallet_manager)
|
|
184
|
+
self._migration_manager.migrate_qs_configs()
|
|
182
185
|
|
|
183
186
|
@property
|
|
184
187
|
def password(self) -> t.Optional[str]:
|
|
@@ -189,7 +192,9 @@ class OperateApp:
|
|
|
189
192
|
def password(self, value: t.Optional[str]) -> None:
|
|
190
193
|
"""Set the password."""
|
|
191
194
|
self._password = value
|
|
195
|
+
self._keys_manager.password = value
|
|
192
196
|
self._wallet_manager.password = value
|
|
197
|
+
self._migration_manager.migrate_keys(self._keys_manager)
|
|
193
198
|
|
|
194
199
|
def _backup_operate_if_new_version(self) -> None:
|
|
195
200
|
"""Backup .operate directory if this is a new version."""
|
|
@@ -256,6 +261,7 @@ class OperateApp:
|
|
|
256
261
|
wallet_manager = self.wallet_manager
|
|
257
262
|
wallet_manager.password = old_password
|
|
258
263
|
wallet_manager.update_password(new_password)
|
|
264
|
+
self._keys_manager.update_password(new_password)
|
|
259
265
|
self.user_account.update(old_password, new_password)
|
|
260
266
|
|
|
261
267
|
def update_password_with_mnemonic(self, mnemonic: str, new_password: str) -> None:
|
|
@@ -45,6 +45,26 @@ class L1StandardBridge(Contract):
|
|
|
45
45
|
|
|
46
46
|
contract_id = PublicId.from_str("valory/l1_standard_bridge:0.1.0")
|
|
47
47
|
|
|
48
|
+
@classmethod
|
|
49
|
+
def supports_bridge_eth_to(
|
|
50
|
+
cls,
|
|
51
|
+
ledger_api: LedgerApi,
|
|
52
|
+
contract_address: str,
|
|
53
|
+
) -> bool:
|
|
54
|
+
"""Checks if native ETH bridging via `bridgeETHTo` is supported."""
|
|
55
|
+
contract_instance = cls.get_instance(
|
|
56
|
+
ledger_api=ledger_api, contract_address=contract_address
|
|
57
|
+
)
|
|
58
|
+
try:
|
|
59
|
+
contract_instance.functions.bridgeETHTo(
|
|
60
|
+
"0x0000000000000000000000000000000000000000",
|
|
61
|
+
0,
|
|
62
|
+
b""
|
|
63
|
+
).call({"from": PLACEHOLDER_NATIVE_TOKEN_ADDRESS, "value": 0})
|
|
64
|
+
return True
|
|
65
|
+
except Exception: # pylint: disable=broad-except
|
|
66
|
+
return False
|
|
67
|
+
|
|
48
68
|
@classmethod
|
|
49
69
|
def build_bridge_eth_to_tx(
|
|
50
70
|
cls,
|
|
@@ -8,7 +8,7 @@ aea_version: '>=1.0.0, <2.0.0'
|
|
|
8
8
|
fingerprint:
|
|
9
9
|
__init__.py: bafybeifsbxn6hlccnpgqnpvaz3ph6ajl4is4mcyerr6aqp7heggagcphye
|
|
10
10
|
build/L1StandardBridge.json: bafybeidq6jt7zmedtuxbbyggiqhu7w6543bunyd2vrbibg6y2svxsi2q5m
|
|
11
|
-
contract.py:
|
|
11
|
+
contract.py: bafybeienitro6gds2ny7guziwdu3uapmj3xxuzmqyvsect4zqmrouity2e
|
|
12
12
|
fingerprint_ignore_patterns: []
|
|
13
13
|
contracts: []
|
|
14
14
|
class_name: L1StandardBridge
|
|
@@ -21,17 +21,17 @@
|
|
|
21
21
|
|
|
22
22
|
import json
|
|
23
23
|
import os
|
|
24
|
-
import shutil
|
|
25
24
|
import tempfile
|
|
26
25
|
from dataclasses import dataclass
|
|
26
|
+
from logging import Logger
|
|
27
27
|
from pathlib import Path
|
|
28
|
-
from typing import Any
|
|
28
|
+
from typing import Any, Optional
|
|
29
29
|
|
|
30
30
|
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
|
|
34
|
+
from operate.utils import SingletonMeta, unrecoverable_delete
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@dataclass
|
|
@@ -42,6 +42,15 @@ class Key(LocalResource):
|
|
|
42
42
|
address: str
|
|
43
43
|
private_key: str
|
|
44
44
|
|
|
45
|
+
def get_decrypted(self, password: str) -> dict:
|
|
46
|
+
"""Get decrypted key json."""
|
|
47
|
+
return {
|
|
48
|
+
"ledger": self.ledger.value,
|
|
49
|
+
"address": self.address,
|
|
50
|
+
"private_key": "0x"
|
|
51
|
+
+ EthereumCrypto.decrypt(self.private_key, password=password),
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
@classmethod
|
|
46
55
|
def load(cls, path: Path) -> "Key":
|
|
47
56
|
"""Load a service"""
|
|
@@ -61,13 +70,41 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
61
70
|
if "path" not in kwargs:
|
|
62
71
|
raise ValueError("Path must be provided for KeysManager")
|
|
63
72
|
|
|
64
|
-
self.path = kwargs["path"]
|
|
65
|
-
self.logger = kwargs["logger"]
|
|
73
|
+
self.path: Path = kwargs["path"]
|
|
74
|
+
self.logger: Logger = kwargs["logger"]
|
|
75
|
+
self.password: Optional[str] = kwargs.get("password")
|
|
66
76
|
self.path.mkdir(exist_ok=True, parents=True)
|
|
67
77
|
|
|
78
|
+
def private_key_to_crypto(
|
|
79
|
+
self, private_key: str, password: Optional[str]
|
|
80
|
+
) -> EthereumCrypto:
|
|
81
|
+
"""Convert private key string to EthereumCrypto instance."""
|
|
82
|
+
with tempfile.NamedTemporaryFile(
|
|
83
|
+
dir=self.path,
|
|
84
|
+
mode="w",
|
|
85
|
+
suffix=".txt",
|
|
86
|
+
delete=False, # Handle cleanup manually
|
|
87
|
+
) as temp_file:
|
|
88
|
+
temp_file_name = temp_file.name
|
|
89
|
+
temp_file.write(private_key)
|
|
90
|
+
temp_file.flush()
|
|
91
|
+
temp_file.close() # Close the file before reading
|
|
92
|
+
|
|
93
|
+
# Set proper file permissions (readable by owner only)
|
|
94
|
+
os.chmod(temp_file_name, 0o600)
|
|
95
|
+
crypto = EthereumCrypto(private_key_path=temp_file_name, password=password)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
unrecoverable_delete(
|
|
99
|
+
Path(temp_file.name)
|
|
100
|
+
) # Clean up the temporary file
|
|
101
|
+
except OSError as e:
|
|
102
|
+
self.logger.error(f"Failed to delete temp file {temp_file.name}: {e}")
|
|
103
|
+
|
|
104
|
+
return crypto
|
|
105
|
+
|
|
68
106
|
def get(self, key: str) -> Key:
|
|
69
107
|
"""Get key object."""
|
|
70
|
-
KeysManager.migrate_format(self.path / key)
|
|
71
108
|
return Key.from_json( # type: ignore
|
|
72
109
|
obj=json.loads(
|
|
73
110
|
(self.path / key).read_text(
|
|
@@ -91,46 +128,20 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
91
128
|
|
|
92
129
|
def get_crypto_instance(self, address: str) -> EthereumCrypto:
|
|
93
130
|
"""Get EthereumCrypto instance for the given address."""
|
|
94
|
-
key: Key =
|
|
95
|
-
|
|
96
|
-
(self.path / address).read_text(
|
|
97
|
-
encoding="utf-8",
|
|
98
|
-
)
|
|
99
|
-
)
|
|
100
|
-
)
|
|
101
|
-
private_key = key.private_key
|
|
102
|
-
# Create temporary file with delete=False to handle it manually
|
|
103
|
-
with tempfile.NamedTemporaryFile(
|
|
104
|
-
dir=self.path,
|
|
105
|
-
mode="w",
|
|
106
|
-
suffix=".txt",
|
|
107
|
-
delete=False, # Handle cleanup manually
|
|
108
|
-
) as temp_file:
|
|
109
|
-
temp_file_name = temp_file.name
|
|
110
|
-
temp_file.write(private_key)
|
|
111
|
-
temp_file.flush()
|
|
112
|
-
temp_file.close() # Close the file before reading
|
|
113
|
-
|
|
114
|
-
# Set proper file permissions (readable by owner only)
|
|
115
|
-
os.chmod(temp_file_name, 0o600)
|
|
116
|
-
crypto = EthereumCrypto(private_key_path=temp_file_name)
|
|
117
|
-
|
|
118
|
-
try:
|
|
119
|
-
with open(temp_file_name, "r+", encoding="utf-8") as f:
|
|
120
|
-
f.seek(0)
|
|
121
|
-
f.write("\0" * len(private_key))
|
|
122
|
-
f.flush()
|
|
123
|
-
f.close()
|
|
124
|
-
os.unlink(temp_file_name) # Clean up the temporary file
|
|
125
|
-
except OSError as e:
|
|
126
|
-
self.logger.error(f"Failed to delete temp file {temp_file.name}: {e}")
|
|
127
|
-
|
|
128
|
-
return crypto
|
|
131
|
+
key: Key = self.get(address)
|
|
132
|
+
return self.private_key_to_crypto(key.private_key, self.password)
|
|
129
133
|
|
|
130
134
|
def create(self) -> str:
|
|
131
135
|
"""Creates new key."""
|
|
132
136
|
self.path.mkdir(exist_ok=True, parents=True)
|
|
133
|
-
crypto = EthereumCrypto()
|
|
137
|
+
crypto = EthereumCrypto(password=self.password)
|
|
138
|
+
key = Key(
|
|
139
|
+
ledger=LedgerType.ETHEREUM,
|
|
140
|
+
address=crypto.address,
|
|
141
|
+
private_key=crypto.encrypt(password=self.password)
|
|
142
|
+
if self.password is not None
|
|
143
|
+
else crypto.private_key,
|
|
144
|
+
)
|
|
134
145
|
for path in (
|
|
135
146
|
self.path / f"{crypto.address}.bak",
|
|
136
147
|
self.path / crypto.address,
|
|
@@ -140,12 +151,8 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
140
151
|
|
|
141
152
|
path.write_text(
|
|
142
153
|
json.dumps(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
address=crypto.address,
|
|
146
|
-
private_key=crypto.private_key,
|
|
147
|
-
).json,
|
|
148
|
-
indent=4,
|
|
154
|
+
key.json,
|
|
155
|
+
indent=2,
|
|
149
156
|
),
|
|
150
157
|
encoding="utf-8",
|
|
151
158
|
)
|
|
@@ -156,25 +163,23 @@ class KeysManager(metaclass=SingletonMeta):
|
|
|
156
163
|
"""Delete key."""
|
|
157
164
|
os.remove(self.path / key)
|
|
158
165
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return migrated
|
|
166
|
+
def update_password(self, new_password: str) -> None:
|
|
167
|
+
"""Update password for all keys."""
|
|
168
|
+
for key_file in self.path.iterdir():
|
|
169
|
+
if not key_file.is_file() or key_file.suffix == ".bak":
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
key = self.get(key_file.name)
|
|
173
|
+
crypto = self.get_crypto_instance(key_file.name)
|
|
174
|
+
encrypted_private_key = crypto.encrypt(password=new_password)
|
|
175
|
+
key.private_key = encrypted_private_key
|
|
176
|
+
key.path = self.path / key_file.name
|
|
177
|
+
key.store()
|
|
178
|
+
|
|
179
|
+
backup_path = self.path / f"{key.address}.bak"
|
|
180
|
+
backup_path.write_text(
|
|
181
|
+
json.dumps(key.json, indent=2),
|
|
182
|
+
encoding="utf-8",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
self.password = new_password
|
|
@@ -28,8 +28,11 @@ from pathlib import Path
|
|
|
28
28
|
from time import time
|
|
29
29
|
|
|
30
30
|
from aea_cli_ipfs.ipfs_utils import IPFSTool
|
|
31
|
+
from aea_ledger_ethereum import EthereumCrypto
|
|
32
|
+
from web3 import Web3
|
|
31
33
|
|
|
32
34
|
from operate.constants import USER_JSON, ZERO_ADDRESS
|
|
35
|
+
from operate.keys import KeysManager
|
|
33
36
|
from operate.operate_types import Chain, LedgerType
|
|
34
37
|
from operate.services.manage import ServiceManager
|
|
35
38
|
from operate.services.service import (
|
|
@@ -38,7 +41,7 @@ from operate.services.service import (
|
|
|
38
41
|
SERVICE_CONFIG_VERSION,
|
|
39
42
|
Service,
|
|
40
43
|
)
|
|
41
|
-
from operate.utils import create_backup
|
|
44
|
+
from operate.utils import create_backup, unrecoverable_delete
|
|
42
45
|
from operate.wallet.master import LEDGER_TYPE_TO_WALLET_CLASS, MasterWalletManager
|
|
43
46
|
|
|
44
47
|
|
|
@@ -454,3 +457,65 @@ class MigrationManager:
|
|
|
454
457
|
self.logger.info(
|
|
455
458
|
"[MIGRATION MANAGER] Migrated quickstart config: %s.", qs_config.name
|
|
456
459
|
)
|
|
460
|
+
|
|
461
|
+
def migrate_keys(self, keys_manager: KeysManager) -> None:
|
|
462
|
+
"""Migrate keys format if needed."""
|
|
463
|
+
self.logger.info("Migrating keys...")
|
|
464
|
+
|
|
465
|
+
for key_file in keys_manager.path.iterdir():
|
|
466
|
+
if (
|
|
467
|
+
not key_file.is_file()
|
|
468
|
+
or key_file.suffix == ".bak"
|
|
469
|
+
or not Web3.is_address(key_file.name)
|
|
470
|
+
):
|
|
471
|
+
self.logger.warning(f"Skipping non-key file: {key_file}")
|
|
472
|
+
continue
|
|
473
|
+
|
|
474
|
+
migrated = False
|
|
475
|
+
backup_path = key_file.with_suffix(".bak")
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
with open(key_file, "r", encoding="utf-8") as file:
|
|
479
|
+
data = json.load(file)
|
|
480
|
+
except Exception as e: # pylint: disable=broad-except
|
|
481
|
+
self.logger.error(
|
|
482
|
+
f"Failed to read key file: {key_file}\n"
|
|
483
|
+
f"Key file content:\n{key_file.read_text(encoding='utf-8')}\n"
|
|
484
|
+
f"Exception {e}: {traceback.format_exc()}"
|
|
485
|
+
)
|
|
486
|
+
raise e
|
|
487
|
+
|
|
488
|
+
old_to_new_ledgers = {0: "ethereum", 1: "solana"}
|
|
489
|
+
if data.get("ledger") in old_to_new_ledgers:
|
|
490
|
+
data["ledger"] = old_to_new_ledgers.get(data["ledger"])
|
|
491
|
+
with open(key_file, "w", encoding="utf-8") as file:
|
|
492
|
+
json.dump(data, file, indent=2)
|
|
493
|
+
|
|
494
|
+
migrated = True
|
|
495
|
+
|
|
496
|
+
private_key = data.get("private_key")
|
|
497
|
+
if (
|
|
498
|
+
private_key
|
|
499
|
+
and keys_manager.password is not None
|
|
500
|
+
and private_key.startswith("0x")
|
|
501
|
+
):
|
|
502
|
+
crypto: EthereumCrypto = keys_manager.private_key_to_crypto(
|
|
503
|
+
private_key=private_key,
|
|
504
|
+
password=None,
|
|
505
|
+
)
|
|
506
|
+
encrypted_private_key = crypto.encrypt(password=keys_manager.password)
|
|
507
|
+
data["private_key"] = encrypted_private_key
|
|
508
|
+
if backup_path.exists():
|
|
509
|
+
unrecoverable_delete(backup_path)
|
|
510
|
+
|
|
511
|
+
migrated = True
|
|
512
|
+
|
|
513
|
+
if migrated:
|
|
514
|
+
with open(key_file, "w", encoding="utf-8") as file:
|
|
515
|
+
json.dump(data, file, indent=2)
|
|
516
|
+
|
|
517
|
+
if not backup_path.exists():
|
|
518
|
+
shutil.copyfile(key_file, backup_path)
|
|
519
|
+
|
|
520
|
+
if migrated:
|
|
521
|
+
self.logger.info(f"Key {key_file.name} has been migrated.")
|
|
@@ -22,10 +22,9 @@ 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.
|
|
25
|
+
from operate.keys import KeysManager
|
|
26
26
|
from operate.quickstart.run_service import ask_confirm_password
|
|
27
27
|
from operate.quickstart.utils import ask_or_get_from_env, print_section, print_title
|
|
28
|
-
from operate.wallet.master import EthereumMasterWallet
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
if TYPE_CHECKING:
|
|
@@ -66,10 +65,7 @@ def reset_password(operate: "OperateApp") -> None:
|
|
|
66
65
|
|
|
67
66
|
print('Resetting password of "ethereum" wallet...')
|
|
68
67
|
operate.password = old_password
|
|
69
|
-
operate.wallet_manager.
|
|
70
|
-
|
|
71
|
-
ledger_type=LedgerType.ETHEREUM
|
|
72
|
-
)
|
|
73
|
-
wallet.update_password(new_password=new_password)
|
|
68
|
+
operate.wallet_manager.update_password(new_password=new_password)
|
|
69
|
+
KeysManager().update_password(new_password=new_password)
|
|
74
70
|
|
|
75
71
|
print_section("Password reset done!")
|