olas-operate-middleware 0.1.0rc59__py3-none-any.whl → 0.13.2__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.
Files changed (98) hide show
  1. olas_operate_middleware-0.13.2.dist-info/METADATA +75 -0
  2. olas_operate_middleware-0.13.2.dist-info/RECORD +101 -0
  3. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/WHEEL +1 -1
  4. operate/__init__.py +17 -0
  5. operate/account/user.py +35 -9
  6. operate/bridge/bridge_manager.py +470 -0
  7. operate/bridge/providers/lifi_provider.py +377 -0
  8. operate/bridge/providers/native_bridge_provider.py +677 -0
  9. operate/bridge/providers/provider.py +469 -0
  10. operate/bridge/providers/relay_provider.py +457 -0
  11. operate/cli.py +1565 -417
  12. operate/constants.py +60 -12
  13. operate/data/README.md +19 -0
  14. operate/data/contracts/{service_staking_token → dual_staking_token}/__init__.py +2 -2
  15. operate/data/contracts/dual_staking_token/build/DualStakingToken.json +443 -0
  16. operate/data/contracts/dual_staking_token/contract.py +132 -0
  17. operate/data/contracts/dual_staking_token/contract.yaml +23 -0
  18. operate/{ledger/base.py → data/contracts/foreign_omnibridge/__init__.py} +2 -19
  19. operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +1372 -0
  20. operate/data/contracts/foreign_omnibridge/contract.py +130 -0
  21. operate/data/contracts/foreign_omnibridge/contract.yaml +23 -0
  22. operate/{ledger/solana.py → data/contracts/home_omnibridge/__init__.py} +2 -20
  23. operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +1421 -0
  24. operate/data/contracts/home_omnibridge/contract.py +80 -0
  25. operate/data/contracts/home_omnibridge/contract.yaml +23 -0
  26. operate/data/contracts/l1_standard_bridge/__init__.py +20 -0
  27. operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +831 -0
  28. operate/data/contracts/l1_standard_bridge/contract.py +158 -0
  29. operate/data/contracts/l1_standard_bridge/contract.yaml +23 -0
  30. operate/data/contracts/l2_standard_bridge/__init__.py +20 -0
  31. operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +626 -0
  32. operate/data/contracts/l2_standard_bridge/contract.py +130 -0
  33. operate/data/contracts/l2_standard_bridge/contract.yaml +23 -0
  34. operate/data/contracts/mech_activity/__init__.py +20 -0
  35. operate/data/contracts/mech_activity/build/MechActivity.json +111 -0
  36. operate/data/contracts/mech_activity/contract.py +44 -0
  37. operate/data/contracts/mech_activity/contract.yaml +23 -0
  38. operate/data/contracts/optimism_mintable_erc20/__init__.py +20 -0
  39. operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +491 -0
  40. operate/data/contracts/optimism_mintable_erc20/contract.py +45 -0
  41. operate/data/contracts/optimism_mintable_erc20/contract.yaml +23 -0
  42. operate/data/contracts/recovery_module/__init__.py +20 -0
  43. operate/data/contracts/recovery_module/build/RecoveryModule.json +811 -0
  44. operate/data/contracts/recovery_module/contract.py +61 -0
  45. operate/data/contracts/recovery_module/contract.yaml +23 -0
  46. operate/data/contracts/requester_activity_checker/__init__.py +20 -0
  47. operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +111 -0
  48. operate/data/contracts/requester_activity_checker/contract.py +33 -0
  49. operate/data/contracts/requester_activity_checker/contract.yaml +23 -0
  50. operate/data/contracts/staking_token/__init__.py +20 -0
  51. operate/data/contracts/staking_token/build/StakingToken.json +1336 -0
  52. operate/data/contracts/{service_staking_token → staking_token}/contract.py +27 -13
  53. operate/data/contracts/staking_token/contract.yaml +23 -0
  54. operate/data/contracts/uniswap_v2_erc20/contract.yaml +3 -1
  55. operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +20 -0
  56. operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +363 -0
  57. operate/keys.py +118 -33
  58. operate/ledger/__init__.py +159 -56
  59. operate/ledger/profiles.py +321 -18
  60. operate/migration.py +555 -0
  61. operate/{http → operate_http}/__init__.py +3 -2
  62. operate/{http → operate_http}/exceptions.py +6 -4
  63. operate/operate_types.py +544 -0
  64. operate/pearl.py +13 -1
  65. operate/quickstart/analyse_logs.py +118 -0
  66. operate/quickstart/claim_staking_rewards.py +104 -0
  67. operate/quickstart/reset_configs.py +106 -0
  68. operate/quickstart/reset_password.py +70 -0
  69. operate/quickstart/reset_staking.py +145 -0
  70. operate/quickstart/run_service.py +726 -0
  71. operate/quickstart/stop_service.py +72 -0
  72. operate/quickstart/terminate_on_chain_service.py +83 -0
  73. operate/quickstart/utils.py +298 -0
  74. operate/resource.py +62 -3
  75. operate/services/agent_runner.py +202 -0
  76. operate/services/deployment_runner.py +868 -0
  77. operate/services/funding_manager.py +929 -0
  78. operate/services/health_checker.py +280 -0
  79. operate/services/manage.py +2356 -620
  80. operate/services/protocol.py +1246 -340
  81. operate/services/service.py +756 -391
  82. operate/services/utils/mech.py +103 -0
  83. operate/services/utils/tendermint.py +86 -12
  84. operate/settings.py +70 -0
  85. operate/utils/__init__.py +135 -0
  86. operate/utils/gnosis.py +407 -80
  87. operate/utils/single_instance.py +226 -0
  88. operate/utils/ssl.py +133 -0
  89. operate/wallet/master.py +708 -123
  90. operate/wallet/wallet_recovery_manager.py +507 -0
  91. olas_operate_middleware-0.1.0rc59.dist-info/METADATA +0 -304
  92. olas_operate_middleware-0.1.0rc59.dist-info/RECORD +0 -41
  93. operate/data/contracts/service_staking_token/build/ServiceStakingToken.json +0 -1273
  94. operate/data/contracts/service_staking_token/contract.yaml +0 -23
  95. operate/ledger/ethereum.py +0 -48
  96. operate/types.py +0 -260
  97. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/entry_points.txt +0 -0
  98. {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info/licenses}/LICENSE +0 -0
