@lifi/sdk 3.4.2 → 3.5.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 (150) hide show
  1. package/package.json +2 -2
  2. package/src/_cjs/constants.js +4 -2
  3. package/src/_cjs/constants.js.map +1 -1
  4. package/src/_cjs/core/EVM/EVM.js +0 -4
  5. package/src/_cjs/core/EVM/EVM.js.map +1 -1
  6. package/src/_cjs/core/EVM/EVMStepExecutor.js +96 -74
  7. package/src/_cjs/core/EVM/EVMStepExecutor.js.map +1 -1
  8. package/src/_cjs/core/EVM/abi.js +36 -43
  9. package/src/_cjs/core/EVM/abi.js.map +1 -1
  10. package/src/_cjs/core/EVM/checkAllowance.js +36 -30
  11. package/src/_cjs/core/EVM/checkAllowance.js.map +1 -1
  12. package/src/_cjs/core/EVM/getAllowance.js.map +1 -1
  13. package/src/_cjs/core/EVM/getEVMBalance.js.map +1 -1
  14. package/src/_cjs/core/EVM/getNativePermit.js +90 -0
  15. package/src/_cjs/core/EVM/getNativePermit.js.map +1 -0
  16. package/src/_cjs/core/EVM/parseEVMErrors.js +3 -0
  17. package/src/_cjs/core/EVM/parseEVMErrors.js.map +1 -1
  18. package/src/_cjs/core/EVM/permit2/allowanceTransfer.js +100 -0
  19. package/src/_cjs/core/EVM/permit2/allowanceTransfer.js.map +1 -0
  20. package/src/_cjs/core/EVM/permit2/constants.js +12 -0
  21. package/src/_cjs/core/EVM/permit2/constants.js.map +1 -0
  22. package/src/_cjs/core/EVM/permit2/domain.js +12 -0
  23. package/src/_cjs/core/EVM/permit2/domain.js.map +1 -0
  24. package/src/_cjs/core/EVM/permit2/signatureTransfer.js +121 -0
  25. package/src/_cjs/core/EVM/permit2/signatureTransfer.js.map +1 -0
  26. package/src/_cjs/core/EVM/setAllowance.js +3 -4
  27. package/src/_cjs/core/EVM/setAllowance.js.map +1 -1
  28. package/src/_cjs/core/EVM/signPermitMessage.js +168 -0
  29. package/src/_cjs/core/EVM/signPermitMessage.js.map +1 -0
  30. package/src/_cjs/core/EVM/types.js.map +1 -1
  31. package/src/_cjs/core/EVM/utils.js +2 -1
  32. package/src/_cjs/core/EVM/utils.js.map +1 -1
  33. package/src/_cjs/core/EVM/waitForBatchTransactionReceipt.js +29 -0
  34. package/src/_cjs/core/EVM/waitForBatchTransactionReceipt.js.map +1 -0
  35. package/src/_cjs/core/checkBalance.js +3 -3
  36. package/src/_cjs/core/checkBalance.js.map +1 -1
  37. package/src/_cjs/index.js.map +1 -1
  38. package/src/_cjs/utils/invariant.js +17 -0
  39. package/src/_cjs/utils/invariant.js.map +1 -0
  40. package/src/_cjs/version.js +1 -1
  41. package/src/_cjs/version.js.map +1 -1
  42. package/src/_esm/constants.js +3 -1
  43. package/src/_esm/constants.js.map +1 -1
  44. package/src/_esm/core/EVM/EVM.js +0 -4
  45. package/src/_esm/core/EVM/EVM.js.map +1 -1
  46. package/src/_esm/core/EVM/EVMStepExecutor.js +124 -84
  47. package/src/_esm/core/EVM/EVMStepExecutor.js.map +1 -1
  48. package/src/_esm/core/EVM/abi.js +37 -42
  49. package/src/_esm/core/EVM/abi.js.map +1 -1
  50. package/src/_esm/core/EVM/checkAllowance.js +41 -32
  51. package/src/_esm/core/EVM/checkAllowance.js.map +1 -1
  52. package/src/_esm/core/EVM/getAllowance.js.map +1 -1
  53. package/src/_esm/core/EVM/getEVMBalance.js.map +1 -1
  54. package/src/_esm/core/EVM/getNativePermit.js +95 -0
  55. package/src/_esm/core/EVM/getNativePermit.js.map +1 -0
  56. package/src/_esm/core/EVM/parseEVMErrors.js +8 -0
  57. package/src/_esm/core/EVM/parseEVMErrors.js.map +1 -1
  58. package/src/_esm/core/EVM/permit2/allowanceTransfer.js +93 -0
  59. package/src/_esm/core/EVM/permit2/allowanceTransfer.js.map +1 -0
  60. package/src/_esm/core/EVM/permit2/constants.js +9 -0
  61. package/src/_esm/core/EVM/permit2/constants.js.map +1 -0
  62. package/src/_esm/core/EVM/permit2/domain.js +9 -0
  63. package/src/_esm/core/EVM/permit2/domain.js.map +1 -0
  64. package/src/_esm/core/EVM/permit2/signatureTransfer.js +117 -0
  65. package/src/_esm/core/EVM/permit2/signatureTransfer.js.map +1 -0
  66. package/src/_esm/core/EVM/setAllowance.js +3 -4
  67. package/src/_esm/core/EVM/setAllowance.js.map +1 -1
  68. package/src/_esm/core/EVM/signPermitMessage.js +162 -0
  69. package/src/_esm/core/EVM/signPermitMessage.js.map +1 -0
  70. package/src/_esm/core/EVM/types.js.map +1 -1
  71. package/src/_esm/core/EVM/utils.js +2 -1
  72. package/src/_esm/core/EVM/utils.js.map +1 -1
  73. package/src/_esm/core/EVM/waitForBatchTransactionReceipt.js +25 -0
  74. package/src/_esm/core/EVM/waitForBatchTransactionReceipt.js.map +1 -0
  75. package/src/_esm/core/checkBalance.js +3 -3
  76. package/src/_esm/core/checkBalance.js.map +1 -1
  77. package/src/_esm/index.js.map +1 -1
  78. package/src/_esm/utils/invariant.js +43 -0
  79. package/src/_esm/utils/invariant.js.map +1 -0
  80. package/src/_esm/version.js +1 -1
  81. package/src/_esm/version.js.map +1 -1
  82. package/src/_types/constants.d.ts +3 -1
  83. package/src/_types/constants.d.ts.map +1 -1
  84. package/src/_types/core/EVM/EVM.d.ts.map +1 -1
  85. package/src/_types/core/EVM/EVMStepExecutor.d.ts +7 -4
  86. package/src/_types/core/EVM/EVMStepExecutor.d.ts.map +1 -1
  87. package/src/_types/core/EVM/abi.d.ts +202 -5
  88. package/src/_types/core/EVM/abi.d.ts.map +1 -1
  89. package/src/_types/core/EVM/checkAllowance.d.ts +12 -2
  90. package/src/_types/core/EVM/checkAllowance.d.ts.map +1 -1
  91. package/src/_types/core/EVM/getAllowance.d.ts +5 -4
  92. package/src/_types/core/EVM/getAllowance.d.ts.map +1 -1
  93. package/src/_types/core/EVM/getEVMBalance.d.ts +2 -1
  94. package/src/_types/core/EVM/getEVMBalance.d.ts.map +1 -1
  95. package/src/_types/core/EVM/getNativePermit.d.ts +18 -0
  96. package/src/_types/core/EVM/getNativePermit.d.ts.map +1 -0
  97. package/src/_types/core/EVM/permit2/allowanceTransfer.d.ts +41 -0
  98. package/src/_types/core/EVM/permit2/allowanceTransfer.d.ts.map +1 -0
  99. package/src/_types/core/EVM/permit2/constants.d.ts +8 -0
  100. package/src/_types/core/EVM/permit2/constants.d.ts.map +1 -0
  101. package/src/_types/core/EVM/permit2/domain.d.ts +8 -0
  102. package/src/_types/core/EVM/permit2/domain.d.ts.map +1 -0
  103. package/src/_types/core/EVM/permit2/signatureTransfer.d.ts +42 -0
  104. package/src/_types/core/EVM/permit2/signatureTransfer.d.ts.map +1 -0
  105. package/src/_types/core/EVM/setAllowance.d.ts +2 -2
  106. package/src/_types/core/EVM/setAllowance.d.ts.map +1 -1
  107. package/src/_types/core/EVM/signPermitMessage.d.ts +22 -0
  108. package/src/_types/core/EVM/signPermitMessage.d.ts.map +1 -0
  109. package/src/_types/core/EVM/types.d.ts +1 -18
  110. package/src/_types/core/EVM/types.d.ts.map +1 -1
  111. package/src/_types/core/EVM/utils.d.ts +2 -2
  112. package/src/_types/core/EVM/utils.d.ts.map +1 -1
  113. package/src/_types/core/EVM/waitForBatchTransactionReceipt.d.ts +4 -0
  114. package/src/_types/core/EVM/waitForBatchTransactionReceipt.d.ts.map +1 -0
  115. package/src/_types/core/checkBalance.d.ts.map +1 -1
  116. package/src/_types/index.d.ts +1 -1
  117. package/src/_types/index.d.ts.map +1 -1
  118. package/src/_types/utils/invariant.d.ts +22 -0
  119. package/src/_types/utils/invariant.d.ts.map +1 -0
  120. package/src/_types/version.d.ts +1 -1
  121. package/src/_types/version.d.ts.map +1 -1
  122. package/src/constants.ts +6 -1
  123. package/src/core/EVM/EVM.ts +0 -4
  124. package/src/core/EVM/EVMStepExecutor.ts +177 -134
  125. package/src/core/EVM/abi.ts +40 -43
  126. package/src/core/EVM/checkAllowance.ts +96 -92
  127. package/src/core/EVM/getAllowance.ts +8 -8
  128. package/src/core/EVM/getEVMBalance.ts +2 -2
  129. package/src/core/EVM/getNativePermit.ts +113 -0
  130. package/src/core/EVM/parseEVMErrors.ts +8 -0
  131. package/src/core/EVM/permit2/allowanceTransfer.ts +168 -0
  132. package/src/core/EVM/permit2/constants.ts +11 -0
  133. package/src/core/EVM/permit2/domain.ts +20 -0
  134. package/src/core/EVM/permit2/signatureTransfer.ts +214 -0
  135. package/src/core/EVM/setAllowance.ts +14 -15
  136. package/src/core/EVM/signPermitMessage.ts +248 -0
  137. package/src/core/EVM/types.ts +1 -27
  138. package/src/core/EVM/utils.ts +4 -3
  139. package/src/core/EVM/waitForBatchTransactionReceipt.ts +49 -0
  140. package/src/core/checkBalance.ts +6 -3
  141. package/src/index.ts +0 -3
  142. package/src/utils/invariant.ts +51 -0
  143. package/src/version.ts +1 -1
  144. package/src/_cjs/core/EVM/multisig.js +0 -29
  145. package/src/_cjs/core/EVM/multisig.js.map +0 -1
  146. package/src/_esm/core/EVM/multisig.js +0 -25
  147. package/src/_esm/core/EVM/multisig.js.map +0 -1
  148. package/src/_types/core/EVM/multisig.d.ts +0 -6
  149. package/src/_types/core/EVM/multisig.d.ts.map +0 -1
  150. package/src/core/EVM/multisig.ts +0 -54
