From edae0d7ba3231a6ca32de7ee04080eca80591fa1 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Mon, 31 Mar 2025 08:29:04 -0500 Subject: [PATCH 1/5] partner fee share --- fee_allocator/accounting/chains.py | 69 +++++++++++++++++--- fee_allocator/accounting/core_pools.py | 48 ++++++++++++-- fee_allocator/accounting/models.py | 54 ++++++++++++++++ fee_allocator/constants.py | 1 + fee_allocator/fee_allocator.py | 88 +++++++++++++++++++++----- main.py | 6 +- 6 files changed, 232 insertions(+), 34 deletions(-) diff --git a/fee_allocator/accounting/chains.py b/fee_allocator/accounting/chains.py index d88f9db..8d56c32 100644 --- a/fee_allocator/accounting/chains.py +++ b/fee_allocator/accounting/chains.py @@ -20,11 +20,14 @@ GlobalFeeConfig, RerouteConfig, InputFees, + AllianceConfig, + AlliancePool ) from fee_allocator.constants import ( FEE_CONSTANTS_URL, CORE_POOLS_URL, REROUTE_CONFIG_URL, + ALLIANCE_CONFIG_URL, ) from fee_allocator.accounting.decorators import round from fee_allocator.logger import logger @@ -62,6 +65,7 @@ def __init__( self.core_pools = core_pools self.fee_config = GlobalFeeConfig(**requests.get(FEE_CONSTANTS_URL).json()) + self.alliance_config = AllianceConfig(**requests.get(ALLIANCE_CONFIG_URL).json()) self.reroute_config = RerouteConfig(**requests.get(REROUTE_CONFIG_URL).json()) # caches a list of `PoolFeeData` for each chain @@ -131,6 +135,10 @@ def set_initial_pool_allocation(self) -> None: for chain in self.all_chains: chain.core_pools = [PoolFee(data, chain) for data in chain.pool_fee_data] + from rich.console import Console + console = Console() + console.print(chain.core_pools) + @property def all_chains(self) -> List[CorePoolChain]: @@ -178,6 +186,7 @@ def __init__(self, chains: CorePoolRunConfig, name: str, fees: int, web3: Web3): self.fees_collected = fees self.web3 = web3 self.core_pools_list = self.chains.core_pools.get(self.name, {}) if self.chains.core_pools else None + self.alliance_pools: List[AlliancePool] = [] try: self.chain_id = AddrBook.chain_ids_by_name[self.name] @@ -191,6 +200,7 @@ def __init__(self, chains: CorePoolRunConfig, name: str, fees: int, web3: Web3): self.block_range = self._set_block_range() self.pool_fee_data: Union[list[PoolFeeData], None] = None self.core_pools: List[PoolFee] = [] + self.alliance_noncore_fee_data: List[PoolFee] = [] def _set_block_range(self) -> tuple[int, int]: start = get_block_by_ts(self.chains.date_range[0], self) @@ -246,26 +256,53 @@ def _fetch_and_process_pool_fee_data(self) -> list[PoolFeeData]: pools_data = [] - core_pools_list = ( + core_pools_list = list( [(pool_id, label) for pool_id, label in self.core_pools_list.items()] if self.core_pools_list is not None else self.bal_pools_gauges.core_pools ) + self.alliance_pools = [ + pool + for member in self.chains.alliance_config.alliance_members + for pool in member.pools + if pool.network == self.name and pool.active + ] + + alliance_pool_ids = {pool.pool_id for pool in self.alliance_pools} + alliance_pool_tuples = [(pool.pool_id, pool.partner) for pool in self.alliance_pools] + + core_pools_dict = dict(core_pools_list) + core_pools_dict.update(dict(alliance_pool_tuples)) + core_pools_list = list(core_pools_dict.items()) + + # process v3 pools if self.chains.protocol_version == "v3": v3_pools = [(p, l) for p, l in core_pools_list if len(p) == 42] for pool_id, label in v3_pools: pool_fee_data = self._fetch_twap_prices_and_init_pool_fee_data_v3(pool_id, label, pool_to_gauge) - pools_data.append(pool_fee_data) - + alliance_pool = next((p for p in self.alliance_pools if p.pool_id == pool_id), None) + if alliance_pool: + if alliance_pool.pool_type != "core": + self.alliance_noncore_fee_data.append(pool_fee_data) + else: + pools_data.append(pool_fee_data) + + # process v2 pools elif self.chains.protocol_version == "v2": v2_pools = [(p, l) for p, l in core_pools_list if len(p) != 42] for pool_id, label in v2_pools: start_snap = self._get_latest_snapshot(start_snaps, pool_id) end_snap = self._get_latest_snapshot(end_snaps, pool_id) - if self._should_add_pool(pool_id, start_snap, end_snap, pool_to_gauge): + + if pool_id in alliance_pool_ids or self._should_add_pool(pool_id, start_snap, end_snap, pool_to_gauge): pool_fee_data = self._fetch_twap_prices_and_init_pool_fee_data_v2(pool_id, label, pool_to_gauge, start_snap, end_snap) - pools_data.append(pool_fee_data) + alliance_pool = next((p for p in self.alliance_pools if p.pool_id == pool_id), None) + if alliance_pool: + if alliance_pool.pool_type != "core": + self.alliance_noncore_fee_data.append(pool_fee_data) + else: + pools_data.append(pool_fee_data) return pools_data @@ -318,12 +355,12 @@ def _fetch_twap_prices_and_init_pool_fee_data_v2( symbol=label, bpt_price=prices.bpt_price.twap_price, tokens_price=prices.token_prices, - gauge_address=pool_to_gauge[pool_id], + gauge_address=pool_to_gauge.get(pool_id), start_pool_snapshot=start_snap, end_pool_snapshot=end_snap, last_join_exit_ts=last_join_exit_ts, ) - + def _fetch_twap_prices_and_init_pool_fee_data_v3( self, pool_id: str, @@ -342,12 +379,13 @@ def _fetch_twap_prices_and_init_pool_fee_data_v3( address=pool_id, symbol=label, tokens_price=None, - gauge_address=pool_to_gauge[pool_id], + gauge_address=pool_to_gauge.get(pool_id), start_pool_snapshot=None, end_pool_snapshot=None, last_join_exit_ts=last_join_exit_ts, total_earned_fees_usd_twap=self.subgraph.get_v3_protocol_fees(pool_id, self.name, self.chains.date_range), ) + @staticmethod def _get_latest_snapshot( @@ -376,7 +414,8 @@ def noncore_fees_collected(self) -> Decimal: if not self.core_pools: raise ValueError("core pools not set") total_core_fees = sum(pool.total_earned_fees_usd_twap for pool in self.core_pools) - return max(self.fees_collected - total_core_fees, Decimal(0)) + total_noncore_fees = sum(pool.total_earned_fees_usd_twap for pool in self.alliance_noncore_fee_data) + return max(self.fees_collected - total_core_fees - total_noncore_fees, Decimal(0)) @property def noncore_to_dao_usd(self) -> Decimal: @@ -385,3 +424,15 @@ def noncore_to_dao_usd(self) -> Decimal: @property def noncore_to_vebal_usd(self) -> Decimal: return self.noncore_fees_collected * self.chains.fee_config.noncore_vebal_share_pct + + @property + def alliance_noncore_fees_collected(self) -> Decimal: + return sum(pool.total_earned_fees_usd_twap for pool in self.alliance_noncore_fee_data) + + @property + def alliance_noncore_to_dao_usd(self) -> Decimal: + return self.alliance_noncore_fees_collected * self.chains.alliance_config.alliance_fee_allocations["non_core"].dao_share_pct + + @property + def alliance_noncore_to_vebal_usd(self) -> Decimal: + return self.alliance_noncore_fees_collected * self.chains.alliance_config.alliance_fee_allocations["non_core"].vebal_share_pct diff --git a/fee_allocator/accounting/core_pools.py b/fee_allocator/accounting/core_pools.py index 67a48de..75767c6 100644 --- a/fee_allocator/accounting/core_pools.py +++ b/fee_allocator/accounting/core_pools.py @@ -37,11 +37,13 @@ class PoolFeeData: last_join_exit_ts: int bpt_price: Decimal = field(default=Decimal(0)) total_earned_fees_usd_twap: Decimal = None + is_alliance_pool: bool = field(default=False) + is_alliance_non_core_pool: bool = field(default=False) def __post_init__(self): if len(self.pool_id) == 42: # v3 pool; earned fees already calculated - if self.total_earned_fees_usd_twap is None: + if self.total_earned_fees_usd_twap is None: raise ValueError(f"v3 pool {self.pool_id} must have total_earned_fees_usd_twap set. got {self.total_earned_fees_usd_twap}") else: # v2 pool @@ -80,6 +82,11 @@ def __init__(self, data: PoolFeeData, chain: CorePoolChain): self.__dict__.update(vars(data)) self.chain = chain + # Check if this is an Alliance pool + self.is_alliance_pool = self._check_if_alliance_pool() + self.alliance_fee_config = self._get_alliance_fee_config() if self.is_alliance_pool else None + self.is_alliance_non_core_pool = self._is_alliance_non_core_pool() + self.original_earned_fee_share = Decimal(0) self.earned_fee_share_of_chain_usd = self._earned_fee_share_of_chain_usd() self.total_to_incentives_usd = self._total_to_incentives_usd() @@ -87,18 +94,40 @@ def __init__(self, data: PoolFeeData, chain: CorePoolChain): self.to_bal_incentives_usd = self._to_bal_incentives_usd() self.to_dao_usd = self._to_dao_usd() self.to_vebal_usd = self._to_vebal_usd() + self.to_partner_usd = self._to_partner_usd() if self.is_alliance_pool else Decimal(0) self.redirected_incentives_usd = Decimal(0) override_cls = overrides.get(self.pool_id) self.override = override_cls(self) if override_cls else None + def _check_if_alliance_pool(self) -> bool: + return self.chain.chains.alliance_config.get_pool_fee_config(self.pool_id, self.chain.name, True) is not None + + def _get_alliance_fee_config(self): + return self.chain.chains.alliance_config.get_pool_fee_config(self.pool_id, self.chain.name, True) + + def _is_alliance_non_core_pool(self) -> bool: + if not self.is_alliance_pool: + return False + + for member in self.chain.chains.alliance_config.alliance_members: + for pool in member.pools: + if pool.pool_type != "core": + print(f"Alliance non-core pool: {pool.pool_id} {pool.network} {pool.active}") + if pool.pool_id == self.pool_id and pool.network == self.chain.name and pool.active: + return pool.pool_type != "core" + return False + def _earned_fee_share_of_chain_usd(self) -> Decimal: if self.chain.total_earned_fees_usd_twap == 0: return Decimal(0) return self.total_earned_fees_usd_twap / self.chain.total_earned_fees_usd_twap def _total_to_incentives_usd(self) -> Decimal: - to_distribute_to_incentives = self.chain.total_earned_fees_usd_twap * self.chain.chains.fee_config.vote_incentive_pct + to_distribute_to_incentives = self.chain.total_earned_fees_usd_twap * ( + self.alliance_fee_config.vote_incentive_pct if self.is_alliance_pool + else self.chain.chains.fee_config.vote_incentive_pct + ) return self.earned_fee_share_of_chain_usd * to_distribute_to_incentives def _to_aura_incentives_usd(self) -> Decimal: @@ -111,12 +140,21 @@ def _to_dao_usd(self) -> Decimal: return ( self.earned_fee_share_of_chain_usd * self.chain.total_earned_fees_usd_twap - * self.chain.chains.fee_config.dao_share_pct + * (self.alliance_fee_config.dao_share_pct if self.is_alliance_pool + else self.chain.chains.fee_config.dao_share_pct) ) def _to_vebal_usd(self) -> Decimal: return ( self.earned_fee_share_of_chain_usd * self.chain.total_earned_fees_usd_twap - * self.chain.chains.fee_config.vebal_share_pct - ) \ No newline at end of file + * (self.alliance_fee_config.vebal_share_pct if self.is_alliance_pool + else self.chain.chains.fee_config.vebal_share_pct) + ) + + def _to_partner_usd(self) -> Decimal: + return ( + self.earned_fee_share_of_chain_usd + * self.chain.total_earned_fees_usd_twap + * self.alliance_fee_config.partner_share_pct + ) if self.is_alliance_pool else Decimal(0) \ No newline at end of file diff --git a/fee_allocator/accounting/models.py b/fee_allocator/accounting/models.py index b2e6bd3..f8d239c 100644 --- a/fee_allocator/accounting/models.py +++ b/fee_allocator/accounting/models.py @@ -39,3 +39,57 @@ def model_post_init(self, __context): if any(self.__dict__.values()): raise ValueError(f"Reroute logic not implemented") + +class AlliancePool(BaseModel): + """ + Represents a pool that is part of the Balancer Alliance program. + """ + pool_id: str + network: str + partner: str + pool_type: str + eligibility_date: str + active: bool + + +class AllianceMember(BaseModel): + """ + Represents a member of the Balancer Alliance program. + """ + name: str + multisig_address: str + active: bool + join_date: str + last_lock_date: str + pools: list[AlliancePool] + + +class AllianceFeeAllocation(BaseModel): + """ + Represents the fee allocation configuration for Alliance pools. + """ + vebal_share_pct: Decimal + vote_incentive_pct: Decimal | None = None # None for non-core pools + partner_share_pct: Decimal + dao_share_pct: Decimal + + +class AllianceConfig(BaseModel): + """ + Represents the complete Alliance configuration including members and fee allocations. + Models the data sourced from the ALLIANCE_CONSTANTS_URL endpoint. + """ + alliance_members: list[AllianceMember] + alliance_fee_allocations: dict[str, AllianceFeeAllocation] + + def get_pool_fee_config(self, pool_id: str, network: str, is_core: bool) -> AllianceFeeAllocation | None: + """ + Returns the fee allocation configuration for a specific pool if it's part of the Alliance program. + Returns None if the pool is not part of the Alliance program. + """ + for member in self.alliance_members: + for pool in member.pools: + if pool.pool_id == pool_id and pool.network == network and pool.active: + return self.alliance_fee_allocations["core" if is_core else "non_core"] + return None + diff --git a/fee_allocator/constants.py b/fee_allocator/constants.py index f834a14..4c025da 100644 --- a/fee_allocator/constants.py +++ b/fee_allocator/constants.py @@ -1,5 +1,6 @@ FEE_CONSTANTS_URL = "https://raw.githubusercontent.com/BalancerMaxis/multisig-ops/f7e0425b59e474b01d2ede125053238460792630/config/protocol_fees_constants.json" CORE_POOLS_URL = "https://raw.githubusercontent.com/BalancerMaxis/bal_addresses/main/outputs/core_pools.json" +ALLIANCE_CONFIG_URL = "https://raw.githubusercontent.com/BalancerMaxis/multisig-ops/4246da32541c57fa5f7b920896089fe421c97187/config/alliance_fee_share.json" REROUTE_CONFIG_URL = "https://raw.githubusercontent.com/BalancerMaxis/multisig-ops/main/config/core_pools_rerouting.json" POOL_OVERRIDES_URL = "https://raw.githubusercontent.com/BalancerMaxis/multisig-ops/main/config/pool_incentives_overrides.json" SNAPSHOT_URL = "https://hub.snapshot.org/graphql?" diff --git a/fee_allocator/fee_allocator.py b/fee_allocator/fee_allocator.py index 500314d..6bd3c0f 100644 --- a/fee_allocator/fee_allocator.py +++ b/fee_allocator/fee_allocator.py @@ -79,18 +79,17 @@ def redistribute_fees(self): 1. Identifies pools with incentives below the minimum threshold. 2. Redistributes fees from these pools to eligible pools above the threshold. 3. Recalculates incentive amounts for Aura and Balancer. - 4. Adjusts DAO and veBAL shares based on the original distribution. - 5. Handles Aura minimum incentives with and without a buffer. + 4. Handles Aura minimum incentives with and without a buffer. """ min_amount = self.run_config.fee_config.min_vote_incentive_amount - for chain in self.run_config.all_chains: - total_earned_fees = sum(p.total_earned_fees_usd_twap for p in chain.core_pools) - for pool in chain.core_pools: - if total_earned_fees > 0: - pool.original_earned_fee_share = pool.total_earned_fees_usd_twap / total_earned_fees - else: - pool.original_earned_fee_share = Decimal(0) + # for chain in self.run_config.all_chains: + # total_earned_fees = sum(p.total_earned_fees_usd_twap for p in chain.core_pools) + # for pool in chain.core_pools: + # if total_earned_fees > 0: + # pool.original_earned_fee_share = pool.total_earned_fees_usd_twap / total_earned_fees + # else: + # pool.original_earned_fee_share = Decimal(0) for chain in self.run_config.all_chains: pools_to_redistribute = [p for p in chain.core_pools if p.total_to_incentives_usd < min_amount] @@ -209,7 +208,7 @@ def generate_bribe_csv( }, ) - noncore_total_to_dao_usd = sum(chain.noncore_to_dao_usd for chain in self.run_config.all_chains) + noncore_total_to_dao_usd = sum(chain.noncore_to_dao_usd + chain.alliance_noncore_to_dao_usd for chain in self.run_config.all_chains) output.append( { "target": "0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f", # DAO msig @@ -274,12 +273,56 @@ def generate_incentives_csv( ) return output_path + + def generate_partner_csv( + self, output_path: Path = Path("fee_allocator/allocations/partner") + ) -> Path: + logger.info("generating partner csv") + output = [] + for chain in self.run_config.all_chains: + for alliance_pool in chain.alliance_pools: + member = next((m for m in self.run_config.alliance_config.alliance_members if alliance_pool.partner == m.name), None) + core_pool = next((p for p in chain.core_pools if p.pool_id == alliance_pool.pool_id), None) + noncore_pool = next((p for p in chain.alliance_noncore_fee_data if p.pool_id == alliance_pool.pool_id), None) + + if core_pool: + partner_fee = core_pool.to_partner_usd + pool_id = core_pool.pool_id + elif noncore_pool: + partner_fee = ( + noncore_pool.total_earned_fees_usd_twap / chain.alliance_noncore_fees_collected + * chain.alliance_noncore_fees_collected + * self.run_config.alliance_config.alliance_fee_allocations["non_core"].partner_share_pct + ) + pool_id = noncore_pool.pool_id + else: + raise ValueError(f"No core or noncore pool found for {alliance_pool.pool_id}") + + output.append({ + "pool_id": pool_id, + "chain": chain.name, + "partner": alliance_pool.partner, + "amount": partner_fee, + "target": member.multisig_address, + }) + + + df = pd.DataFrame(output) + output_path = PROJECT_ROOT / output_path / f"{self.run_config.protocol_version}_partner.csv" + output_path.parent.mkdir(exist_ok=True) + df.to_csv(output_path, index=False) + return output_path def generate_bribe_payload( - self, input_csv: str, output_path: Path = Path("fee_allocator/payloads") + self, input_csv: str, output_path: Path = Path("fee_allocator/payloads"), partner_csv: str = None ) -> Path: """ - builds a safe payload from the bribe csv + builds a safe payload from the bribe csv and optionally partner csv + + Args: + input_csv (str): Path to the bribe CSV file + output_path (Path): Path to save the output payload + partner_csv (str, optional): Path to the partner CSV file containing partner fee allocations """ logger.info("generating payload") builder = SafeTxBuilder(self.book["multisigs/fees"]) @@ -322,7 +365,16 @@ def generate_bribe_payload( """ usdc.transfer(payment_df["target"], dao_fee_usdc) - spent_usdc = int(total_bribe_usdc + dao_fee_usdc) + total_partner_usdc = 0 + if partner_csv: + partner_df = pd.read_csv(partner_csv) + for _, row in partner_df.iterrows(): + if row["amount"] > 0: + partner_amount = int(row["amount"] * 1e6) + total_partner_usdc += partner_amount + usdc.transfer(row["target"], partner_amount) + + spent_usdc = int(total_bribe_usdc + dao_fee_usdc + total_partner_usdc) vebal_usdc_amount = int( self.run_config.mainnet.web3.eth.contract(usdc.address, abi=get_abi("ERC20")) .functions.balanceOf(builder.safe_address) @@ -387,8 +439,8 @@ def recon(self) -> None: total_dao += pool.to_dao_usd total_vebal += pool.to_vebal_usd - total_dao += chain.noncore_to_dao_usd - total_vebal += chain.noncore_to_vebal_usd + total_dao += chain.noncore_to_dao_usd + chain.alliance_noncore_to_dao_usd + total_vebal += chain.noncore_to_vebal_usd + chain.alliance_noncore_to_vebal_usd total_incentives = total_aura + total_bal + total_dao + total_vebal total_pct = (total_aura + total_bal + total_dao + total_vebal) / total_incentives @@ -399,8 +451,10 @@ def recon(self) -> None: core_pool_incentives = total_aura + total_bal aura_share = total_aura / core_pool_incentives if core_pool_incentives > 0 else Decimal(0) target_share = self.run_config.aura_vebal_share - assert abs(aura_share - target_share) < Decimal('0.05'), \ - f"Aura share {aura_share} deviates from target {target_share}" + + # NOTE: alliance pools will make this check fail + # assert abs(aura_share - target_share) < Decimal('0.05'), \ + # f"Aura share {aura_share} deviates from target {target_share}" summary = { "feesCollected": float(round(total_fees, 2)), diff --git a/main.py b/main.py index a1af2f5..5993dc4 100644 --- a/main.py +++ b/main.py @@ -49,9 +49,9 @@ def main() -> None: fee_allocator.recon() fee_allocator.generate_incentives_csv() - file_name = fee_allocator.generate_bribe_csv() - fee_allocator.generate_bribe_payload(file_name) - + bribe_file_name = fee_allocator.generate_bribe_csv() + partner_file_name = fee_allocator.generate_partner_csv() + fee_allocator.generate_bribe_payload(bribe_file_name, partner_csv=partner_file_name) if __name__ == "__main__": main() From f74b15fd8c01b3e0f98909b83c7c2ee533cf9683 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Tue, 1 Apr 2025 10:55:27 -0500 Subject: [PATCH 2/5] remove debug --- fee_allocator/accounting/chains.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fee_allocator/accounting/chains.py b/fee_allocator/accounting/chains.py index 8d56c32..0fd44fe 100644 --- a/fee_allocator/accounting/chains.py +++ b/fee_allocator/accounting/chains.py @@ -135,10 +135,6 @@ def set_initial_pool_allocation(self) -> None: for chain in self.all_chains: chain.core_pools = [PoolFee(data, chain) for data in chain.pool_fee_data] - from rich.console import Console - console = Console() - console.print(chain.core_pools) - @property def all_chains(self) -> List[CorePoolChain]: From 606450e5534956d20e8b31374e5ca7462c660a50 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Wed, 2 Apr 2025 09:14:34 -0500 Subject: [PATCH 3/5] fix: non alliance core pools not being accounted for --- fee_allocator/accounting/chains.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/fee_allocator/accounting/chains.py b/fee_allocator/accounting/chains.py index 0fd44fe..0e38c85 100644 --- a/fee_allocator/accounting/chains.py +++ b/fee_allocator/accounting/chains.py @@ -278,11 +278,10 @@ def _fetch_and_process_pool_fee_data(self) -> list[PoolFeeData]: for pool_id, label in v3_pools: pool_fee_data = self._fetch_twap_prices_and_init_pool_fee_data_v3(pool_id, label, pool_to_gauge) alliance_pool = next((p for p in self.alliance_pools if p.pool_id == pool_id), None) - if alliance_pool: - if alliance_pool.pool_type != "core": - self.alliance_noncore_fee_data.append(pool_fee_data) - else: - pools_data.append(pool_fee_data) + if alliance_pool and alliance_pool.pool_type != "core": + self.alliance_noncore_fee_data.append(pool_fee_data) + else: + pools_data.append(pool_fee_data) # process v2 pools elif self.chains.protocol_version == "v2": @@ -294,11 +293,10 @@ def _fetch_and_process_pool_fee_data(self) -> list[PoolFeeData]: if pool_id in alliance_pool_ids or self._should_add_pool(pool_id, start_snap, end_snap, pool_to_gauge): pool_fee_data = self._fetch_twap_prices_and_init_pool_fee_data_v2(pool_id, label, pool_to_gauge, start_snap, end_snap) alliance_pool = next((p for p in self.alliance_pools if p.pool_id == pool_id), None) - if alliance_pool: - if alliance_pool.pool_type != "core": - self.alliance_noncore_fee_data.append(pool_fee_data) - else: - pools_data.append(pool_fee_data) + if alliance_pool and alliance_pool.pool_type != "core": + self.alliance_noncore_fee_data.append(pool_fee_data) + else: + pools_data.append(pool_fee_data) return pools_data From 3e139b101ed8fe8677ef010ceeb4e433fa017632 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Thu, 3 Apr 2025 07:39:20 -0500 Subject: [PATCH 4/5] cache alliance pools --- fee_allocator/accounting/chains.py | 32 +++++++++++++++++++++--------- fee_allocator/fee_allocator.py | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/fee_allocator/accounting/chains.py b/fee_allocator/accounting/chains.py index 0e38c85..4f47366 100644 --- a/fee_allocator/accounting/chains.py +++ b/fee_allocator/accounting/chains.py @@ -224,12 +224,31 @@ def _cache_file_path(self) -> Path: filename = f"{self.name}_{self.chains.protocol_version}_{self.chains.date_range[0]}_{self.chains.date_range[1]}.joblib" return self.chains.cache_dir / filename + def _init_alliance_pools(self) -> None: + """ + Initialize alliance pools for the current chain + """ + self.alliance_pools = [ + pool + for member in self.chains.alliance_config.alliance_members + for pool in member.pools + if pool.network == self.name and pool.active + ] + def _load_core_pools_from_cache(self) -> list[PoolFeeData]: logger.info(f"loading core pools from cache for {self.name}") - return joblib.load(self._cache_file_path()) + cached_data = joblib.load(self._cache_file_path()) + self.alliance_pools = cached_data.get('alliance_pools', []) + self.alliance_noncore_fee_data = cached_data.get('alliance_noncore_fee_data', []) + return cached_data.get('pool_fee_data', []) def _save_core_pools_to_cache(self, pool_data: list[PoolFeeData]) -> None: - joblib.dump(pool_data, self._cache_file_path()) + cache_data = { + 'pool_fee_data': pool_data, + 'alliance_pools': self.alliance_pools, + 'alliance_noncore_fee_data': self.alliance_noncore_fee_data + } + joblib.dump(cache_data, self._cache_file_path()) def _fetch_and_process_pool_fee_data(self) -> list[PoolFeeData]: """ @@ -258,13 +277,8 @@ def _fetch_and_process_pool_fee_data(self) -> list[PoolFeeData]: else self.bal_pools_gauges.core_pools ) - self.alliance_pools = [ - pool - for member in self.chains.alliance_config.alliance_members - for pool in member.pools - if pool.network == self.name and pool.active - ] - + self._init_alliance_pools() + alliance_pool_ids = {pool.pool_id for pool in self.alliance_pools} alliance_pool_tuples = [(pool.pool_id, pool.partner) for pool in self.alliance_pools] diff --git a/fee_allocator/fee_allocator.py b/fee_allocator/fee_allocator.py index 6bd3c0f..06d2a03 100644 --- a/fee_allocator/fee_allocator.py +++ b/fee_allocator/fee_allocator.py @@ -254,6 +254,7 @@ def generate_incentives_csv( ), "reroute_incentives": 0, "last_join_exit": core_pool.last_join_exit_ts, + "is_partner": any(pool.pool_id == core_pool.pool_id for pool in chain.alliance_pools), }, ) From e63ac902667964559a7702743e12657e0db3d60e Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Thu, 3 Apr 2025 10:45:35 -0500 Subject: [PATCH 5/5] allocate 100% of voting incentive to aura for partner pools --- fee_allocator/accounting/core_pools.py | 4 ++++ fee_allocator/fee_allocator.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fee_allocator/accounting/core_pools.py b/fee_allocator/accounting/core_pools.py index 75767c6..3c2963a 100644 --- a/fee_allocator/accounting/core_pools.py +++ b/fee_allocator/accounting/core_pools.py @@ -131,9 +131,13 @@ def _total_to_incentives_usd(self) -> Decimal: return self.earned_fee_share_of_chain_usd * to_distribute_to_incentives def _to_aura_incentives_usd(self) -> Decimal: + if self.is_alliance_pool: + return self.total_to_incentives_usd return self.total_to_incentives_usd * self.chain.chains.aura_vebal_share def _to_bal_incentives_usd(self) -> Decimal: + if self.is_alliance_pool: + return Decimal(0) return self.total_to_incentives_usd * (1 - self.chain.chains.aura_vebal_share) def _to_dao_usd(self) -> Decimal: diff --git a/fee_allocator/fee_allocator.py b/fee_allocator/fee_allocator.py index 06d2a03..30f8b16 100644 --- a/fee_allocator/fee_allocator.py +++ b/fee_allocator/fee_allocator.py @@ -112,8 +112,8 @@ def redistribute_fees(self): total = total_fees_to_redistribute * weight pool.total_to_incentives_usd += total pool.redirected_incentives_usd += total - pool.to_aura_incentives_usd += total * self.run_config.aura_vebal_share - pool.to_bal_incentives_usd += total * (1 - self.run_config.aura_vebal_share) + pool.to_aura_incentives_usd += total if pool.is_alliance_pool else total * self.run_config.aura_vebal_share + pool.to_bal_incentives_usd += Decimal(0) if pool.is_alliance_pool else total * (1 - self.run_config.aura_vebal_share) # for pool in chain.core_pools: # pool.to_dao_usd = pool.original_earned_fee_share * chain.fees_collected * self.run_config.fee_config.dao_share_pct