From dd7c8fa27a80b257ac463c1f854d01dea3fa50da Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 6 Aug 2024 12:31:30 +0200 Subject: [PATCH 01/13] chore: refactor staking choices --- scripts/choose_staking.py | 163 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 scripts/choose_staking.py diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py new file mode 100644 index 00000000..cacee326 --- /dev/null +++ b/scripts/choose_staking.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2023-2024 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +import argparse +import requests +import textwrap +from dotenv import dotenv_values, set_key, unset_key +from pathlib import Path + +SCRIPT_PATH = Path(__file__).resolve().parent +STORE_PATH = Path(SCRIPT_PATH, "..", ".trader_runner") +DOTENV_PATH = Path(STORE_PATH, ".env") + + +def _fetch_json(url): + response = requests.get(url) + response.raise_for_status() + return response.json() + + +staking_programs = { + "quickstart_beta_hobbyist": { + "name": "Quickstart Beta - Hobbyist", + "description": "The Quickstart Beta - Hobbyist staking contract offers 100 slots for operators running Olas Predict agents with the quickstart. It is designed as a step up from Coastal Staker Expeditions, requiring 100 OLAS for staking. The rewards are also more attractive than with Coastal Staker Expeditions.", + "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/e8c023886ebf1f10f2ccd3519a934c1f2590b78d/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json"), + "stakingProxyAddress": "0x389B46c259631Acd6a69Bde8B6cEe218230bAE8C" + }, + "quickstart_beta_expert": { + "name": "Quickstart Beta - Expert", + "description": "The Quickstart Beta - Expert staking contract offers 20 slots for operators running Olas Predict agents with the quickstart. It is designed for professional agent operators, requiring 1000 OLAS for staking. The rewards are proportional to the Quickstart Beta - Hobbyist.", + "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/e8c023886ebf1f10f2ccd3519a934c1f2590b78d/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json"), + "stakingProxyAddress": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e" + } +} + +no_staking = { + "name": "No staking", + "description": "No staking", + "deployment": { + "gnosisSafeAddress": "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", + "gnosisSafeProxyFactoryAddress": "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", + "serviceRegistryAddress": "0x9338b5153AE39BB89f50468E608eD9d764B755fD", + "serviceRegistryTokenUtilityAddress": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", + "olasAddress": "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", + "stakingTokenAddress": "0xEa00be6690a871827fAfD705440D20dd75e67AB1", + "agentMechAddress": "0x77af31De935740567Cf4fF1986D04B2c964A786a", + "mechActivityCheckerAddress": "0x87E6a97bD97D41904B1125A014B16bec50C6A89D", + "stakingFactoryAddress": "0xb0228CA253A88Bc8eb4ca70BCAC8f87b381f4700", + "stakingParams": { + "agentIds": [ + "25" + ], + "serviceRegistry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD", + "activityChecker": "0x87E6a97bD97D41904B1125A014B16bec50C6A89D" + }, + "stakingTokenInstanceAddress": "" + }, + "stakingProxyAddress": "0x0000000000000000000000000000000000000000" +} + + +def _prompt_use_staking(): + env_file_vars = dotenv_values(DOTENV_PATH) + + if 'USE_STAKING' in env_file_vars: + return + + print("Use staking?") + print("------------") + + while True: + use_staking = input("Do you want to stake this service? (yes/no): ").strip().lower() + + if use_staking in ('yes', 'y'): + use_staking_value = "true" + break + elif use_staking in ('no', 'n'): + use_staking_value = "false" + break + else: + print("Please enter 'yes' or 'no'.") + + set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set=use_staking_value, quote_mode="never") + print("") + + +def _prompt_select_staking_program(): + env_file_vars = dotenv_values(DOTENV_PATH) + + if 'USE_STAKING' not in env_file_vars: + print("'USE_STAKING' is not defined.") + return + + if not env_file_vars.get('USE_STAKING').lower() == "true": + print("'USE_STAKING' is not set to 'true'.") + selected_program = no_staking + elif 'STAKING_PROGRAM' in env_file_vars: + print("The staking program is already selected.") + selected_key = env_file_vars.get('STAKING_PROGRAM') + else: + print("Select a staking program") + print("------------------------") + program_keys = list(staking_programs.keys()) + for index, key in enumerate(program_keys): + program = staking_programs[key] + wrapped_description = textwrap.fill(program['description'], width=80, initial_indent=' ', subsequent_indent=' ') + print(f"{index + 1}) {program['name']}\n{wrapped_description}\n") + + while True: + try: + choice = int(input(f"Enter your choice (1 - {len(program_keys)}): ")) - 1 + if not (0 <= choice < len(program_keys)): + raise ValueError + selected_key = program_keys[choice] + break + except ValueError: + print(f"Please enter a valid option (1 - {len(program_keys)}).") + + selected_program = staking_programs[selected_key] + print(f"Selected staking program: {selected_program['name']}") + + print("Setting the staking program variables in the .env file") + set_key(dotenv_path=DOTENV_PATH, key_to_set="STAKING_PROGRAM", value_to_set=selected_key) + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_ADDRESS", value_to_set=selected_program["deployment"]["serviceRegistryAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_STAKING_ADDRESS", value_to_set=selected_program["stakingProxyAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_OLAS_ADDRESS", value_to_set=selected_program["deployment"]["olasAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS", value_to_set=selected_program["deployment"]["serviceRegistryTokenUtilityAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_CONTRACT_ADDRESS", value_to_set=selected_program["deployment"]["agentMechAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_ACTIVITY_CHECKER_CONTRACT", value_to_set=selected_program["deployment"]["mechActivityCheckerAddress"]) + set_key(dotenv_path=DOTENV_PATH, key_to_set="AGENT_ID", value_to_set=selected_program["deployment"]["stakingParams"]["agentIds"][0], quote_mode="never") + print("") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Set up staking configuration.') + parser.add_argument('--reset', action='store_true', help='Reset USE_STAKING and STAKING_PROGRAM in .env file') + args = parser.parse_args() + + if args.reset: + unset_key(dotenv_path=DOTENV_PATH, key_to_unset="USE_STAKING") + unset_key(dotenv_path=DOTENV_PATH, key_to_unset="STAKING_PROGRAM") + print("Environment variables USE_STAKING and STAKING_PROGRAM have been reset.") + print("") + + _prompt_use_staking() + _prompt_select_staking_program() From 39d9eef89c3877fb485e3ab9ea060d319557cc91 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 6 Aug 2024 12:32:02 +0200 Subject: [PATCH 02/13] chore: refactor staking choices --- run_service.sh | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/run_service.sh b/run_service.sh index c8133f04..21108f7a 100755 --- a/run_service.sh +++ b/run_service.sh @@ -430,30 +430,6 @@ perform_staking_ops() { echo "" } -# Prompt user for staking preference -prompt_use_staking() { - while true; do - echo "Use staking?" - echo "------------" - read -p "Do you want to stake this service? (yes/no): " use_staking - - case "$use_staking" in - [Yy]|[Yy][Ee][Ss]) - USE_STAKING="true" - break - ;; - [Nn]|[Nn][Oo]) - USE_STAKING="false" - break - ;; - *) - echo "Please enter 'yes' or 'no'." - ;; - esac - done - echo "" -} - # Prompt user for subgraph API key prompt_subgraph_api_key() { echo "Provide a Subgraph API key" @@ -639,10 +615,7 @@ try_read_storage() { fi # INFO: This is a fix to avoid corrupting already-created stores - if [ -z "$USE_STAKING" ]; then - prompt_use_staking - dotenv_set_key "$env_file_path" "USE_STAKING" "$USE_STAKING" - fi + cd trader; poetry run python "../scripts/choose_staking.py"; cd .. # INFO: This is a fix to avoid corrupting already-created stores if [ -z "$AGENT_ID" ]; then @@ -684,15 +657,10 @@ suggested_safe_top_up_default=500000000000000000 export RPC_RETRIES=40 export RPC_TIMEOUT_SECONDS=120 export CUSTOM_SERVICE_MANAGER_ADDRESS="0x04b0007b2aFb398015B76e5f22993a1fddF83644" -export CUSTOM_SERVICE_REGISTRY_ADDRESS="0x9338b5153AE39BB89f50468E608eD9d764B755fD" -export CUSTOM_STAKING_ADDRESS="0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" -export CUSTOM_OLAS_ADDRESS="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" -export CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" export CUSTOM_GNOSIS_SAFE_PROXY_FACTORY_ADDRESS="0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE" export CUSTOM_GNOSIS_SAFE_SAME_ADDRESS_MULTISIG_ADDRESS="0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06" export CUSTOM_MULTISEND_ADDRESS="0x40A2aCCbd92BCA938b02010E17A5b8929b49130D" export WXDAI_ADDRESS="0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" -export MECH_CONTRACT_ADDRESS="0x77af31De935740567Cf4fF1986D04B2c964A786a" # check if USE_NEVERMINED is set to true if [ "$USE_NEVERMINED" == "true" ]; From 89aefc395a1dc338dbbc02e9a502af565f2eb3d1 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 6 Aug 2024 21:50:12 +0200 Subject: [PATCH 03/13] chore: refactor staking choices --- report.py | 90 ++++++++-------------- run_service.sh | 60 +++++++-------- scripts/choose_staking.py | 158 +++++++++++++++++++++++++------------- 3 files changed, 165 insertions(+), 143 deletions(-) diff --git a/report.py b/report.py index 0312038d..268234c9 100644 --- a/report.py +++ b/report.py @@ -25,9 +25,12 @@ import time import traceback from argparse import ArgumentParser +from dotenv import dotenv_values from enum import Enum from pathlib import Path -from typing import Any +import requests +import sys +from typing import Any, List import docker import trades @@ -46,48 +49,12 @@ SCRIPT_PATH = Path(__file__).resolve().parent STORE_PATH = Path(SCRIPT_PATH, ".trader_runner") +DOTENV_PATH = Path(STORE_PATH, ".env") RPC_PATH = Path(STORE_PATH, "rpc.txt") AGENT_KEYS_JSON_PATH = Path(STORE_PATH, "keys.json") OPERATOR_KEYS_JSON_PATH = Path(STORE_PATH, "operator_keys.json") SAFE_ADDRESS_PATH = Path(STORE_PATH, "service_safe_address.txt") SERVICE_ID_PATH = Path(STORE_PATH, "service_id.txt") -SERVICE_STAKING_CONTRACT_ADDRESS = "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" -SERVICE_STAKING_TOKEN_JSON_PATH = Path( - SCRIPT_PATH, - "trader", - "packages", - "valory", - "contracts", - "service_staking_token", - "build", - "ServiceStakingToken.json", -) -SERVICE_REGISTRY_L2_JSON_PATH = Path( - SCRIPT_PATH, - "trader", - "packages", - "valory", - "contracts", - "service_registry", - "build", - "ServiceRegistryL2.json", -) -SERVICE_REGISTRY_TOKEN_UTILITY_JSON_PATH = Path( - SCRIPT_PATH, - "contracts", - "ServiceRegistryTokenUtility.json", -) -MECH_CONTRACT_ADDRESS = "0x77af31De935740567Cf4fF1986D04B2c964A786a" -MECH_CONTRACT_JSON_PATH = Path( - SCRIPT_PATH, - "trader", - "packages", - "valory", - "contracts", - "mech", - "build", - "mech.json", -) SAFE_BALANCE_THRESHOLD = 500000000000000000 AGENT_XDAI_BALANCE_THRESHOLD = 50000000000000000 @@ -134,6 +101,23 @@ def _color_percent(p: float, multiplier: float = 100, symbol: str = "%") -> str: return _color_string(f"{p*multiplier:.2f} {symbol}", ColorCode.RED) +def _get_abi(contract_address: str) -> List: + contract_abi_url = "https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}" + response = requests.get(contract_abi_url.format(contract_address=contract_address)).json() + + if "result" in response: + result = response["result"] + try: + abi = json.loads(result) + except json.JSONDecodeError: + print("Error: Failed to parse 'result' field as JSON") + sys.exit(1) + else: + abi = response.get("abi") + + return abi if abi else [] + + def _trades_since_message(trades_json: dict[str, Any], utc_ts: float = 0) -> str: filtered_trades = [ trade @@ -228,6 +212,8 @@ def _parse_args() -> Any: with open(RPC_PATH, "r", encoding="utf-8") as file: rpc = file.read().strip() + env_file_vars = dotenv_values(DOTENV_PATH) + # Prediction market trading mech_requests = trades.get_mech_requests(safe_address) mech_statistics = trades.get_mech_statistics(mech_requests) @@ -247,12 +233,11 @@ def _parse_args() -> Any: try: w3 = Web3(HTTPProvider(rpc)) - with open(SERVICE_STAKING_TOKEN_JSON_PATH, "r", encoding="utf-8") as file: - service_staking_token_data = json.load(file) - service_staking_token_abi = service_staking_token_data.get("abi", []) + service_staking_contract_address = env_file_vars.get("CUSTOM_STAKING_ADDRESS") + service_staking_token_abi = _get_abi(service_staking_contract_address) service_staking_token_contract = w3.eth.contract( - address=SERVICE_STAKING_CONTRACT_ADDRESS, abi=service_staking_token_abi + address=service_staking_contract_address, abi=service_staking_token_abi # type: ignore ) service_staking_state = StakingState( service_staking_token_contract.functions.getServiceStakingState( @@ -265,36 +250,27 @@ def _parse_args() -> Any: or service_staking_state == StakingState.EVICTED ) _print_status("Is service staked?", _color_bool(is_staked, "Yes", "No")) + if is_staked: + _print_status("Staking program", env_file_vars.get("STAKING_PROGRAM")) # type: ignore if service_staking_state == StakingState.STAKED: _print_status("Staking state", service_staking_state.name) elif service_staking_state == StakingState.EVICTED: _print_status("Staking state", _color_string(service_staking_state.name, ColorCode.RED)) - if is_staked: - with open( - SERVICE_REGISTRY_TOKEN_UTILITY_JSON_PATH, "r", encoding="utf-8" - ) as file: - service_registry_token_utility_data = json.load(file) - service_registry_token_utility_contract_address = ( service_staking_token_contract.functions.serviceRegistryTokenUtility().call() ) - service_registry_token_utility_abi = ( - service_registry_token_utility_data.get("abi", []) - ) + service_registry_token_utility_abi = _get_abi(service_registry_token_utility_contract_address) service_registry_token_utility_contract = w3.eth.contract( address=service_registry_token_utility_contract_address, abi=service_registry_token_utility_abi, ) - with open(MECH_CONTRACT_JSON_PATH, "r", encoding="utf-8") as file: - mech_contract_data = json.load(file) - - mech_contract_abi = mech_contract_data.get("abi", []) - + mech_contract_address = env_file_vars.get("MECH_CONTRACT_ADDRESS") + mech_contract_abi = _get_abi(mech_contract_address) mech_contract = w3.eth.contract( - address=MECH_CONTRACT_ADDRESS, abi=mech_contract_abi + address=mech_contract_address, abi=mech_contract_abi # type: ignore ) security_deposit = ( diff --git a/run_service.sh b/run_service.sh index 21108f7a..4a593f5f 100755 --- a/run_service.sh +++ b/run_service.sh @@ -493,6 +493,15 @@ dotenv_set_key() { export "$key_to_set=$value_to_set" } +export_dotenv() { + local dotenv_path="$1" + unamestr=$(uname) + if [ "$unamestr" = 'Linux' ]; then + export $(grep -v '^#' $dotenv_path | xargs -d '\n') + elif [ "$unamestr" = 'FreeBSD' ] || [ "$unamestr" = 'Darwin' ]; then + export $(grep -v '^#' $dotenv_path | xargs -0) + fi +} store=".trader_runner" path_to_store="$PWD/$store/" @@ -520,8 +529,6 @@ create_storage() { ask_confirm_password - # Prompt use staking - prompt_use_staking prompt_subgraph_api_key verify_staking_slots @@ -533,10 +540,6 @@ create_storage() { ' Please back up this folder and be cautious if you are modifying or sharing these files to avoid potential asset loss.' > "../$store_readme_path" dotenv_set_key "../$env_file_path" "SUBGRAPH_API_KEY" "$SUBGRAPH_API_KEY" true - dotenv_set_key "../$env_file_path" "USE_STAKING" "$USE_STAKING" - - AGENT_ID=14 - dotenv_set_key "../$env_file_path" "AGENT_ID" "$AGENT_ID" # Generate the RPC file echo -n "$rpc" > "../$rpc_path" @@ -597,8 +600,6 @@ try_read_storage() { fi done - unset USE_STAKING - unset AGENT_ID source "$env_file_path" rpc=$(cat $rpc_path) @@ -614,15 +615,6 @@ try_read_storage() { dotenv_set_key "$env_file_path" "SUBGRAPH_API_KEY" "$SUBGRAPH_API_KEY" true fi - # INFO: This is a fix to avoid corrupting already-created stores - cd trader; poetry run python "../scripts/choose_staking.py"; cd .. - - # INFO: This is a fix to avoid corrupting already-created stores - if [ -z "$AGENT_ID" ]; then - AGENT_ID=14 - dotenv_set_key "$env_file_path" "AGENT_ID" "$AGENT_ID" - fi - ask_password_if_needed else first_run=true @@ -656,21 +648,19 @@ suggested_safe_top_up_default=500000000000000000 export RPC_RETRIES=40 export RPC_TIMEOUT_SECONDS=120 + +# export CUSTOM_SERVICE_REGISTRY_ADDRESS="0x9338b5153AE39BB89f50468E608eD9d764B755fD" # Set up in .env file +# export CUSTOM_STAKING_ADDRESS="0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" # Set up in .env file +# export CUSTOM_OLAS_ADDRESS="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" # Set up in .env file +# export CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" # Set up in .env file +# export MECH_CONTRACT_ADDRESS="0x77af31De935740567Cf4fF1986D04B2c964A786a" # Set up in env file + export CUSTOM_SERVICE_MANAGER_ADDRESS="0x04b0007b2aFb398015B76e5f22993a1fddF83644" export CUSTOM_GNOSIS_SAFE_PROXY_FACTORY_ADDRESS="0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE" export CUSTOM_GNOSIS_SAFE_SAME_ADDRESS_MULTISIG_ADDRESS="0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06" export CUSTOM_MULTISEND_ADDRESS="0x40A2aCCbd92BCA938b02010E17A5b8929b49130D" export WXDAI_ADDRESS="0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" -# check if USE_NEVERMINED is set to true -if [ "$USE_NEVERMINED" == "true" ]; -then - echo "A Nevermined subscription will be used to pay for the mech requests." - export MECH_CONTRACT_ADDRESS="0x327E26bDF1CfEa50BFAe35643B23D5268E41F7F9" - export AGENT_REGISTRY_ADDRESS="0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" - export MECH_REQUEST_PRICE=0 -fi - sleep_duration=12 echo "" @@ -832,12 +822,20 @@ echo "" echo "-----------------------------------------" echo "Checking Autonolas Protocol service state" echo "-----------------------------------------" +echo "" -# We set by default AGENT_ID=14. In Everest the AGENT_ID was 12. -# This script does not allow to stake on Everest anymore, therefore -# all stores must be correctly updated with AGENT_ID=14. -AGENT_ID=14 -dotenv_set_key "../$env_file_path" "AGENT_ID" "$AGENT_ID" +# Prompt use staking +poetry run python "../scripts/choose_staking.py" +export_dotenv "../$env_file_path" + +# check if USE_NEVERMINED is set to true +if [ "$USE_NEVERMINED" == "true" ]; +then + echo "A Nevermined subscription will be used to pay for the mech requests." + export MECH_CONTRACT_ADDRESS="0x327E26bDF1CfEa50BFAe35643B23D5268E41F7F9" + export AGENT_REGISTRY_ADDRESS="0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" + export MECH_REQUEST_PRICE=0 +fi if [ -z ${service_id+x} ]; then # Check balances diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index cacee326..9b966ca5 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -20,13 +20,19 @@ import argparse import requests +import sys import textwrap +import json from dotenv import dotenv_values, set_key, unset_key from pathlib import Path +from typing import Any, Dict, List +from web3 import Web3 +from eth_utils import keccak SCRIPT_PATH = Path(__file__).resolve().parent STORE_PATH = Path(SCRIPT_PATH, "..", ".trader_runner") DOTENV_PATH = Path(STORE_PATH, ".env") +RPC_PATH = Path(STORE_PATH, "rpc.txt") def _fetch_json(url): @@ -35,48 +41,39 @@ def _fetch_json(url): return response.json() +# Information stored in the "deployment" key is used only to retrieve "stakingTokenInstanceAddress" (proxy) +# and "stakingTokenAddress" (implementation). The rest of the parameters are read on-chain. staking_programs = { + "no_staking": { + "name": "No staking", + "description": "Your Olas Predict agent will still actively participate in prediction markets, but it will not be staked within any staking program.", + "deployment": { + "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + } + }, "quickstart_beta_hobbyist": { "name": "Quickstart Beta - Hobbyist", "description": "The Quickstart Beta - Hobbyist staking contract offers 100 slots for operators running Olas Predict agents with the quickstart. It is designed as a step up from Coastal Staker Expeditions, requiring 100 OLAS for staking. The rewards are also more attractive than with Coastal Staker Expeditions.", - "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/e8c023886ebf1f10f2ccd3519a934c1f2590b78d/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json"), - "stakingProxyAddress": "0x389B46c259631Acd6a69Bde8B6cEe218230bAE8C" + "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json"), }, "quickstart_beta_expert": { "name": "Quickstart Beta - Expert", "description": "The Quickstart Beta - Expert staking contract offers 20 slots for operators running Olas Predict agents with the quickstart. It is designed for professional agent operators, requiring 1000 OLAS for staking. The rewards are proportional to the Quickstart Beta - Hobbyist.", - "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/e8c023886ebf1f10f2ccd3519a934c1f2590b78d/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json"), - "stakingProxyAddress": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e" + "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json"), + }, + "quickstart_alpha_coastal": { + "name": "Quickstart Alpha - Coastal (Deprecated)", + "description": "The Quickstart Alpha - Coastal is a deprecated staking contract. It offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", + "deployment": { + "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + } } } -no_staking = { - "name": "No staking", - "description": "No staking", - "deployment": { - "gnosisSafeAddress": "0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", - "gnosisSafeProxyFactoryAddress": "0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", - "serviceRegistryAddress": "0x9338b5153AE39BB89f50468E608eD9d764B755fD", - "serviceRegistryTokenUtilityAddress": "0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8", - "olasAddress": "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", - "stakingTokenAddress": "0xEa00be6690a871827fAfD705440D20dd75e67AB1", - "agentMechAddress": "0x77af31De935740567Cf4fF1986D04B2c964A786a", - "mechActivityCheckerAddress": "0x87E6a97bD97D41904B1125A014B16bec50C6A89D", - "stakingFactoryAddress": "0xb0228CA253A88Bc8eb4ca70BCAC8f87b381f4700", - "stakingParams": { - "agentIds": [ - "25" - ], - "serviceRegistry": "0x9338b5153AE39BB89f50468E608eD9d764B755fD", - "activityChecker": "0x87E6a97bD97D41904B1125A014B16bec50C6A89D" - }, - "stakingTokenInstanceAddress": "" - }, - "stakingProxyAddress": "0x0000000000000000000000000000000000000000" -} - -def _prompt_use_staking(): +def _prompt_use_staking() -> None: env_file_vars = dotenv_values(DOTENV_PATH) if 'USE_STAKING' in env_file_vars: @@ -101,22 +98,22 @@ def _prompt_use_staking(): print("") -def _prompt_select_staking_program(): +def _prompt_select_staking_program() -> None: env_file_vars = dotenv_values(DOTENV_PATH) - if 'USE_STAKING' not in env_file_vars: - print("'USE_STAKING' is not defined.") - return - - if not env_file_vars.get('USE_STAKING').lower() == "true": - print("'USE_STAKING' is not set to 'true'.") - selected_program = no_staking - elif 'STAKING_PROGRAM' in env_file_vars: + selected_key = None + if 'STAKING_PROGRAM' in env_file_vars: print("The staking program is already selected.") + selected_key = env_file_vars.get('STAKING_PROGRAM') - else: - print("Select a staking program") - print("------------------------") + if selected_key not in staking_programs: + selected_key = None + print(f"WARNING: Selected staking program {selected_key} is unknown.") + print("") + + if not selected_key: + print("Please, select your staking program preference") + print("----------------------------------------------") program_keys = list(staking_programs.keys()) for index, key in enumerate(program_keys): program = staking_programs[key] @@ -133,21 +130,73 @@ def _prompt_select_staking_program(): except ValueError: print(f"Please enter a valid option (1 - {len(program_keys)}).") - selected_program = staking_programs[selected_key] - print(f"Selected staking program: {selected_program['name']}") - - print("Setting the staking program variables in the .env file") - set_key(dotenv_path=DOTENV_PATH, key_to_set="STAKING_PROGRAM", value_to_set=selected_key) - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_ADDRESS", value_to_set=selected_program["deployment"]["serviceRegistryAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_STAKING_ADDRESS", value_to_set=selected_program["stakingProxyAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_OLAS_ADDRESS", value_to_set=selected_program["deployment"]["olasAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS", value_to_set=selected_program["deployment"]["serviceRegistryTokenUtilityAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_CONTRACT_ADDRESS", value_to_set=selected_program["deployment"]["agentMechAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_ACTIVITY_CHECKER_CONTRACT", value_to_set=selected_program["deployment"]["mechActivityCheckerAddress"]) - set_key(dotenv_path=DOTENV_PATH, key_to_set="AGENT_ID", value_to_set=selected_program["deployment"]["stakingParams"]["agentIds"][0], quote_mode="never") + selected_staking_program_data = staking_programs[selected_key] + print(f"Selected staking program: {selected_staking_program_data['name']}") + + print("Populating the staking program variables in the .env file") + _populate_env_file_variables(selected_key) print("") +def _get_abi(contract_address: str) -> List: + contract_abi_url = "https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}" + response = requests.get(contract_abi_url.format(contract_address=contract_address)).json() + + if "result" in response: + result = response["result"] + try: + abi = json.loads(result) + except json.JSONDecodeError: + print("Error: Failed to parse 'result' field as JSON") + sys.exit(1) + else: + abi = response.get("abi") + + return abi if abi else [] + + +def _populate_env_file_variables(staking_program_key: str) -> None: + + staking_program_data = staking_programs.get(staking_program_key) + + with open(RPC_PATH, 'r', encoding="utf-8") as file: + rpc = file.read().strip() + + w3 = Web3(Web3.HTTPProvider(rpc)) + staking_token_instance_address = staking_program_data["deployment"]["stakingTokenInstanceAddress"] # Instance/proxy + staking_token_address = staking_program_data["deployment"]["stakingTokenAddress"] # Implementation + abi = _get_abi(staking_token_address) + staking_token_contract = w3.eth.contract(address=staking_token_instance_address, abi=abi) + + agent_id = staking_token_contract.functions.agentIds(0).call() + service_registry = staking_token_contract.functions.serviceRegistry().call() + staking_token = staking_token_contract.functions.stakingToken().call() + service_registry_token_utility = staking_token_contract.functions.serviceRegistryTokenUtility().call() + + if 'activityChecker' in staking_token_contract.all_functions(): + activity_checker = staking_token_contract.functions.activityChecker().call() + abi = _get_abi(activity_checker) + activity_checker_contract = w3.eth.contract(address=activity_checker, abi=abi) + agent_mech = activity_checker_contract.functions.agentMech().call() + else: + activity_checker = '0x0000000000000000000000000000000000000000' + agent_mech = staking_token_contract.functions.agentMech().call() + + if staking_program_key == "no_staking": + set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set="false", quote_mode="never") + else: + set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set="true", quote_mode="never") + + set_key(dotenv_path=DOTENV_PATH, key_to_set="STAKING_PROGRAM", value_to_set=staking_program_key, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_STAKING_ADDRESS", value_to_set=staking_token_instance_address, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="AGENT_ID", value_to_set=agent_id, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_ADDRESS", value_to_set=service_registry, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_OLAS_ADDRESS", value_to_set=staking_token, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS", value_to_set=service_registry_token_utility, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_ACTIVITY_CHECKER_CONTRACT", value_to_set=activity_checker, quote_mode="never") + set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_CONTRACT_ADDRESS", value_to_set=agent_mech, quote_mode="never") + + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Set up staking configuration.') parser.add_argument('--reset', action='store_true', help='Reset USE_STAKING and STAKING_PROGRAM in .env file') @@ -159,5 +208,4 @@ def _prompt_select_staking_program(): print("Environment variables USE_STAKING and STAKING_PROGRAM have been reset.") print("") - _prompt_use_staking() _prompt_select_staking_program() From 145245fecfe219d695988868021824cbbe2f13a0 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 6 Aug 2024 22:28:12 +0200 Subject: [PATCH 04/13] chore: refactor staking choices --- scripts/staking.py | 3 ++- terminate_on_chain_service.sh | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/scripts/staking.py b/scripts/staking.py index 55f95589..b9127319 100644 --- a/scripts/staking.py +++ b/scripts/staking.py @@ -47,7 +47,8 @@ OLD_STAKING_PROGRAMS = { "Everest": "0x5add592ce0a1B5DceCebB5Dcac086Cd9F9e3eA5C", - "Alpine": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326" + "Alpine": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326", + "Coastal": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" } diff --git a/terminate_on_chain_service.sh b/terminate_on_chain_service.sh index d245b832..b05021fa 100755 --- a/terminate_on_chain_service.sh +++ b/terminate_on_chain_service.sh @@ -94,6 +94,16 @@ validate_password() { fi } +export_dotenv() { + local dotenv_path="$1" + unamestr=$(uname) + if [ "$unamestr" = 'Linux' ]; then + export $(grep -v '^#' $dotenv_path | xargs -d '\n') + elif [ "$unamestr" = 'FreeBSD' ] || [ "$unamestr" = 'Darwin' ]; then + export $(grep -v '^#' $dotenv_path | xargs -0) + fi +} + store=".trader_runner" env_file_path="$store/.env" rpc_path="$store/rpc.txt" @@ -120,16 +130,18 @@ export RPC_TIMEOUT_SECONDS=120 export CUSTOM_CHAIN_RPC=$rpc export CUSTOM_CHAIN_ID=$gnosis_chain_id export CUSTOM_SERVICE_MANAGER_ADDRESS="0x04b0007b2aFb398015B76e5f22993a1fddF83644" -export CUSTOM_SERVICE_REGISTRY_ADDRESS="0x9338b5153AE39BB89f50468E608eD9d764B755fD" -export CUSTOM_STAKING_ADDRESS="0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" -export CUSTOM_OLAS_ADDRESS="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" -export CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" +# export CUSTOM_SERVICE_REGISTRY_ADDRESS="0x9338b5153AE39BB89f50468E608eD9d764B755fD" +# export CUSTOM_STAKING_ADDRESS="0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" +# export CUSTOM_OLAS_ADDRESS="0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" +# export CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS="0xa45E64d13A30a51b91ae0eb182e88a40e9b18eD8" export CUSTOM_GNOSIS_SAFE_PROXY_FACTORY_ADDRESS="0x3C1fF68f5aa342D296d4DEe4Bb1cACCA912D95fE" export CUSTOM_GNOSIS_SAFE_SAME_ADDRESS_MULTISIG_ADDRESS="0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06" export CUSTOM_MULTISEND_ADDRESS="0x40A2aCCbd92BCA938b02010E17A5b8929b49130D" -export MECH_AGENT_ADDRESS="0x77af31De935740567Cf4fF1986D04B2c964A786a" +# export MECH_AGENT_ADDRESS="0x77af31De935740567Cf4fF1986D04B2c964A786a" export WXDAI_ADDRESS="0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" +export_dotenv "$env_file_path" + set -e # Exit script on first error echo "--------------------------" echo "Terminate on-chain service" From cc5e7a0682cc853c14c59dded8b071b7da6ccb74 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 6 Aug 2024 22:40:27 +0200 Subject: [PATCH 05/13] fix: check --- scripts/choose_staking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index 9b966ca5..6a1b02b2 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -173,7 +173,7 @@ def _populate_env_file_variables(staking_program_key: str) -> None: staking_token = staking_token_contract.functions.stakingToken().call() service_registry_token_utility = staking_token_contract.functions.serviceRegistryTokenUtility().call() - if 'activityChecker' in staking_token_contract.all_functions(): + if 'activityChecker' in [func.fn_name for func in staking_token_contract.all_functions()]: activity_checker = staking_token_contract.functions.activityChecker().call() abi = _get_abi(activity_checker) activity_checker_contract = w3.eth.contract(address=activity_checker, abi=abi) From e37459ad9d44751e12af74a2ee96f178d97beaf1 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Wed, 7 Aug 2024 13:17:22 +0200 Subject: [PATCH 06/13] chore: updat Nevermined vars --- run_service.sh | 27 ++----- scripts/choose_staking.py | 159 +++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 82 deletions(-) diff --git a/run_service.sh b/run_service.sh index 4a593f5f..0a4ede2b 100755 --- a/run_service.sh +++ b/run_service.sh @@ -640,9 +640,7 @@ service_version="v0.18.0" # Define constants for on-chain interaction gnosis_chain_id=100 n_agents=1 -olas_balance_required_to_bond=10000000000000000000 -olas_balance_required_to_stake=10000000000000000000 -xdai_balance_required_to_bond=10000000000000000 +MIN_STAKING_BOND_XDAI=10000000000000000 suggested_top_up_default=50000000000000000 suggested_safe_top_up_default=500000000000000000 @@ -828,15 +826,6 @@ echo "" poetry run python "../scripts/choose_staking.py" export_dotenv "../$env_file_path" -# check if USE_NEVERMINED is set to true -if [ "$USE_NEVERMINED" == "true" ]; -then - echo "A Nevermined subscription will be used to pay for the mech requests." - export MECH_CONTRACT_ADDRESS="0x327E26bDF1CfEa50BFAe35643B23D5268E41F7F9" - export AGENT_REGISTRY_ADDRESS="0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" - export MECH_REQUEST_PRICE=0 -fi - if [ -z ${service_id+x} ]; then # Check balances suggested_amount=$suggested_top_up_default @@ -860,10 +849,10 @@ if [ -z ${service_id+x} ]; then --threshold $n_agents" if [ "${USE_STAKING}" = true ]; then - cost_of_bonding=$olas_balance_required_to_bond + cost_of_bonding=$MIN_STAKING_BOND_OLAS cmd+=" -c $cost_of_bonding --token $CUSTOM_OLAS_ADDRESS" else - cost_of_bonding=$xdai_balance_required_to_bond + cost_of_bonding=$MIN_STAKING_BOND_XDAI cmd+=" -c $cost_of_bonding" fi service_id=$(eval $cmd) @@ -979,10 +968,10 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then nft="bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq" export cmd="" if [ "${USE_STAKING}" = true ]; then - cost_of_bonding=$olas_balance_required_to_bond + cost_of_bonding=$MIN_STAKING_BOND_OLAS poetry run python "../scripts/update_service.py" "../$operator_pkey_path" "$nft" "$AGENT_ID" "$service_id" "$CUSTOM_OLAS_ADDRESS" "$cost_of_bonding" "packages/valory/services/trader/" "$rpc" $password_argument else - cost_of_bonding=$xdai_balance_required_to_bond + cost_of_bonding=$MIN_STAKING_BOND_XDAI cmd="poetry run autonomy mint \ --retries $RPC_RETRIES \ --timeout $RPC_TIMEOUT_SECONDS \ @@ -1023,11 +1012,11 @@ if [ "$(get_on_chain_service_state "$service_id")" == "PRE_REGISTRATION" ]; then echo "[Service owner] Activating registration for on-chain service $service_id..." export cmd="poetry run autonomy service --retries $RPC_RETRIES --timeout $RPC_TIMEOUT_SECONDS --use-custom-chain activate --key "../$operator_pkey_path" $password_argument "$service_id"" if [ "${USE_STAKING}" = true ]; then - minimum_olas_balance=$($PYTHON_CMD -c "print(int($olas_balance_required_to_bond) + int($olas_balance_required_to_stake))") + minimum_olas_balance=$($PYTHON_CMD -c "print(int($MIN_STAKING_DEPOSIT_OLAS) + int($MIN_STAKING_BOND_OLAS))") echo "Your service is using staking. Therefore, you need to provide a total of $(wei_to_dai "$minimum_olas_balance") OLAS to your owner/operator's address." - echo " $(wei_to_dai "$olas_balance_required_to_bond") OLAS for security deposit (service owner)" + echo " $(wei_to_dai "$MIN_STAKING_DEPOSIT_OLAS") OLAS for security deposit (service owner)" echo " +" - echo " $(wei_to_dai "$olas_balance_required_to_stake") OLAS for slashable bond (operator)." + echo " $(wei_to_dai "$MIN_STAKING_BOND_OLAS") OLAS for slashable bond (operator)." echo "" ensure_erc20_balance "$operator_address" $minimum_olas_balance "owner/operator's address" $CUSTOM_OLAS_ADDRESS "OLAS" diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index 6a1b02b2..c2a210db 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -34,6 +34,11 @@ DOTENV_PATH = Path(STORE_PATH, ".env") RPC_PATH = Path(STORE_PATH, "rpc.txt") +NEVERMINED_MECH_CONTRACT_ADDRESS = "0x327E26bDF1CfEa50BFAe35643B23D5268E41F7F9" +NEVERMINED_AGENT_REGISTRY_ADDRESS = "0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" +NEVERMINED_MECH_REQUEST_PRICE = "0" +ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" + def _fetch_json(url): response = requests.get(url) @@ -73,69 +78,41 @@ def _fetch_json(url): } -def _prompt_use_staking() -> None: +def _prompt_select_staking_program() -> str: env_file_vars = dotenv_values(DOTENV_PATH) - if 'USE_STAKING' in env_file_vars: - return - - print("Use staking?") - print("------------") - - while True: - use_staking = input("Do you want to stake this service? (yes/no): ").strip().lower() - - if use_staking in ('yes', 'y'): - use_staking_value = "true" - break - elif use_staking in ('no', 'n'): - use_staking_value = "false" - break - else: - print("Please enter 'yes' or 'no'.") - - set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set=use_staking_value, quote_mode="never") - print("") - - -def _prompt_select_staking_program() -> None: - env_file_vars = dotenv_values(DOTENV_PATH) - - selected_key = None + program_id = None if 'STAKING_PROGRAM' in env_file_vars: print("The staking program is already selected.") - selected_key = env_file_vars.get('STAKING_PROGRAM') - if selected_key not in staking_programs: - selected_key = None - print(f"WARNING: Selected staking program {selected_key} is unknown.") + program_id = env_file_vars.get('STAKING_PROGRAM') + if program_id not in staking_programs: + print(f"WARNING: Selected staking program {program_id} is unknown.") print("") + program_id = None - if not selected_key: + if not program_id: print("Please, select your staking program preference") print("----------------------------------------------") - program_keys = list(staking_programs.keys()) - for index, key in enumerate(program_keys): + ids = list(staking_programs.keys()) + for index, key in enumerate(ids): program = staking_programs[key] wrapped_description = textwrap.fill(program['description'], width=80, initial_indent=' ', subsequent_indent=' ') print(f"{index + 1}) {program['name']}\n{wrapped_description}\n") while True: try: - choice = int(input(f"Enter your choice (1 - {len(program_keys)}): ")) - 1 - if not (0 <= choice < len(program_keys)): + choice = int(input(f"Enter your choice (1 - {len(ids)}): ")) - 1 + if not (0 <= choice < len(ids)): raise ValueError - selected_key = program_keys[choice] + program_id = ids[choice] break except ValueError: - print(f"Please enter a valid option (1 - {len(program_keys)}).") - - selected_staking_program_data = staking_programs[selected_key] - print(f"Selected staking program: {selected_staking_program_data['name']}") + print(f"Please enter a valid option (1 - {len(ids)}).") - print("Populating the staking program variables in the .env file") - _populate_env_file_variables(selected_key) + print(f"Selected staking program: {staking_programs[program_id]['name']}") print("") + return program_id def _get_abi(contract_address: str) -> List: @@ -155,9 +132,8 @@ def _get_abi(contract_address: str) -> List: return abi if abi else [] -def _populate_env_file_variables(staking_program_key: str) -> None: - - staking_program_data = staking_programs.get(staking_program_key) +def _get_staking_env_variables(program_id: str) -> Dict[str, str]: + staking_program_data = staking_programs.get(program_id) with open(RPC_PATH, 'r', encoding="utf-8") as file: rpc = file.read().strip() @@ -172,6 +148,8 @@ def _populate_env_file_variables(staking_program_key: str) -> None: service_registry = staking_token_contract.functions.serviceRegistry().call() staking_token = staking_token_contract.functions.stakingToken().call() service_registry_token_utility = staking_token_contract.functions.serviceRegistryTokenUtility().call() + min_staking_deposit = staking_token_contract.functions.minStakingDeposit().call() + min_staking_bond = min_staking_deposit if 'activityChecker' in [func.fn_name for func in staking_token_contract.all_functions()]: activity_checker = staking_token_contract.functions.activityChecker().call() @@ -179,33 +157,88 @@ def _populate_env_file_variables(staking_program_key: str) -> None: activity_checker_contract = w3.eth.contract(address=activity_checker, abi=abi) agent_mech = activity_checker_contract.functions.agentMech().call() else: - activity_checker = '0x0000000000000000000000000000000000000000' + activity_checker = ZERO_ADDRESS agent_mech = staking_token_contract.functions.agentMech().call() - if staking_program_key == "no_staking": - set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set="false", quote_mode="never") + if program_id == "no_staking": + use_staking = "false" else: - set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_STAKING", value_to_set="true", quote_mode="never") + use_staking = "true" + + return { + "USE_STAKING": use_staking, + "STAKING_PROGRAM": program_id, + "CUSTOM_STAKING_ADDRESS": staking_token_instance_address, + "AGENT_ID": agent_id, + "CUSTOM_SERVICE_REGISTRY_ADDRESS": service_registry, + "CUSTOM_OLAS_ADDRESS": staking_token, + "CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS": service_registry_token_utility, + "MECH_ACTIVITY_CHECKER_CONTRACT": activity_checker, + "MECH_CONTRACT_ADDRESS": agent_mech, + "MIN_STAKING_DEPOSIT_OLAS": min_staking_deposit, + "MIN_STAKING_BOND_OLAS": min_staking_bond + } - set_key(dotenv_path=DOTENV_PATH, key_to_set="STAKING_PROGRAM", value_to_set=staking_program_key, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_STAKING_ADDRESS", value_to_set=staking_token_instance_address, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="AGENT_ID", value_to_set=agent_id, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_ADDRESS", value_to_set=service_registry, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_OLAS_ADDRESS", value_to_set=staking_token, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="CUSTOM_SERVICE_REGISTRY_TOKEN_UTILITY_ADDRESS", value_to_set=service_registry_token_utility, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_ACTIVITY_CHECKER_CONTRACT", value_to_set=activity_checker, quote_mode="never") - set_key(dotenv_path=DOTENV_PATH, key_to_set="MECH_CONTRACT_ADDRESS", value_to_set=agent_mech, quote_mode="never") +def _set_dotenv_file_variables(env_vars: Dict[str, str]) -> None: + for key, value in env_vars.items(): + if value: + set_key(dotenv_path=DOTENV_PATH, key_to_set=key, value_to_set=value, quote_mode="never") + else: + unset_key(dotenv_path=DOTENV_PATH, key_to_unset=key) -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Set up staking configuration.') - parser.add_argument('--reset', action='store_true', help='Reset USE_STAKING and STAKING_PROGRAM in .env file') + +def _get_nevermined_env_variables() -> Dict[str, str]: + env_file_vars = dotenv_values(DOTENV_PATH) + use_nevermined = False + + if 'USE_NEVERMINED' not in env_file_vars: + set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_NEVERMINED", value_to_set="false", quote_mode="never") + elif env_file_vars.get('USE_NEVERMINED').strip() not in ("True", "true"): + set_key(dotenv_path=DOTENV_PATH, key_to_set="USE_NEVERMINED", value_to_set="false", quote_mode="never") + else: + use_nevermined = True + + if use_nevermined: + print("A Nevermined subscription will be used to pay for the mech requests.") + return { + "MECH_CONTRACT_ADDRESS": NEVERMINED_MECH_CONTRACT_ADDRESS, + "AGENT_REGISTRY_ADDRESS": NEVERMINED_AGENT_REGISTRY_ADDRESS, + "MECH_REQUEST_PRICE": NEVERMINED_MECH_REQUEST_PRICE + } + else: + print("No Nevermined subscription set.") + return { + "AGENT_REGISTRY_ADDRESS": "", + "MECH_REQUEST_PRICE": "" + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Set up staking configuration.") + parser.add_argument("--reset", action="store_true", help="Reset USE_STAKING and STAKING_PROGRAM in .env file") args = parser.parse_args() if args.reset: unset_key(dotenv_path=DOTENV_PATH, key_to_unset="USE_STAKING") unset_key(dotenv_path=DOTENV_PATH, key_to_unset="STAKING_PROGRAM") - print("Environment variables USE_STAKING and STAKING_PROGRAM have been reset.") + print(f"Environment variables USE_STAKING and STAKING_PROGRAM have been reset in '{DOTENV_PATH}'.") + print("You can now execute './run_service.sh' and select a different staking program.") print("") + return + + program_id = _prompt_select_staking_program() + + print("Populating staking program variables in the .env file") + print("") + staking_env_variables = _get_staking_env_variables(program_id) + _set_dotenv_file_variables(staking_env_variables) - _prompt_select_staking_program() + print("Populating Nevermined variables in the .env file") + print("") + nevermined_env_variables = _get_nevermined_env_variables() + _set_dotenv_file_variables(nevermined_env_variables) + + +if __name__ == "__main__": + main() From 173c1b03293f625244326d70711b2d27073e0b21 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Wed, 7 Aug 2024 15:23:42 +0200 Subject: [PATCH 07/13] chore: refactor --- scripts/choose_staking.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index c2a210db..1f274c8c 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -38,6 +38,7 @@ NEVERMINED_AGENT_REGISTRY_ADDRESS = "0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" NEVERMINED_MECH_REQUEST_PRICE = "0" ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" +DEPRECATED_TEXT = "(DEPRECATED)" def _fetch_json(url): @@ -51,28 +52,40 @@ def _fetch_json(url): staking_programs = { "no_staking": { "name": "No staking", + "deprecated": False, "description": "Your Olas Predict agent will still actively participate in prediction markets, but it will not be staked within any staking program.", "deployment": { - "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" } }, "quickstart_beta_hobbyist": { "name": "Quickstart Beta - Hobbyist", + "deprecated": False, "description": "The Quickstart Beta - Hobbyist staking contract offers 100 slots for operators running Olas Predict agents with the quickstart. It is designed as a step up from Coastal Staker Expeditions, requiring 100 OLAS for staking. The rewards are also more attractive than with Coastal Staker Expeditions.", - "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json"), + # https://github.com/valory-xyz/autonolas-staking-programmes/blob/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json + "deployment": { + "stakingTokenAddress": "0xEa00be6690a871827fAfD705440D20dd75e67AB1", + "stakingTokenInstanceAddress": "0x389B46c259631Acd6a69Bde8B6cEe218230bAE8C" + } }, "quickstart_beta_expert": { "name": "Quickstart Beta - Expert", + "deprecated": False, "description": "The Quickstart Beta - Expert staking contract offers 20 slots for operators running Olas Predict agents with the quickstart. It is designed for professional agent operators, requiring 1000 OLAS for staking. The rewards are proportional to the Quickstart Beta - Hobbyist.", - "deployment": _fetch_json("https://raw.githubusercontent.com/valory-xyz/autonolas-staking-programmes/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json"), + # https://github.com/valory-xyz/autonolas-staking-programmes/blob/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json + "deployment": { + "stakingTokenAddress": "0xEa00be6690a871827fAfD705440D20dd75e67AB1", + "stakingTokenInstanceAddress": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e" + } }, "quickstart_alpha_coastal": { - "name": "Quickstart Alpha - Coastal (Deprecated)", - "description": "The Quickstart Alpha - Coastal is a deprecated staking contract. It offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", + "name": "Quickstart Alpha - Coastal", + "deprecated": True, + "description": "The Quickstart Alpha - Coastal offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", "deployment": { - "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" } } } @@ -97,8 +110,14 @@ def _prompt_select_staking_program() -> str: ids = list(staking_programs.keys()) for index, key in enumerate(ids): program = staking_programs[key] + + if program.get("deprecated", False) is True: + program_name = f"{program['name']} {DEPRECATED_TEXT}" + else: + program_name = program['name'] + wrapped_description = textwrap.fill(program['description'], width=80, initial_indent=' ', subsequent_indent=' ') - print(f"{index + 1}) {program['name']}\n{wrapped_description}\n") + print(f"{index + 1}) {program_name}\n{wrapped_description}\n") while True: try: From 7a5872a9489bf7c43dd392de1dbd921059857ea5 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Wed, 7 Aug 2024 16:04:17 +0200 Subject: [PATCH 08/13] chore: update deprecated programs --- scripts/choose_staking.py | 43 +++++++++++++++++++++++---------------- scripts/staking.py | 34 +++++++++++++++++++------------ 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index 1f274c8c..5c6a5ce4 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -49,10 +49,9 @@ def _fetch_json(url): # Information stored in the "deployment" key is used only to retrieve "stakingTokenInstanceAddress" (proxy) # and "stakingTokenAddress" (implementation). The rest of the parameters are read on-chain. -staking_programs = { +STAKING_PROGRAMS = { "no_staking": { "name": "No staking", - "deprecated": False, "description": "Your Olas Predict agent will still actively participate in prediction markets, but it will not be staked within any staking program.", "deployment": { "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", @@ -61,7 +60,6 @@ def _fetch_json(url): }, "quickstart_beta_hobbyist": { "name": "Quickstart Beta - Hobbyist", - "deprecated": False, "description": "The Quickstart Beta - Hobbyist staking contract offers 100 slots for operators running Olas Predict agents with the quickstart. It is designed as a step up from Coastal Staker Expeditions, requiring 100 OLAS for staking. The rewards are also more attractive than with Coastal Staker Expeditions.", # https://github.com/valory-xyz/autonolas-staking-programmes/blob/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_hobbyist.json "deployment": { @@ -71,7 +69,6 @@ def _fetch_json(url): }, "quickstart_beta_expert": { "name": "Quickstart Beta - Expert", - "deprecated": False, "description": "The Quickstart Beta - Expert staking contract offers 20 slots for operators running Olas Predict agents with the quickstart. It is designed for professional agent operators, requiring 1000 OLAS for staking. The rewards are proportional to the Quickstart Beta - Hobbyist.", # https://github.com/valory-xyz/autonolas-staking-programmes/blob/main/scripts/deployment/globals_gnosis_mainnet_qs_beta_expert.json "deployment": { @@ -81,7 +78,6 @@ def _fetch_json(url): }, "quickstart_alpha_coastal": { "name": "Quickstart Alpha - Coastal", - "deprecated": True, "description": "The Quickstart Alpha - Coastal offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", "deployment": { "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", @@ -90,6 +86,25 @@ def _fetch_json(url): } } +DEPRECATED_STAKING_PROGRAMS = { + "quickstart_alpha_everest": { + "name": "Quickstart Alpha - Everest", + "description": "", + "deployment": { + "stakingTokenAddress": "0x5add592ce0a1B5DceCebB5Dcac086Cd9F9e3eA5C", + "stakingTokenInstanceAddress": "0x5add592ce0a1B5DceCebB5Dcac086Cd9F9e3eA5C" + } + }, + "quickstart_alpha_alpine": { + "name": "Quickstart Alpha - Alpine", + "description": "", + "deployment": { + "stakingTokenAddress": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326", + "stakingTokenInstanceAddress": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326" + } + } +} + def _prompt_select_staking_program() -> str: env_file_vars = dotenv_values(DOTENV_PATH) @@ -99,7 +114,7 @@ def _prompt_select_staking_program() -> str: print("The staking program is already selected.") program_id = env_file_vars.get('STAKING_PROGRAM') - if program_id not in staking_programs: + if program_id not in STAKING_PROGRAMS: print(f"WARNING: Selected staking program {program_id} is unknown.") print("") program_id = None @@ -107,17 +122,11 @@ def _prompt_select_staking_program() -> str: if not program_id: print("Please, select your staking program preference") print("----------------------------------------------") - ids = list(staking_programs.keys()) + ids = list(STAKING_PROGRAMS.keys()) for index, key in enumerate(ids): - program = staking_programs[key] - - if program.get("deprecated", False) is True: - program_name = f"{program['name']} {DEPRECATED_TEXT}" - else: - program_name = program['name'] - + program = STAKING_PROGRAMS[key] wrapped_description = textwrap.fill(program['description'], width=80, initial_indent=' ', subsequent_indent=' ') - print(f"{index + 1}) {program_name}\n{wrapped_description}\n") + print(f"{index + 1}) {program['name']}\n{wrapped_description}\n") while True: try: @@ -129,7 +138,7 @@ def _prompt_select_staking_program() -> str: except ValueError: print(f"Please enter a valid option (1 - {len(ids)}).") - print(f"Selected staking program: {staking_programs[program_id]['name']}") + print(f"Selected staking program: {STAKING_PROGRAMS[program_id]['name']}") print("") return program_id @@ -152,7 +161,7 @@ def _get_abi(contract_address: str) -> List: def _get_staking_env_variables(program_id: str) -> Dict[str, str]: - staking_program_data = staking_programs.get(program_id) + staking_program_data = STAKING_PROGRAMS.get(program_id) with open(RPC_PATH, 'r', encoding="utf-8") as file: rpc = file.read().strip() diff --git a/scripts/staking.py b/scripts/staking.py index b9127319..a4773efc 100644 --- a/scripts/staking.py +++ b/scripts/staking.py @@ -25,10 +25,12 @@ import time import traceback from datetime import datetime +from dotenv import dotenv_values from pathlib import Path import dotenv from aea_ledger_ethereum.ethereum import EthereumApi, EthereumCrypto +from choose_staking import STAKING_PROGRAMS, DEPRECATED_STAKING_PROGRAMS from utils import ( get_available_rewards, get_available_staking_slots, @@ -45,13 +47,6 @@ ) -OLD_STAKING_PROGRAMS = { - "Everest": "0x5add592ce0a1B5DceCebB5Dcac086Cd9F9e3eA5C", - "Alpine": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326", - "Coastal": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" -} - - def _format_duration(duration_seconds: int) -> str: days, remainder = divmod(duration_seconds, 86400) hours, remainder = divmod(remainder, 3600) @@ -70,11 +65,11 @@ def _unstake_old_program( print(f"Checking if service is staked on {staking_program}...") # Check if service is staked - if staking_program.startswith("Everest"): + if staking_program.startswith("quickstart_alpha_everest"): if service_id not in get_service_ids(ledger_api, staking_contract_address): print(f"Service {service_id} is not staked on {staking_program}.") return - elif staking_program.startswith("Alpine"): + else: if not is_service_staked( ledger_api, service_id, staking_contract_address ): @@ -119,11 +114,16 @@ def _unstake_old_program( def _unstake_all_old_programs( - ledger_api: EthereumApi, service_id: int, owner_crypto: EthereumCrypto + ledger_api: EthereumApi, + service_id: int, + owner_crypto: EthereumCrypto, + current_staking_contract_address: str ) -> None: print("Unstaking from old programs...") - for program, address in OLD_STAKING_PROGRAMS.items(): - _unstake_old_program(ledger_api, service_id, address, program, owner_crypto) + for program_id, details in DEPRECATED_STAKING_PROGRAMS.items(): + staking_token_instance_address = details['deployment']['stakingTokenInstanceAddress'] + if staking_token_instance_address != current_staking_contract_address: + _unstake_old_program(ledger_api, service_id, staking_token_instance_address, program_id, owner_crypto) def _check_unstaking_availability( @@ -235,7 +235,12 @@ def main() -> None: private_key_path=args.owner_private_key_path, password=args.password ) - _unstake_all_old_programs(ledger_api, args.service_id, owner_crypto) + _unstake_all_old_programs( + ledger_api=ledger_api, + service_id=args.service_id, + owner_crypto=owner_crypto, + current_staking_contract_address=args.staking_contract_address + ) # Collect information next_ts = get_next_checkpoint_ts(ledger_api, args.staking_contract_address) @@ -246,6 +251,9 @@ def main() -> None: ledger_api, args.staking_contract_address ) + print(args.staking_contract_address) + print(".........") + if args.unstake: if not is_service_staked( ledger_api, args.service_id, args.staking_contract_address From ad2e8a5c65825ddbd051550cf98e875352395f65 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Wed, 7 Aug 2024 17:44:44 +0200 Subject: [PATCH 09/13] chore: refactor --- scripts/staking.py | 3 -- scripts/utils.py | 80 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/scripts/staking.py b/scripts/staking.py index a4773efc..d479b9b8 100644 --- a/scripts/staking.py +++ b/scripts/staking.py @@ -251,9 +251,6 @@ def main() -> None: ledger_api, args.staking_contract_address ) - print(args.staking_contract_address) - print(".........") - if args.unstake: if not is_service_staked( ledger_api, args.service_id, args.staking_contract_address diff --git a/scripts/utils.py b/scripts/utils.py index 90f7d89e..bb3dbc79 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -33,6 +33,7 @@ from packages.valory.contracts.service_staking_token.contract import ( ServiceStakingTokenContract, ) +from packages.valory.contracts.staking_token.contract import StakingTokenContract from autonomy.chain.tx import ( TxSettler, should_retry, @@ -46,9 +47,14 @@ TxBuildError, ) from autonomy.chain.config import ChainType - +from dotenv import dotenv_values +from choose_staking import ZERO_ADDRESS from packages.valory.skills.staking_abci.rounds import StakingState +from pathlib import Path +SCRIPT_PATH = Path(__file__).resolve().parent +STORE_PATH = Path(SCRIPT_PATH, "..", ".trader_runner") +DOTENV_PATH = Path(STORE_PATH, ".env") DEFAULT_ON_CHAIN_INTERACT_TIMEOUT = 120.0 DEFAULT_ON_CHAIN_INTERACT_RETRIES = 10 @@ -176,30 +182,46 @@ def is_service_staked( ledger_api: EthereumApi, service_id: int, staking_contract_address: str ) -> bool: """Check if service is staked.""" - service_staking_state = staking_contract.get_service_staking_state( - ledger_api, staking_contract_address, service_id - ).pop("data") + # TODO Not best approach. This is required because staking.py might call + # different contract versions. + for staking_contract in all_staking_contracts: + try: + service_staking_state = staking_contract.get_service_staking_state( + ledger_api, staking_contract_address, service_id + ).pop("data") - if isinstance(service_staking_state, int): - service_staking_state = StakingState(service_staking_state) + if isinstance(service_staking_state, int): + service_staking_state = StakingState(service_staking_state) - is_staked = service_staking_state == StakingState.STAKED or service_staking_state == StakingState.EVICTED - return is_staked + is_staked = service_staking_state == StakingState.STAKED or service_staking_state == StakingState.EVICTED + return is_staked + except: # noqa + continue + + raise Exception("Unable to retrieve staking state.") def is_service_evicted( ledger_api: EthereumApi, service_id: int, staking_contract_address: str ) -> bool: """Check if service is staked.""" - service_staking_state = staking_contract.get_service_staking_state( - ledger_api, staking_contract_address, service_id - ).pop("data") + # TODO Not best approach. This is required because staking.py might call + # different contract versions. + for staking_contract in all_staking_contracts: + try: + service_staking_state = staking_contract.get_service_staking_state( + ledger_api, staking_contract_address, service_id + ).pop("data") - if isinstance(service_staking_state, int): - service_staking_state = StakingState(service_staking_state) + if isinstance(service_staking_state, int): + service_staking_state = StakingState(service_staking_state) - is_evicted = service_staking_state == StakingState.EVICTED - return is_evicted + is_evicted = service_staking_state == StakingState.EVICTED + return is_evicted + except: # noqa + continue + + raise Exception("Unable to retrieve eviction state.") def get_next_checkpoint_ts( @@ -359,7 +381,29 @@ def send_tx_and_wait_for_receipt( return receipt -staking_contract = typing.cast( - typing.Type[ServiceStakingTokenContract], load_contract(ServiceStakingTokenContract) -) +# TODO 'staking_contract' refers to the current active program.abs +# There are methods above that will be called for other programs, +# and whose ABI might differ. A "patch" is currently implemented, +# but it should be refactored in a more elegant and robust way. +env_file_vars = dotenv_values(DOTENV_PATH) +activity_checker = env_file_vars.get("MECH_ACTIVITY_CHECKER_CONTRACT") + +if activity_checker is None or activity_checker == ZERO_ADDRESS: + staking_contract = typing.cast( + typing.Type[Contract], load_contract(ServiceStakingTokenContract) + ) +else: + staking_contract = typing.cast( + typing.Type[Contract], load_contract(StakingTokenContract) + ) + +all_staking_contracts = [ + typing.cast( + typing.Type[Contract], load_contract(ServiceStakingTokenContract) + ), + typing.cast( + typing.Type[Contract], load_contract(StakingTokenContract) + ) +] + erc20 = typing.cast(typing.Type[ERC20], load_contract(ERC20)) From 2efe1d1510dffa0cb29e7da1238637407e066d56 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Wed, 7 Aug 2024 17:52:58 +0200 Subject: [PATCH 10/13] chore: revert blockscout dependency --- report.py | 74 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/report.py b/report.py index 268234c9..4950a5bb 100644 --- a/report.py +++ b/report.py @@ -55,6 +55,42 @@ OPERATOR_KEYS_JSON_PATH = Path(STORE_PATH, "operator_keys.json") SAFE_ADDRESS_PATH = Path(STORE_PATH, "service_safe_address.txt") SERVICE_ID_PATH = Path(STORE_PATH, "service_id.txt") +SERVICE_STAKING_TOKEN_JSON_PATH = Path( + SCRIPT_PATH, + "trader", + "packages", + "valory", + "contracts", + "service_staking_token", + "build", + "ServiceStakingToken.json", +) +SERVICE_REGISTRY_L2_JSON_PATH = Path( + SCRIPT_PATH, + "trader", + "packages", + "valory", + "contracts", + "service_registry", + "build", + "ServiceRegistryL2.json", +) +SERVICE_REGISTRY_TOKEN_UTILITY_JSON_PATH = Path( + SCRIPT_PATH, + "contracts", + "ServiceRegistryTokenUtility.json", +) +MECH_CONTRACT_ADDRESS = "0x77af31De935740567Cf4fF1986D04B2c964A786a" +MECH_CONTRACT_JSON_PATH = Path( + SCRIPT_PATH, + "trader", + "packages", + "valory", + "contracts", + "mech", + "build", + "mech.json", +) SAFE_BALANCE_THRESHOLD = 500000000000000000 AGENT_XDAI_BALANCE_THRESHOLD = 50000000000000000 @@ -101,23 +137,6 @@ def _color_percent(p: float, multiplier: float = 100, symbol: str = "%") -> str: return _color_string(f"{p*multiplier:.2f} {symbol}", ColorCode.RED) -def _get_abi(contract_address: str) -> List: - contract_abi_url = "https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}" - response = requests.get(contract_abi_url.format(contract_address=contract_address)).json() - - if "result" in response: - result = response["result"] - try: - abi = json.loads(result) - except json.JSONDecodeError: - print("Error: Failed to parse 'result' field as JSON") - sys.exit(1) - else: - abi = response.get("abi") - - return abi if abi else [] - - def _trades_since_message(trades_json: dict[str, Any], utc_ts: float = 0) -> str: filtered_trades = [ trade @@ -235,7 +254,10 @@ def _parse_args() -> Any: w3 = Web3(HTTPProvider(rpc)) service_staking_contract_address = env_file_vars.get("CUSTOM_STAKING_ADDRESS") - service_staking_token_abi = _get_abi(service_staking_contract_address) + with open(SERVICE_STAKING_TOKEN_JSON_PATH, "r", encoding="utf-8") as file: + service_staking_token_data = json.load(file) + + service_staking_token_abi = service_staking_token_data.get("abi", []) service_staking_token_contract = w3.eth.contract( address=service_staking_contract_address, abi=service_staking_token_abi # type: ignore ) @@ -257,18 +279,30 @@ def _parse_args() -> Any: elif service_staking_state == StakingState.EVICTED: _print_status("Staking state", _color_string(service_staking_state.name, ColorCode.RED)) + if is_staked: + with open( + SERVICE_REGISTRY_TOKEN_UTILITY_JSON_PATH, "r", encoding="utf-8" + ) as file: + service_registry_token_utility_data = json.load(file) + service_registry_token_utility_contract_address = ( service_staking_token_contract.functions.serviceRegistryTokenUtility().call() ) - service_registry_token_utility_abi = _get_abi(service_registry_token_utility_contract_address) + service_registry_token_utility_abi = ( + service_registry_token_utility_data.get("abi", []) + ) service_registry_token_utility_contract = w3.eth.contract( address=service_registry_token_utility_contract_address, abi=service_registry_token_utility_abi, ) mech_contract_address = env_file_vars.get("MECH_CONTRACT_ADDRESS") - mech_contract_abi = _get_abi(mech_contract_address) + with open(MECH_CONTRACT_JSON_PATH, "r", encoding="utf-8") as file: + mech_contract_data = json.load(file) + + mech_contract_abi = mech_contract_data.get("abi", []) + mech_contract = w3.eth.contract( address=mech_contract_address, abi=mech_contract_abi # type: ignore ) From 5948a9edf5cd20652659a5b81738b1b84d6e7f2c Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Fri, 9 Aug 2024 10:05:46 +0200 Subject: [PATCH 11/13] chore: update --- scripts/choose_staking.py | 3 ++- scripts/staking.py | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index 5c6a5ce4..8618844c 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -34,6 +34,7 @@ DOTENV_PATH = Path(STORE_PATH, ".env") RPC_PATH = Path(STORE_PATH, "rpc.txt") +IPFS_ADDRESS = "https://gateway.autonolas.tech/ipfs/f01701220{}" NEVERMINED_MECH_CONTRACT_ADDRESS = "0x327E26bDF1CfEa50BFAe35643B23D5268E41F7F9" NEVERMINED_AGENT_REGISTRY_ADDRESS = "0xAed729d4f4b895d8ca84ba022675bB0C44d2cD52" NEVERMINED_MECH_REQUEST_PRICE = "0" @@ -172,7 +173,7 @@ def _get_staking_env_variables(program_id: str) -> Dict[str, str]: abi = _get_abi(staking_token_address) staking_token_contract = w3.eth.contract(address=staking_token_instance_address, abi=abi) - agent_id = staking_token_contract.functions.agentIds(0).call() + agent_id = 14 # staking_token_contract.functions.agentIds(0).call() service_registry = staking_token_contract.functions.serviceRegistry().call() staking_token = staking_token_contract.functions.stakingToken().call() service_registry_token_utility = staking_token_contract.functions.serviceRegistryTokenUtility().call() diff --git a/scripts/staking.py b/scripts/staking.py index d479b9b8..610db5c6 100644 --- a/scripts/staking.py +++ b/scripts/staking.py @@ -195,8 +195,6 @@ def _try_stake_service( def main() -> None: try: - staking_program = "Coastal" - print(f"Starting {Path(__file__).name} script ({staking_program})...\n") parser = argparse.ArgumentParser( description="Stake or unstake the service based on the state." @@ -230,6 +228,10 @@ def main() -> None: ) parser.add_argument("--password", type=str, help="Private key password") args = parser.parse_args() + + staking_program = args.staking_contract_address + print(f"Starting {Path(__file__).name} script ({staking_program})...\n") + ledger_api = EthereumApi(address=args.rpc) owner_crypto = EthereumCrypto( private_key_path=args.owner_private_key_path, password=args.password From 8ba57a8ed3d112282699f08949111ea8496ffb0c Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Fri, 9 Aug 2024 11:29:55 +0200 Subject: [PATCH 12/13] chore: update scripts --- run_service.sh | 1 + scripts/choose_staking.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/run_service.sh b/run_service.sh index 0a4ede2b..7fcd857b 100755 --- a/run_service.sh +++ b/run_service.sh @@ -658,6 +658,7 @@ export CUSTOM_GNOSIS_SAFE_PROXY_FACTORY_ADDRESS="0x3C1fF68f5aa342D296d4DEe4Bb1cA export CUSTOM_GNOSIS_SAFE_SAME_ADDRESS_MULTISIG_ADDRESS="0x6e7f594f680f7aBad18b7a63de50F0FeE47dfD06" export CUSTOM_MULTISEND_ADDRESS="0x40A2aCCbd92BCA938b02010E17A5b8929b49130D" export WXDAI_ADDRESS="0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" +export OPEN_AUTONOMY_SUBGRAPH_URL="https://subgraph.autonolas.tech/subgraphs/name/autonolas-staging" sleep_duration=12 diff --git a/scripts/choose_staking.py b/scripts/choose_staking.py index 8618844c..468cb1c3 100644 --- a/scripts/choose_staking.py +++ b/scripts/choose_staking.py @@ -77,14 +77,6 @@ def _fetch_json(url): "stakingTokenInstanceAddress": "0x5344B7DD311e5d3DdDd46A4f71481bD7b05AAA3e" } }, - "quickstart_alpha_coastal": { - "name": "Quickstart Alpha - Coastal", - "description": "The Quickstart Alpha - Coastal offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", - "deployment": { - "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", - "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" - } - } } DEPRECATED_STAKING_PROGRAMS = { @@ -103,6 +95,14 @@ def _fetch_json(url): "stakingTokenAddress": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326", "stakingTokenInstanceAddress": "0x2Ef503950Be67a98746F484DA0bBAdA339DF3326" } + }, + "quickstart_alpha_coastal": { + "name": "Quickstart Alpha - Coastal", + "description": "The Quickstart Alpha - Coastal offers 100 slots for operators running Olas Predict agents with the quickstart. It requires 20 OLAS for staking.", + "deployment": { + "stakingTokenAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237", + "stakingTokenInstanceAddress": "0x43fB32f25dce34EB76c78C7A42C8F40F84BCD237" + } } } @@ -173,7 +173,7 @@ def _get_staking_env_variables(program_id: str) -> Dict[str, str]: abi = _get_abi(staking_token_address) staking_token_contract = w3.eth.contract(address=staking_token_instance_address, abi=abi) - agent_id = 14 # staking_token_contract.functions.agentIds(0).call() + agent_id = staking_token_contract.functions.agentIds(0).call() service_registry = staking_token_contract.functions.serviceRegistry().call() staking_token = staking_token_contract.functions.stakingToken().call() service_registry_token_utility = staking_token_contract.functions.serviceRegistryTokenUtility().call() From 7512f373fd6a68e7d0ba2b6649e6ac5fb2244d3a Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Fri, 9 Aug 2024 16:02:09 +0200 Subject: [PATCH 13/13] chore: fix --- run_service.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/run_service.sh b/run_service.sh index 7fcd857b..642bf659 100755 --- a/run_service.sh +++ b/run_service.sh @@ -273,6 +273,13 @@ get_on_chain_service_state() { echo "$state" } +get_on_chain_agent_ids() { + local service_id="$1" + local service_info=$(poetry run autonomy service --use-custom-chain info "$service_id") + local agent_ids="$(echo "$service_info" | awk '/Cannonical Agents/ {sub(/\|[ \t]*Cannonical Agents[ \t]*\|[ \t]*/, ""); sub(/[ \t]*\|[ \t]*/, ""); print}')" + echo "$agent_ids" +} + # Move a file if it exists move_if_exists() { local source_file="$1" @@ -875,16 +882,19 @@ packages="packages/packages.json" local_service_hash="$(grep 'service/valory/trader' $packages | awk -F: '{print $2}' | tr -d '", ' | head -n 1)" remote_service_hash=$(poetry run python "../scripts/service_hash.py") operator_address=$(get_address "../$operator_keys_file") +on_chain_agent_id=$(get_on_chain_agent_ids "$service_id") -if [ "$local_service_hash" != "$remote_service_hash" ]; then +if [ "$local_service_hash" != "$remote_service_hash" ] || [ "$on_chain_agent_id" != "$AGENT_ID" ]; then echo "" echo "WARNING: Your on-chain service configuration is out-of-date" echo "-----------------------------------------------------------" - echo "Your currently minted on-chain service (id $service_id) mismatches the local trader service ($service_version):" + echo "Your currently minted on-chain service (id $service_id) mismatches the local configuration:" echo " - Local service hash ($service_version): $local_service_hash" - echo " - On-chain service hash (id $service_id): $remote_service_hash" + echo " - On-chain service hash: $remote_service_hash" + echo " - Local agent id: $AGENT_ID" + echo " - On-chain agent id: $on_chain_agent_id" echo "" - echo "This is most likely caused due to an update of the trader service code." + echo "This is most likely caused due to an update of the trader service code or agent id." echo "The script will proceed now to update the on-chain service." echo "The operator and agent addresses need to have enough funds to complete the process." echo ""