Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions fee_allocator/abi/laposte.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"inputs": [
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"name": "sentNonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]
122 changes: 88 additions & 34 deletions fee_allocator/bribe_platforms/stakedao.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,19 @@
import os


AURA_VEBAL_LOCKER = Web3.to_checksum_address("0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2")


class StakeDAOPlatform(BribePlatform):
SUPPORTED_L2_CHAINS = ["arbitrum", "optimism", "base", "polygon"]
AURA_VEBAL_LOCKER = Web3.to_checksum_address("0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2")
BASE_GAS_LIMIT = 50000
GAS_BUFFER_MULTIPLIER = 1.25
ABI_DIR = Path(__file__).parent.parent / "abi"
CCIP_ROUTERS = {
"mainnet": Web3.to_checksum_address("0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D"),
"arbitrum": Web3.to_checksum_address("0x141fa059441E0ca23ce184B6A78bafD2A517DdE8"),
"optimism": Web3.to_checksum_address("0x3206695CaE29952f4b0c22a169725a865bc8Ce0f"),
"base": Web3.to_checksum_address("0x881e3A65B4d4a04dD529061dd0071cf975F58bCD"),
"polygon": Web3.to_checksum_address("0x849c5ED5a80F5B408Dd4969b78c2C8fdf0565Bfe"),
}

def __init__(self, book: Dict[str, str], run_config: Any):
super().__init__(book, run_config)
Expand All @@ -38,54 +46,101 @@ def _build_gauge_to_chain_map(self):
if pool.gauge_address:
self._gauge_to_chain_cache[pool.gauge_address.lower()] = chain.name

def _load_abi(self, name: str):
with open(self.ABI_DIR / f"{name}.json", 'r') as f:
return json.load(f)

def _get_chain_selector(self, chain_id: int) -> int:
base_dir = Path(__file__).parent.parent
with open(f"{base_dir}/abi/laposte_adapter.json", 'r') as f:
adapter_abi = json.load(f)
adapter_abi = self._load_abi("laposte_adapter")

adapter_contract = self.w3.eth.contract(
address=Web3.to_checksum_address(self.laposte_adapter_address),
abi=adapter_abi
)

try:
selector = adapter_contract.functions.getBridgeChainId(chain_id).call()
return selector
return adapter_contract.functions.getBridgeChainId(chain_id).call()
except Exception as e:
logger.error(f"Failed to get chain selector for chain {chain_id}: {e}")
raise

def _calculate_ccip_fee(self, destination_chain_id: int, campaign_params: tuple) -> int:
destination_selector = self._get_chain_selector(destination_chain_id)

base_dir = Path(__file__).parent.parent
with open(f"{base_dir}/abi/ccip_router.json", 'r') as f:
router_abi = json.load(f)

router_contract = self.w3.eth.contract(
address=self.ccip_router_address,
abi=router_abi
)

payload_data = encode(
def _build_laposte_message(self, destination_chain_id: int, campaign_params: tuple, votemarket_address: str, sender_address: str) -> bytes:
payload_params = encode(
['(uint256,address,address,address,uint8,uint256,uint256,address[],address,bool)'],
[campaign_params]
)
payload = encode(
['(uint8,address,address,bytes)'],
[(0, sender_address, votemarket_address, payload_params)]
)

laposte_message = encode(
['(uint256,address,address,(address,uint256)[],bytes)'],
token = self.w3.eth.contract(address=Web3.to_checksum_address(self.usdc_address), abi=self._load_abi("ERC20"))
token_name = token.functions.name().call()
token_symbol = token.functions.symbol().call()
token_decimals = token.functions.decimals().call()

laposte = self.w3.eth.contract(address=Web3.to_checksum_address(self.laposte_address), abi=self._load_abi("laposte"))
nonce = laposte.functions.sentNonces(destination_chain_id).call() + 1

return encode(
['(uint256,address,address,(address,uint256)[],(string,string,uint8)[],bytes,uint256)'],
[(
destination_chain_id,
self.campaign_remote_manager_address,
self.campaign_remote_manager_address,
[(self.usdc_address, campaign_params[6])],
payload_data
[(token_name, token_symbol, token_decimals)],
payload,
nonce
)]
)

gas_limit = 200000
def _simulate_ccip_receive(self, destination_chain_name: str, laposte_message: bytes) -> int:
dest_w3 = Web3Rpc(destination_chain_name, os.environ.get("DRPC_KEY"))
source_chain_selector = self._get_chain_selector(1)

message_id = Web3.keccak(text='gas_estimation')
any2evm_message = (
message_id,
source_chain_selector,
encode(['address'], [self.laposte_address]),
laposte_message,
[]
)

ccip_receive_sig = Web3.keccak(text='ccipReceive((bytes32,uint64,bytes,bytes,(address,uint256)[]))').hex()[:8]
encoded_params = encode(
['(bytes32,uint64,bytes,bytes,(address,uint256)[])'],
[any2evm_message]
)
calldata = bytes.fromhex(ccip_receive_sig) + encoded_params

estimated_gas = dest_w3.eth.estimate_gas({
'from': self.CCIP_ROUTERS[destination_chain_name],
'to': self.laposte_adapter_address,
'data': '0x' + calldata.hex()
})

base_gas = estimated_gas - self.BASE_GAS_LIMIT
additional_gas_limit = int(base_gas * self.GAS_BUFFER_MULTIPLIER)

logger.info(f"CCIP gas estimation: base={base_gas}, with_buffer={additional_gas_limit}, total={additional_gas_limit + self.BASE_GAS_LIMIT}")
return additional_gas_limit

def _calculate_ccip_fee(self, destination_chain_id: int, destination_chain_name: str, campaign_params: tuple, votemarket_address: str, sender_address: str) -> Tuple[int, int]:
destination_selector = self._get_chain_selector(destination_chain_id)

router_contract = self.w3.eth.contract(
address=self.ccip_router_address,
abi=self._load_abi("ccip_router")
)

laposte_message = self._build_laposte_message(destination_chain_id, campaign_params, votemarket_address, sender_address)
additional_gas_limit = self._simulate_ccip_receive(destination_chain_name, laposte_message)
total_gas_limit = additional_gas_limit + self.BASE_GAS_LIMIT

evm_extra_args_tag = bytes.fromhex('97a657c9')
extra_args_data = encode(['uint256'], [gas_limit])
extra_args_data = encode(['uint256'], [total_gas_limit])
evm_extra_args = evm_extra_args_tag + extra_args_data

ccip_message = {
Expand All @@ -101,20 +156,19 @@ def _calculate_ccip_fee(self, destination_chain_id: int, campaign_params: tuple)
ccip_message
).call()

fee_with_buffer = int(fee * 1.50)
fee_with_buffer = int(fee * 1.5)

logger.info(f"CCIP fee for chain {destination_chain_id}: {Web3.from_wei(fee_with_buffer, 'ether')} ETH (with 50% buffer)")
return fee_with_buffer
logger.info(f"CCIP fee for chain {destination_chain_id}: {Web3.from_wei(fee_with_buffer, 'ether')} ETH (gas_limit={total_gas_limit})")
return fee_with_buffer, additional_gas_limit

def process_bribes(self, bribes_df: pd.DataFrame, builder: Any, usdc: Any) -> None:
if bribes_df.empty or bribes_df["amount"].sum() == 0:
logger.info("No bribes to process for StakeDAO")
return

base_dir = Path(__file__).parent.parent
campaign_manager = SafeContract(
self.campaign_remote_manager_address,
abi_file_path=f"{base_dir}/abi/stakedao_marketv2.json"
abi_file_path=str(self.ABI_DIR / "stakedao_marketv2.json")
)

total_usdc = sum(int(row["amount"] * 1e6) for _, row in bribes_df.iterrows() if row["amount"] > 0)
Expand Down Expand Up @@ -152,7 +206,7 @@ def process_bribes(self, bribes_df: pd.DataFrame, builder: Any, usdc: Any) -> No

aura_only = is_alliance or voting_override == "aura"
bal_only = voting_override == "bal"
addresses = [AURA_VEBAL_LOCKER] if aura_only or bal_only else []
addresses = [self.AURA_VEBAL_LOCKER] if aura_only or bal_only else []
is_whitelist = aura_only

campaign_params = (
Expand All @@ -168,12 +222,12 @@ def process_bribes(self, bribes_df: pd.DataFrame, builder: Any, usdc: Any) -> No
is_whitelist,
)

ccip_fee = self._calculate_ccip_fee(destination_chain_id, campaign_params)
ccip_fee, additional_gas_limit = self._calculate_ccip_fee(destination_chain_id, destination_chain_name, campaign_params, vote_market_v2_address, builder.safe_address)

campaign_manager.createCampaign(
campaign_params,
destination_chain_id,
0,
additional_gas_limit,
vote_market_v2_address,
value=ccip_fee
)
Expand Down
Loading