@@ -21,79 +21,182 @@
21
21
 
22
22
  import os
23
23
  import typing as t
24
+ from copy import deepcopy
25
+ from math import ceil
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
31
+
32
+ from operate.operate_types import Chain
33
+
34
+
35
+ CHAINS = [
36
+ Chain.ARBITRUM_ONE,
37
+ Chain.BASE,
38
+ Chain.CELO,
39
+ Chain.ETHEREUM,
40
+ Chain.GNOSIS,
41
+ Chain.MODE,
42
+ Chain.OPTIMISM,
43
+ Chain.POLYGON,
44
+ Chain.SOLANA,
45
+ ]
46
+
47
+ ARBITRUM_ONE_RPC = os.environ.get("ARBITRUM_ONE_RPC", "https://arb1.arbitrum.io/rpc")
48
+ BASE_RPC = os.environ.get("BASE_RPC", "https://mainnet.base.org")
49
+ CELO_RPC = os.environ.get("CELO_RPC", "https://forno.celo.org")
50
+ ETHEREUM_RPC = os.environ.get("ETHEREUM_RPC", "https://ethereum.publicnode.com")
51
+ GNOSIS_RPC = os.environ.get("GNOSIS_RPC", "https://gnosis-rpc.publicnode.com")
52
+ MODE_RPC = os.environ.get("MODE_RPC", "https://mainnet.mode.network")
53
+ OPTIMISM_RPC = os.environ.get("OPTIMISM_RPC", "https://mainnet.optimism.io")
54
+ POLYGON_RPC = os.environ.get("POLYGON_RPC", "https://polygon-rpc.com")
55
+ SOLANA_RPC = os.environ.get("SOLANA_RPC", "https://api.mainnet-beta.solana.com")
24
56
 
25
- from operate.ledger.base import LedgerHelper
26
- from operate.ledger.ethereum import Ethereum
27
- from operate.ledger.solana import Solana
28
- from operate.types import ChainType, LedgerType
29
-
30
-
31
- ETHEREUM_PUBLIC_RPC = os.environ.get("DEV_RPC", "https://ethereum.publicnode.com")
32
- GNOSIS_PUBLIC_RPC = os.environ.get("DEV_RPC", "https://gnosis-rpc.publicnode.com")
33
- GOERLI_PUBLIC_RPC = os.environ.get("DEV_RPC", "https://ethereum-goerli.publicnode.com")
34
- SOLANA_PUBLIC_RPC = os.environ.get("DEV_RPC", "https://api.mainnet-beta.solana.com")
35
-
36
- ETHEREUM_RPC = os.environ.get("DEV_RPC", "https://ethereum.publicnode.com")
37
- GNOSIS_RPC = os.environ.get("DEV_RPC", "https://rpc-gate.autonolas.tech/gnosis-rpc/")
38
- GOERLI_RPC = os.environ.get("DEV_RPC", "https://ethereum-goerli.publicnode.com")
39
- SOLANA_RPC = os.environ.get("DEV_RPC", "https://api.mainnet-beta.solana.com")
40
-
41
- PUBLIC_RPCS = {
42
- ChainType.ETHEREUM: ETHEREUM_PUBLIC_RPC,
43
- ChainType.GNOSIS: GNOSIS_PUBLIC_RPC,
44
- ChainType.GOERLI: GOERLI_PUBLIC_RPC,
45
- ChainType.SOLANA: SOLANA_PUBLIC_RPC,
46
- }
47
57
 