@@ -1,5 +1,6 @@
1
- import type { Chain, LiFiStep, Process, ProcessType } from '@lifi/types'
1
+ import type { ExtendedChain, LiFiStep, Process, ProcessType } from '@lifi/types'
2
2
  import type { Address, Client, Hash } from 'viem'
3
+ import { MaxUint256 } from '../../constants.js'
3
4
  import type { StatusManager } from '../StatusManager.js'
4
5
  import type { ExecutionOptions } from '../types.js'
5
6
  import { getAllowance } from './getAllowance.js'
@@ -7,108 +8,107 @@ import { parseEVMErrors } from './parseEVMErrors.js'
7
8
  import { setAllowance } from './setAllowance.js'
8
9
  import { waitForTransactionReceipt } from './waitForTransactionReceipt.js'
9
10
 
10
- export const checkAllowance = async (
11
- client: Client,
12
- chain: Chain,
13
- step: LiFiStep,
14
- statusManager: StatusManager,
15
- settings?: ExecutionOptions,
11
+ export type CheckAllowanceParams = {
12
+ client: Client
13
+ chain: ExtendedChain
14
+ step: LiFiStep
15
+ statusManager: StatusManager
16
+ executionOptions?: ExecutionOptions
17
+ allowUserInteraction?: boolean
18
+ atomicBatchSupported?: boolean
19
+ permit2Supported?: boolean
20
+ }
21
+
22
+ export const checkAllowance = async ({
23
+ client,
24
+ chain,
25
+ step,
26
+ statusManager,
27
+ executionOptions,
16
28
  allowUserInteraction = false,
17
- shouldBatchTransactions = false
18
- ): Promise<Hash | void> => {
19
- // Ask the user to set an allowance
20
- let allowanceProcess: Process = statusManager.findOrCreateProcess({
29
+ atomicBatchSupported = false,
30
+ permit2Supported = false,
31
+ }: CheckAllowanceParams): Promise<Hash | void> => {
32
+ // Find existing or create new allowance process
33
+ const allowanceProcess: Process = statusManager.findOrCreateProcess({
21
34
  step,
22
35
  type: 'TOKEN_ALLOWANCE',
23
36
  chainId: step.action.fromChainId,
24
37
  })
25
38
 
26
- // Check allowance
27
39
  try {
40
+ // Handle existing pending transaction
28
41
  if (allowanceProcess.txHash && allowanceProcess.status !== 'DONE') {
29
42
  await waitForApprovalTransaction(
30
43
  client,
31
- allowanceProcess.txHash! as Address,
44
+ allowanceProcess.txHash as Address,
32
45
  allowanceProcess.type,
33
46
  step,
34
47
  chain,
35
48
  statusManager
36
49
  )
37
- } else {
38
- allowanceProcess = statusManager.updateProcess(
39
- step,
40
- allowanceProcess.type,
41
- 'STARTED'
42
- )
50
+ return
51
+ }
43
52
 
44
- const approved = await getAllowance(
45
- chain.id,
46
- step.action.fromToken.address,
47
- client.account!.address,
48
- step.estimate.approvalAddress
49
- )
53
+ // Start new allowance check
54
+ statusManager.updateProcess(step, allowanceProcess.type, 'STARTED')
50
55
 
51
- const fromAmount = BigInt(step.action.fromAmount)
52
-
53
- if (fromAmount > approved) {
54
- if (!allowUserInteraction) {
55
- return
56
- }
57
-
58
- if (shouldBatchTransactions) {
59
- const approveTxHash = await setAllowance(
60
- client,
61
- step.action.fromToken.address,
62
- step.estimate.approvalAddress,
63
- fromAmount,
64
- settings,
65
- true
66
- )
67
-
68
- allowanceProcess = statusManager.updateProcess(
69
- step,
70
- allowanceProcess.type,
71
- 'DONE'
72
- )
73
-
74
- return approveTxHash
75
- }
76
-
77
- const approveTxHash = await setAllowance(
78
- client,
79
- step.action.fromToken.address,
80
- step.estimate.approvalAddress,
81
- fromAmount
82
- )
83
- await waitForApprovalTransaction(
84
- client,
85
- approveTxHash,
86
- allowanceProcess.type,
87
- step,
88
- chain,
89
- statusManager
90
- )
91
- } else {
92
- allowanceProcess = statusManager.updateProcess(
93
- step,
94
- allowanceProcess.type,
95
- 'DONE'
96
- )
97
- }
56
+ const spenderAddress = permit2Supported
57
+ ? chain.permit2
58
+ : step.estimate.approvalAddress
59
+ const fromAmount = BigInt(step.action.fromAmount)
60
+
61
+ const approved = await getAllowance(
62
+ chain.id,
63
+ step.action.fromToken.address as Address,
64
+ client.account!.address,
65
+ spenderAddress as Address
66
+ )
67
+
68
+ // Return early if already approved
69
+ if (fromAmount <= approved) {
70
+ statusManager.updateProcess(step, allowanceProcess.type, 'DONE')
71
+ return
98
72
  }
99
- } catch (e: any) {
100
- const error = await parseEVMErrors(e, step, allowanceProcess)
101
- allowanceProcess = statusManager.updateProcess(
102
- step,
73
+
74
+ if (!allowUserInteraction) {
75
+ return
76
+ }
77
+
78
+ statusManager.updateProcess(step, allowanceProcess.type, 'ACTION_REQUIRED')
79
+
80
+ // Set new allowance
81
+ const approveAmount = permit2Supported ? MaxUint256 : fromAmount
82
+ const approveTxHash = await setAllowance(
83
+ client,
84
+ step.action.fromToken.address as Address,
85
+ spenderAddress as Address,
86
+ approveAmount,
87
+ executionOptions,
88
+ atomicBatchSupported
89
+ )
90
+
91
+ if (atomicBatchSupported) {
92
+ statusManager.updateProcess(step, allowanceProcess.type, 'DONE')
93
+ return approveTxHash
94
+ }
95
+
96
+ await waitForApprovalTransaction(
97
+ client,
98
+ approveTxHash,
103
99
  allowanceProcess.type,
104
- 'FAILED',
105
- {
106
- error: {
107
- message: error.cause.message,
108
- code: error.code,
109
- },
110
- }
100
+ step,
101
+ chain,
102
+ statusManager
111
103
  )
104
+ } catch (e: any) {
105
+ const error = await parseEVMErrors(e, step, allowanceProcess)
106
+ statusManager.updateProcess(step, allowanceProcess.type, 'FAILED', {
107
+ error: {
108
+ message: error.cause.message,
109
+ code: error.code,
110
+ },
111
+ })
112
112
  statusManager.updateExecution(step, 'FAILED')
113
113
  throw error
114
114
  }
@@ -119,29 +119,33 @@ const waitForApprovalTransaction = async (
119
119
  txHash: Hash,
120
120
  processType: ProcessType,
121
121
  step: LiFiStep,
122
- chain: Chain,
122
+ chain: ExtendedChain,
123
123
  statusManager: StatusManager
124
124
  ) => {
125
+ const baseExplorerUrl = chain.metamask.blockExplorerUrls[0]
126
+ const getTxLink = (hash: Hash) => `${baseExplorerUrl}tx/${hash}`
127
+
125
128
  statusManager.updateProcess(step, processType, 'PENDING', {
126
129
  txHash,
127
- txLink: `${chain.metamask.blockExplorerUrls[0]}tx/${txHash}`,
130
+ txLink: getTxLink(txHash),
128
131
  })
129
132
 
130
133
  const transactionReceipt = await waitForTransactionReceipt({
131
- client: client,
134
+ client,
132
135
  chainId: chain.id,
133
- txHash: txHash,
136
+ txHash,
134
137
  onReplaced(response) {
138
+ const newHash = response.transaction.hash
135
139
  statusManager.updateProcess(step, processType, 'PENDING', {
136
- txHash: response.transaction.hash,
137
- txLink: `${chain.metamask.blockExplorerUrls[0]}tx/${response.transaction.hash}`,
140
+ txHash: newHash,
141
+ txLink: getTxLink(newHash),
138
142
  })
139
143
  },
140
144
  })
141
145
 
142
- const transactionHash = transactionReceipt?.transactionHash || txHash
146
+ const finalHash = transactionReceipt?.transactionHash || txHash
143
147
  statusManager.updateProcess(step, processType, 'DONE', {
144
- txHash: transactionHash,
145
- txLink: `${chain.metamask.blockExplorerUrls[0]}tx/${transactionHash}`,
148
+ txHash: finalHash,
149
+ txLink: getTxLink(finalHash),
146
150
  })
147
151
  }
@@ -13,9 +13,9 @@ import { getMulticallAddress } from './utils.js'
13
13
 
14
14
  export const getAllowance = async (
15
15
  chainId: ChainId,
16
- tokenAddress: string,
17
- ownerAddress: string,
18
- spenderAddress: string
16
+ tokenAddress: Address,
17
+ ownerAddress: Address,
18
+ spenderAddress: Address
19
19
  ): Promise<bigint> => {
20
20
  const client = await getPublicClient(chainId)
21
21
  try {
@@ -34,7 +34,7 @@ export const getAllowance = async (
34
34
  export const getAllowanceMulticall = async (
35
35
  chainId: ChainId,
36
36
  tokens: TokenSpender[],
37
- ownerAddress: string
37
+ ownerAddress: Address
38
38
  ): Promise<TokenSpenderAllowance[]> => {
39
39
  if (!tokens.length) {
40
40
  return []
@@ -80,8 +80,8 @@ export const getAllowanceMulticall = async (
80
80
  */
81
81
  export const getTokenAllowance = async (
82
82
  token: BaseToken,
83
- ownerAddress: string,
84
- spenderAddress: string
83
+ ownerAddress: Address,
84
+ spenderAddress: Address
85
85
  ): Promise<bigint | undefined> => {
86
86
  // native token don't need approval
87
87
  if (isNativeTokenAddress(token.address)) {
@@ -90,7 +90,7 @@ export const getTokenAllowance = async (
90
90
 
91
91
  const approved = await getAllowance(
92
92
  token.chainId,
93
- token.address,
93
+ token.address as Address,
94
94
  ownerAddress,
95
95
  spenderAddress
96
96
  )
@@ -104,7 +104,7 @@ export const getTokenAllowance = async (
104
104
  * @returns Returns array of tokens and their allowance
105
105
  */
106
106
  export const getTokenAllowanceMulticall = async (
107
- ownerAddress: string,
107
+ ownerAddress: Address,
108
108
  tokens: TokenSpender[]
109
109
  ): Promise<TokenAllowance[]> => {
110
110
  // filter out native tokens
@@ -12,7 +12,7 @@ import { getPublicClient } from './publicClient.js'
12
12
  import { getMulticallAddress } from './utils.js'
13
13
 
14
14
  export const getEVMBalance = async (
15
- walletAddress: string,
15
+ walletAddress: Address,
16
16
  tokens: Token[]
17
17
  ): Promise<TokenAmount[]> => {
18
18
  if (tokens.length === 0) {
@@ -85,7 +85,7 @@ const getEVMBalanceMulticall = async (
85
85
  const getEVMBalanceDefault = async (
86
86
  chainId: ChainId,
87
87
  tokens: Token[],
88
- walletAddress: string
88
+ walletAddress: Address
89
89
  ): Promise<TokenAmount[]> => {
90
90
  const client = await getPublicClient(chainId)
91
91
  const blockNumber = await getBlockNumber(client)
@@ -0,0 +1,113 @@
1
+ import type { ExtendedChain } from '@lifi/types'
2
+ import type { Address, Client } from 'viem'
3
+ import { multicall, readContract } from 'viem/actions'
4
+ import { eip2612Abi } from './abi.js'
5
+ import { getMulticallAddress } from './utils.js'
6
+
7
+ export type NativePermitData = {
8
+ name: string
9
+ version: string
10
+ nonce: bigint
11
+ supported: boolean
12
+ }
13
+ /**
14
+ * Retrieves native permit data (EIP-2612) for a token on a specific chain
15
+ * @link https://eips.ethereum.org/EIPS/eip-2612
16
+ * @param client - The Viem client instance
17
+ * @param chain - The extended chain object containing chain details
18
+ * @param tokenAddress - The address of the token to check for permit support
19
+ * @returns {Promise<NativePermitData>} Object containing permit data including name, version, nonce and support status
20
+ */
21
+ export const getNativePermit = async (
22
+ client: Client,
23
+ chain: ExtendedChain,
24
+ tokenAddress: Address
25
+ ): Promise<NativePermitData> => {
26
+ try {
27
+ const multicallAddress = await getMulticallAddress(chain.id)
28
+
29
+ if (multicallAddress) {
30
+ const [nameResult, domainSeparatorResult, noncesResult, versionResult] =
31
+ await multicall(client, {
32
+ contracts: [
33
+ {
34
+ address: tokenAddress,
35
+ abi: eip2612Abi,
36
+ functionName: 'name',
37
+ },
38
+ {
39
+ address: tokenAddress,
40
+ abi: eip2612Abi,
41
+ functionName: 'DOMAIN_SEPARATOR',
42
+ },
43
+ {
44
+ address: tokenAddress,
45
+ abi: eip2612Abi,
46
+ functionName: 'nonces',
47
+ args: [client.account!.address],
48
+ },
49
+ {
50
+ address: tokenAddress,
51
+ abi: eip2612Abi,
52
+ functionName: 'version',
53
+ },
54
+ ],
55
+ multicallAddress,
56
+ })
57
+
58
+ const supported =
59
+ nameResult.status === 'success' &&
60
+ domainSeparatorResult.status === 'success' &&
61
+ noncesResult.status === 'success' &&
62
+ !!nameResult.result &&
63
+ !!domainSeparatorResult.result &&
64
+ noncesResult.result !== undefined
65
+
66
+ return {
67
+ name: nameResult.result!,
68
+ version: versionResult.result ?? '1',
69
+ nonce: noncesResult.result!,
70
+ supported,
71
+ }
72
+ }
73
+
74
+ // Fallback to individual calls
75
+ const [name, domainSeparator, nonce, version] = await Promise.all([
76
+ readContract(client, {
77
+ address: tokenAddress,
78
+ abi: eip2612Abi,
79
+ functionName: 'name',
80
+ }),
81
+ readContract(client, {
82
+ address: tokenAddress,
83
+ abi: eip2612Abi,
84
+ functionName: 'DOMAIN_SEPARATOR',
85
+ }),
86
+ readContract(client, {
87
+ address: tokenAddress,
88
+ abi: eip2612Abi,
89
+ functionName: 'nonces',
90
+ args: [client.account!.address],
91
+ }),
92
+ readContract(client, {
93
+ address: tokenAddress,
94
+ abi: eip2612Abi,
95
+ functionName: 'version',
96
+ }),
97
+ ])
98
+
99
+ return {
100
+ name,
101
+ version: version ?? '1',
102
+ nonce,
103
+ supported: !!name && !!domainSeparator && nonce !== undefined,
104
+ }
105
+ } catch {
106
+ return {
107
+ name: '',
108
+ version: '1',
109
+ nonce: 0n,
110
+ supported: false,
111
+ }
112
+ }
113
+ }
@@ -29,6 +29,14 @@ const handleSpecificErrors = async (
29
29
  if (e.cause?.name === 'UserRejectedRequestError') {
30
30
  return new TransactionError(LiFiErrorCode.SignatureRejected, e.message, e)
31
31
  }
32
+ // Safe Wallet via WalletConnect returns -32000 code when user rejects the signature
33
+ // {
34
+ // code: -32000,
35
+ // message: 'User rejected transaction',
36
+ // }
37
+ if (e.cause?.code === -32000) {
38
+ return new TransactionError(LiFiErrorCode.SignatureRejected, e.message, e)
39
+ }
32
40
 
33
41
  if (
34
42
  step &&
@@ -0,0 +1,168 @@
1
+ import {
2
+ type Address,
3
+ type TypedData,
4
+ type TypedDataDomain,
5
+ hashTypedData,
6
+ } from 'viem'
7
+ import { MaxUint48, MaxUint160 } from '../../../constants.js'
8
+ import { invariant } from '../../../utils/invariant.js'
9
+ import { MaxSigDeadline } from './constants.js'
10
+ import { permit2Domain } from './domain.js'
11
+
12
+ export const MaxAllowanceTransferAmount = MaxUint160
13
+ export const MaxAllowanceExpiration = MaxUint48
14
+ export const MaxOrderedNonce = MaxUint48
15
+
16
+ export interface PermitDetails {
17
+ token: Address
18
+ amount: bigint
19
+ expiration: number
20
+ nonce: number
21
+ }
22
+
23
+ export interface PermitSingle {
24
+ details: PermitDetails
25
+ spender: Address
26
+ sigDeadline: bigint
27
+ }
28
+
29
+ export interface PermitBatch {
30
+ details: PermitDetails[]
31
+ spender: Address
32
+ sigDeadline: bigint
33
+ }
34
+
35
+ export type PermitSingleData = {
36
+ domain: TypedDataDomain
37
+ types: TypedData
38
+ values: PermitSingle
39
+ }
40
+
41
+ export type PermitBatchData = {
42
+ domain: TypedDataDomain
43
+ types: TypedData
44
+ values: PermitBatch
45
+ }
46
+
47
+ const PERMIT_DETAILS = [
48
+ { name: 'token', type: 'address' },
49
+ { name: 'amount', type: 'uint160' },
50
+ { name: 'expiration', type: 'uint48' },
51
+ { name: 'nonce', type: 'uint48' },
52
+ ] as const
53
+
54
+ const PERMIT_TYPES = {
55
+ PermitDetails: PERMIT_DETAILS,
56
+ PermitSingle: [
57
+ { name: 'details', type: 'PermitDetails' },
58
+ { name: 'spender', type: 'address' },
59
+ { name: 'sigDeadline', type: 'uint256' },
60
+ ],
61
+ } as const
62
+
63
+ const PERMIT_BATCH_TYPES = {
64
+ PermitDetails: PERMIT_DETAILS,
65
+ PermitBatch: [
66
+ { name: 'details', type: 'PermitDetails[]' },
67
+ { name: 'spender', type: 'address' },
68
+ { name: 'sigDeadline', type: 'uint256' },
69
+ ],
70
+ } as const
71
+
72
+ function isPermit(permit: PermitSingle | PermitBatch): permit is PermitSingle {
73
+ return !Array.isArray(permit.details)
74
+ }
75
+
76
+ export function getPermitSingleData(
77
+ permit: PermitSingle,
78
+ permit2Address: Address,
79
+ chainId: number
80
+ ) {
81
+ invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
82
+
83
+ const domain = permit2Domain(permit2Address, chainId)
84
+ validatePermitDetails(permit.details)
85
+
86
+ return {
87
+ domain,
88
+ values: permit,
89
+ }
90
+ }
91
+
92
+ export function getPermitBatchData(
93
+ permit: PermitBatch,
94
+ permit2Address: Address,
95
+ chainId: number
96
+ ) {
97
+ invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
98
+
99
+ const domain = permit2Domain(permit2Address, chainId)
100
+ permit.details.forEach(validatePermitDetails)
101
+
102
+ return {
103
+ domain,
104
+ values: permit,
105
+ }
106
+ }
107
+
108
+ export function getPermitData(
109
+ permit: PermitSingle | PermitBatch,
110
+ permit2Address: Address,
111
+ chainId: number
112
+ ): PermitSingleData | PermitBatchData {
113
+ invariant(MaxSigDeadline >= permit.sigDeadline, 'SIG_DEADLINE_OUT_OF_RANGE')
114
+
115
+ const domain = permit2Domain(permit2Address, chainId)
116
+ if (isPermit(permit)) {
117
+ validatePermitDetails(permit.details)
118
+ return {
119
+ domain,
120
+ types: PERMIT_TYPES,
121
+ values: permit,
122
+ }
123
+ }
124
+ permit.details.forEach(validatePermitDetails)
125
+ return {
126
+ domain,
127
+ types: PERMIT_BATCH_TYPES,
128
+ values: permit,
129
+ }
130
+ }
131
+
132
+ export function hash(
133
+ permit: PermitSingle | PermitBatch,
134
+ permit2Address: Address,
135
+ chainId: number
136
+ ): string {
137
+ if (isPermit(permit)) {
138
+ const { domain, values } = getPermitSingleData(
139
+ permit,
140
+ permit2Address,
141
+ chainId
142
+ )
143
+
144
+ return hashTypedData({
145
+ domain,
146
+ types: PERMIT_TYPES,
147
+ primaryType: 'PermitSingle',
148
+ message: values,
149
+ })
150
+ }
151
+ const { domain, values } = getPermitBatchData(permit, permit2Address, chainId)
152
+
153
+ return hashTypedData({
154
+ domain,
155
+ types: PERMIT_BATCH_TYPES,
156
+ primaryType: 'PermitBatch',
157
+ message: values,
158
+ })
159
+ }
160
+
161
+ function validatePermitDetails(details: PermitDetails) {
162
+ invariant(MaxOrderedNonce >= details.nonce, 'NONCE_OUT_OF_RANGE')
163
+ invariant(MaxAllowanceTransferAmount >= details.amount, 'AMOUNT_OUT_OF_RANGE')
164
+ invariant(
165
+ MaxAllowanceExpiration >= details.expiration,
166
+ 'EXPIRATION_OUT_OF_RANGE'
167
+ )
168
+ }
@@ -0,0 +1,11 @@
1
+ import { MaxUint48, MaxUint160, MaxUint256 } from '../../../constants.js'
2
+
3
+ export const MaxAllowanceTransferAmount = MaxUint160
4
+ export const MaxAllowanceExpiration = MaxUint48
5
+ export const MaxOrderedNonce = MaxUint48
6
+
7
+ export const MaxSignatureTransferAmount = MaxUint256
8
+ export const MaxUnorderedNonce = MaxUint256
9
+ export const MaxSigDeadline = MaxUint256
10
+
11
+ export const InstantExpiration = 0n
@@ -0,0 +1,20 @@
1
+ import type { Address, TypedData, TypedDataDomain } from 'viem'
2
+
3
+ const PERMIT2_DOMAIN_NAME = 'Permit2'
4
+
5
+ export function permit2Domain(
6
+ permit2Address: Address,
7
+ chainId: number
8
+ ): TypedDataDomain {
9
+ return {
10
+ name: PERMIT2_DOMAIN_NAME,
11
+ chainId,
12
+ verifyingContract: permit2Address,
13
+ }
14
+ }
15
+
16
+ export type PermitData = {
17
+ domain: TypedDataDomain
18
+ types: TypedData
19
+ values: any
20
+ }