From da6cdda4189741225b84a51769bc60d32c153844 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Wed, 12 Nov 2025 17:27:51 -0600 Subject: [PATCH 1/3] calc quantamm retroactive fees, add amount to reporting --- fee_allocator/payload_visualizer.py | 84 ++++++++---- main_combined.py | 13 +- quantamm/retroactive_fees.py | 197 ++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 32 deletions(-) create mode 100644 quantamm/retroactive_fees.py diff --git a/fee_allocator/payload_visualizer.py b/fee_allocator/payload_visualizer.py index e971cbc5..787ce27d 100644 --- a/fee_allocator/payload_visualizer.py +++ b/fee_allocator/payload_visualizer.py @@ -1,7 +1,7 @@ import json from decimal import Decimal from pathlib import Path -from typing import Dict, List, Any, Optional +from typing import Dict, List, Any import requests from rich.console import Console @@ -28,7 +28,9 @@ class PayloadVisualizer: # Transfer group types for method categorization TRANSFER_GROUPS = ["veBAL Transfers", "DAO Transfers", "Partner Transfers", "Alliance Transfers", "Beets Transfers", "Unknown Transfers"] - + + USDC_ADDRESS = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + def __init__(self): self.console = Console() self.book = AddrBook("mainnet").flatbook @@ -72,6 +74,11 @@ def _load_fee_share_config(self) -> tuple[List[str], Dict[str, str], List[str], partner_addresses.append(ezkl_addr) partner_names[ezkl_addr] = "ezkl" + quantamm_addr = "0xd785201fd2d9be7602f6682296bb415530c027ef" + if quantamm_addr not in partner_addresses: + partner_addresses.append(quantamm_addr) + partner_names[quantamm_addr] = "QuantAMM" + return alliance_addresses, alliance_names, partner_addresses, partner_names def format_amount(self, amount: str, token: str = "USDC") -> str: @@ -89,26 +96,26 @@ def format_address(self, address: str) -> str: """Format address with name lookup""" if not address: return "" - - # Check if it's a known address + + addr_lower = address.lower() + + # Check partners first (includes QuantAMM) + if addr_lower in self.partner_names: + partner_name = self.partner_names[addr_lower] + return f"{partner_name} ({address[:6]}...{address[-4:]})" + + # Check alliance members + if addr_lower in self.alliance_names: + alliance_name = self.alliance_names[addr_lower] + return f"{alliance_name} ({address[:6]}...{address[-4:]})" + + # Check if it's a known address in the book for key, value in self.book.items(): if value and value.lower() == address.lower(): parts = key.split("/") name = parts[-1].replace("_", " ").title() return f"{name} ({address[:6]}...{address[-4:]})" - addr_lower = address.lower() - - # Check if it's an alliance member - if addr_lower in self.alliance_names: - alliance_name = self.alliance_names[addr_lower] - return f"{alliance_name} ({address[:6]}...{address[-4:]})" - - # Check if it's a partner - if addr_lower in self.partner_names: - partner_name = self.partner_names[addr_lower] - return f"{partner_name} ({address[:6]}...{address[-4:]})" - # Special case for Beets Treasury if addr_lower == "0xea06e3e20658d2e27dcd1a6d5248fd3667e66e26": return f"Beets Treasury ({address[:6]}...{address[-4:]})" @@ -372,19 +379,28 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: method = tx.get("contractMethod", {}).get("name", "") if method in ["createRangedQuest", "createFixedQuest"]: gauge = tx.get("contractInputsValues", {}).get("gauge", "") - data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge + data["col1"] = self.format_address(gauge) # For Paladin, add totalRewardAmount + feeAmount to show full allocated amount total_reward = int(tx.get("contractInputsValues", {}).get("totalRewardAmount", "0")) fee_amount = int(tx.get("contractInputsValues", {}).get("feeAmount", "0")) data["col2"] = self.format_amount(str(total_reward + fee_amount)) - data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("rewardToken", "")) + # Just show "USDC" for reward token in bribes + reward_token = tx.get("contractInputsValues", {}).get("rewardToken", "") + if reward_token.lower() == self.USDC_ADDRESS: + data["col3"] = "USDC" + else: + data["col3"] = self.format_address(reward_token) data["col4"] = "Paladin" elif method == "createBounty": gauge = tx.get("contractInputsValues", {}).get("gauge", "") - data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge + data["col1"] = self.format_address(gauge) amount = tx.get("contractInputsValues", {}).get("totalRewardAmount", "0") data["col2"] = self.format_amount(amount) - data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("rewardToken", "")) + reward_token = tx.get("contractInputsValues", {}).get("rewardToken", "") + if reward_token.lower() == self.USDC_ADDRESS: + data["col3"] = "USDC" + else: + data["col3"] = self.format_address(reward_token) data["col4"] = "StakeDAO v1" elif method == "createCampaign": params_str = tx.get("contractInputsValues", {}).get("params", "") @@ -398,17 +414,24 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: params = ast.literal_eval(params_str) # params = (chainId, gauge, manager, token, periods, maxReward, totalReward, whitelist, hook, isWhitelist) gauge = params[1] # gauge address - data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge + data["col1"] = self.format_address(gauge) token = params[3] # reward token - data["col3"] = self.format_address(token) + if token.lower() == self.USDC_ADDRESS: + data["col3"] = "USDC" + else: + data["col3"] = self.format_address(token) amount = str(params[6]) # totalRewardAmount data["col2"] = self.format_amount(amount) data["col4"] = "StakeDAO v2" else: proposal = tx.get("contractInputsValues", {}).get("_proposal", "") - data["col1"] = f"{proposal[:10]}..." if len(proposal) > 10 else proposal + data["col1"] = self.format_address(proposal) data["col2"] = self.format_amount(tx.get("contractInputsValues", {}).get("_amount", "0")) - data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("_token", "")) + reward_token = tx.get("contractInputsValues", {}).get("_token", "") + if reward_token.lower() == self.USDC_ADDRESS: + data["col3"] = "USDC" + else: + data["col3"] = self.format_address(reward_token) data["col4"] = "HiddenHand" elif group_name in self.TRANSFER_GROUPS: @@ -427,7 +450,11 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: data["col3"] = self.format_address(token_addr) elif group_name == "Token Approvals": - data["col1"] = self.format_address(tx.get("to", "")) + token_addr = tx.get("to", "") + if token_addr.lower() == self.USDC_ADDRESS: + data["col1"] = "USDC" + else: + data["col1"] = self.format_address(token_addr) data["col2"] = self.format_address(tx.get("contractInputsValues", {}).get("_spender", "")) data["col3"] = self.format_amount(tx.get("contractInputsValues", {}).get("_value", "0")) @@ -436,7 +463,7 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: def get_table_headers(self, group_name: str) -> List[str]: """Get table headers based on transaction group""" if "Bribe" in group_name: - return ["Gauge/Proposal", "Amount", "Token", "Market"] + return ["Proposal", "Amount", "Token", "Market"] elif group_name in self.TRANSFER_GROUPS: return ["Recipient", "Amount", "Token"] elif group_name == "Token Approvals": @@ -517,7 +544,7 @@ def generate_markdown_summary(self, payload: Dict, groups: Dict[str, List[Dict]] if totals['partner_usdc'] > 0: md.append(f"**Partner Fees:** ${totals['partner_usdc']/Decimal(1e6):,.2f} ({metrics.get('partner_pct', 0)}% of total)\n") - + md.append(f"\n### 💰 **TOTAL USDC DISTRIBUTED: ${totals['total_usdc']/Decimal(1e6):,.2f}**\n") if 'allocation_efficiency' in metrics: @@ -572,7 +599,8 @@ def export_markdown(self, payload_path: Path, fee_files: List[Path] = None, gaug # Add summary md.append(self.generate_markdown_summary(payload, groups, total_fees_collected, recon_data)) - + + # Check for gauge issues gauge_issues = self.load_gauge_issues(gauge_issues_path) if gauge_issues: diff --git a/main_combined.py b/main_combined.py index 2cf28b42..555b9c9a 100644 --- a/main_combined.py +++ b/main_combined.py @@ -101,13 +101,18 @@ def main() -> None: ) print("\n" + "="*80 + "\n") - save_combined_report( - combined_payload_path, - v2_fee_file_path, - v3_fee_file_path, + report_path = save_combined_report( + combined_payload_path, + v2_fee_file_path, + v3_fee_file_path, gauge_issues_paths if gauge_issues_paths else None ) + from quantamm.retroactive_fees import generate_quantamm_retroactive_report + quantamm_report, _ = generate_quantamm_retroactive_report(combined_payload_path) + with open(report_path, 'a') as f: + f.write(quantamm_report) + if __name__ == "__main__": main() \ No newline at end of file diff --git a/quantamm/retroactive_fees.py b/quantamm/retroactive_fees.py new file mode 100644 index 00000000..5668dad1 --- /dev/null +++ b/quantamm/retroactive_fees.py @@ -0,0 +1,197 @@ +from decimal import Decimal +from datetime import datetime, timedelta +import logging +import json +import csv +from pathlib import Path +from typing import Dict +from dotenv import load_dotenv +from bal_tools import Subgraph + + +logging.basicConfig(level=logging.INFO, format='%(message)s') +logger = logging.getLogger(__name__) + +load_dotenv() + +# Per BIP-871: Non-core pools with gauge get 50%, without gauge get 80% +QUANTAMM_POOLS = { + "mainnet": { + "0x6b61d8680c4f9e560c8306807908553f95c749c5": 0.50, # non-core with gauge + "0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852": 0.80, # non-core without gauge + }, + "base": { + "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d": 0.50, # non-core with gauge + } +} + +RETROACTIVE_START = datetime(2025, 5, 8) +RETROACTIVE_END = datetime(2025, 8, 28) + + +class RetroactiveFeeCalculator: + def __init__(self, end_date: datetime): + self.end = end_date + self.start = end_date - timedelta(days=14) + self.date_range = (int(self.start.timestamp()), int(self.end.timestamp())) + self.subgraphs = {chain: Subgraph(chain) for chain in ["mainnet", "base"]} + + def calculate_for_period(self) -> Dict: + date_str = f"{self.start.strftime('%Y-%m-%d')} to {self.end.strftime('%Y-%m-%d')}" + logger.info(f"\nProcessing {date_str}") + + period_total = Decimal(0) + pool_details = [] + + for chain_name in ["mainnet", "base"]: + quantamm_pool_data = [] + for pool_id, partner_rate in QUANTAMM_POOLS.get(chain_name, {}).items(): + earned = self.subgraphs[chain_name].get_v3_protocol_fees(pool_id, chain_name, self.date_range) + if earned > 0: + quantamm_pool_data.append({'pool_id': pool_id, 'partner_rate': partner_rate, 'earned_fees': earned}) + + quantamm_earned = sum(p['earned_fees'] for p in quantamm_pool_data) + if not quantamm_earned: + continue + + csv_path = Path(f"fee_allocator/allocations/noncore/v3_noncore_{date_str.replace(' to ', '_')}.csv") + + with open(csv_path, 'r') as f: + chain_data = {row['chain']: row for row in csv.DictReader(f)} + + row = chain_data[chain_name] + core_earned = Decimal(row['total_fees_earned_twap']) + noncore_earned = Decimal(row['noncore_fees']) + total_fees_collected = Decimal(row['total_fees_collected']) + + total_fees_earned = core_earned + noncore_earned + partner_noncore_collected = total_fees_collected * (quantamm_earned / total_fees_earned) + + for pool_data in quantamm_pool_data: + pool_share = pool_data['earned_fees'] / quantamm_earned + partner_amount = partner_noncore_collected * pool_share * Decimal(str(pool_data['partner_rate'])) + + period_total += partner_amount + pool_details.append({ + 'pool_id': pool_data['pool_id'], + 'chain': chain_name, + 'has_gauge': pool_data['partner_rate'] == 0.50, + 'earned_fees': str(pool_data['earned_fees']), + 'pool_share': str(pool_share), + 'allocated_amount': str(partner_noncore_collected * pool_share), + 'partner_share_pct': str(pool_data['partner_rate']), + 'partner_amount': str(partner_amount) + }) + + return { + 'period': date_str, + 'total': str(period_total), + 'pools': pool_details + } + + +def calculate_retroactive_fees(): + periods = [RETROACTIVE_START + timedelta(days=14*i) for i in range((RETROACTIVE_END - RETROACTIVE_START).days // 14 + 1)] + + total_retroactive = Decimal(0) + all_periods = [] + pool_totals = {} + + for period_end in periods: + result = RetroactiveFeeCalculator(period_end).calculate_for_period() + all_periods.append(result) + total_retroactive += Decimal(result['total']) + + for pool_detail in result.get('pools', []): + pool_key = f"{pool_detail['chain']}_{pool_detail['pool_id']}" + if pool_key not in pool_totals: + pool_totals[pool_key] = { + 'pool_id': pool_detail['pool_id'], + 'chain': pool_detail['chain'], + 'has_gauge': pool_detail.get('has_gauge', False), + 'partner_share_pct': pool_detail.get('partner_share_pct'), + 'total_partner_amount': Decimal(0) + } + pool_totals[pool_key]['total_partner_amount'] += Decimal(pool_detail.get('partner_amount', 0)) + + for pool_key in pool_totals: + pool_totals[pool_key]['total_partner_amount'] = str(pool_totals[pool_key]['total_partner_amount']) + + output = { + 'total_usdc': str(total_retroactive), + 'total_usdc_raw': int(total_retroactive * Decimal('1e6')), + 'periods_count': len(periods), + 'periods': all_periods, + 'pool_totals': pool_totals + } + + return output + + +def generate_quantamm_retroactive_report(payload_path=None): + results = calculate_retroactive_fees() + + amount = Decimal(results['total_usdc']) + quantamm_multisig = "0xd785201fd2D9be7602F6682296Bb415530C027Ef" + + vebal_injector = "0x8AD2512819A7eae1dd398973EFfaE48dafBe8255" + + vebal_line = None + quantamm_line = None + + if payload_path and payload_path.exists(): + with open(payload_path) as f: + payload_data = json.load(f) + + vebal_value = None + quantamm_value = None + + for tx in payload_data.get('transactions', []): + if tx.get('contractInputsValues', {}).get('_to') == vebal_injector: + vebal_value = tx['contractInputsValues']['_value'] + elif tx.get('contractInputsValues', {}).get('_to') == quantamm_multisig: + quantamm_value = tx['contractInputsValues']['_value'] + + if vebal_value or quantamm_value: + with open(payload_path) as f: + for i, line in enumerate(f, 1): + if vebal_value and f'"{vebal_value}"' in line: + vebal_line = i + if quantamm_value and f'"{quantamm_value}"' in line: + quantamm_line = i + + report_lines = [] + report_lines.append("\n## QuantAMM Retroactive Adjustment (Action Needed)") + report_lines.append(f"\n**Period:** May 8, 2025 - August 28, 2025") + report_lines.append("") + report_lines.append("| Pool | Chain | Type | Partner Share | Amount |") + report_lines.append("|------|-------|------|---------------|--------|") + + for _, pool_data in results['pool_totals'].items(): + share_pct = int(Decimal(str(pool_data['partner_share_pct'])) * 100) + pool_type = "Non-core w/ gauge" if pool_data['has_gauge'] else "Non-core no gauge" + partner_amount = Decimal(pool_data['total_partner_amount']) + report_lines.append(f"| {pool_data['pool_id'][:8]}... | {pool_data['chain'].capitalize()} | {pool_type} | {share_pct}% | ${partner_amount:,.2f} |") + + report_lines.append(f"| **TOTAL** | | | | **${amount:,.2f}** |") + + report_lines.append("") + report_lines.append(f"**Action Required:**") + + raw_amount = int(amount * Decimal('1000000')) + + if vebal_line and payload_path: + report_lines.append(f"1. Subtract ${amount:,.2f} (raw: {raw_amount}) from the veBAL fee ([line {vebal_line}](../payloads/{payload_path.name}#L{vebal_line}))") + else: + report_lines.append(f"1. Subtract ${amount:,.2f} (raw: {raw_amount}) from the veBAL fee") + + if quantamm_line and payload_path: + report_lines.append(f"2. Add ${amount:,.2f} (raw: {raw_amount}) to QuantAMM partner transfer ([line {quantamm_line}](../payloads/{payload_path.name}#L{quantamm_line}))") + else: + report_lines.append(f"2. Add ${amount:,.2f} (raw: {raw_amount}) to QuantAMM partner transfer (`{quantamm_multisig}`)") + + output_file = Path(__file__).parent / "retroactive_fees.json" + with open(output_file, 'w') as f: + json.dump(results, f, indent=2) + + return "\n".join(report_lines), amount \ No newline at end of file From 341638680f6994252549bee018755604db6580f7 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Tue, 16 Dec 2025 13:03:30 -0600 Subject: [PATCH 2/3] isolate retroactive fees script, revert integrated changes --- fee_allocator/payload_visualizer.py | 84 +++++++------------ main_combined.py | 13 +-- {quantamm => retroactive}/retroactive_fees.py | 0 3 files changed, 32 insertions(+), 65 deletions(-) rename {quantamm => retroactive}/retroactive_fees.py (100%) diff --git a/fee_allocator/payload_visualizer.py b/fee_allocator/payload_visualizer.py index 787ce27d..e971cbc5 100644 --- a/fee_allocator/payload_visualizer.py +++ b/fee_allocator/payload_visualizer.py @@ -1,7 +1,7 @@ import json from decimal import Decimal from pathlib import Path -from typing import Dict, List, Any +from typing import Dict, List, Any, Optional import requests from rich.console import Console @@ -28,9 +28,7 @@ class PayloadVisualizer: # Transfer group types for method categorization TRANSFER_GROUPS = ["veBAL Transfers", "DAO Transfers", "Partner Transfers", "Alliance Transfers", "Beets Transfers", "Unknown Transfers"] - - USDC_ADDRESS = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" - + def __init__(self): self.console = Console() self.book = AddrBook("mainnet").flatbook @@ -74,11 +72,6 @@ def _load_fee_share_config(self) -> tuple[List[str], Dict[str, str], List[str], partner_addresses.append(ezkl_addr) partner_names[ezkl_addr] = "ezkl" - quantamm_addr = "0xd785201fd2d9be7602f6682296bb415530c027ef" - if quantamm_addr not in partner_addresses: - partner_addresses.append(quantamm_addr) - partner_names[quantamm_addr] = "QuantAMM" - return alliance_addresses, alliance_names, partner_addresses, partner_names def format_amount(self, amount: str, token: str = "USDC") -> str: @@ -96,26 +89,26 @@ def format_address(self, address: str) -> str: """Format address with name lookup""" if not address: return "" - - addr_lower = address.lower() - - # Check partners first (includes QuantAMM) - if addr_lower in self.partner_names: - partner_name = self.partner_names[addr_lower] - return f"{partner_name} ({address[:6]}...{address[-4:]})" - - # Check alliance members - if addr_lower in self.alliance_names: - alliance_name = self.alliance_names[addr_lower] - return f"{alliance_name} ({address[:6]}...{address[-4:]})" - - # Check if it's a known address in the book + + # Check if it's a known address for key, value in self.book.items(): if value and value.lower() == address.lower(): parts = key.split("/") name = parts[-1].replace("_", " ").title() return f"{name} ({address[:6]}...{address[-4:]})" + addr_lower = address.lower() + + # Check if it's an alliance member + if addr_lower in self.alliance_names: + alliance_name = self.alliance_names[addr_lower] + return f"{alliance_name} ({address[:6]}...{address[-4:]})" + + # Check if it's a partner + if addr_lower in self.partner_names: + partner_name = self.partner_names[addr_lower] + return f"{partner_name} ({address[:6]}...{address[-4:]})" + # Special case for Beets Treasury if addr_lower == "0xea06e3e20658d2e27dcd1a6d5248fd3667e66e26": return f"Beets Treasury ({address[:6]}...{address[-4:]})" @@ -379,28 +372,19 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: method = tx.get("contractMethod", {}).get("name", "") if method in ["createRangedQuest", "createFixedQuest"]: gauge = tx.get("contractInputsValues", {}).get("gauge", "") - data["col1"] = self.format_address(gauge) + data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge # For Paladin, add totalRewardAmount + feeAmount to show full allocated amount total_reward = int(tx.get("contractInputsValues", {}).get("totalRewardAmount", "0")) fee_amount = int(tx.get("contractInputsValues", {}).get("feeAmount", "0")) data["col2"] = self.format_amount(str(total_reward + fee_amount)) - # Just show "USDC" for reward token in bribes - reward_token = tx.get("contractInputsValues", {}).get("rewardToken", "") - if reward_token.lower() == self.USDC_ADDRESS: - data["col3"] = "USDC" - else: - data["col3"] = self.format_address(reward_token) + data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("rewardToken", "")) data["col4"] = "Paladin" elif method == "createBounty": gauge = tx.get("contractInputsValues", {}).get("gauge", "") - data["col1"] = self.format_address(gauge) + data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge amount = tx.get("contractInputsValues", {}).get("totalRewardAmount", "0") data["col2"] = self.format_amount(amount) - reward_token = tx.get("contractInputsValues", {}).get("rewardToken", "") - if reward_token.lower() == self.USDC_ADDRESS: - data["col3"] = "USDC" - else: - data["col3"] = self.format_address(reward_token) + data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("rewardToken", "")) data["col4"] = "StakeDAO v1" elif method == "createCampaign": params_str = tx.get("contractInputsValues", {}).get("params", "") @@ -414,24 +398,17 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: params = ast.literal_eval(params_str) # params = (chainId, gauge, manager, token, periods, maxReward, totalReward, whitelist, hook, isWhitelist) gauge = params[1] # gauge address - data["col1"] = self.format_address(gauge) + data["col1"] = f"{gauge[:10]}..." if len(gauge) > 10 else gauge token = params[3] # reward token - if token.lower() == self.USDC_ADDRESS: - data["col3"] = "USDC" - else: - data["col3"] = self.format_address(token) + data["col3"] = self.format_address(token) amount = str(params[6]) # totalRewardAmount data["col2"] = self.format_amount(amount) data["col4"] = "StakeDAO v2" else: proposal = tx.get("contractInputsValues", {}).get("_proposal", "") - data["col1"] = self.format_address(proposal) + data["col1"] = f"{proposal[:10]}..." if len(proposal) > 10 else proposal data["col2"] = self.format_amount(tx.get("contractInputsValues", {}).get("_amount", "0")) - reward_token = tx.get("contractInputsValues", {}).get("_token", "") - if reward_token.lower() == self.USDC_ADDRESS: - data["col3"] = "USDC" - else: - data["col3"] = self.format_address(reward_token) + data["col3"] = self.format_address(tx.get("contractInputsValues", {}).get("_token", "")) data["col4"] = "HiddenHand" elif group_name in self.TRANSFER_GROUPS: @@ -450,11 +427,7 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: data["col3"] = self.format_address(token_addr) elif group_name == "Token Approvals": - token_addr = tx.get("to", "") - if token_addr.lower() == self.USDC_ADDRESS: - data["col1"] = "USDC" - else: - data["col1"] = self.format_address(token_addr) + data["col1"] = self.format_address(tx.get("to", "")) data["col2"] = self.format_address(tx.get("contractInputsValues", {}).get("_spender", "")) data["col3"] = self.format_amount(tx.get("contractInputsValues", {}).get("_value", "0")) @@ -463,7 +436,7 @@ def extract_transaction_data(self, group_name: str, tx: Dict) -> Dict[str, str]: def get_table_headers(self, group_name: str) -> List[str]: """Get table headers based on transaction group""" if "Bribe" in group_name: - return ["Proposal", "Amount", "Token", "Market"] + return ["Gauge/Proposal", "Amount", "Token", "Market"] elif group_name in self.TRANSFER_GROUPS: return ["Recipient", "Amount", "Token"] elif group_name == "Token Approvals": @@ -544,7 +517,7 @@ def generate_markdown_summary(self, payload: Dict, groups: Dict[str, List[Dict]] if totals['partner_usdc'] > 0: md.append(f"**Partner Fees:** ${totals['partner_usdc']/Decimal(1e6):,.2f} ({metrics.get('partner_pct', 0)}% of total)\n") - + md.append(f"\n### 💰 **TOTAL USDC DISTRIBUTED: ${totals['total_usdc']/Decimal(1e6):,.2f}**\n") if 'allocation_efficiency' in metrics: @@ -599,8 +572,7 @@ def export_markdown(self, payload_path: Path, fee_files: List[Path] = None, gaug # Add summary md.append(self.generate_markdown_summary(payload, groups, total_fees_collected, recon_data)) - - + # Check for gauge issues gauge_issues = self.load_gauge_issues(gauge_issues_path) if gauge_issues: diff --git a/main_combined.py b/main_combined.py index 555b9c9a..2cf28b42 100644 --- a/main_combined.py +++ b/main_combined.py @@ -101,18 +101,13 @@ def main() -> None: ) print("\n" + "="*80 + "\n") - report_path = save_combined_report( - combined_payload_path, - v2_fee_file_path, - v3_fee_file_path, + save_combined_report( + combined_payload_path, + v2_fee_file_path, + v3_fee_file_path, gauge_issues_paths if gauge_issues_paths else None ) - from quantamm.retroactive_fees import generate_quantamm_retroactive_report - quantamm_report, _ = generate_quantamm_retroactive_report(combined_payload_path) - with open(report_path, 'a') as f: - f.write(quantamm_report) - if __name__ == "__main__": main() \ No newline at end of file diff --git a/quantamm/retroactive_fees.py b/retroactive/retroactive_fees.py similarity index 100% rename from quantamm/retroactive_fees.py rename to retroactive/retroactive_fees.py From 6bcf687dff59358a201d23d4397fc1d8c73a8767 Mon Sep 17 00:00:00 2001 From: jalbrekt85 Date: Tue, 16 Dec 2025 13:16:33 -0600 Subject: [PATCH 3/3] add rocket fees script, generate report --- retroactive/quantamm_retroactive_fees.json | 284 +++++++++++++++ ...e_fees.py => quantamm_retroactive_fees.py} | 2 +- retroactive/rocket_fees.py | 129 +++++++ retroactive/rocket_retroactive_fees.json | 342 ++++++++++++++++++ 4 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 retroactive/quantamm_retroactive_fees.json rename retroactive/{retroactive_fees.py => quantamm_retroactive_fees.py} (99%) create mode 100644 retroactive/rocket_fees.py create mode 100644 retroactive/rocket_retroactive_fees.json diff --git a/retroactive/quantamm_retroactive_fees.json b/retroactive/quantamm_retroactive_fees.json new file mode 100644 index 00000000..58b50db1 --- /dev/null +++ b/retroactive/quantamm_retroactive_fees.json @@ -0,0 +1,284 @@ +{ + "total_usdc": "6577.782282500361283903292685", + "total_usdc_raw": 6577782282, + "periods_count": 9, + "periods": [ + { + "period": "2025-04-24 to 2025-05-08", + "total": "74.16172512121201520047660587", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "95.4150290903167296476236", + "pool_share": "0.7426288528708881198572391170", + "allocated_amount": "95.41502850376159288640610819", + "partner_share_pct": "0.5", + "partner_amount": "47.70751425188079644320305410" + }, + { + "pool_id": "0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852", + "chain": "mainnet", + "has_gauge": false, + "earned_fees": "33.0677637899450672938792", + "pool_share": "0.2573711471291118801427608830", + "allocated_amount": "33.06776358666402344659193971", + "partner_share_pct": "0.8", + "partner_amount": "26.45421086933121875727355177" + } + ] + }, + { + "period": "2025-05-08 to 2025-05-22", + "total": "480.5063702021535042601699892", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "958.5093295334281736639985916", + "pool_share": "0.9999874828911354835621853460", + "allocated_amount": "958.5093295334281736639985916", + "partner_share_pct": "0.5", + "partner_amount": "479.2546647667140868319992958" + }, + { + "pool_id": "0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852", + "chain": "mainnet", + "has_gauge": false, + "earned_fees": "0.01199791580464285525730000000", + "pool_share": "0.00001251710886451643781465400366", + "allocated_amount": "0.01199791580464285525730000000", + "partner_share_pct": "0.8", + "partner_amount": "0.009598332643714284205840000000" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "2.484214205591406287929706861", + "pool_share": "1", + "allocated_amount": "2.484214205591406287929706861", + "partner_share_pct": "0.5", + "partner_amount": "1.242107102795703143964853430" + } + ] + }, + { + "period": "2025-05-22 to 2025-06-05", + "total": "1144.930762617622548465798323", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "2056.246190444725498325285000", + "pool_share": "1", + "allocated_amount": "2056.246190444725498325285000", + "partner_share_pct": "0.5", + "partner_amount": "1028.123095222362749162642500" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "233.6153347905195986063116459", + "pool_share": "1", + "allocated_amount": "233.6153347905195986063116458", + "partner_share_pct": "0.5", + "partner_amount": "116.8076673952597993031558229" + } + ] + }, + { + "period": "2025-06-05 to 2025-06-19", + "total": "101.0003651637071985886467849", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "19.80264187100861393477592105", + "pool_share": "1", + "allocated_amount": "19.80264187100861393477592105", + "partner_share_pct": "0.5", + "partner_amount": "9.901320935504306967387960525" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "182.1980896188176468812549779", + "pool_share": "1", + "allocated_amount": "182.1980884564057832425176488", + "partner_share_pct": "0.5", + "partner_amount": "91.09904422820289162125882440" + } + ] + }, + { + "period": "2025-06-19 to 2025-07-03", + "total": "1817.531229714934442202493830", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "3322.858281136683633056703242", + "pool_share": "1", + "allocated_amount": "3322.858263257774427457332240", + "partner_share_pct": "0.5", + "partner_amount": "1661.429131628887213728666120" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "312.2041961720944569476554206", + "pool_share": "1", + "allocated_amount": "312.2041961720944569476554206", + "partner_share_pct": "0.5", + "partner_amount": "156.1020980860472284738277103" + } + ] + }, + { + "period": "2025-07-03 to 2025-07-17", + "total": "159.1650859192276357634283248", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "118.4224023386547125962532699", + "pool_share": "1", + "allocated_amount": "118.4224023386547125962532699", + "partner_share_pct": "0.5", + "partner_amount": "59.21120116932735629812663495" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "199.9077694998005589306033797", + "pool_share": "1", + "allocated_amount": "199.9077694998005589306033797", + "partner_share_pct": "0.5", + "partner_amount": "99.95388474990027946530168985" + } + ] + }, + { + "period": "2025-07-17 to 2025-07-31", + "total": "429.9890809054184029144323892", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "690.4472348110026376859537763", + "pool_share": "1", + "allocated_amount": "690.4472348110026376859537764", + "partner_share_pct": "0.5", + "partner_amount": "345.2236174055013188429768882" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "169.5309269998341681429110021", + "pool_share": "1", + "allocated_amount": "169.5309269998341681429110021", + "partner_share_pct": "0.5", + "partner_amount": "84.76546349991708407145550105" + } + ] + }, + { + "period": "2025-07-31 to 2025-08-14", + "total": "521.9602726295442262287539934", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "428.9860011887731234167670132", + "pool_share": "1", + "allocated_amount": "428.9860002510825997663777162", + "partner_share_pct": "0.5", + "partner_amount": "214.4930001255412998831888581" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "614.9345450080058526911302706", + "pool_share": "1", + "allocated_amount": "614.9345450080058526911302706", + "partner_share_pct": "0.5", + "partner_amount": "307.4672725040029263455651353" + } + ] + }, + { + "period": "2025-08-14 to 2025-08-28", + "total": "1848.537390226541310279092445", + "pools": [ + { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "earned_fees": "467.3260022358989233406383185", + "pool_share": "0.9999950914622752299197291134", + "allocated_amount": "467.3260022358989233406383185", + "partner_share_pct": "0.5", + "partner_amount": "233.6630011179494616703191592" + }, + { + "pool_id": "0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852", + "chain": "mainnet", + "has_gauge": false, + "earned_fees": "0.002293898571428571400000000000", + "pool_share": "0.000004908537724770080270886596216", + "allocated_amount": "0.002293898571428571400000000000", + "partner_share_pct": "0.8", + "partner_amount": "0.001835118857142857120000000000" + }, + { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "earned_fees": "3229.745107979469411503306573", + "pool_share": "1", + "allocated_amount": "3229.745107979469411503306573", + "partner_share_pct": "0.5", + "partner_amount": "1614.872553989734705751653286" + } + ] + } + ], + "pool_totals": { + "mainnet_0x6b61d8680c4f9e560c8306807908553f95c749c5": { + "pool_id": "0x6b61d8680c4f9e560c8306807908553f95c749c5", + "chain": "mainnet", + "has_gauge": true, + "partner_share_pct": "0.5", + "total_partner_amount": "4079.006546623668589828510471" + }, + "mainnet_0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852": { + "pool_id": "0xd4ed17bbf48af09b87fd7d8c60970f5da79d4852", + "chain": "mainnet", + "has_gauge": false, + "partner_share_pct": "0.8", + "total_partner_amount": "26.46564432083207589859939177" + }, + "base_0xb4161aea25bd6c5c8590ad50deb4ca752532f05d": { + "pool_id": "0xb4161aea25bd6c5c8590ad50deb4ca752532f05d", + "chain": "base", + "has_gauge": true, + "partner_share_pct": "0.5", + "total_partner_amount": "2472.310091555860618176182823" + } + } +} \ No newline at end of file diff --git a/retroactive/retroactive_fees.py b/retroactive/quantamm_retroactive_fees.py similarity index 99% rename from retroactive/retroactive_fees.py rename to retroactive/quantamm_retroactive_fees.py index 5668dad1..cb388bda 100644 --- a/retroactive/retroactive_fees.py +++ b/retroactive/quantamm_retroactive_fees.py @@ -190,7 +190,7 @@ def generate_quantamm_retroactive_report(payload_path=None): else: report_lines.append(f"2. Add ${amount:,.2f} (raw: {raw_amount}) to QuantAMM partner transfer (`{quantamm_multisig}`)") - output_file = Path(__file__).parent / "retroactive_fees.json" + output_file = Path(__file__).parent / "quantamm_retroactive_fees.json" with open(output_file, 'w') as f: json.dump(results, f, indent=2) diff --git a/retroactive/rocket_fees.py b/retroactive/rocket_fees.py new file mode 100644 index 00000000..0466655f --- /dev/null +++ b/retroactive/rocket_fees.py @@ -0,0 +1,129 @@ +import csv +import json +from pathlib import Path +from datetime import datetime + +# Rocket Pool L2 pools owed alliance fees from 2025-06-04 +ROCKET_POOL_L2_POOLS = { + "0x5418a64e0cdb20548acb394f5d00a089baf02161": "Arbitrum", + "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347": "Base", + "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be": "Optimism", +} + +PARTNER_SHARE_PCT = 0.175 # 17.5% +START_DATE = datetime(2025, 6, 4) + +def parse_date_from_filename(filename: str) -> tuple[datetime, datetime]: + parts = filename.replace(".csv", "").split("_") + dates = [p for p in parts if len(p) == 10 and p[4] == "-" and p[7] == "-"] + if len(dates) >= 2: + start = datetime.strptime(dates[0], "%Y-%m-%d") + end = datetime.strptime(dates[1], "%Y-%m-%d") + return start, end + return None, None + +def main(): + incentives_dir = Path("fee_allocator/allocations/incentives") + v3_files = sorted([f for f in incentives_dir.glob("v3_incentives_*.csv")]) + + results = [] + total_by_pool = {pool_id: {"earned_fees": 0, "owed_partner_share": 0, "chain": chain} + for pool_id, chain in ROCKET_POOL_L2_POOLS.items()} + + print(f"Analyzing Rocket Pool L2 alliance fees owed from {START_DATE.strftime('%Y-%m-%d')}\n") + print("=" * 100) + + for csv_file in v3_files: + start_date, end_date = parse_date_from_filename(csv_file.name) + if start_date is None: + continue + + if end_date < START_DATE: + continue + + print(f"\nProcessing: {csv_file.name} (Period: {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')})") + + with open(csv_file, "r") as f: + reader = csv.DictReader(f) + period_data = [] + + for row in reader: + pool_id = row["pool_id"].lower() + if pool_id in ROCKET_POOL_L2_POOLS: + earned_fees = float(row["earned_fees"]) + partner_share = earned_fees * PARTNER_SHARE_PCT + chain = row["chain"] + symbol = row["symbol"] + + period_data.append({ + "pool_id": pool_id, + "chain": chain, + "symbol": symbol, + "earned_fees": earned_fees, + "partner_share": partner_share + }) + + total_by_pool[pool_id]["earned_fees"] += earned_fees + total_by_pool[pool_id]["owed_partner_share"] += partner_share + + results.append({ + "period_start": start_date.strftime("%Y-%m-%d"), + "period_end": end_date.strftime("%Y-%m-%d"), + "pool_id": pool_id, + "chain": chain, + "symbol": symbol, + "earned_fees": earned_fees, + "partner_share": partner_share + }) + + if period_data: + for data in period_data: + print(f" {data['chain']:10} | {data['symbol']:30} | Earned: ${data['earned_fees']:>10.2f} | Partner Share: ${data['partner_share']:>8.2f}") + + print("\n" + "=" * 100) + print("\nSUMMARY BY POOL:") + print("-" * 80) + + grand_total_earned = 0 + grand_total_owed = 0 + + for pool_id, data in total_by_pool.items(): + chain = data["chain"] + earned = data["earned_fees"] + owed = data["owed_partner_share"] + grand_total_earned += earned + grand_total_owed += owed + print(f"{chain:12} ({pool_id[:10]}...): Earned: ${earned:>12.2f} | Owed Partner Share: ${owed:>10.2f}") + + print("-" * 80) + print(f"{'GRAND TOTAL':12} : Earned: ${grand_total_earned:>12.2f} | Owed Partner Share: ${grand_total_owed:>10.2f}") + print("\n" + "=" * 100) + + unique_periods = set((r["period_start"], r["period_end"]) for r in results) + print(f"\nAnalysis covers {len(unique_periods)} bi-weekly periods") + print(f"Total owed to Rocket Pool for L2 pools: ${grand_total_owed:,.2f}") + + output = { + "total_usdc": str(grand_total_owed), + "total_usdc_raw": int(grand_total_owed * 1e6), + "periods_count": len(unique_periods), + "pool_totals": { + pool_id: { + "pool_id": pool_id, + "chain": data["chain"], + "earned_fees": str(data["earned_fees"]), + "partner_share": str(data["owed_partner_share"]), + } + for pool_id, data in total_by_pool.items() + }, + "periods": results, + } + + output_file = Path(__file__).parent / "rocket_retroactive_fees.json" + with open(output_file, "w") as f: + json.dump(output, f, indent=2) + + print(f"\nReport saved to: {output_file}") + +if __name__ == "__main__": + main() diff --git a/retroactive/rocket_retroactive_fees.json b/retroactive/rocket_retroactive_fees.json new file mode 100644 index 00000000..7611e144 --- /dev/null +++ b/retroactive/rocket_retroactive_fees.json @@ -0,0 +1,342 @@ +{ + "total_usdc": "2128.9493574999997", + "total_usdc_raw": 2128949357, + "periods_count": 13, + "pool_totals": { + "0x5418a64e0cdb20548acb394f5d00a089baf02161": { + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "Arbitrum", + "earned_fees": "2653.1632", + "partner_share": "464.30355999999995" + }, + "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347": { + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "Base", + "earned_fees": "4617.9883", + "partner_share": "808.1479525" + }, + "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be": { + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "Optimism", + "earned_fees": "4894.2734", + "partner_share": "856.4978449999998" + } + }, + "periods": [ + { + "period_start": "2025-05-22", + "period_end": "2025-06-05", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 101.327, + "partner_share": 17.732225 + }, + { + "period_start": "2025-06-05", + "period_end": "2025-06-19", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 237.0952, + "partner_share": 41.491659999999996 + }, + { + "period_start": "2025-06-05", + "period_end": "2025-06-19", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 75.7072, + "partner_share": 13.248759999999999 + }, + { + "period_start": "2025-06-19", + "period_end": "2025-07-03", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 229.0584, + "partner_share": 40.08522 + }, + { + "period_start": "2025-06-19", + "period_end": "2025-07-03", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 78.714, + "partner_share": 13.774949999999999 + }, + { + "period_start": "2025-07-03", + "period_end": "2025-07-17", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 193.6637, + "partner_share": 33.891147499999995 + }, + { + "period_start": "2025-07-03", + "period_end": "2025-07-17", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 276.8036, + "partner_share": 48.44063 + }, + { + "period_start": "2025-07-03", + "period_end": "2025-07-17", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 108.5875, + "partner_share": 19.0028125 + }, + { + "period_start": "2025-07-17", + "period_end": "2025-07-31", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 1536.3933, + "partner_share": 268.86882749999995 + }, + { + "period_start": "2025-07-17", + "period_end": "2025-07-31", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 320.2039, + "partner_share": 56.03568249999999 + }, + { + "period_start": "2025-07-17", + "period_end": "2025-07-31", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 188.1955, + "partner_share": 32.9342125 + }, + { + "period_start": "2025-07-31", + "period_end": "2025-08-14", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 553.8101, + "partner_share": 96.9167675 + }, + { + "period_start": "2025-07-31", + "period_end": "2025-08-14", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 363.5477, + "partner_share": 63.620847499999996 + }, + { + "period_start": "2025-07-31", + "period_end": "2025-08-14", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 210.9926, + "partner_share": 36.923705 + }, + { + "period_start": "2025-08-14", + "period_end": "2025-08-28", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 771.0331, + "partner_share": 134.9307925 + }, + { + "period_start": "2025-08-14", + "period_end": "2025-08-28", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 416.5546, + "partner_share": 72.897055 + }, + { + "period_start": "2025-08-14", + "period_end": "2025-08-28", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 173.4008, + "partner_share": 30.345139999999997 + }, + { + "period_start": "2025-08-28", + "period_end": "2025-09-11", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 469.2277, + "partner_share": 82.1148475 + }, + { + "period_start": "2025-08-28", + "period_end": "2025-09-11", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 393.6186, + "partner_share": 68.88325499999999 + }, + { + "period_start": "2025-08-28", + "period_end": "2025-09-11", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 253.077, + "partner_share": 44.288475 + }, + { + "period_start": "2025-09-11", + "period_end": "2025-09-25", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 418.9607, + "partner_share": 73.31812249999999 + }, + { + "period_start": "2025-09-11", + "period_end": "2025-09-25", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 444.991, + "partner_share": 77.873425 + }, + { + "period_start": "2025-09-11", + "period_end": "2025-09-25", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 304.9736, + "partner_share": 53.37037999999999 + }, + { + "period_start": "2025-09-25", + "period_end": "2025-10-09", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 251.1014, + "partner_share": 43.942745 + }, + { + "period_start": "2025-09-25", + "period_end": "2025-10-09", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 357.5793, + "partner_share": 62.57637749999999 + }, + { + "period_start": "2025-09-25", + "period_end": "2025-10-09", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 368.202, + "partner_share": 64.43535 + }, + { + "period_start": "2025-10-09", + "period_end": "2025-10-23", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 231.318, + "partner_share": 40.48065 + }, + { + "period_start": "2025-10-09", + "period_end": "2025-10-23", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 439.0226, + "partner_share": 76.828955 + }, + { + "period_start": "2025-10-09", + "period_end": "2025-10-23", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 180.1746, + "partner_share": 31.530554999999996 + }, + { + "period_start": "2025-10-23", + "period_end": "2025-11-06", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 272.8469, + "partner_share": 47.7482075 + }, + { + "period_start": "2025-10-23", + "period_end": "2025-11-06", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 332.4686, + "partner_share": 58.18200499999999 + }, + { + "period_start": "2025-10-23", + "period_end": "2025-11-06", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 260.9435, + "partner_share": 45.66511249999999 + }, + { + "period_start": "2025-11-06", + "period_end": "2025-11-20", + "pool_id": "0x870c0af8a1af0b58b4b0bd31ce4fe72864ae45be", + "chain": "optimism", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 195.9185, + "partner_share": 34.285737499999996 + }, + { + "period_start": "2025-11-06", + "period_end": "2025-11-20", + "pool_id": "0xb7b8b3afc010169779c5c2385ec0eb0477fe3347", + "chain": "base", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 705.7178, + "partner_share": 123.500615 + }, + { + "period_start": "2025-11-06", + "period_end": "2025-11-20", + "pool_id": "0x5418a64e0cdb20548acb394f5d00a089baf02161", + "chain": "arbitrum", + "symbol": "Balancer rETH-Aave wETH", + "earned_fees": 450.1949, + "partner_share": 78.7841075 + } + ] +} \ No newline at end of file