Last active
May 1, 2025 20:45
-
-
Save YourAverageLord/0e8c5c636a517afe341f2914a6447e0a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import os | |
import logging | |
from datetime import datetime, timezone, timedelta | |
from dotenv import load_dotenv | |
from web3 import Web3 | |
from time import sleep | |
# ─── CONFIG ───────────────────────────────────────────────────────────────────── | |
load_dotenv() | |
RPC_URL = os.getenv("RPC_ADDRESS") | |
PRIVATE_KEY = os.getenv("PRIVATE_KEY") | |
FORWARD_TO_ADDY = os.getenv("FORWARD_TO") | |
DISTRIB_CONTRACT_ADDY = "0xd16d5ec345dd86fb63c6a9c43c517210f1027914" | |
VECRV_CONTRACT_ADDY = "0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2" | |
CRV_TOKEN_ADDY = "0xD533a949740bb3306d119CC777fa900bA034cd52" | |
CRVUSD_TOKEN_ADDY = "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E" | |
# ──────────────────────────────────────────────────────────────────────────────── | |
logger = logging.getLogger("claimer") | |
logger.setLevel(logging.INFO) | |
h = logging.StreamHandler() | |
h.setFormatter(logging.Formatter("[%(levelname)s] %(message)s")) | |
logger.addHandler(h) | |
# ─── LOAD ABIs ─────────────────────────────────────────────────────── | |
with open("abi_fee_distrubtor.json", encoding="utf-8") as f: | |
ABI_fee_distributor = json.load(f) | |
with open("abi_vecrv.json", encoding="utf-8") as f: | |
ABI_VECRV = json.load(f) | |
ERC20_ABI = [ | |
{"constant": True, "inputs": [{"name": "_owner", "type": "address"}], | |
"name": "balanceOf", "outputs": [{"name": "balance", "type": "uint256"}], | |
"type": "function"}, | |
{"constant": False, "inputs": [ | |
{"name": "_to", "type": "address"}, | |
{"name": "_value", "type": "uint256"} | |
], "name": "transfer", "outputs": [{"name": "", "type": "bool"}], | |
"type": "function"} | |
] | |
# ──────────────────────────────────────────────────────────────────────────────── | |
def block_explorer_link(tx_hash: str) -> str: | |
if not tx_hash.startswith("0x"): | |
tx_hash = "0x" + tx_hash | |
return f"https://etherscan.io/tx/{tx_hash}" | |
def forward_tokens(w3, token_name, token_addr: str, my_addr: str, to_addr: str): | |
gas_price = w3.eth.gas_price | |
token_addr = Web3.to_checksum_address(token_addr) | |
token = w3.eth.contract(token_addr, abi=ERC20_ABI) | |
bal = token.functions.balanceOf(my_addr).call() | |
if bal == 0: | |
logger.warning(f"No {token_name} balance to forward") | |
return | |
logger.info(f"Forwarding {bal/10**18:.6f} {token_name} tokens to {to_addr}") | |
fn_fwd = token.functions.transfer(to_addr, bal) | |
gas_est = fn_fwd.estimate_gas({"from": my_addr}) | |
tx_fwd = fn_fwd.build_transaction({ | |
"from": my_addr, | |
"nonce": w3.eth.get_transaction_count(my_addr), | |
"gas": gas_est, | |
"gasPrice": gas_price | |
}) | |
signed2 = w3.eth.account.sign_transaction(tx_fwd, PRIVATE_KEY) | |
txh2 = w3.eth.send_raw_transaction(signed2.raw_transaction) | |
logger.info("Sent forward → " + block_explorer_link(txh2.hex())) | |
rcpt2 = w3.eth.wait_for_transaction_receipt(txh2, timeout=180) | |
logger.info(f"✅ Forwarded in block {rcpt2.blockNumber}") | |
return rcpt2 | |
def main(): | |
logger.info("Forwarding everything to: " + FORWARD_TO_ADDY) | |
# Setup Web3 | |
w3 = Web3(Web3.HTTPProvider(RPC_URL)) | |
if not w3.is_connected(): | |
raise RuntimeError("🔴 RPC connection failed") | |
acct = w3.eth.account.from_key(PRIVATE_KEY) | |
my_addr = acct.address | |
logger.info(f"Using account {my_addr}") | |
# 1) Get Unlock time from contract | |
vecrv = Web3.to_checksum_address(VECRV_CONTRACT_ADDY) | |
contract = w3.eth.contract(vecrv, abi=ABI_VECRV) | |
unlock_time = contract.functions.locked__end(my_addr).call() | |
unlock_local = datetime.fromtimestamp(unlock_time, tz=timezone.utc).astimezone(timezone(timedelta(hours=-4))) | |
logger.info(f"Unlock time: {unlock_local.strftime('%Y-%m-%d %I:%M:%S %p')} ET") | |
logger.info("🔒 Waiting for unlock time...") | |
# Wait for claim unlock time using local UTC clock | |
while datetime.now(timezone.utc) < unlock_local: | |
# busy waiting to check on point, no sleep | |
continue | |
logger.info("🔓 Unlock time reached; proceeding with claim.") | |
# 2) Withdraw unlocked CRV | |
gas_price = w3.eth.gas_price | |
fn_withdraw = contract.functions.withdraw() | |
est0 = fn_withdraw.estimate_gas({"from": my_addr}) | |
logger.info(f"estimateGas(withdraw): {est0}") | |
tx_withdraw = fn_withdraw.build_transaction({ | |
"from": my_addr, | |
"nonce": w3.eth.get_transaction_count(my_addr), | |
"gas": est0, | |
"gasPrice": gas_price | |
}) | |
signed0 = w3.eth.account.sign_transaction(tx_withdraw, PRIVATE_KEY) | |
txh0 = w3.eth.send_raw_transaction(signed0.raw_transaction) | |
logger.info("Sent withdraw → " + block_explorer_link(txh0.hex())) | |
rcpt0 = w3.eth.wait_for_transaction_receipt(txh0, timeout=180) | |
logger.info(f"✅ Withdrawn in block {rcpt0.blockNumber}") | |
sleep(3) | |
# 3) Forward CRV to another address | |
forward_tokens(w3, "CRV", CRV_TOKEN_ADDY, my_addr, FORWARD_TO_ADDY) | |
# 4) Claim from Fee Distributor | |
gas_price = w3.eth.gas_price | |
distributor = Web3.to_checksum_address(DISTRIB_CONTRACT_ADDY) | |
contract = w3.eth.contract(distributor, abi=ABI_fee_distributor) | |
fn_claim = contract.functions.claim() | |
est1 = fn_claim.estimate_gas({"from": my_addr}) | |
logger.info(f"estimateGas(claim): {est1}") | |
tx_claim = fn_claim.build_transaction({ | |
"from": my_addr, | |
"nonce": w3.eth.get_transaction_count(my_addr), | |
"gas": est1, | |
"gasPrice": gas_price | |
}) | |
signed = w3.eth.account.sign_transaction(tx_claim, PRIVATE_KEY) | |
txh = w3.eth.send_raw_transaction(signed.raw_transaction) | |
logger.info("Sent claim → " + block_explorer_link(txh.hex())) | |
rcpt = w3.eth.wait_for_transaction_receipt(txh, timeout=180) | |
logger.info(f"✅ Claimed in block {rcpt.blockNumber}") | |
sleep(3) | |
# 5) Forward CRVUSD to another address | |
forward_tokens(w3, "CRVUSD", CRVUSD_TOKEN_ADDY, my_addr, FORWARD_TO_ADDY) | |
logger.info("Done.") | |
if __name__ == "__main__": | |
while True: | |
try: | |
main() | |
except Exception as e: | |
logger.error(f"Error: {e}") | |
sleep(60) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment