From 1c9b9232fece070edb8ea730f4421d3adfbe96c5 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Tue, 2 Dec 2025 16:05:36 -0600 Subject: [PATCH 1/2] calc min aura incentive threshold based on previous rounds --- bal_tools/ecosystem.py | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/bal_tools/ecosystem.py b/bal_tools/ecosystem.py index 111d32f..0377c51 100644 --- a/bal_tools/ecosystem.py +++ b/bal_tools/ecosystem.py @@ -1,5 +1,8 @@ from collections import defaultdict +import math import re +import statistics +from decimal import Decimal from typing import Dict, List from .errors import ( UnexpectedListLengthError, @@ -193,6 +196,7 @@ def get_votes_from_snapshot(self, snapshot_id: str): class HiddenHand: AURA_URL = "https://api.hiddenhand.finance/proposal/aura" + SNAPSHOT_URL = "https://hub.snapshot.org/graphql" def fetch_aura_bribs(self) -> List[PropData]: """ @@ -206,3 +210,67 @@ def fetch_aura_bribs(self) -> List[PropData]: if res_parsed["error"]: raise ValueError("HH API returned error") return [PropData(**prop_data) for prop_data in res_parsed["data"]] + + def get_min_aura_incentive( + self, n_rounds: int = 4, buffer_pct: float = 0.25 + ) -> Decimal: + """ + Calculate dynamic min_aura_incentive from Snapshot votes and Hidden Hand CPV. + + Formula: ceil(max_votes * 0.005 * (1 + buffer_pct) * median_cpv / 10) * 10 + + params: + - n_rounds: number of recent gauge weight proposals to consider (default: 4) + - buffer_pct: buffer percentage to add to the threshold (default: 0.25 = 25%) + + returns: + - Decimal threshold value in USD + """ + first = n_rounds * 2 + query = f"""{{ + proposals( + first: {first}, + where: {{space: "gauges.aurafinance.eth"}}, + orderBy: "created", + orderDirection: desc + ) {{ + title + scores_total + }} + }}""" + + resp = requests.post(self.SNAPSHOT_URL, json={"query": query}, timeout=10) + resp.raise_for_status() + proposals = resp.json().get("data", {}).get("proposals", []) + gauge_proposals = [ + p + for p in proposals + if "Gauge Weight for Week of " in p.get("title", "") + ][:n_rounds] + + if not gauge_proposals: + raise ValueError("No gauge weight proposals found in Snapshot") + + max_votes = max(p["scores_total"] for p in gauge_proposals) + + resp = requests.get(self.AURA_URL, timeout=10) + resp.raise_for_status() + cpv_values = [ + p["valuePerVote"] + for p in resp.json().get("data", []) + if p.get("valuePerVote", 0) > 0 + ] + + if not cpv_values: + raise ValueError("No valid CPV data found in Hidden Hand") + + median_cpv = statistics.median(cpv_values) + buffer_multiplier = Decimal(str(1 + buffer_pct)) + + raw = ( + Decimal(str(max_votes)) + * Decimal("0.005") + * buffer_multiplier + * Decimal(str(median_cpv)) + ) + return Decimal(math.ceil(float(raw) / 10)) * 10 From 825bced1c8e494f970ebc0d9441203e49fe4ed2a Mon Sep 17 00:00:00 2001 From: jalbrekt85 <33009898+jalbrekt85@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:24:31 +0000 Subject: [PATCH 2/2] style: ci lint with `black` --- bal_tools/ecosystem.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bal_tools/ecosystem.py b/bal_tools/ecosystem.py index 0377c51..fbec26e 100644 --- a/bal_tools/ecosystem.py +++ b/bal_tools/ecosystem.py @@ -243,9 +243,7 @@ def get_min_aura_incentive( resp.raise_for_status() proposals = resp.json().get("data", {}).get("proposals", []) gauge_proposals = [ - p - for p in proposals - if "Gauge Weight for Week of " in p.get("title", "") + p for p in proposals if "Gauge Weight for Week of " in p.get("title", "") ][:n_rounds] if not gauge_proposals: