@lifi/sdk 3.10.0 → 3.11.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/package.json +1 -1
  2. package/src/_cjs/core/EVM/EVMStepExecutor.js +122 -131
  3. package/src/_cjs/core/EVM/EVMStepExecutor.js.map +1 -1
  4. package/src/_cjs/core/EVM/abi.js +1 -0
  5. package/src/_cjs/core/EVM/abi.js.map +1 -1
  6. package/src/_cjs/core/EVM/checkAllowance.js +126 -44
  7. package/src/_cjs/core/EVM/checkAllowance.js.map +1 -1
  8. package/src/_cjs/core/EVM/permits/getNativePermit.js +123 -16
  9. package/src/_cjs/core/EVM/permits/getNativePermit.js.map +1 -1
  10. package/src/_cjs/core/EVM/permits/isNativePermitValid.js +34 -0
  11. package/src/_cjs/core/EVM/permits/isNativePermitValid.js.map +1 -0
  12. package/src/_cjs/core/EVM/switchChain.js +8 -14
  13. package/src/_cjs/core/EVM/switchChain.js.map +1 -1
  14. package/src/_cjs/core/EVM/utils.js +10 -1
  15. package/src/_cjs/core/EVM/utils.js.map +1 -1
  16. package/src/_cjs/core/execution.js +1 -1
  17. package/src/_cjs/core/execution.js.map +1 -1
  18. package/src/_cjs/core/prepareRestart.js +5 -2
  19. package/src/_cjs/core/prepareRestart.js.map +1 -1
  20. package/src/_cjs/core/processMessages.js +4 -8
  21. package/src/_cjs/core/processMessages.js.map +1 -1
  22. package/src/_cjs/index.js +2 -1
  23. package/src/_cjs/index.js.map +1 -1
  24. package/src/_cjs/version.js +1 -1
  25. package/src/_cjs/version.js.map +1 -1
  26. package/src/_esm/core/EVM/EVMStepExecutor.js +136 -148
  27. package/src/_esm/core/EVM/EVMStepExecutor.js.map +1 -1
  28. package/src/_esm/core/EVM/abi.js +2 -0
  29. package/src/_esm/core/EVM/abi.js.map +1 -1
  30. package/src/_esm/core/EVM/checkAllowance.js +141 -45
  31. package/src/_esm/core/EVM/checkAllowance.js.map +1 -1
  32. package/src/_esm/core/EVM/permits/getNativePermit.js +144 -21
  33. package/src/_esm/core/EVM/permits/getNativePermit.js.map +1 -1
  34. package/src/_esm/core/EVM/permits/isNativePermitValid.js +41 -0
  35. package/src/_esm/core/EVM/permits/isNativePermitValid.js.map +1 -0
  36. package/src/_esm/core/EVM/switchChain.js +8 -15
  37. package/src/_esm/core/EVM/switchChain.js.map +1 -1
  38. package/src/_esm/core/EVM/utils.js +12 -0
  39. package/src/_esm/core/EVM/utils.js.map +1 -1
  40. package/src/_esm/core/execution.js +1 -1
  41. package/src/_esm/core/execution.js.map +1 -1
  42. package/src/_esm/core/prepareRestart.js +6 -3
  43. package/src/_esm/core/prepareRestart.js.map +1 -1
  44. package/src/_esm/core/processMessages.js +4 -8
  45. package/src/_esm/core/processMessages.js.map +1 -1
  46. package/src/_esm/index.js +1 -1
  47. package/src/_esm/index.js.map +1 -1
  48. package/src/_esm/version.js +1 -1
  49. package/src/_esm/version.js.map +1 -1
  50. package/src/_types/core/EVM/EVMStepExecutor.d.ts +3 -2
  51. package/src/_types/core/EVM/EVMStepExecutor.d.ts.map +1 -1
  52. package/src/_types/core/EVM/abi.d.ts +20 -0
  53. package/src/_types/core/EVM/abi.d.ts.map +1 -1
  54. package/src/_types/core/EVM/checkAllowance.d.ts +10 -7
  55. package/src/_types/core/EVM/checkAllowance.d.ts.map +1 -1
  56. package/src/_types/core/EVM/permits/getNativePermit.d.ts +2 -2
  57. package/src/_types/core/EVM/permits/getNativePermit.d.ts.map +1 -1
  58. package/src/_types/core/EVM/permits/isNativePermitValid.d.ts +6 -0
  59. package/src/_types/core/EVM/permits/isNativePermitValid.d.ts.map +1 -0
  60. package/src/_types/core/EVM/switchChain.d.ts +2 -2
  61. package/src/_types/core/EVM/switchChain.d.ts.map +1 -1
  62. package/src/_types/core/EVM/utils.d.ts +6 -1
  63. package/src/_types/core/EVM/utils.d.ts.map +1 -1
  64. package/src/_types/core/prepareRestart.d.ts +1 -1
  65. package/src/_types/core/prepareRestart.d.ts.map +1 -1
  66. package/src/_types/core/processMessages.d.ts.map +1 -1
  67. package/src/_types/core/types.d.ts +2 -2
  68. package/src/_types/core/types.d.ts.map +1 -1
  69. package/src/_types/index.d.ts +1 -1
  70. package/src/_types/index.d.ts.map +1 -1
  71. package/src/_types/version.d.ts +1 -1
  72. package/src/_types/version.d.ts.map +1 -1
  73. package/src/core/EVM/EVMStepExecutor.ts +203 -197
  74. package/src/core/EVM/abi.ts +2 -0
  75. package/src/core/EVM/checkAllowance.ts +206 -63
  76. package/src/core/EVM/permits/getNativePermit.ts +189 -22
  77. package/src/core/EVM/permits/isNativePermitValid.ts +57 -0
  78. package/src/core/EVM/switchChain.ts +14 -22
  79. package/src/core/EVM/utils.ts +17 -1
  80. package/src/core/execution.ts +1 -1
  81. package/src/core/prepareRestart.ts +6 -3
  82. package/src/core/processMessages.ts +4 -8
  83. package/src/core/types.ts +1 -2
  84. package/src/index.ts +1 -0
  85. package/src/version.ts +1 -1
@@ -45,11 +45,16 @@ import {
45
45
  } from './parseEVMErrors.js'
46
46
  import { encodeNativePermitData } from './permits/encodeNativePermitData.js'
47
47
  import { encodePermit2Data } from './permits/encodePermit2Data.js'
48
+ import { isNativePermitValid } from './permits/isNativePermitValid.js'
48
49
  import { signPermit2Message } from './permits/signPermit2Message.js'
49
50
  import { switchChain } from './switchChain.js'
50
51
  import { isGaslessStep, isRelayerStep } from './typeguards.js'
51
52
  import type { Call, WalletCallReceipt } from './types.js'
52
- import { convertExtendedChain, getMaxPriorityFeePerGas } from './utils.js'
53
+ import {
54
+ convertExtendedChain,
55
+ getMaxPriorityFeePerGas,
56
+ isSaltMatchingChainId,
57
+ } from './utils.js'
53
58
  import { waitForBatchTransactionReceipt } from './waitForBatchTransactionReceipt.js'
54
59
  import { waitForRelayedTransactionReceipt } from './waitForRelayedTransactionReceipt.js'
55
60
  import { waitForTransactionReceipt } from './waitForTransactionReceipt.js'
@@ -67,13 +72,19 @@ export class EVMStepExecutor extends BaseStepExecutor {
67
72
  }
68
73
 
69
74
  // Ensure that we are using the right chain and wallet when executing transactions.
70
- checkClient = async (step: LiFiStepExtended, process?: Process) => {
75
+ checkClient = async (
76
+ step: LiFiStepExtended,
77
+ process: Process,
78
+ targetChainId?: number
79
+ ) => {
71
80
  const updatedClient = await switchChain(
72
81
  this.client,
73
82
  this.statusManager,
74
83
  step,
84
+ process,
85
+ targetChainId ?? step.action.fromChainId,
75
86
  this.allowUserInteraction,
76
- this.executionOptions?.switchChainHook
87
+ this.executionOptions
77
88
  )
78
89
  if (updatedClient) {
79
90
  this.client = updatedClient
@@ -92,17 +103,9 @@ export class EVMStepExecutor extends BaseStepExecutor {
92
103
  if (
93
104
  accountAddress?.toLowerCase() !== step.action.fromAddress?.toLowerCase()
94
105
  ) {
95
- let processToUpdate = process
96
- if (!processToUpdate) {
97
- // We need to create some process if we don't have one so we can show the error
98
- processToUpdate = this.statusManager.findOrCreateProcess({
99
- step,
100
- type: 'TRANSACTION',
101
- })
102
- }
103
106
  const errorMessage =
104
107
  'The wallet address that requested the quote does not match the wallet address attempting to sign the transaction.'
105
- this.statusManager.updateProcess(step, processToUpdate.type, 'FAILED', {
108
+ this.statusManager.updateProcess(step, process.type, 'FAILED', {
106
109
  error: {
107
110
  code: LiFiErrorCode.WalletChangedDuringExecution,
108
111
  message: errorMessage,
@@ -202,14 +205,15 @@ export class EVMStepExecutor extends BaseStepExecutor {
202
205
  )
203
206
  }
204
207
 
205
- private getUpdatedStep = async (
208
+ private prepareUpdatedStep = async (
206
209
  step: LiFiStepExtended,
207
- signedNativePermitTypedData?: SignedTypedData
208
- ): Promise<LiFiStep> => {
210
+ signedTypedData?: SignedTypedData[]
211
+ ) => {
209
212
  // biome-ignore lint/correctness/noUnusedVariables: destructuring
210
213
  const { execution, ...stepBase } = step
211
214
  const relayerStep = isRelayerStep(step)
212
215
  const gaslessStep = isGaslessStep(step)
216
+ let updatedStep: LiFiStep
213
217
  if (relayerStep && gaslessStep) {
214
218
  const updatedRelayedStep = await getRelayerQuote({
215
219
  fromChain: stepBase.action.fromChainId,
@@ -222,18 +226,118 @@ export class EVMStepExecutor extends BaseStepExecutor {
222
226
  toAddress: stepBase.action.toAddress,
223
227
  allowBridges: [stepBase.tool],
224
228
  })
225
- return {
229
+ updatedStep = {
226
230
  ...updatedRelayedStep,
227
231
  id: stepBase.id,
228
232
  }
233
+ } else {
234
+ const params = signedTypedData?.length
235
+ ? { ...stepBase, typedData: signedTypedData }
236
+ : stepBase
237
+ updatedStep = await getStepTransaction(params)
229
238
  }
230
239
 
231
- const params =
232
- relayerStep && !gaslessStep && signedNativePermitTypedData
233
- ? { ...stepBase, typedData: [signedNativePermitTypedData] }
234
- : stepBase
240
+ const comparedStep = await stepComparison(
241
+ this.statusManager,
242
+ step,
243
+ updatedStep,
244
+ this.allowUserInteraction,
245
+ this.executionOptions
246
+ )
247
+ Object.assign(step, {
248
+ ...comparedStep,
249
+ execution: step.execution,
250
+ typedData: updatedStep.typedData ?? step.typedData,
251
+ })
252
+
253
+ if (!step.transactionRequest && !step.typedData?.length) {
254
+ throw new TransactionError(
255
+ LiFiErrorCode.TransactionUnprepared,
256
+ 'Unable to prepare transaction.'
257
+ )
258
+ }
259
+
260
+ let transactionRequest: TransactionParameters | undefined
261
+ if (step.transactionRequest) {
262
+ transactionRequest = {
263
+ to: step.transactionRequest.to,
264
+ from: step.transactionRequest.from,
265
+ data: step.transactionRequest.data,
266
+ value: step.transactionRequest.value
267
+ ? BigInt(step.transactionRequest.value)
268
+ : undefined,
269
+ gas: step.transactionRequest.gasLimit
270
+ ? BigInt(step.transactionRequest.gasLimit)
271
+ : undefined,
272
+ // gasPrice: step.transactionRequest.gasPrice
273
+ // ? BigInt(step.transactionRequest.gasPrice as string)
274
+ // : undefined,
275
+ // maxFeePerGas: step.transactionRequest.maxFeePerGas
276
+ // ? BigInt(step.transactionRequest.maxFeePerGas as string)
277
+ // : undefined,
278
+ maxPriorityFeePerGas:
279
+ this.client.account?.type === 'local'
280
+ ? await getMaxPriorityFeePerGas(this.client)
281
+ : step.transactionRequest.maxPriorityFeePerGas
282
+ ? BigInt(step.transactionRequest.maxPriorityFeePerGas)
283
+ : undefined,
284
+ }
285
+ }
235
286
 
236
- return getStepTransaction(params)
287
+ if (
288
+ this.executionOptions?.updateTransactionRequestHook &&
289
+ transactionRequest
290
+ ) {
291
+ const customizedTransactionRequest: TransactionParameters =
292
+ await this.executionOptions.updateTransactionRequestHook({
293
+ requestType: 'transaction',
294
+ ...transactionRequest,
295
+ })
296
+ transactionRequest = {
297
+ ...transactionRequest,
298
+ ...customizedTransactionRequest,
299
+ }
300
+ }
301
+
302
+ return {
303
+ transactionRequest,
304
+ // We should always check against the updated step,
305
+ // because the step may be updated with typed data from the previously signed typed data
306
+ isRelayerTransaction: isRelayerStep(updatedStep),
307
+ }
308
+ }
309
+
310
+ private estimateTransactionRequest = async (
311
+ transactionRequest: TransactionParameters,
312
+ fromChain: ExtendedChain
313
+ ) => {
314
+ // Target address should be the Permit2 proxy contract in case of native permit or Permit2
315
+ transactionRequest.to = fromChain.permit2Proxy
316
+ try {
317
+ // Try to re-estimate the gas due to additional Permit data
318
+ const estimatedGas = await getActionWithFallback(
319
+ this.client,
320
+ estimateGas,
321
+ 'estimateGas',
322
+ {
323
+ account: this.client.account!,
324
+ to: transactionRequest.to as Address,
325
+ data: transactionRequest.data as Hex,
326
+ value: transactionRequest.value,
327
+ }
328
+ )
329
+ transactionRequest.gas =
330
+ transactionRequest.gas && transactionRequest.gas > estimatedGas
331
+ ? transactionRequest.gas
332
+ : estimatedGas
333
+ } catch (_) {
334
+ // If we fail to estimate the gas, we add 80_000 gas units Permit buffer to the gas limit
335
+ if (transactionRequest.gas) {
336
+ transactionRequest.gas = transactionRequest.gas + 80_000n
337
+ }
338
+ }
339
+
340
+ return transactionRequest
237
341
  }
238
342
 
239
343
  executeStep = async (
@@ -252,8 +356,14 @@ export class EVMStepExecutor extends BaseStepExecutor {
252
356
  // If the step is waiting for a transaction on the destination chain, we do not switch the chain
253
357
  // All changes are already done from the source chain
254
358
  // Return the step
255
- if (destinationChainProcess?.substatus !== 'WAIT_DESTINATION_TRANSACTION') {
256
- const updatedClient = await this.checkClient(step)
359
+ if (
360
+ destinationChainProcess &&
361
+ destinationChainProcess.substatus !== 'WAIT_DESTINATION_TRANSACTION'
362
+ ) {
363
+ const updatedClient = await this.checkClient(
364
+ step,
365
+ destinationChainProcess
366
+ )
257
367
  if (!updatedClient) {
258
368
  return step
259
369
  }
@@ -262,18 +372,17 @@ export class EVMStepExecutor extends BaseStepExecutor {
262
372
  const fromChain = await config.getChainById(step.action.fromChainId)
263
373
  const toChain = await config.getChainById(step.action.toChainId)
264
374
 
265
- // Check if step requires permit signature and will be used with relayer service
266
- let isRelayerTransaction = isRelayerStep(step)
267
-
268
375
  // Check if the wallet supports atomic batch transactions (EIP-5792)
269
376
  const calls: Call[] = []
377
+ // Signed typed data for native permits and other messages
378
+ let signedTypedData: SignedTypedData[] = []
270
379
 
271
380
  // Batching via EIP-5792 is disabled in the next cases:
272
381
  // 1. When atomicity is not ready or the wallet rejected the upgrade to 7702 account (atomicityNotReady is true)
273
382
  // 2. When the step is using thorswap tool (temporary disabled)
274
383
  // 3. When using relayer transactions
275
384
  const batchingSupported =
276
- atomicityNotReady || step.tool === 'thorswap' || isRelayerTransaction
385
+ atomicityNotReady || step.tool === 'thorswap' || isRelayerStep(step)
277
386
  ? false
278
387
  : await isBatchingSupported({
279
388
  client: this.client,
@@ -317,11 +426,10 @@ export class EVMStepExecutor extends BaseStepExecutor {
317
426
  // Approval address is required for allowance checks, but may be null in special cases (e.g. direct transfers)
318
427
  !!step.estimate.approvalAddress
319
428
 
320
- let signedNativePermitTypedData: SignedTypedData | undefined
321
429
  if (checkForAllowance) {
322
430
  // Check if token needs approval and get approval transaction or message data when available
323
431
  const allowanceResult = await checkAllowance({
324
- client: this.client,
432
+ checkClient: this.checkClient,
325
433
  chain: fromChain,
326
434
  step,
327
435
  statusManager: this.statusManager,
@@ -332,21 +440,22 @@ export class EVMStepExecutor extends BaseStepExecutor {
332
440
  disableMessageSigning,
333
441
  })
334
442
 
335
- if (allowanceResult.status === 'BATCH_APPROVAL') {
336
- // Create approval transaction call
337
- // No value needed since we're only approving ERC20 tokens
338
- if (batchingSupported) {
339
- calls.push(allowanceResult.data)
340
- }
341
- }
342
- if (allowanceResult.status === 'NATIVE_PERMIT') {
343
- signedNativePermitTypedData = allowanceResult.data
344
- }
345
- if (
346
- allowanceResult.status === 'ACTION_REQUIRED' &&
347
- !this.allowUserInteraction
348
- ) {
349
- return step
443
+ switch (allowanceResult.status) {
444
+ case 'BATCH_APPROVAL':
445
+ calls.push(allowanceResult.data.call)
446
+ signedTypedData = allowanceResult.data.signedTypedData
447
+ break
448
+ case 'NATIVE_PERMIT':
449
+ signedTypedData = allowanceResult.data
450
+ break
451
+ case 'DONE':
452
+ signedTypedData = allowanceResult.data
453
+ break
454
+ default:
455
+ if (!this.allowUserInteraction) {
456
+ return step
457
+ }
458
+ break
350
459
  }
351
460
  }
352
461
 
@@ -382,93 +491,18 @@ export class EVMStepExecutor extends BaseStepExecutor {
382
491
  return step
383
492
  }
384
493
 
385
- const permitRequired =
386
- !batchingSupported && !signedNativePermitTypedData && permit2Supported
387
494
  process = this.statusManager.findOrCreateProcess({
388
495
  step,
389
- type: permitRequired ? 'PERMIT' : currentProcessType,
496
+ type: currentProcessType,
390
497
  status: 'STARTED',
391
498
  chainId: fromChain.id,
392
499
  })
393
500
 
394
- // Check balance
395
501
  await checkBalance(this.client.account!.address, step)
396
502
 
397
- // Create new transaction request
398
- if (
399
- !step.transactionRequest &&
400
- !step.typedData?.some((p) => p.primaryType !== 'Permit')
401
- ) {
402
- const updatedStep = await this.getUpdatedStep(
403
- step,
404
- signedNativePermitTypedData
405
- )
406
- const comparedStep = await stepComparison(
407
- this.statusManager,
408
- step,
409
- updatedStep,
410
- this.allowUserInteraction,
411
- this.executionOptions
412
- )
413
- Object.assign(step, {
414
- ...comparedStep,
415
- execution: step.execution,
416
- })
417
- isRelayerTransaction = isRelayerStep(comparedStep)
418
- }
419
-
420
- if (
421
- !step.transactionRequest &&
422
- !step.typedData?.some((p) => p.primaryType !== 'Permit')
423
- ) {
424
- throw new TransactionError(
425
- LiFiErrorCode.TransactionUnprepared,
426
- 'Unable to prepare transaction.'
427
- )
428
- }
429
-
430
- let transactionRequest: TransactionParameters | undefined
431
- if (step.transactionRequest) {
432
- transactionRequest = {
433
- to: step.transactionRequest.to,
434
- from: step.transactionRequest.from,
435
- data: step.transactionRequest.data,
436
- value: step.transactionRequest.value
437
- ? BigInt(step.transactionRequest.value)
438
- : undefined,
439
- gas: step.transactionRequest.gasLimit
440
- ? BigInt(step.transactionRequest.gasLimit)
441
- : undefined,
442
- // gasPrice: step.transactionRequest.gasPrice
443
- // ? BigInt(step.transactionRequest.gasPrice as string)
444
- // : undefined,
445
- // maxFeePerGas: step.transactionRequest.maxFeePerGas
446
- // ? BigInt(step.transactionRequest.maxFeePerGas as string)
447
- // : undefined,
448
- maxPriorityFeePerGas:
449
- this.client.account?.type === 'local'
450
- ? await getMaxPriorityFeePerGas(this.client)
451
- : step.transactionRequest.maxPriorityFeePerGas
452
- ? BigInt(step.transactionRequest.maxPriorityFeePerGas)
453
- : undefined,
454
- }
455
- }
456
-
457
- if (
458
- this.executionOptions?.updateTransactionRequestHook &&
459
- transactionRequest
460
- ) {
461
- const customizedTransactionRequest: TransactionParameters =
462
- await this.executionOptions.updateTransactionRequestHook({
463
- requestType: 'transaction',
464
- ...transactionRequest,
465
- })
466
-
467
- transactionRequest = {
468
- ...transactionRequest,
469
- ...customizedTransactionRequest,
470
- }
471
- }
503
+ // Try to prepare a new transaction request and update the step with typed data
504
+ let { transactionRequest, isRelayerTransaction } =
505
+ await this.prepareUpdatedStep(step, signedTypedData)
472
506
 
473
507
  // Make sure that the chain is still correct
474
508
  const updatedClient = await this.checkClient(step, process)
@@ -512,48 +546,39 @@ export class EVMStepExecutor extends BaseStepExecutor {
512
546
  taskId = id as Hash
513
547
  txType = 'batched'
514
548
  } else if (isRelayerTransaction) {
515
- const relayerTypedData = step.typedData?.find(
516
- (p) => p.primaryType !== 'Permit'
549
+ const intentTypedData = step.typedData?.filter(
550
+ (typedData) =>
551
+ !signedTypedData.some((signedPermit) =>
552
+ isNativePermitValid(signedPermit, typedData)
553
+ )
517
554
  )
518
-
519
- if (!relayerTypedData) {
555
+ if (!intentTypedData?.length) {
520
556
  throw new TransactionError(
521
557
  LiFiErrorCode.TransactionUnprepared,
522
558
  'Unable to prepare transaction. Typed data for transfer is not found.'
523
559
  )
524
560
  }
525
-
526
- const signature = await getAction(
527
- this.client,
528
- signTypedData,
529
- 'signTypedData'
530
- )({
531
- account: this.client.account!,
532
- primaryType: relayerTypedData.primaryType,
533
- domain: relayerTypedData.domain,
534
- types: relayerTypedData.types,
535
- message: relayerTypedData.message,
536
- })
537
-
538
- this.statusManager.updateProcess(step, process.type, 'DONE')
539
-
540
- process = this.statusManager.findOrCreateProcess({
541
- step,
542
- type: currentProcessType,
543
- status: 'PENDING',
544
- chainId: fromChain.id,
545
- })
546
-
547
- const signedTypedData: SignedTypedData[] = [
548
- {
549
- ...relayerTypedData,
561
+ this.statusManager.updateProcess(step, process.type, 'MESSAGE_REQUIRED')
562
+ for (const typedData of intentTypedData) {
563
+ const signature = await getAction(
564
+ this.client,
565
+ signTypedData,
566
+ 'signTypedData'
567
+ )({
568
+ account: this.client.account!,
569
+ primaryType: typedData.primaryType,
570
+ domain: typedData.domain,
571
+ types: typedData.types,
572
+ message: typedData.message,
573
+ })
574
+ signedTypedData.push({
575
+ ...typedData,
550
576
  signature: signature,
551
- },
552
- ]
553
- // Add native permit if available as first element, order is important
554
- if (signedNativePermitTypedData) {
555
- signedTypedData.unshift(signedNativePermitTypedData)
577
+ })
556
578
  }
579
+
580
+ this.statusManager.updateProcess(step, process.type, 'PENDING')
581
+
557
582
  // biome-ignore lint/correctness/noUnusedVariables: destructuring
558
583
  const { execution, ...stepBase } = step
559
584
  const relayedTransaction = await relayTransaction({
@@ -570,6 +595,12 @@ export class EVMStepExecutor extends BaseStepExecutor {
570
595
  'Unable to prepare transaction. Transaction request is not found.'
571
596
  )
572
597
  }
598
+ const signedNativePermitTypedData = signedTypedData.find(
599
+ (p) =>
600
+ p.primaryType === 'Permit' &&
601
+ (p.domain.chainId === fromChain.id ||
602
+ isSaltMatchingChainId(p.domain.salt as Hex, fromChain.id))
603
+ )
573
604
  if (signedNativePermitTypedData) {
574
605
  transactionRequest.data = encodeNativePermitData(
575
606
  step.action.fromToken.address as Address,
@@ -579,6 +610,11 @@ export class EVMStepExecutor extends BaseStepExecutor {
579
610
  transactionRequest.data as Hex
580
611
  )
581
612
  } else if (permit2Supported) {
613
+ this.statusManager.updateProcess(
614
+ step,
615
+ process.type,
616
+ 'MESSAGE_REQUIRED'
617
+ )
582
618
  const permit2Signature = await signPermit2Message({
583
619
  client: this.client,
584
620
  chain: fromChain,
@@ -586,14 +622,6 @@ export class EVMStepExecutor extends BaseStepExecutor {
586
622
  amount: BigInt(step.action.fromAmount),
587
623
  data: transactionRequest.data as Hex,
588
624
  })
589
- this.statusManager.updateProcess(step, process.type, 'DONE')
590
-
591
- process = this.statusManager.findOrCreateProcess({
592
- step,
593
- type: currentProcessType,
594
- status: 'PENDING',
595
- chainId: fromChain.id,
596
- })
597
625
  transactionRequest.data = encodePermit2Data(
598
626
  step.action.fromToken.address as Address,
599
627
  BigInt(step.action.fromAmount),
@@ -602,42 +630,20 @@ export class EVMStepExecutor extends BaseStepExecutor {
602
630
  transactionRequest.data as Hex,
603
631
  permit2Signature.signature
604
632
  )
633
+ this.statusManager.updateProcess(
634
+ step,
635
+ process.type,
636
+ 'ACTION_REQUIRED'
637
+ )
605
638
  }
606
639
 
607
640
  if (signedNativePermitTypedData || permit2Supported) {
608
- // Target address should be the Permit2 proxy contract in case of native permit or Permit2
609
- transactionRequest.to = fromChain.permit2Proxy
610
- try {
611
- // Try to re-estimate the gas due to additional Permit data
612
- const estimatedGas = await getActionWithFallback(
613
- this.client,
614
- estimateGas,
615
- 'estimateGas',
616
- {
617
- account: this.client.account!,
618
- to: transactionRequest.to as Address,
619
- data: transactionRequest.data as Hex,
620
- value: transactionRequest.value,
621
- }
622
- )
623
- transactionRequest.gas =
624
- transactionRequest.gas && transactionRequest.gas > estimatedGas
625
- ? transactionRequest.gas
626
- : estimatedGas
627
- } catch (_) {
628
- // If we fail to estimate the gas, we add 80_000 gas units Permit buffer to the gas limit
629
- if (transactionRequest.gas) {
630
- transactionRequest.gas = transactionRequest.gas + 80_000n
631
- }
632
- } finally {
633
- this.statusManager.updateProcess(step, process.type, 'DONE')
634
- }
641
+ transactionRequest = await this.estimateTransactionRequest(
642
+ transactionRequest,
643
+ fromChain
644
+ )
635
645
  }
636
- process = this.statusManager.updateProcess(
637
- step,
638
- process.type,
639
- 'ACTION_REQUIRED'
640
- )
646
+
641
647
  txHash = await getAction(
642
648
  this.client,
643
649
  sendTransaction,
@@ -14,6 +14,8 @@ export const eip2612Abi = parseAbi([
14
14
  'function name() external view returns (string)',
15
15
  'function version() external view returns (string)',
16
16
  'function PERMIT_TYPEHASH() external view returns (bytes32)',
17
+ // EIP-5267
18
+ 'function eip712Domain() external view returns (bytes1, string, string, uint256, address, bytes32, uint256[])',
17
19
  ])
18
20
 
19
21
  export const approveAbi = parseAbi([