1212import os
1313
1414
15- AURA_VEBAL_LOCKER = Web3 .to_checksum_address ("0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2" )
16-
17-
1815class StakeDAOPlatform (BribePlatform ):
1916 SUPPORTED_L2_CHAINS = ["arbitrum" , "optimism" , "base" , "polygon" ]
17+ AURA_VEBAL_LOCKER = Web3 .to_checksum_address ("0xaF52695E1bB01A16D33D7194C28C42b10e0Dbec2" )
18+ BASE_GAS_LIMIT = 50000
19+ GAS_BUFFER_MULTIPLIER = 1.25
20+ ABI_DIR = Path (__file__ ).parent .parent / "abi"
21+ CCIP_ROUTERS = {
22+ "mainnet" : Web3 .to_checksum_address ("0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D" ),
23+ "arbitrum" : Web3 .to_checksum_address ("0x141fa059441E0ca23ce184B6A78bafD2A517DdE8" ),
24+ "optimism" : Web3 .to_checksum_address ("0x3206695CaE29952f4b0c22a169725a865bc8Ce0f" ),
25+ "base" : Web3 .to_checksum_address ("0x881e3A65B4d4a04dD529061dd0071cf975F58bCD" ),
26+ "polygon" : Web3 .to_checksum_address ("0x849c5ED5a80F5B408Dd4969b78c2C8fdf0565Bfe" ),
27+ }
2028
2129 def __init__ (self , book : Dict [str , str ], run_config : Any ):
2230 super ().__init__ (book , run_config )
@@ -38,54 +46,101 @@ def _build_gauge_to_chain_map(self):
3846 if pool .gauge_address :
3947 self ._gauge_to_chain_cache [pool .gauge_address .lower ()] = chain .name
4048
49+ def _load_abi (self , name : str ):
50+ with open (self .ABI_DIR / f"{ name } .json" , 'r' ) as f :
51+ return json .load (f )
52+
4153 def _get_chain_selector (self , chain_id : int ) -> int :
42- base_dir = Path (__file__ ).parent .parent
43- with open (f"{ base_dir } /abi/laposte_adapter.json" , 'r' ) as f :
44- adapter_abi = json .load (f )
54+ adapter_abi = self ._load_abi ("laposte_adapter" )
4555
4656 adapter_contract = self .w3 .eth .contract (
4757 address = Web3 .to_checksum_address (self .laposte_adapter_address ),
4858 abi = adapter_abi
4959 )
5060
5161 try :
52- selector = adapter_contract .functions .getBridgeChainId (chain_id ).call ()
53- return selector
62+ return adapter_contract .functions .getBridgeChainId (chain_id ).call ()
5463 except Exception as e :
5564 logger .error (f"Failed to get chain selector for chain { chain_id } : { e } " )
5665 raise
5766
58- def _calculate_ccip_fee (self , destination_chain_id : int , campaign_params : tuple ) -> int :
59- destination_selector = self ._get_chain_selector (destination_chain_id )
60-
61- base_dir = Path (__file__ ).parent .parent
62- with open (f"{ base_dir } /abi/ccip_router.json" , 'r' ) as f :
63- router_abi = json .load (f )
64-
65- router_contract = self .w3 .eth .contract (
66- address = self .ccip_router_address ,
67- abi = router_abi
68- )
69-
70- payload_data = encode (
67+ def _build_laposte_message (self , destination_chain_id : int , campaign_params : tuple , votemarket_address : str , sender_address : str ) -> bytes :
68+ payload_params = encode (
7169 ['(uint256,address,address,address,uint8,uint256,uint256,address[],address,bool)' ],
7270 [campaign_params ]
7371 )
72+ payload = encode (
73+ ['(uint8,address,address,bytes)' ],
74+ [(0 , sender_address , votemarket_address , payload_params )]
75+ )
7476
75- laposte_message = encode (
76- ['(uint256,address,address,(address,uint256)[],bytes)' ],
77+ token = self .w3 .eth .contract (address = Web3 .to_checksum_address (self .usdc_address ), abi = self ._load_abi ("ERC20" ))
78+ token_name = token .functions .name ().call ()
79+ token_symbol = token .functions .symbol ().call ()
80+ token_decimals = token .functions .decimals ().call ()
81+
82+ laposte = self .w3 .eth .contract (address = Web3 .to_checksum_address (self .laposte_address ), abi = self ._load_abi ("laposte" ))
83+ nonce = laposte .functions .sentNonces (destination_chain_id ).call () + 1
84+
85+ return encode (
86+ ['(uint256,address,address,(address,uint256)[],(string,string,uint8)[],bytes,uint256)' ],
7787 [(
7888 destination_chain_id ,
7989 self .campaign_remote_manager_address ,
8090 self .campaign_remote_manager_address ,
8191 [(self .usdc_address , campaign_params [6 ])],
82- payload_data
92+ [(token_name , token_symbol , token_decimals )],
93+ payload ,
94+ nonce
8395 )]
8496 )
8597
86- gas_limit = 200000
98+ def _simulate_ccip_receive (self , destination_chain_name : str , laposte_message : bytes ) -> int :
99+ dest_w3 = Web3Rpc (destination_chain_name , os .environ .get ("DRPC_KEY" ))
100+ source_chain_selector = self ._get_chain_selector (1 )
101+
102+ message_id = Web3 .keccak (text = 'gas_estimation' )
103+ any2evm_message = (
104+ message_id ,
105+ source_chain_selector ,
106+ encode (['address' ], [self .laposte_address ]),
107+ laposte_message ,
108+ []
109+ )
110+
111+ ccip_receive_sig = Web3 .keccak (text = 'ccipReceive((bytes32,uint64,bytes,bytes,(address,uint256)[]))' ).hex ()[:8 ]
112+ encoded_params = encode (
113+ ['(bytes32,uint64,bytes,bytes,(address,uint256)[])' ],
114+ [any2evm_message ]
115+ )
116+ calldata = bytes .fromhex (ccip_receive_sig ) + encoded_params
117+
118+ estimated_gas = dest_w3 .eth .estimate_gas ({
119+ 'from' : self .CCIP_ROUTERS [destination_chain_name ],
120+ 'to' : self .laposte_adapter_address ,
121+ 'data' : '0x' + calldata .hex ()
122+ })
123+
124+ base_gas = estimated_gas - self .BASE_GAS_LIMIT
125+ additional_gas_limit = int (base_gas * self .GAS_BUFFER_MULTIPLIER )
126+
127+ logger .info (f"CCIP gas estimation: base={ base_gas } , with_buffer={ additional_gas_limit } , total={ additional_gas_limit + self .BASE_GAS_LIMIT } " )
128+ return additional_gas_limit
129+
130+ 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 ]:
131+ destination_selector = self ._get_chain_selector (destination_chain_id )
132+
133+ router_contract = self .w3 .eth .contract (
134+ address = self .ccip_router_address ,
135+ abi = self ._load_abi ("ccip_router" )
136+ )
137+
138+ laposte_message = self ._build_laposte_message (destination_chain_id , campaign_params , votemarket_address , sender_address )
139+ additional_gas_limit = self ._simulate_ccip_receive (destination_chain_name , laposte_message )
140+ total_gas_limit = additional_gas_limit + self .BASE_GAS_LIMIT
141+
87142 evm_extra_args_tag = bytes .fromhex ('97a657c9' )
88- extra_args_data = encode (['uint256' ], [gas_limit ])
143+ extra_args_data = encode (['uint256' ], [total_gas_limit ])
89144 evm_extra_args = evm_extra_args_tag + extra_args_data
90145
91146 ccip_message = {
@@ -101,20 +156,19 @@ def _calculate_ccip_fee(self, destination_chain_id: int, campaign_params: tuple)
101156 ccip_message
102157 ).call ()
103158
104- fee_with_buffer = int (fee * 1.50 )
159+ fee_with_buffer = int (fee * 1.5 )
105160
106- logger .info (f"CCIP fee for chain { destination_chain_id } : { Web3 .from_wei (fee_with_buffer , 'ether' )} ETH (with 50% buffer )" )
107- return fee_with_buffer
161+ logger .info (f"CCIP fee for chain { destination_chain_id } : { Web3 .from_wei (fee_with_buffer , 'ether' )} ETH (gas_limit= { total_gas_limit } )" )
162+ return fee_with_buffer , additional_gas_limit
108163
109164 def process_bribes (self , bribes_df : pd .DataFrame , builder : Any , usdc : Any ) -> None :
110165 if bribes_df .empty or bribes_df ["amount" ].sum () == 0 :
111166 logger .info ("No bribes to process for StakeDAO" )
112167 return
113168
114- base_dir = Path (__file__ ).parent .parent
115169 campaign_manager = SafeContract (
116170 self .campaign_remote_manager_address ,
117- abi_file_path = f" { base_dir } /abi/ stakedao_marketv2.json"
171+ abi_file_path = str ( self . ABI_DIR / " stakedao_marketv2.json")
118172 )
119173
120174 total_usdc = sum (int (row ["amount" ] * 1e6 ) for _ , row in bribes_df .iterrows () if row ["amount" ] > 0 )
@@ -152,7 +206,7 @@ def process_bribes(self, bribes_df: pd.DataFrame, builder: Any, usdc: Any) -> No
152206
153207 aura_only = is_alliance or voting_override == "aura"
154208 bal_only = voting_override == "bal"
155- addresses = [AURA_VEBAL_LOCKER ] if aura_only or bal_only else []
209+ addresses = [self . AURA_VEBAL_LOCKER ] if aura_only or bal_only else []
156210 is_whitelist = aura_only
157211
158212 campaign_params = (
@@ -168,12 +222,12 @@ def process_bribes(self, bribes_df: pd.DataFrame, builder: Any, usdc: Any) -> No
168222 is_whitelist ,
169223 )
170224
171- ccip_fee = self ._calculate_ccip_fee (destination_chain_id , campaign_params )
225+ ccip_fee , additional_gas_limit = self ._calculate_ccip_fee (destination_chain_id , destination_chain_name , campaign_params , vote_market_v2_address , builder . safe_address )
172226
173227 campaign_manager .createCampaign (
174228 campaign_params ,
175229 destination_chain_id ,
176- 0 ,
230+ additional_gas_limit ,
177231 vote_market_v2_address ,
178232 value = ccip_fee
179233 )
0 commit comments