48
58
  DEFAULT_RPCS = {
49
- ChainType.ETHEREUM: ETHEREUM_RPC,
50
- ChainType.GNOSIS: GNOSIS_RPC,
51
- ChainType.GOERLI: GOERLI_RPC,
52
- ChainType.SOLANA: SOLANA_RPC,
59
+ Chain.ARBITRUM_ONE: ARBITRUM_ONE_RPC,
60
+ Chain.BASE: BASE_RPC,
61
+ Chain.CELO: CELO_RPC,
62
+ Chain.ETHEREUM: ETHEREUM_RPC,
63
+ Chain.GNOSIS: GNOSIS_RPC,
64
+ Chain.MODE: MODE_RPC,
65
+ Chain.OPTIMISM: OPTIMISM_RPC,
66
+ Chain.POLYGON: POLYGON_RPC,
67
+ Chain.SOLANA: SOLANA_RPC,
53
68
  }
54
69
 
55
- CHAIN_HELPERS: t.Dict[ChainType, t.Type[LedgerHelper]] = {
56
- ChainType.ETHEREUM: Ethereum,
57
- ChainType.GNOSIS: Ethereum,
58
- ChainType.GOERLI: Ethereum,
59
- ChainType.SOLANA: Solana,
70
+ # Base currency for each chain
71
+ CURRENCY_DENOMS = {
72
+ Chain.ARBITRUM_ONE: "ETH",
73
+ Chain.BASE: "ETH",
74
+ Chain.CELO: "CELO",
75
+ Chain.ETHEREUM: "ETH",
76
+ Chain.GNOSIS: "xDAI",
77
+ Chain.MODE: "ETH",
78
+ Chain.OPTIMISM: "ETH",
79
+ Chain.POLYGON: "POL",
80
+ Chain.SOLANA: "SOL",
60
81
  }
61
82
 
62
- LEDGER_HELPERS: t.Dict[LedgerType, t.Type[LedgerHelper]] = {
63
- LedgerType.ETHEREUM: Ethereum,
64
- LedgerType.SOLANA: Solana,
83
+ # Smallest denomination for each chain
84
+ CURRENCY_SMALLEST_UNITS = {
85
+ Chain.ARBITRUM_ONE: "Wei",
86
+ Chain.BASE: "Wei",
87
+ Chain.CELO: "Wei",
88
+ Chain.ETHEREUM: "Wei",
89
+ Chain.GNOSIS: "Wei",
90
+ Chain.MODE: "Wei",
91
+ Chain.OPTIMISM: "Wei",
92
+ Chain.POLYGON: "Wei",
93
+ Chain.SOLANA: "Lamport",
65
94
  }
66
95
 
67
- CURRENCY_DENOMS = {
68
- ChainType.ETHEREUM: "Wei",
69
- ChainType.GNOSIS: "xDai",
70
- ChainType.GOERLI: "GWei",
71
- ChainType.SOLANA: "Lamp",
72
- }
96
+ NATIVE_CURRENCY_DECIMALS = 18
73
97
 
74
98
 
75
- def get_default_rpc(chain: ChainType) -> str:
99
+ def get_default_rpc(chain: Chain) -> str:
76
100
  """Get default RPC chain type."""
77
- return DEFAULT_RPCS.get(chain, ETHEREUM_RPC)
101
+ return DEFAULT_RPCS[chain]
78
102
 
79
103
 
80
- def get_ledger_type_from_chain_type(chain: ChainType) -> LedgerType:
81
- """Get LedgerType from ChainType."""
82
- if chain in (ChainType.ETHEREUM, ChainType.GOERLI, ChainType.GNOSIS):
83
- return LedgerType.ETHEREUM
84
- return LedgerType.SOLANA
104
+ def get_currency_denom(chain: Chain) -> str:
105
+ """Get currency denom by chain type."""
106
+ return CURRENCY_DENOMS.get(chain, "NATIVE")
85
107
 
86
108
 
87
- def get_ledger_helper_by_chain(rpc: str, chain: ChainType) -> LedgerHelper:
88
- """Get ledger helper by chain type."""
89
- return CHAIN_HELPERS.get(chain, Ethereum)(rpc=rpc)
109
+ def get_currency_smallest_unit(chain: Chain) -> str:
110
+ """Get currency denom by chain type."""
111
+ return CURRENCY_SMALLEST_UNITS.get(chain, "Wei")
90
112
 
91
113
 
92
- def get_ledger_helper_by_ledger(rpc: str, ledger: LedgerHelper) -> LedgerHelper:
93
- """Get ledger helper by ledger type."""
94
- return LEDGER_HELPERS.get(ledger, Ethereum)(rpc=rpc) # type: ignore
114
+ DEFAULT_LEDGER_APIS: t.Dict[Chain, LedgerApi] = {}
95
115
 
96
116
 
97
- def get_currency_denom(chain: ChainType) -> str:
98
- """Get currency denom by chain type."""
99
- return CURRENCY_DENOMS.get(chain, "Wei")
117
+ def make_chain_ledger_api(
118
+ chain: Chain,
119
+ rpc: t.Optional[str] = None,
120
+ ) -> LedgerApi:
121
+ """Get default RPC chain type."""
122
+ if chain == Chain.SOLANA: # TODO: Complete when Solana is supported
123
+ raise NotImplementedError("Solana not yet supported.")
124
+
125
+ gas_price_strategies = deepcopy(DEFAULT_GAS_PRICE_STRATEGIES)
126
+ if chain in (Chain.BASE, Chain.MODE, Chain.OPTIMISM):
127
+ gas_price_strategies[EIP1559]["fallback_estimate"]["maxFeePerGas"] = to_wei(
128
+ 5, GWEI
129
+ )
130
+
131
+ ledger_api = make_ledger_api(
132
+ chain.ledger_type.name.lower(),
133
+ address=rpc or get_default_rpc(chain=chain),
134
+ chain_id=chain.id,
135
+ gas_price_strategies=gas_price_strategies,
136
+ poa_chain=chain == Chain.POLYGON,
137
+ )
138
+
139
+ if chain == Chain.OPTIMISM:
140
+ ledger_api.api.middleware_onion.inject(geth_poa_middleware, layer=0)
141
+
142
+ return ledger_api
143
+
144
+
145
+ def get_default_ledger_api(chain: Chain) -> LedgerApi:
146
+ """Get default RPC chain type."""
147
+ if chain not in DEFAULT_LEDGER_APIS:
148
+ DEFAULT_LEDGER_APIS[chain] = make_chain_ledger_api(
149
+ chain=chain, rpc=get_default_rpc(chain=chain)
150
+ )
151
+ return DEFAULT_LEDGER_APIS[chain]
152
+
153
+
154
+ GAS_ESTIMATE_FALLBACK_ADDRESSES = [
155
+ "0x000000000000000000000000000000000000dEaD",
156
+ "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", # nosec
157
+ ]
158
+ GAS_ESTIMATE_BUFFER = 1.10
159
+
160
+
161
+ # TODO backport to open aea/autonomy
162
+ # TODO This gas pricing management should be done at a lower level in the library
163
+ def update_tx_with_gas_pricing(tx: t.Dict, ledger_api: LedgerApi) -> None:
164
+ """Update transaction with gas pricing."""
165
+ tx.pop("maxFeePerGas", None)
166
+ tx.pop("gasPrice", None)
167
+ tx.pop("maxPriorityFeePerGas", None)
168
+
169
+ gas_pricing = ledger_api.try_get_gas_pricing()
170
+ if gas_pricing is None:
171
+ raise RuntimeError("Unable to retrieve gas pricing.")
172
+
173
+ if "maxFeePerGas" in gas_pricing and "maxPriorityFeePerGas" in gas_pricing:
174
+ tx["maxFeePerGas"] = gas_pricing["maxFeePerGas"]
175
+ tx["maxPriorityFeePerGas"] = gas_pricing["maxPriorityFeePerGas"]
176
+ elif "gasPrice" in gas_pricing:
177
+ tx["gasPrice"] = gas_pricing["gasPrice"]
178
+ else:
179
+ raise RuntimeError("Retrieved invalid gas pricing.")
180
+
181
+
182
+ # TODO backport to open aea/autonomy
183
+ # TODO This gas management should be done at a lower level in the library
184
+ def update_tx_with_gas_estimate(tx: t.Dict, ledger_api: LedgerApi) -> None:
185
+ """Update transaction with gas estimate."""
186
+ print(f"[LEDGER] Trying to update transaction gas {tx['from']=} {tx['gas']=}.")
187
+ original_from = tx["from"]
188
+ original_gas = tx.get("gas", 1)
189
+
190
+ for address in [original_from] + GAS_ESTIMATE_FALLBACK_ADDRESSES:
191
+ tx["from"] = address
192
+ tx["gas"] = 1
193
+ ledger_api.update_with_gas_estimate(tx)
194
+ if tx["gas"] > 1:
195
+ print(f"[LEDGER] Gas estimated successfully {tx['from']=} {tx['gas']=}.")
196
+ break
197
+
198
+ tx["from"] = original_from
199
+ if tx["gas"] == 1:
200
+ tx["gas"] = original_gas
201
+ print(f"[LEDGER] Unable to estimate gas. Restored {tx['gas']=}.")
202
+ tx["gas"] = ceil(tx["gas"] * GAS_ESTIMATE_BUFFER)
@@ -19,26 +19,329 @@
19
19
 
20
20
  """Chain profiles."""
21
21
 
22
- from operate.types import ChainType, ContractAddresses
23
-
24
-
25
- CONTRACTS = {
26
- ChainType.GNOSIS: ContractAddresses(
27
- {
28
- "service_manager": "0x04b0007b2aFb398015B76e5f22993a1fddF83644",
29
- "service_registry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD",
30
- "service_registry_token_utility": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8",
31
- "gnosis_safe_proxy_factory": "0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE",
32
- "gnosis_safe_same_address_multisig": "0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06",
33
- "multisend": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D",
34
- }
35
- )
22
+ import typing as t
23
+ from functools import cache
24
+
25
+ from autonomy.chain.base import registry_contracts
26
+ from autonomy.chain.constants import CHAIN_PROFILES, DEFAULT_MULTISEND
27
+
28
+ from operate.constants import NO_STAKING_PROGRAM_ID, ZERO_ADDRESS
29
+ from operate.ledger import (
30
+ CHAINS,
31
+ NATIVE_CURRENCY_DECIMALS,
32
+ get_currency_denom,
33
+ get_default_ledger_api,
34
+ )
35
+ from operate.operate_types import Chain, ContractAddresses
36
+
37
+
38
+ # TODO: Refactor, remove the usage of CONTRACTS and use CHAIN_PROFILES from Open Autonomy instead.
39
+ CONTRACTS: t.Dict[Chain, ContractAddresses] = {}
40
+ for _chain in CHAINS:
41
+ if _chain.value in CHAIN_PROFILES:
42
+ profile = CHAIN_PROFILES[_chain.value]
43
+ CONTRACTS[_chain] = ContractAddresses(
44
+ {
45
+ "service_registry": profile["service_registry"],
46
+ "service_registry_token_utility": profile[
47
+ "service_registry_token_utility"
48
+ ],
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,
59
+ }
60
+ )
61
+
62
+ STAKING: t.Dict[Chain, t.Dict[str, str]] = {
63
+ Chain.ARBITRUM_ONE: {},
64
+ Chain.GNOSIS: {
65
+ "pearl_alpha": "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A",
66
+ "pearl_beta": "0xeF44Fb0842DDeF59D37f85D61A1eF492bbA6135d",
67
+ "pearl_beta_2": "0x1c2F82413666d2a3fD8bC337b0268e62dDF67434",
68
+ "pearl_beta_3": "0xBd59Ff0522aA773cB6074ce83cD1e4a05A457bc1",
69
+ "pearl_beta_4": "0x3052451e1eAee78e62E169AfdF6288F8791F2918",
70
+ "pearl_beta_5": "0x4Abe376Fda28c2F43b84884E5f822eA775DeA9F4",
71
+ "pearl_beta_6": "0x6C6D01e8eA8f806eF0c22F0ef7ed81D868C1aB39",
72
+ "pearl_beta_mech_marketplace": "0xDaF34eC46298b53a3d24CBCb431E84eBd23927dA",
73
+ "quickstart_beta_hobbyist": "0x389B46c259631Acd6a69Bde8B6cEe218230bAE8C",
74
+ "quickstart_beta_hobbyist_2": "0x238EB6993b90a978ec6AAD7530d6429c949C08DA",
75
+ "quickstart_beta_expert": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e",
76
+ "quickstart_beta_expert_2": "0xb964e44c126410df341ae04B13aB10A985fE3513",
77
+ "quickstart_beta_expert_3": "0x80faD33Cadb5F53f9D29F02Db97D682E8b101618",
78
+ "quickstart_beta_expert_4": "0xaD9d891134443B443D7F30013c7e14Fe27F2E029",
79
+ "quickstart_beta_expert_5": "0xE56dF1E563De1B10715cB313D514af350D207212",
80
+ "quickstart_beta_expert_6": "0x2546214aEE7eEa4bEE7689C81231017CA231Dc93",
81
+ "quickstart_beta_expert_7": "0xD7A3C8b975f71030135f1a66e9e23164d54fF455",
82
+ "quickstart_beta_expert_8": "0x356C108D49C5eebd21c84c04E9162de41933030c",
83
+ "quickstart_beta_expert_9": "0x17dBAe44BC5618Cc254055b386A29576b4F87015",
84
+ "quickstart_beta_expert_10": "0xB0ef657b8302bd2c74B6E6D9B2b4b39145b19c6f",
85
+ "quickstart_beta_expert_11": "0x3112c1613eAC3dBAE3D4E38CeF023eb9E2C91CF7",
86
+ "quickstart_beta_expert_12": "0xF4a75F476801B3fBB2e7093aCDcc3576593Cc1fc",
87
+ "quickstart_beta_expert_15_mech_marketplace": "0x88eB38FF79fBa8C19943C0e5Acfa67D5876AdCC1",
88
+ "quickstart_beta_expert_16_mech_marketplace": "0x6c65430515c70a3f5E62107CC301685B7D46f991",
89
+ "quickstart_beta_expert_17_mech_marketplace": "0x1430107A785C3A36a0C1FC0ee09B9631e2E72aFf",
90
+ "quickstart_beta_expert_18_mech_marketplace": "0x041e679d04Fc0D4f75Eb937Dea729Df09a58e454",
91
+ "pearl_beta_mech_marketplace_1": "0xAb10188207Ea030555f53C8A84339A92f473aa5e",
92
+ "pearl_beta_mech_marketplace_2": "0x8d7bE092d154b01d404f1aCCFA22Cef98C613B5D",
93
+ "pearl_beta_mech_marketplace_3": "0x9D00A0551F20979080d3762005C9B74D7Aa77b85",
94
+ "pearl_beta_mech_marketplace_4": "0xE2f80659dB1069f3B6a08af1A62064190c119543",
95
+ "quickstart_beta_mech_marketplace_expert_1": "0xdB9E2713c3dA3C403F2eA6E570eB978b00304e9E",
96
+ "quickstart_beta_mech_marketplace_expert_2": "0x1E90522b45c771DCF5f79645B9e96551d2ECaF62",
97
+ "quickstart_beta_mech_marketplace_expert_3": "0x75EECA6207be98cAc3fDE8a20eCd7B01e50b3472",
98
+ "quickstart_beta_mech_marketplace_expert_4": "0x9c7F6103e3a72E4d1805b9C683Ea5B370Ec1a99f",
99
+ "quickstart_beta_mech_marketplace_expert_5": "0xcdC603e0Ee55Aae92519f9770f214b2Be4967f7d",
100
+ "quickstart_beta_mech_marketplace_expert_6": "0x22D6cd3d587D8391C3aAE83a783f26c67ab54A85",
101
+ "quickstart_beta_mech_marketplace_expert_7": "0xaaEcdf4d0CBd6Ca0622892Ac6044472f3912A5f3",
102
+ "quickstart_beta_mech_marketplace_expert_8": "0x168aED532a0CD8868c22Fc77937Af78b363652B1",
103
+ "quickstart_beta_mech_marketplace_expert_9": "0xdDa9cD214F12e7C2D58E871404A0A3B1177065C8",
104
+ "quickstart_beta_mech_marketplace_expert_10": "0x53a38655B4e659eF4C7F88A26fbF5c67932C7156",
105
+ "mech_marketplace": "0x998dEFafD094817EF329f6dc79c703f1CF18bC90",
106
+ "marketplace_supply_alpha": "0xCAbD0C941E54147D40644CF7DA7e36d70DF46f44",
107
+ "marketplace_demand_alpha_1": "0x9d6e7aB0B5B48aE5c146936147C639fEf4575231",
108
+ "marketplace_demand_alpha_2": "0x9fb17E549FefcCA630dd92Ea143703CeE4Ea4340",
109
+ },
110
+ Chain.OPTIMISM: {
111
+ "optimus_alpha_1": "0x88996bbdE7f982D93214881756840cE2c77C4992",
112
+ "optimus_alpha_2": "0xBCA056952D2A7a8dD4A002079219807CFDF9fd29",
113
+ "optimus_alpha_3": "0x0f69f35652B1acdbD769049334f1AC580927E139",
114
+ "optimus_alpha_4": "0x6891Cf116f9a3bDbD1e89413118eF81F69D298C3",
115
+ },
116
+ Chain.ETHEREUM: {},
117
+ Chain.BASE: {
118
+ "meme_base_alpha_2": "0xc653622FD75026a020995a1d8c8651316cBBc4dA",
119
+ "meme_base_beta": "0x6011E09e7c095e76980b22498d69dF18EB62BeD8",
120
+ "meme_base_beta_2": "0xfb7669c3AdF673b3A545Fa5acd987dbfdA805e22",
121
+ "meme_base_beta_3": "0xCA61633b03c54F64b6A7F1f9A9C0A6Feb231Cc4D",
122
+ "marketplace_supply_alpha": "0xB14Cd66c6c601230EA79fa7Cc072E5E0C2F3A756",
123
+ "marketplace_demand_alpha_1": "0x38Eb3838Dab06932E7E1E965c6F922aDfE494b88",
124
+ "marketplace_demand_alpha_2": "0xBE6E12364B549622395999dB0dB53f163994D7AF",
125
+ "agents_fun_1": "0x2585e63df7BD9De8e058884D496658a030b5c6ce",
126
+ "agents_fun_2": "0x26FA75ef9Ccaa60E58260226A71e9d07564C01bF",
127
+ "agents_fun_3": "0x4D4233EBF0473Ca8f34d105A6256A2389176F0Ce",
128
+ },
129
+ Chain.CELO: {
130
+ "meme_celo_alpha_2": "0x95D12D193d466237Bc1E92a1a7756e4264f574AB",
131
+ },
132
+ Chain.MODE: {
133
+ "optimus_alpha": "0x5fc25f50E96857373C64dC0eDb1AbCBEd4587e91",
134
+ "modius_alpha": "0x534C0A05B6d4d28d5f3630D6D74857B253cf8332",
135
+ "modius_alpha_2": "0xeC013E68FE4B5734643499887941eC197fd757D0",
136
+ "modius_alpha_3": "0x9034D0413D122015710f1744A19eFb1d7c2CEB13",
137
+ "modius_alpha_4": "0x8BcAdb2c291C159F9385964e5eD95a9887302862",
138
+ },
139
+ Chain.POLYGON: {},
36
140
  }
37
141
 
38
- STAKING = {
39
- ChainType.GNOSIS: "0xEE9F19b5DF06c7E8Bfc7B28745dcf944C504198A",
142
+
143
+ DEFAULT_PRIORITY_MECH = { # maps mech marketplace address to its default priority mech address and service id
144
+ "0x4554fE75c1f5576c1d7F765B2A036c199Adae329": (
145
+ "0x552cEA7Bc33CbBEb9f1D90c1D11D2C6daefFd053",
146
+ 975,
147
+ ),
148
+ "0x735FAAb1c4Ec41128c367AFb5c3baC73509f70bB": (
149
+ "0xC05e7412439bD7e91730a6880E18d5D5873F632C",
150
+ 2182,
151
+ ),
40
152
  }
41
153
 
42
- OLAS = {
43
- ChainType.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
154
+
155
+ # ERC20 token addresses
156
+ OLAS: t.Dict[Chain, str] = {
157
+ Chain.ARBITRUM_ONE: "0x064F8B858C2A603e1b106a2039f5446D32dc81c1",
158
+ Chain.BASE: "0x54330d28ca3357F294334BDC454a032e7f353416",
159
+ Chain.CELO: "0xaCFfAe8e57Ec6E394Eb1b41939A8CF7892DbDc51",
160
+ Chain.ETHEREUM: "0x0001A500A6B18995B03f44bb040A5fFc28E45CB0",
161
+ Chain.GNOSIS: "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f",
162
+ Chain.MODE: "0xcfD1D50ce23C46D3Cf6407487B2F8934e96DC8f9",
163
+ Chain.OPTIMISM: "0xFC2E6e6BCbd49ccf3A5f029c79984372DcBFE527",
164
+ Chain.POLYGON: "0xFEF5d947472e72Efbb2E388c730B7428406F2F95",
44
165
  }
166
+
167
+ USDC: t.Dict[Chain, str] = {
168
+ Chain.ARBITRUM_ONE: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
169
+ Chain.BASE: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
170
+ Chain.CELO: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
171
+ Chain.ETHEREUM: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
172
+ Chain.GNOSIS: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
173
+ Chain.MODE: "0xd988097fb8612cc24eeC14542bC03424c656005f",
174
+ Chain.OPTIMISM: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
175
+ Chain.POLYGON: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
176
+ }
177
+
178
+ WRAPPED_NATIVE_ASSET = {
179
+ Chain.ARBITRUM_ONE: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
180
+ Chain.BASE: "0x4200000000000000000000000000000000000006",
181
+ Chain.CELO: "0x471EcE3750Da237f93B8E339c536989b8978a438", # Dual token
182
+ Chain.ETHEREUM: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
183
+ Chain.GNOSIS: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
184
+ Chain.MODE: "0x4200000000000000000000000000000000000006",
185
+ Chain.OPTIMISM: "0x4200000000000000000000000000000000000006",
186
+ Chain.POLYGON: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
187
+ }
188
+
189
+ ERC20_TOKENS = {
190
+ "OLAS": OLAS,
191
+ "USDC": USDC,
192
+ "WRAPPED_NATIVE": WRAPPED_NATIVE_ASSET,
193
+ }
194
+
195
+ DUST = {
196
+ Chain.ARBITRUM_ONE: int(1e14),
197
+ Chain.BASE: int(1e14),
198
+ Chain.CELO: int(1e15),
199
+ Chain.ETHEREUM: int(1e14),
200
+ Chain.GNOSIS: int(1e15),
201
+ Chain.MODE: int(1e14),
202
+ Chain.OPTIMISM: int(1e14),
203
+ Chain.POLYGON: int(1e14),
204
+ }
205
+
206
+ DEFAULT_NEW_SAFE_FUNDS: t.Dict[Chain, t.Dict[str, int]] = {
207
+ Chain.ARBITRUM_ONE: {
208
+ ZERO_ADDRESS: int(1e15 / 4),
209
+ },
210
+ Chain.BASE: {
211
+ ZERO_ADDRESS: int(1e15 / 4),
212
+ },
213
+ Chain.CELO: {
214
+ ZERO_ADDRESS: int(1e18),
215
+ },
216
+ Chain.ETHEREUM: {
217
+ ZERO_ADDRESS: int(1e15 / 4),
218
+ },
219
+ Chain.GNOSIS: {
220
+ ZERO_ADDRESS: int(1e18),
221
+ },
222
+ Chain.MODE: {
223
+ ZERO_ADDRESS: int(1e15 / 4),
224
+ },
225
+ Chain.OPTIMISM: {
226
+ ZERO_ADDRESS: int(1e15 / 4),
227
+ },
228
+ Chain.POLYGON: {
229
+ ZERO_ADDRESS: int(1e18),
230
+ },
231
+ }
232
+
233
+ DEFAULT_EOA_TOPUPS = {
234
+ Chain.ARBITRUM_ONE: {ZERO_ADDRESS: 2_500_000_000_000_000},
235
+ Chain.BASE: {ZERO_ADDRESS: 2_500_000_000_000_000},
236
+ Chain.CELO: {ZERO_ADDRESS: 750_000_000_000_000_000},
237
+ Chain.ETHEREUM: {ZERO_ADDRESS: 10_000_000_000_000_000},
238
+ Chain.GNOSIS: {ZERO_ADDRESS: 750_000_000_000_000_000},
239
+ Chain.MODE: {ZERO_ADDRESS: 250_000_000_000_000},
240
+ Chain.OPTIMISM: {ZERO_ADDRESS: 2_500_000_000_000_000},
241
+ Chain.POLYGON: {ZERO_ADDRESS: 750_000_000_000_000_000},
242
+ }
243
+
244
+ DEFAULT_EOA_TOPUPS_WITHOUT_SAFE = {
245
+ chain: {asset: amount * 2 for asset, amount in amounts.items()}
246
+ for chain, amounts in DEFAULT_EOA_TOPUPS.items()
247
+ }
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
+
260
+ DEFAULT_EOA_THRESHOLD = 0.5
261
+
262
+ EXPLORER_URL = {
263
+ Chain.ARBITRUM_ONE: {
264
+ "tx": "https://arbiscan.io/tx/{tx_hash}",
265
+ "address": "https://arbiscan.io/address/{address}",
266
+ },
267
+ Chain.BASE: {
268
+ "tx": "https://basescan.org/tx/{tx_hash}",
269
+ "address": "https://basescan.org/address/{address}",
270
+ },
271
+ Chain.CELO: {
272
+ "tx": "https://celoscan.io/tx/{tx_hash}",
273
+ "address": "https://celoscan.io/address/{address}",
274
+ },
275
+ Chain.ETHEREUM: {
276
+ "tx": "https://etherscan.io/tx/{tx_hash}",
277
+ "address": "https://etherscan.io/address/{address}",
278
+ },
279
+ Chain.GNOSIS: {
280
+ "tx": "https://gnosisscan.io/tx/{tx_hash}",
281
+ "address": "https://gnosisscan.io/address/{address}",
282
+ },
283
+ Chain.MODE: {
284
+ "tx": "https://explorer.mode.network/tx/{tx_hash}",
285
+ "address": "https://explorer.mode.network/address/{address}",
286
+ },
287
+ Chain.OPTIMISM: {
288
+ "tx": "https://optimistic.etherscan.io/tx/{tx_hash}",
289
+ "address": "https://optimistic.etherscan.io/address/{address}",
290
+ },
291
+ Chain.POLYGON: {
292
+ "tx": "https://polygonscan.com/tx/{tx_hash}",
293
+ "address": "https://polygonscan.com/address/{address}",
294
+ },
295
+ }
296
+
297
+
298
+ @cache
299
+ def get_asset_name(chain: Chain, asset_address: str) -> str:
300
+ """Get token name."""
301
+ if asset_address == ZERO_ADDRESS:
302
+ return get_currency_denom(chain)
303
+
304
+ if WRAPPED_NATIVE_ASSET.get(chain) == asset_address:
305
+ return f"W{get_currency_denom(chain)}"
306
+
307
+ for symbol, tokens in ERC20_TOKENS.items():
308
+ if tokens.get(chain) == asset_address:
309
+ return symbol
310
+
311
+ return asset_address
312
+
313
+
314
+ @cache
315
+ def get_asset_decimals(chain: Chain, asset_address: str) -> int:
316
+ """Get token decimals."""
317
+ if asset_address == ZERO_ADDRESS:
318
+ return NATIVE_CURRENCY_DECIMALS
319
+ erc20_token = registry_contracts.erc20.get_instance(
320
+ ledger_api=get_default_ledger_api(chain),
321
+ contract_address=asset_address,
322
+ )
323
+ return erc20_token.functions.decimals().call()
324
+
325
+
326
+ def format_asset_amount(
327
+ chain: Chain, asset_address: str, amount: int, precision: int = 4
328
+ ) -> str:
329
+ """Convert smallest unit to human-readable string with token symbol."""
330
+ decimals = get_asset_decimals(chain, asset_address)
331
+ symbol = get_asset_name(chain, asset_address)
332
+ value = amount / (10**decimals)
333
+ return f"{value:.{precision}f} {symbol}"
334
+
335
+
336
+ # TODO: Deprecate in favour of StakingManager method
337
+ def get_staking_contract(
338
+ chain: str, staking_program_id: t.Optional[str]
339
+ ) -> t.Optional[str]:
340
+ """Get staking contract based on the config and the staking program."""
341
+ if staking_program_id == NO_STAKING_PROGRAM_ID or staking_program_id is None:
342
+ return None
343
+
344
+ return STAKING[Chain(chain)].get(
345
+ staking_program_id,
346
+ staking_program_id,
347
+ )