@@ -2,13 +2,17 @@ package client
22
33import (
44 "context"
5+ "encoding/hex"
6+ "errors"
57 "fmt"
6- "log "
8+ "math/big "
79 "strings"
810 "time"
911
12+ "github.com/1inch/1inch-sdk/golang/client/tenderly"
1013 "github.com/1inch/1inch-sdk/golang/helpers"
1114 "github.com/1inch/1inch-sdk/golang/helpers/consts/chains"
15+ "github.com/1inch/1inch-sdk/golang/helpers/consts/tokens"
1216 "github.com/ethereum/go-ethereum/common"
1317
1418 "github.com/1inch/1inch-sdk/golang/client/onchain"
@@ -18,6 +22,8 @@ import (
1822 "github.com/1inch/1inch-sdk/golang/helpers/consts/typehashes"
1923)
2024
25+ // This file provides helper functions that execute swaps onchain.
26+
2127type ActionService service
2228
2329// TODO temporarily adding a bool to the function call until config refactor
@@ -59,10 +65,6 @@ func (s *ActionService) SwapTokens(swapParams swap.AggregationControllerGetSwapP
5965 Slippage : swapParams .Slippage ,
6066 }
6167
62- if shouldTryPermit (s .client .ChainId , approvalType ) {
63-
64- }
65-
6668 var usePermit bool
6769
6870 // Currently, Permit1 swaps are only tested on Ethereum and Polygon
@@ -77,7 +79,7 @@ func (s *ActionService) SwapTokens(swapParams swap.AggregationControllerGetSwapP
7779 if typehash == typehashes .Permit1 {
7880 usePermit = true
7981 } else {
80- log .Printf ("Typehash exists, but it is not recognized: %v\n " , typehash )
82+ fmt .Printf ("WARN: Typehash exists, but it is not recognized: %v\n " , typehash )
8183 }
8284 }
8385 }
@@ -143,6 +145,176 @@ func (s *ActionService) SwapTokens(swapParams swap.AggregationControllerGetSwapP
143145 return nil
144146}
145147
146- func shouldTryPermit (chainId int , approvalType swap.ApprovalType ) bool {
147- return approvalType == swap .PermitIfPossible || approvalType == swap .PermitAlways
148+ // ExecuteSwap executes a swap on the Ethereum blockchain using swap data generated by GetSwapData
149+ func (s * SwapService ) ExecuteSwap (config * swap.ExecuteSwapConfig ) error {
150+
151+ if s .client .WalletKey == "" {
152+ return fmt .Errorf ("wallet key must be set in the client config" )
153+ }
154+
155+ if ! config .SkipWarnings {
156+ ok , err := swap .ConfirmExecuteSwapWithUser (config , s .client .EthClient )
157+ if err != nil {
158+ return fmt .Errorf ("failed to confirm swap: %v" , err )
159+ }
160+ if ! ok {
161+ return errors .New ("user rejected trade" )
162+ }
163+ }
164+
165+ aggregationRouter , err := contracts .Get1inchRouterFromChainId (s .client .ChainId )
166+ if err != nil {
167+ return fmt .Errorf ("failed to get 1inch router address: %v" , err )
168+ }
169+
170+ if ! config .IsPermitSwap {
171+ err = s .executeSwapWithApproval (aggregationRouter , config .FromToken , config .Amount , config .TransactionData , config .SkipWarnings )
172+ if err != nil {
173+ return fmt .Errorf ("failed to execute swap with approval: %v" , err )
174+ }
175+ } else {
176+ err = s .executeSwapWithPermit (config .FromToken , config .TransactionData )
177+ if err != nil {
178+ return fmt .Errorf ("failed to execute swap with permit: %v" , err )
179+ }
180+ }
181+
182+ return nil
183+ }
184+
185+ func (s * SwapService ) executeSwapWithApproval (spenderAddress string , fromToken string , amount string , transactionData string , skipWarnings bool ) error {
186+
187+ var value * big.Int
188+ var err error
189+ var approveFirst bool
190+ if fromToken != tokens .NativeToken {
191+ // When swapping erc20 tokens, the value set on the transaction will be 0
192+ value = big .NewInt (0 )
193+
194+ allowance , err := onchain .ReadContractAllowance (s .client .EthClient , common .HexToAddress (fromToken ), s .client .PublicAddress , common .HexToAddress (spenderAddress ))
195+ if err != nil {
196+ return fmt .Errorf ("failed to read allowance: %v" , err )
197+ }
198+
199+ amountBig , err := helpers .BigIntFromString (amount )
200+ if err != nil {
201+ return fmt .Errorf ("failed to convert amount to big.Int: %v" , err )
202+ }
203+ if allowance .Cmp (amountBig ) <= 0 {
204+ if ! skipWarnings {
205+ ok , err := swap .ConfirmApprovalWithUser (s .client .EthClient , s .client .PublicAddress .Hex (), fromToken )
206+ if err != nil {
207+ return fmt .Errorf ("failed to confirm approval: %v" , err )
208+ }
209+ if ! ok {
210+ return errors .New ("user rejected approval" )
211+ }
212+ }
213+
214+ approveFirst = true
215+
216+ // Only run the approval if a tenderly key is not present
217+ if s .client .TenderlyKey == "" {
218+ erc20Config := onchain.Erc20ApprovalConfig {
219+ ChainId : s .client .ChainId ,
220+ Key : s .client .WalletKey ,
221+ Erc20Address : common .HexToAddress (fromToken ),
222+ PublicAddress : s .client .PublicAddress ,
223+ SpenderAddress : common .HexToAddress (spenderAddress ),
224+ }
225+ err = onchain .ApproveTokenForRouter (s .client .EthClient , s .client .NonceCache , erc20Config )
226+ if err != nil {
227+ return fmt .Errorf ("failed to approve token for router: %v" , err )
228+ }
229+ helpers .Sleep ()
230+ }
231+ }
232+ } else {
233+ // When swapping from the native token, there is no need for an approval and the amount passed in must be explicitly set
234+ value , err = helpers .BigIntFromString (amount )
235+ if err != nil {
236+ return fmt .Errorf ("failed to convert amount to big.Int: %v" , err )
237+ }
238+ }
239+
240+ hexData , err := hex .DecodeString (transactionData [2 :])
241+ if err != nil {
242+ return fmt .Errorf ("failed to decode swap data: %v" , err )
243+ }
244+
245+ aggregationRouter , err := contracts .Get1inchRouterFromChainId (s .client .ChainId )
246+ if err != nil {
247+ return fmt .Errorf ("failed to get 1inch router address: %v" , err )
248+ }
249+
250+ txConfig := onchain.TxConfig {
251+ Description : "Swap" ,
252+ PublicAddress : s .client .PublicAddress ,
253+ PrivateKey : s .client .WalletKey ,
254+ ChainId : big .NewInt (int64 (s .client .ChainId )),
255+ Value : value ,
256+ To : aggregationRouter ,
257+ Data : hexData ,
258+ }
259+
260+ if s .client .TenderlyKey != "" {
261+ _ , err := tenderly .SimulateSwap (s .client .TenderlyKey , tenderly.SwapConfig {
262+ ChainId : s .client .ChainId ,
263+ PublicAddress : s .client .PublicAddress .Hex (),
264+ FromToken : fromToken ,
265+ TransactionData : transactionData ,
266+ ApproveFirst : approveFirst ,
267+ Value : value .String (),
268+ })
269+ if err != nil {
270+ return fmt .Errorf ("failed to execute tenderly simulation: %v" , err )
271+ }
272+ } else {
273+ err = onchain .ExecuteTransaction (txConfig , s .client .EthClient , s .client .NonceCache )
274+ if err != nil {
275+ return fmt .Errorf ("failed to execute transaction: %v" , err )
276+ }
277+ }
278+ return nil
279+ }
280+
281+ func (s * SwapService ) executeSwapWithPermit (fromToken string , transactionData string ) error {
282+
283+ hexData , err := hex .DecodeString (transactionData [2 :])
284+ if err != nil {
285+ return fmt .Errorf ("failed to decode swap data: %v" , err )
286+ }
287+
288+ aggregationRouter , err := contracts .Get1inchRouterFromChainId (s .client .ChainId )
289+ if err != nil {
290+ return fmt .Errorf ("failed to get 1inch router address: %v" , err )
291+ }
292+
293+ txConfig := onchain.TxConfig {
294+ Description : "Swap" ,
295+ PublicAddress : s .client .PublicAddress ,
296+ PrivateKey : s .client .WalletKey ,
297+ ChainId : big .NewInt (int64 (s .client .ChainId )),
298+ Value : big .NewInt (0 ),
299+ To : aggregationRouter ,
300+ Data : hexData ,
301+ }
302+ if s .client .TenderlyKey != "" {
303+ _ , err := tenderly .SimulateSwap (s .client .TenderlyKey , tenderly.SwapConfig {
304+ ChainId : s .client .ChainId ,
305+ PublicAddress : s .client .PublicAddress .Hex (),
306+ FromToken : fromToken ,
307+ TransactionData : transactionData ,
308+ Value : "0" ,
309+ })
310+ if err != nil {
311+ return fmt .Errorf ("failed to execute tenderly simulation: %v" , err )
312+ }
313+ } else {
314+ err = onchain .ExecuteTransaction (txConfig , s .client .EthClient , s .client .NonceCache )
315+ if err != nil {
316+ return fmt .Errorf ("failed to execute transaction: %v" , err )
317+ }
318+ }
319+ return nil
148320}
0 commit comments