33
33
contract:: {
34
34
abigen,
35
35
ContractError ,
36
+ ContractRevert ,
36
37
EthError ,
37
38
EthEvent ,
38
39
FunctionCall ,
@@ -107,17 +108,21 @@ pub type SignableProvider = TransformerMiddleware<
107
108
> ;
108
109
pub type SignableExpressRelayContract = ExpressRelay < SignableProvider > ;
109
110
110
- impl From < ( [ u8 ; 16 ] , H160 , Bytes , U256 ) > for MulticallData {
111
- fn from ( x : ( [ u8 ; 16 ] , H160 , Bytes , U256 ) ) -> Self {
111
+ impl From < ( [ u8 ; 16 ] , H160 , Bytes , U256 , U256 , bool ) > for MulticallData {
112
+ fn from ( x : ( [ u8 ; 16 ] , H160 , Bytes , U256 , U256 , bool ) ) -> Self {
112
113
MulticallData {
113
- bid_id : x. 0 ,
114
- target_contract : x. 1 ,
115
- target_calldata : x. 2 ,
116
- bid_amount : x. 3 ,
114
+ bid_id : x. 0 ,
115
+ target_contract : x. 1 ,
116
+ target_calldata : x. 2 ,
117
+ bid_amount : x. 3 ,
118
+ gas_limit : x. 4 ,
119
+ revert_on_failure : x. 5 ,
117
120
}
118
121
}
119
122
}
120
123
124
+ const EXTRA_GAS_FOR_SUBMISSION : u32 = 500 * 1000 ;
125
+
121
126
pub fn get_simulation_call (
122
127
relayer : Address ,
123
128
provider : Provider < TracedClient > ,
@@ -157,26 +162,30 @@ impl Transformer for LegacyTxTransformer {
157
162
pub async fn submit_bids (
158
163
express_relay_contract : Arc < SignableExpressRelayContract > ,
159
164
permission : Bytes ,
160
- multicall_data : Vec < MulticallData > ,
165
+ bids : Vec < SimulatedBid > ,
161
166
) -> Result < H256 , ContractError < SignableProvider > > {
162
- let call = express_relay_contract. multicall ( permission, multicall_data) ;
163
- let mut gas_estimate = call. estimate_gas ( ) . await ?;
164
-
165
- let gas_multiplier = U256 :: from ( 2 ) ; //TODO: smarter gas estimation
166
- gas_estimate *= gas_multiplier;
167
- let call_with_gas = call. gas ( gas_estimate) ;
168
- let send_call = call_with_gas. send ( ) . await ?;
169
-
170
- Ok ( send_call. tx_hash ( ) )
167
+ let gas_estimate = bids. iter ( ) . fold ( U256 :: zero ( ) , |sum, b| sum + b. gas_limit ) ;
168
+ let tx_hash = express_relay_contract
169
+ . multicall (
170
+ permission,
171
+ bids. into_iter ( ) . map ( |b| ( b, false ) . into ( ) ) . collect ( ) ,
172
+ )
173
+ . gas ( gas_estimate + EXTRA_GAS_FOR_SUBMISSION )
174
+ . send ( )
175
+ . await ?
176
+ . tx_hash ( ) ;
177
+ Ok ( tx_hash)
171
178
}
172
179
173
- impl From < SimulatedBid > for MulticallData {
174
- fn from ( bid : SimulatedBid ) -> Self {
180
+ impl From < ( SimulatedBid , bool ) > for MulticallData {
181
+ fn from ( ( bid, revert_on_failure ) : ( SimulatedBid , bool ) ) -> Self {
175
182
MulticallData {
176
- bid_id : bid. id . into_bytes ( ) ,
183
+ bid_id : bid. id . into_bytes ( ) ,
177
184
target_contract : bid. target_contract ,
178
185
target_calldata : bid. target_calldata ,
179
- bid_amount : bid. bid_amount ,
186
+ bid_amount : bid. bid_amount ,
187
+ gas_limit : bid. gas_limit ,
188
+ revert_on_failure,
180
189
}
181
190
}
182
191
}
@@ -208,7 +217,10 @@ async fn get_winner_bids(
208
217
chain_store. provider . clone ( ) ,
209
218
chain_store. config . clone ( ) ,
210
219
permission_key. clone ( ) ,
211
- bids. clone ( ) . into_iter ( ) . map ( |b| b. into ( ) ) . collect ( ) ,
220
+ bids. clone ( )
221
+ . into_iter ( )
222
+ . map ( |b| ( b, false ) . into ( ) )
223
+ . collect ( ) ,
212
224
)
213
225
. await ?;
214
226
@@ -371,7 +383,7 @@ async fn submit_auction_for_bids<'a>(
371
383
let submit_bids_call = submit_bids (
372
384
chain_store. express_relay_contract . clone ( ) ,
373
385
permission_key. clone ( ) ,
374
- winner_bids. clone ( ) . into_iter ( ) . map ( |b| b . into ( ) ) . collect ( ) ,
386
+ winner_bids. clone ( ) ,
375
387
) ;
376
388
377
389
match submit_bids_call. await {
@@ -612,9 +624,49 @@ pub async fn handle_bid(
612
624
bid. target_contract,
613
625
bid. target_calldata. clone( ) ,
614
626
bid. amount,
627
+ U256 :: max_value( ) ,
628
+ // The gas estimation use some binary search algorithm to find the gas limit.
629
+ // It reduce the upper bound threshold on success and increase the lower bound on revert.
630
+ // If the contract does not reverts, the gas estimation will not be accurate in case of external call failures.
631
+ // So we need to make sure in order to calculate the gas estimation correctly, the contract will revert if the external call fails.
632
+ true ,
615
633
) ) ] ,
616
634
) ;
617
635
636
+ match call. clone ( ) . await {
637
+ Ok ( results) => {
638
+ if !results[ 0 ] . external_success {
639
+ return Err ( RestError :: SimulationError {
640
+ result : results[ 0 ] . external_result . clone ( ) ,
641
+ reason : results[ 0 ] . multicall_revert_reason . clone ( ) ,
642
+ } ) ;
643
+ }
644
+ }
645
+ Err ( e) => {
646
+ return match e {
647
+ ContractError :: Revert ( reason) => {
648
+ if let Some ( decoded_error) = ExpressRelayErrors :: decode_with_selector ( & reason) {
649
+ if let ExpressRelayErrors :: ExternalCallFailed ( failure_result) =
650
+ decoded_error
651
+ {
652
+ return Err ( RestError :: SimulationError {
653
+ result : failure_result. status . external_result ,
654
+ reason : failure_result. status . multicall_revert_reason ,
655
+ } ) ;
656
+ }
657
+ }
658
+ Err ( RestError :: BadParameters ( format ! (
659
+ "Contract Revert Error: {}" ,
660
+ reason,
661
+ ) ) )
662
+ }
663
+ ContractError :: MiddlewareError { e : _ } => Err ( RestError :: TemporarilyUnavailable ) ,
664
+ ContractError :: ProviderError { e : _ } => Err ( RestError :: TemporarilyUnavailable ) ,
665
+ _ => Err ( RestError :: BadParameters ( format ! ( "Error: {}" , e) ) ) ,
666
+ } ;
667
+ }
668
+ }
669
+
618
670
let estimated_gas = call
619
671
. estimate_gas ( )
620
672
. await
@@ -639,29 +691,6 @@ pub async fn handle_bid(
639
691
)
640
692
. await ?;
641
693
642
- match call. await {
643
- Ok ( results) => {
644
- if !results[ 0 ] . external_success {
645
- return Err ( RestError :: SimulationError {
646
- result : results[ 0 ] . external_result . clone ( ) ,
647
- reason : results[ 0 ] . multicall_revert_reason . clone ( ) ,
648
- } ) ;
649
- }
650
- }
651
- Err ( e) => {
652
- return match e {
653
- ContractError :: Revert ( reason) => Err ( RestError :: BadParameters ( format ! (
654
- "Contract Revert Error: {}" ,
655
- String :: decode_with_selector( & reason)
656
- . unwrap_or( "unable to decode revert" . to_string( ) )
657
- ) ) ) ,
658
- ContractError :: MiddlewareError { e : _ } => Err ( RestError :: TemporarilyUnavailable ) ,
659
- ContractError :: ProviderError { e : _ } => Err ( RestError :: TemporarilyUnavailable ) ,
660
- _ => Err ( RestError :: BadParameters ( format ! ( "Error: {}" , e) ) ) ,
661
- } ;
662
- }
663
- }
664
-
665
694
let bid_id = Uuid :: new_v4 ( ) ;
666
695
let simulated_bid = SimulatedBid {
667
696
target_contract : bid. target_contract ,
@@ -676,6 +705,8 @@ pub async fn handle_bid(
676
705
Auth :: Authorized ( _, profile) => Some ( profile. id ) ,
677
706
_ => None ,
678
707
} ,
708
+ // Add a 25% more for estimation errors
709
+ gas_limit : estimated_gas * U256 :: from ( 125 ) / U256 :: from ( 100 ) ,
679
710
} ;
680
711
store. add_bid ( simulated_bid) . await ?;
681
712
Ok ( bid_id)
0 commit comments