1+ use crate :: gasless:: {
2+ Permit2RelayTokensDetails , fetch_permit2_addresses, permit2, permit2_relay_tokens_details,
3+ } ;
14use crate :: model:: { RequestId , Trade } ;
25use crate :: network:: Network ;
36use crate :: profitability:: { ErasedProfitabilityEstimator , ProfitabilityEstimator } ;
47use crate :: util:: normalise_chain_id;
58use alloy:: primitives:: { Address , TxHash } ;
69use alloy:: providers:: Provider ;
10+ use alloy:: signers:: Signer ;
711use anyhow:: { Context , anyhow} ;
812use config:: timeout:: TimeoutConfig ;
9- use generated:: onlyswaps:: erc20_faucet_token:: ERC20FaucetToken :: ERC20FaucetTokenInstance ;
1013use generated:: onlyswaps:: errors_lib:: ErrorsLib :: ErrorsLibErrors ;
11- use generated:: onlyswaps:: i_router:: IRouter :: IRouterInstance ;
14+ use generated:: onlyswaps:: i_router:: IRouter :: { IRouterInstance , RelayTokensPermit2Params } ;
1215use generated:: onlyswaps:: ierc20_errors:: IERC20Errors :: IERC20ErrorsErrors as IERC20Errors ;
1316use moka:: future:: Cache ;
1417use std:: collections:: HashMap ;
1518use tokio:: time:: timeout;
1619
17- pub ( crate ) struct TradeExecutor < ' a , P > {
20+ pub ( crate ) struct TradeExecutor < ' a , P , S > {
21+ signer : S ,
1822 own_address : Address ,
19- routers : HashMap < u64 , & ' a IRouterInstance < P > > ,
20- tokens : HashMap < u64 , & ' a Vec < ERC20FaucetTokenInstance < P > > > ,
23+ configs : HashMap < u64 , ChainConfig < ' a , P > > ,
2124 profitability_estimator : ErasedProfitabilityEstimator ,
2225}
2326
24- impl < ' a , P > TradeExecutor < ' a , P >
27+ pub ( crate ) struct ChainConfig < ' a , P > {
28+ router : & ' a IRouterInstance < P > ,
29+ permit2_relayer_address : Address ,
30+ permit2_addr : Address ,
31+ }
32+
33+ impl < ' a , P , S > TradeExecutor < ' a , P , S >
2534where
2635 P : Provider ,
2736{
28- pub fn new (
37+ pub async fn new (
38+ signer : S ,
2939 networks : & ' a HashMap < u64 , Network < P > > ,
3040 profitability_estimator : ErasedProfitabilityEstimator ,
31- ) -> Self {
32- let routers = networks
41+ ) -> anyhow:: Result < Self > {
42+ let permit2_addresses: HashMap < _ , _ > =
43+ fetch_permit2_addresses ( networks. iter ( ) ) . await ?. collect ( ) ;
44+ let configs = networks
3345 . iter ( )
34- . map ( |( chain_id, net) | ( * chain_id, & net. router ) )
35- . collect ( ) ;
36-
37- let tokens = networks
38- . iter ( )
39- . map ( |( chain_id, net) | ( * chain_id, & net. tokens ) )
40- . collect ( ) ;
46+ . map ( |( chain_id, net) | -> anyhow:: Result < _ > {
47+ let permit2_addr = * permit2_addresses. get ( chain_id) . with_context ( || {
48+ format ! ( "failed to get permit2 address of chain {chain_id}" )
49+ } ) ?;
50+ Ok ( (
51+ * chain_id,
52+ ChainConfig {
53+ router : & net. router ,
54+ permit2_relayer_address : net. permit2_relayer_address ,
55+ permit2_addr,
56+ } ,
57+ ) )
58+ } )
59+ . collect :: < anyhow:: Result < _ > > ( ) ?;
4160
4261 let own_address = networks
4362 . iter ( )
4463 . next ( )
4564 . map ( |( _, network) | network. own_addr )
4665 . expect ( "if we don't have a network by now, something is very wrong" ) ;
4766
48- Self {
49- routers ,
50- tokens ,
67+ Ok ( Self {
68+ signer ,
69+ configs ,
5170 own_address,
5271 profitability_estimator,
53- }
72+ } )
5473 }
74+ }
75+
76+ impl < ' a , P , S > TradeExecutor < ' a , P , S >
77+ where
78+ P : Provider ,
79+ S : Signer ,
80+ {
5581 pub async fn execute (
5682 & self ,
5783 trades : Vec < Trade > ,
@@ -64,26 +90,21 @@ where
6490 in_flight. insert ( trade. request_id , ( ) ) . await ;
6591
6692 // then we get the contract bindings for the destination chain
67- let router = self
68- . routers
93+ let config = self
94+ . configs
6995 . get ( & normalise_chain_id ( trade. dest_chain_id ) )
7096 . expect ( "somehow didn't have a router binding for a solved trade" ) ;
71- let token = self
72- . tokens
73- . get ( & normalise_chain_id ( trade. dest_chain_id ) )
74- . expect ( "somehow didn't have a token binding for a solved trade" )
75- . iter ( )
76- . find ( |contract| contract. address ( ) == & trade. token_out_addr )
77- . expect ( "somehow didn't have a token contract binding for a solved trade" ) ;
7897
7998 // and finally execute the trade with a timeout
8099 match timeout (
81100 timeout_config. request_timeout ,
82101 execute_trade (
83102 & trade,
84- router,
85- token,
103+ config. router ,
104+ config. permit2_relayer_address ,
105+ config. permit2_addr ,
86106 self . own_address ,
107+ & self . signer ,
87108 & self . profitability_estimator ,
88109 ) ,
89110 )
@@ -119,42 +140,42 @@ where
119140 }
120141}
121142
122- async fn execute_trade (
143+ async fn execute_trade < S > (
123144 trade : & Trade ,
124145 router : & IRouterInstance < impl Provider > ,
125- token : & ERC20FaucetTokenInstance < impl Provider > ,
146+ permit2_relayer_address : Address ,
147+ permit2_addr : Address ,
126148 own_addr : Address ,
149+ signer : & S ,
127150 profitability_estimator : & ErasedProfitabilityEstimator ,
128- ) -> anyhow:: Result < TxHash > {
129- // in theory, we shouldn't need to wait until the next block because txs will be processed in nonce order
130- // but for whatever reason this doesn't seem to be the case :(
131- let tx = token
132- . approve ( * router. address ( ) , trade. amount_out )
133- . send ( )
134- . await
135- . map_err ( |e| {
136- // Try to decode it as an IERC20 error
137- if let Some ( erc20_err) = e. as_decoded_interface_error :: < IERC20Errors > ( ) {
138- return anyhow ! ( "erc20 contract error: {erc20_err:?}" ) ;
139- }
140- e. into ( )
141- } )
142- . context ( "error approving funds" ) ?;
143- tx. watch ( ) . await . context ( "error approving funds" ) ?;
144-
145- let relay_tokens_call = router. relayTokens (
146- own_addr,
147- trade. request_id ,
148- trade. sender_addr ,
149- trade. recipient_addr ,
150- trade. token_in_addr ,
151- trade. token_out_addr ,
152- trade. amount_out ,
153- trade. src_chain_id ,
154- trade. nonce ,
155- trade. pre_hooks . to_vec ( ) ,
156- trade. post_hooks . to_vec ( ) ,
157- ) ;
151+ ) -> anyhow:: Result < TxHash >
152+ where
153+ S : Signer ,
154+ {
155+ let Permit2RelayTokensDetails {
156+ message_hash,
157+ nonce : permit_nonce,
158+ deadline : permit_deadline,
159+ } = permit2_relay_tokens_details ( trade, permit2_relayer_address, own_addr, Some ( permit2_addr) ) ?;
160+ let permit2_signed_allowance = signer. sign_hash ( & message_hash) . await ?;
161+
162+ let relay_tokens_call = router. relayTokensPermit2 ( RelayTokensPermit2Params {
163+ solver : own_addr,
164+ solverRefundAddress : own_addr,
165+ requestId : trade. request_id ,
166+ sender : trade. sender_addr ,
167+ recipient : trade. recipient_addr ,
168+ tokenIn : trade. token_in_addr ,
169+ tokenOut : trade. token_out_addr ,
170+ amountOut : trade. amount_out ,
171+ srcChainId : trade. src_chain_id ,
172+ nonce : trade. nonce ,
173+ permitNonce : permit_nonce,
174+ permitDeadline : permit_deadline,
175+ signature : permit2_signed_allowance. as_erc2098 ( ) . into ( ) ,
176+ preHooks : trade. pre_hooks . to_vec ( ) ,
177+ postHooks : trade. post_hooks . to_vec ( ) ,
178+ } ) ;
158179
159180 let gas = relay_tokens_call
160181 . clone ( )
@@ -193,6 +214,10 @@ async fn execute_trade(
193214}
194215
195216fn decode_irouter_error ( e : alloy:: contract:: Error ) -> anyhow:: Error {
217+ // Try to decode it as a permit2 error
218+ if let Some ( permit2_err) = permit2:: decode_error ( & e) {
219+ return anyhow ! ( "permit2 contract error: {permit2_err:?}" ) ;
220+ }
196221 // Try to decode it as an IERC20 error
197222 if let Some ( erc20_err) = e. as_decoded_interface_error :: < IERC20Errors > ( ) {
198223 return anyhow ! ( "erc20 contract error: {erc20_err:?}" ) ;
0 commit comments