From 1f9b65e690878a7f105d25859993f4dede1fad16 Mon Sep 17 00:00:00 2001 From: "walter.riddle" Date: Sat, 7 Dec 2024 21:48:39 +0100 Subject: [PATCH 1/4] Allow claiming olas staking rewards for password protected keys --- README.md | 6 +- claim_olas.sh => claim_staking_rewards.sh | 2 +- .../{claim.py => claim_staking_rewards.py} | 57 ++++++++++++------- 3 files changed, 39 insertions(+), 26 deletions(-) rename claim_olas.sh => claim_staking_rewards.sh (91%) rename scripts/{claim.py => claim_staking_rewards.py} (76%) diff --git a/README.md b/README.md index 26fd3a1f..425e7c24 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,12 @@ To stop your agent, use: ./stop_service.sh ``` -### Claim accrued OLAS +### Claim accrued OLAS staking rewards -If your service is staked, you can claim accrued OLAS through the script +If your service is staked, you can claim accrued OLAS staking rewards through the script ```bash -./claim_olas.sh +./claim_staking_rewards.sh ``` The accrued OLAS will be transferred to your service Safe without having to unstake your service. diff --git a/claim_olas.sh b/claim_staking_rewards.sh similarity index 91% rename from claim_olas.sh rename to claim_staking_rewards.sh index ae4c7b40..8c8761c5 100755 --- a/claim_olas.sh +++ b/claim_staking_rewards.sh @@ -18,4 +18,4 @@ # # ------------------------------------------------------------------------------ -cd trader; poetry run python "../scripts/claim.py"; cd .. +cd trader; poetry run python "../scripts/claim_staking_rewards.py"; cd .. diff --git a/scripts/claim.py b/scripts/claim_staking_rewards.py similarity index 76% rename from scripts/claim.py rename to scripts/claim_staking_rewards.py index 3c613d26..26b637bc 100644 --- a/scripts/claim.py +++ b/scripts/claim_staking_rewards.py @@ -23,20 +23,21 @@ import json import os import sys +from getpass import getpass from pathlib import Path from typing import Any, Dict +from aea_ledger_ethereum.ethereum import EthereumCrypto from dotenv import dotenv_values from web3 import Web3 - 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") SERVICE_ID_PATH = Path(STORE_PATH, "service_id.txt") SERVICE_SAFE_ADDRESS_PATH = Path(STORE_PATH, "service_safe_address.txt") -OWNER_KEYS_JSON_PATH = Path(STORE_PATH, "operator_keys.json") +OPERATOR_PKEY_PATH = Path(STORE_PATH, "operator_pkey.txt") DEFAULT_ENCODING = "utf-8" OLAS_TOKEN_ADDRESS_GNOSIS = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f" @@ -70,6 +71,15 @@ ) +def _is_keystore(pkeypath: Path): + try: + with open(pkeypath, 'r') as f: + json.load(f) + return True + except json.JSONDecodeError: + return False + + def _load_abi_from_file(path: Path) -> Dict[str, Any]: if not os.path.exists(path): print( @@ -98,7 +108,7 @@ def _erc20_balance( return f"{balance / 10**18:.{decimal_precision}f} {token_name}" -def _claim_rewards() -> None: +def _claim_rewards(password: str = None) -> None: service_safe_address = SERVICE_SAFE_ADDRESS_PATH.read_text(encoding=DEFAULT_ENCODING).strip() print( f"OLAS Balance on service Safe {service_safe_address}: {_erc20_balance(service_safe_address)}" @@ -113,12 +123,16 @@ def _claim_rewards() -> None: abi = _load_abi_from_file(STAKING_TOKEN_IMPLEMENTATION_ABI_PATH) staking_token_contract = w3.eth.contract(address=staking_token_address, abi=abi) - owner_private_key = json.loads(OWNER_KEYS_JSON_PATH.read_text(encoding=DEFAULT_ENCODING))[0][ - "private_key" - ] - owner_address = Web3.to_checksum_address( - w3.eth.account.from_key(owner_private_key).address - ) + try: + ethereum_crypto = EthereumCrypto(OPERATOR_PKEY_PATH, password=password) + operator_address = ethereum_crypto.address + operator_pkey = ethereum_crypto.private_key + except DecryptError: + print(f"Could not decrypt key file at {OPERATOR_PKEY_PATH}. Please verify if your key file are password-protected, and if the provided password is correct (passwords are case-sensitive).") + raise + except KeyIsIncorrect: + print(f"Inccorect operator key at {OPERATOR_PKEY_PATH}. Please verify your key file.") + raise function = staking_token_contract.functions.claim(service_id) claim_transaction = function.build_transaction( @@ -126,11 +140,11 @@ def _claim_rewards() -> None: "chainId": GNOSIS_CHAIN_ID, "gas": DEFAULT_GAS, "gasPrice": w3.to_wei("3", "gwei"), - "nonce": w3.eth.get_transaction_count(owner_address), + "nonce": w3.eth.get_transaction_count(operator_address), } ) - signed_tx = w3.eth.account.sign_transaction(claim_transaction, owner_private_key) + signed_tx = w3.eth.account.sign_transaction(claim_transaction, operator_pkey) tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) print(f"Claim transaction done. Hash: {tx_hash.hex()}") @@ -143,21 +157,20 @@ def _claim_rewards() -> None: print("") print(f"Claimed OLAS transferred to your service Safe {service_safe_address}") print( - f"You can use your Owner/Operator wallet (address {owner_address}) to connect your Safe at" + f"You can use your Owner/Operator wallet (address {operator_address}) to connect your Safe at" ) print(f"{SAFE_WEBAPP_URL}{service_safe_address}") -def main() -> None: - "Main method" +if __name__ == "__main__": print("This script will claim accrued OLAS on the current staking contract to your service Safe.") - response = input("Do you want to continue (yes/no)? ").strip().lower() - - if response not in ("y", "yes"): - return + _continue = input("Do you want to continue (yes/no)? ").strip().lower() - _claim_rewards() + if _continue not in ("y", "yes"): + exit(0) + + password = None + if _is_keystore(OPERATOR_PKEY_PATH): + password = getpass(f"Your operator key file is protected with a password. Please, enter password:").strip() - -if __name__ == "__main__": - main() + _claim_rewards(password) From 3a4e2ef1b28488ae74bcc0e9dea5ec354f2b8219 Mon Sep 17 00:00:00 2001 From: "walter.riddle" Date: Sat, 7 Dec 2024 22:00:42 +0100 Subject: [PATCH 2/4] Add missing return type --- scripts/claim_staking_rewards.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/claim_staking_rewards.py b/scripts/claim_staking_rewards.py index 26b637bc..9e4849fc 100644 --- a/scripts/claim_staking_rewards.py +++ b/scripts/claim_staking_rewards.py @@ -71,7 +71,7 @@ ) -def _is_keystore(pkeypath: Path): +def _is_keystore(pkeypath: Path) -> bool: try: with open(pkeypath, 'r') as f: json.load(f) From 00c204538d614f9ab4570acc90bbd829441e6d97 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 17 Dec 2024 18:13:03 +0100 Subject: [PATCH 3/4] chore: fixes --- scripts/claim_staking_rewards.py | 53 +++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/scripts/claim_staking_rewards.py b/scripts/claim_staking_rewards.py index 9e4849fc..ab6e3e3c 100644 --- a/scripts/claim_staking_rewards.py +++ b/scripts/claim_staking_rewards.py @@ -25,12 +25,14 @@ import sys from getpass import getpass from pathlib import Path -from typing import Any, Dict +from typing import Any, Dict, Optional +from aea.crypto.helpers import DecryptError, KeyIsIncorrect from aea_ledger_ethereum.ethereum import EthereumCrypto from dotenv import dotenv_values from web3 import Web3 + SCRIPT_PATH = Path(__file__).resolve().parent STORE_PATH = Path(SCRIPT_PATH, "..", ".trader_runner") DOTENV_PATH = Path(STORE_PATH, ".env") @@ -73,7 +75,7 @@ def _is_keystore(pkeypath: Path) -> bool: try: - with open(pkeypath, 'r') as f: + with open(pkeypath, "r", encoding="utf-8") as f: json.load(f) return True except json.JSONDecodeError: @@ -97,7 +99,7 @@ def _erc20_balance( address: str, token_address: str = OLAS_TOKEN_ADDRESS_GNOSIS, token_name: str = "OLAS", - decimal_precision: int = 2 + decimal_precision: int = 2, ) -> str: """Get ERC20 balance""" rpc = RPC_PATH.read_text(encoding=DEFAULT_ENCODING).strip() @@ -108,8 +110,12 @@ def _erc20_balance( return f"{balance / 10**18:.{decimal_precision}f} {token_name}" -def _claim_rewards(password: str = None) -> None: - service_safe_address = SERVICE_SAFE_ADDRESS_PATH.read_text(encoding=DEFAULT_ENCODING).strip() +def _claim_rewards( # pylint: disable=too-many-locals + password: Optional[str] = None, +) -> None: + service_safe_address = SERVICE_SAFE_ADDRESS_PATH.read_text( + encoding=DEFAULT_ENCODING + ).strip() print( f"OLAS Balance on service Safe {service_safe_address}: {_erc20_balance(service_safe_address)}" ) @@ -128,11 +134,13 @@ def _claim_rewards(password: str = None) -> None: operator_address = ethereum_crypto.address operator_pkey = ethereum_crypto.private_key except DecryptError: - print(f"Could not decrypt key file at {OPERATOR_PKEY_PATH}. Please verify if your key file are password-protected, and if the provided password is correct (passwords are case-sensitive).") - raise + print( + f"Could not decrypt key {OPERATOR_PKEY_PATH}. Please verify if your key file is password-protected, and if the provided password is correct (passwords are case-sensitive)." + ) + sys.exit(1) except KeyIsIncorrect: - print(f"Inccorect operator key at {OPERATOR_PKEY_PATH}. Please verify your key file.") - raise + print(f"Error decoding key file {OPERATOR_PKEY_PATH}.") + sys.exit(1) function = staking_token_contract.functions.claim(service_id) claim_transaction = function.build_transaction( @@ -162,15 +170,32 @@ def _claim_rewards(password: str = None) -> None: print(f"{SAFE_WEBAPP_URL}{service_safe_address}") -if __name__ == "__main__": - print("This script will claim accrued OLAS on the current staking contract to your service Safe.") +def main() -> None: + """Main method.""" + print("---------------------") + print("Claim staking rewards") + print("---------------------") + print("") + print( + "This script will claim the OLAS staking rewards accrued in the current staking contract and transfer them to your service Safe." + ) _continue = input("Do you want to continue (yes/no)? ").strip().lower() if _continue not in ("y", "yes"): - exit(0) - + sys.exit(0) + + print("") + password = None if _is_keystore(OPERATOR_PKEY_PATH): - password = getpass(f"Your operator key file is protected with a password. Please, enter password:").strip() + print("Enter your password") + print("-------------------") + print("Your key files are protected with a password.") + password = getpass("Please, enter your password: ").strip() + print("") _claim_rewards(password) + + +if __name__ == "__main__": + main() From ecc5d7d04c3b7e3dd146ca9f00677a527496ef98 Mon Sep 17 00:00:00 2001 From: jmoreira-valory Date: Tue, 17 Dec 2024 18:16:39 +0100 Subject: [PATCH 4/4] chore: minor updates --- scripts/claim_staking_rewards.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/claim_staking_rewards.py b/scripts/claim_staking_rewards.py index ab6e3e3c..af56c55c 100644 --- a/scripts/claim_staking_rewards.py +++ b/scripts/claim_staking_rewards.py @@ -117,7 +117,7 @@ def _claim_rewards( # pylint: disable=too-many-locals encoding=DEFAULT_ENCODING ).strip() print( - f"OLAS Balance on service Safe {service_safe_address}: {_erc20_balance(service_safe_address)}" + f"OLAS Balance of service Safe {service_safe_address}: {_erc20_balance(service_safe_address)}" ) env_file_vars = dotenv_values(DOTENV_PATH) @@ -159,15 +159,16 @@ def _claim_rewards( # pylint: disable=too-many-locals if "status" in tx_receipt and tx_receipt["status"] == 0: print( - "The transaction was reverted. This may be caused because your service does not have rewards to claim." + "WARNING: The transaction was reverted. This may be caused because your service does not have rewards to claim." ) else: print("") print(f"Claimed OLAS transferred to your service Safe {service_safe_address}") - print( - f"You can use your Owner/Operator wallet (address {operator_address}) to connect your Safe at" - ) - print(f"{SAFE_WEBAPP_URL}{service_safe_address}") + + print("") + print( + f"You can use your Owner/Operator wallet (address {operator_address}) to connect your Safe at {SAFE_WEBAPP_URL}{service_safe_address}." + ) def main() -> None: