Skip to content

Instantly share code, notes, and snippets.

@YourAverageLord
Last active May 1, 2025 20:45
Show Gist options
  • Save YourAverageLord/0e8c5c636a517afe341f2914a6447e0a to your computer and use it in GitHub Desktop.
Save YourAverageLord/0e8c5c636a517afe341f2914a6447e0a to your computer and use it in GitHub Desktop.
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