Skip to content
This repository was archived by the owner on May 29, 2025. It is now read-only.

Commit e0dd807

Browse files
Merge pull request #395 from valory-xyz/feat/claim_script
Claim script
2 parents ee0fb29 + 877be71 commit e0dd807

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ To stop your agent, use:
114114
./stop_service.sh
115115
```
116116

117+
### Claim accrued OLAS
118+
119+
If your service is staked, you can claim accrued OLAS through the script
120+
121+
```bash
122+
./claim_olas.sh
123+
```
124+
125+
The accrued OLAS will be transferred to your service Safe without having to unstake your service.
126+
117127
### Backups
118128

119129
Agent runners are recommended to create a [backup](https://github.com/valory-xyz/trader-quickstart#backup-and-recovery) of the relevant secret key material.

claim_olas.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
3+
# ------------------------------------------------------------------------------
4+
#
5+
# Copyright 2023-2024 Valory AG
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
# ------------------------------------------------------------------------------
20+
21+
cd trader; poetry run python "../scripts/claim.py"; cd ..

scripts/claim.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# ------------------------------------------------------------------------------
4+
#
5+
# Copyright 2024 Valory AG
6+
#
7+
# Licensed under the Apache License, Version 2.0 (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
# ------------------------------------------------------------------------------
20+
21+
"""Claim earned OLAS"""
22+
23+
import json
24+
import os
25+
import sys
26+
from pathlib import Path
27+
from typing import Any, Dict
28+
29+
from dotenv import dotenv_values
30+
from web3 import Web3
31+
32+
33+
SCRIPT_PATH = Path(__file__).resolve().parent
34+
STORE_PATH = Path(SCRIPT_PATH, "..", ".trader_runner")
35+
DOTENV_PATH = Path(STORE_PATH, ".env")
36+
RPC_PATH = Path(STORE_PATH, "rpc.txt")
37+
SERVICE_ID_PATH = Path(STORE_PATH, "service_id.txt")
38+
SERVICE_SAFE_ADDRESS_PATH = Path(STORE_PATH, "service_safe_address.txt")
39+
OWNER_KEYS_JSON_PATH = Path(STORE_PATH, "operator_keys.json")
40+
DEFAULT_ENCODING = "utf-8"
41+
42+
OLAS_TOKEN_ADDRESS_GNOSIS = "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f"
43+
GNOSIS_CHAIN_ID = 100
44+
DEFAULT_GAS = 100000
45+
SAFE_WEBAPP_URL = "https://app.safe.global/home?safe=gno:"
46+
47+
STAKING_TOKEN_INSTANCE_ABI_PATH = Path(
48+
SCRIPT_PATH,
49+
"..",
50+
"trader",
51+
"packages",
52+
"valory",
53+
"contracts",
54+
"staking_token",
55+
"build",
56+
"StakingToken.json",
57+
)
58+
STAKING_TOKEN_IMPLEMENTATION_ABI_PATH = STAKING_TOKEN_INSTANCE_ABI_PATH
59+
60+
ERC20_ABI_PATH = Path(
61+
SCRIPT_PATH,
62+
"..",
63+
"trader",
64+
"packages",
65+
"valory",
66+
"contracts",
67+
"erc20",
68+
"build",
69+
"ERC20.json",
70+
)
71+
72+
73+
def _load_abi_from_file(path: Path) -> Dict[str, Any]:
74+
if not os.path.exists(path):
75+
print(
76+
"Error: Contract airtfacts not found. Please execute 'run_service.sh' before executing this script."
77+
)
78+
sys.exit(1)
79+
80+
with open(path, "r", encoding=DEFAULT_ENCODING) as f:
81+
data = json.load(f)
82+
83+
return data.get("abi")
84+
85+
86+
def _erc20_balance(
87+
address: str,
88+
token_address: str = OLAS_TOKEN_ADDRESS_GNOSIS,
89+
token_name: str = "OLAS",
90+
decimal_precision: int = 2
91+
) -> str:
92+
"""Get ERC20 balance"""
93+
rpc = RPC_PATH.read_text(encoding=DEFAULT_ENCODING).strip()
94+
w3 = Web3(Web3.HTTPProvider(rpc))
95+
abi = _load_abi_from_file(ERC20_ABI_PATH)
96+
contract = w3.eth.contract(address=token_address, abi=abi)
97+
balance = contract.functions.balanceOf(address).call()
98+
return f"{balance / 10**18:.{decimal_precision}f} {token_name}"
99+
100+
101+
def _claim_rewards() -> None:
102+
service_safe_address = SERVICE_SAFE_ADDRESS_PATH.read_text(encoding=DEFAULT_ENCODING).strip()
103+
print(
104+
f"OLAS Balance on service Safe {service_safe_address}: {_erc20_balance(service_safe_address)}"
105+
)
106+
107+
env_file_vars = dotenv_values(DOTENV_PATH)
108+
staking_token_address = env_file_vars["CUSTOM_STAKING_ADDRESS"]
109+
service_id = int(SERVICE_ID_PATH.read_text(encoding=DEFAULT_ENCODING).strip())
110+
111+
rpc = RPC_PATH.read_text(encoding=DEFAULT_ENCODING).strip()
112+
w3 = Web3(Web3.HTTPProvider(rpc))
113+
abi = _load_abi_from_file(STAKING_TOKEN_IMPLEMENTATION_ABI_PATH)
114+
staking_token_contract = w3.eth.contract(address=staking_token_address, abi=abi)
115+
116+
owner_private_key = json.loads(OWNER_KEYS_JSON_PATH.read_text(encoding=DEFAULT_ENCODING))[0][
117+
"private_key"
118+
]
119+
owner_address = Web3.to_checksum_address(
120+
w3.eth.account.from_key(owner_private_key).address
121+
)
122+
123+
function = staking_token_contract.functions.claim(service_id)
124+
claim_transaction = function.build_transaction(
125+
{
126+
"chainId": GNOSIS_CHAIN_ID,
127+
"gas": DEFAULT_GAS,
128+
"gasPrice": w3.to_wei("3", "gwei"),
129+
"nonce": w3.eth.get_transaction_count(owner_address),
130+
}
131+
)
132+
133+
signed_tx = w3.eth.account.sign_transaction(claim_transaction, owner_private_key)
134+
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
135+
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
136+
print(f"Claim transaction done. Hash: {tx_hash.hex()}")
137+
138+
if "status" in tx_receipt and tx_receipt["status"] == 0:
139+
print(
140+
"The transaction was reverted. This may be caused because your service does not have rewards to claim."
141+
)
142+
else:
143+
print("")
144+
print(f"Claimed OLAS transferred to your service Safe {service_safe_address}")
145+
print(
146+
f"You can use your Owner/Operator wallet (address {owner_address}) to connect your Safe at"
147+
)
148+
print(f"{SAFE_WEBAPP_URL}{service_safe_address}")
149+
150+
151+
def main() -> None:
152+
"Main method"
153+
print("This script will claim accrued OLAS on the current staking contract to your service Safe.")
154+
response = input("Do you want to continue (yes/no)? ").strip().lower()
155+
156+
if response not in ("y", "yes"):
157+
return
158+
159+
_claim_rewards()
160+
161+
162+
if __name__ == "__main__":
163+
main()

0 commit comments

Comments
 (0)