@lifi/sdk 3.10.1 → 3.11.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 (89) hide show
  1. package/package.json +6 -6
  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/services/api.js +6 -5
  23. package/src/_cjs/services/api.js.map +1 -1
  24. package/src/_cjs/services/balance.js +9 -9
  25. package/src/_cjs/services/balance.js.map +1 -1
  26. package/src/_cjs/version.js +1 -1
  27. package/src/_esm/core/EVM/EVMStepExecutor.js +136 -148
  28. package/src/_esm/core/EVM/EVMStepExecutor.js.map +1 -1
  29. package/src/_esm/core/EVM/abi.js +2 -0
  30. package/src/_esm/core/EVM/abi.js.map +1 -1
  31. package/src/_esm/core/EVM/checkAllowance.js +141 -45
  32. package/src/_esm/core/EVM/checkAllowance.js.map +1 -1
  33. package/src/_esm/core/EVM/permits/getNativePermit.js +144 -21
  34. package/src/_esm/core/EVM/permits/getNativePermit.js.map +1 -1
  35. package/src/_esm/core/EVM/permits/isNativePermitValid.js +41 -0
  36. package/src/_esm/core/EVM/permits/isNativePermitValid.js.map +1 -0
  37. package/src/_esm/core/EVM/switchChain.js +8 -15
  38. package/src/_esm/core/EVM/switchChain.js.map +1 -1
  39. package/src/_esm/core/EVM/utils.js +12 -0
  40. package/src/_esm/core/EVM/utils.js.map +1 -1
  41. package/src/_esm/core/execution.js +1 -1
  42. package/src/_esm/core/execution.js.map +1 -1
  43. package/src/_esm/core/prepareRestart.js +6 -3
  44. package/src/_esm/core/prepareRestart.js.map +1 -1
  45. package/src/_esm/core/processMessages.js +4 -8
  46. package/src/_esm/core/processMessages.js.map +1 -1
  47. package/src/_esm/services/api.js +3 -8
  48. package/src/_esm/services/api.js.map +1 -1
  49. package/src/_esm/services/balance.js +4 -18
  50. package/src/_esm/services/balance.js.map +1 -1
  51. package/src/_esm/version.js +1 -1
  52. package/src/_types/core/EVM/EVMStepExecutor.d.ts +3 -2
  53. package/src/_types/core/EVM/EVMStepExecutor.d.ts.map +1 -1
  54. package/src/_types/core/EVM/abi.d.ts +20 -0
  55. package/src/_types/core/EVM/abi.d.ts.map +1 -1
  56. package/src/_types/core/EVM/checkAllowance.d.ts +10 -7
  57. package/src/_types/core/EVM/checkAllowance.d.ts.map +1 -1
  58. package/src/_types/core/EVM/permits/getNativePermit.d.ts +2 -2
  59. package/src/_types/core/EVM/permits/getNativePermit.d.ts.map +1 -1
  60. package/src/_types/core/EVM/permits/isNativePermitValid.d.ts +6 -0
  61. package/src/_types/core/EVM/permits/isNativePermitValid.d.ts.map +1 -0
  62. package/src/_types/core/EVM/switchChain.d.ts +2 -2
  63. package/src/_types/core/EVM/switchChain.d.ts.map +1 -1
  64. package/src/_types/core/EVM/utils.d.ts +6 -1
  65. package/src/_types/core/EVM/utils.d.ts.map +1 -1
  66. package/src/_types/core/prepareRestart.d.ts +1 -1
  67. package/src/_types/core/prepareRestart.d.ts.map +1 -1
  68. package/src/_types/core/processMessages.d.ts.map +1 -1
  69. package/src/_types/core/types.d.ts +2 -2
  70. package/src/_types/core/types.d.ts.map +1 -1
  71. package/src/_types/services/api.d.ts +7 -2
  72. package/src/_types/services/api.d.ts.map +1 -1
  73. package/src/_types/services/balance.d.ts +5 -5
  74. package/src/_types/services/balance.d.ts.map +1 -1
  75. package/src/_types/version.d.ts +1 -1
  76. package/src/core/EVM/EVMStepExecutor.ts +203 -197
  77. package/src/core/EVM/abi.ts +2 -0
  78. package/src/core/EVM/checkAllowance.ts +206 -63
  79. package/src/core/EVM/permits/getNativePermit.ts +189 -22
  80. package/src/core/EVM/permits/isNativePermitValid.ts +57 -0
  81. package/src/core/EVM/switchChain.ts +14 -22
  82. package/src/core/EVM/utils.ts +17 -1
  83. package/src/core/execution.ts +1 -1
  84. package/src/core/prepareRestart.ts +6 -3
  85. package/src/core/processMessages.ts +4 -8
  86. package/src/core/types.ts +1 -2
  87. package/src/services/api.ts +17 -8
  88. package/src/services/balance.ts +20 -8
  89. package/src/version.ts +1 -1
@@ -0,0 +1,57 @@
1
+ import type { SignedTypedData, TypedData } from '@lifi/types'
2
+
3
+ /**
4
+ * Checks if an existing native permit is valid for the given requirements
5
+ */
6
+ export const isNativePermitValid = (
7
+ permit: SignedTypedData,
8
+ typedData: TypedData
9
+ ): boolean => {
10
+ // Only check native permits (EIP-2612)
11
+ if (permit.primaryType !== 'Permit') {
12
+ return false
13
+ }
14
+
15
+ // Check if the permit is for the correct chain
16
+ if (
17
+ permit.domain.chainId !== typedData.domain.chainId ||
18
+ (permit.domain.salt && permit.domain.salt !== typedData.domain.salt)
19
+ ) {
20
+ return false
21
+ }
22
+
23
+ // Check if the permit message has the required fields
24
+ const message = permit.message as any
25
+ if (!message) {
26
+ return false
27
+ }
28
+
29
+ // Check spender
30
+ if (
31
+ message.spender?.toLowerCase() !== typedData.message.spender?.toLowerCase()
32
+ ) {
33
+ return false
34
+ }
35
+
36
+ // Check owner
37
+ if (message.owner?.toLowerCase() !== typedData.message.owner?.toLowerCase()) {
38
+ return false
39
+ }
40
+
41
+ // Check amount (value field in native permits)
42
+ const permitAmount = BigInt(message.value || 0)
43
+ if (permitAmount < BigInt(typedData.message.value || 0)) {
44
+ return false
45
+ }
46
+
47
+ // Check deadline (must have at least 5 minutes remaining)
48
+ const deadlineTimestamp = parseInt(message.deadline || 0, 10)
49
+ // Add 5 minutes bufferto the current timestamp
50
+ const allowedTimestamp = Math.floor(Date.now() / 1000) + 5 * 60
51
+
52
+ if (deadlineTimestamp <= allowedTimestamp) {
53
+ return false
54
+ }
55
+
56
+ return true
57
+ }
@@ -4,7 +4,7 @@ import { getAction } from 'viem/utils'
4
4
  import { LiFiErrorCode } from '../../errors/constants.js'
5
5
  import { ProviderError } from '../../errors/errors.js'
6
6
  import type { StatusManager } from '../StatusManager.js'
7
- import type { LiFiStepExtended, SwitchChainHook } from '../types.js'
7
+ import type { ExecutionOptions, LiFiStepExtended, Process } from '../types.js'
8
8
 
9
9
  /**
10
10
  * This method checks whether the wallet client is configured for the correct chain.
@@ -27,8 +27,10 @@ export const switchChain = async (
27
27
  client: Client,
28
28
  statusManager: StatusManager,
29
29
  step: LiFiStepExtended,
30
+ process: Process,
31
+ targetChainId: number,
30
32
  allowUserInteraction: boolean,
31
- switchChainHook?: SwitchChainHook
33
+ executionOptions?: ExecutionOptions
32
34
  ): Promise<Client | undefined> => {
33
35
  // if we are already on the correct chain we can proceed directly
34
36
  const currentChainId = (await getAction(
@@ -36,26 +38,22 @@ export const switchChain = async (
36
38
  getChainId,
37
39
  'getChainId'
38
40
  )(undefined)) as GetChainIdReturnType
39
- if (currentChainId === step.action.fromChainId) {
41
+ if (currentChainId === targetChainId) {
40
42
  return client
41
43
  }
42
44
 
43
- // -> set status message
44
- step.execution = statusManager.initExecutionObject(step)
45
- statusManager.updateExecution(step, 'ACTION_REQUIRED')
46
-
47
- let switchProcess = statusManager.findOrCreateProcess({
48
- step,
49
- type: 'SWITCH_CHAIN',
50
- status: 'ACTION_REQUIRED',
51
- })
52
-
53
45
  if (!allowUserInteraction) {
54
46
  return
55
47
  }
56
48
 
57
49
  try {
58
- const updatedClient = await switchChainHook?.(step.action.fromChainId)
50
+ if (!executionOptions?.switchChainHook) {
51
+ throw new ProviderError(
52
+ LiFiErrorCode.ChainSwitchError,
53
+ 'Chain switch hook is not provided.'
54
+ )
55
+ }
56
+ const updatedClient = await executionOptions.switchChainHook(targetChainId)
59
57
  let updatedChainId: number | undefined
60
58
  if (updatedClient) {
61
59
  updatedChainId = (await getAction(
@@ -64,22 +62,16 @@ export const switchChain = async (
64
62
  'getChainId'
65
63
  )(undefined)) as GetChainIdReturnType
66
64
  }
67
- if (updatedChainId !== step.action.fromChainId) {
65
+ if (updatedChainId !== targetChainId) {
68
66
  throw new ProviderError(
69
67
  LiFiErrorCode.ChainSwitchError,
70
68
  'Chain switch required.'
71
69
  )
72
70
  }
73
71
 
74
- switchProcess = statusManager.updateProcess(
75
- step,
76
- switchProcess.type,
77
- 'DONE'
78
- )
79
- statusManager.updateExecution(step, 'PENDING')
80
72
  return updatedClient
81
73
  } catch (error: any) {
82
- statusManager.updateProcess(step, switchProcess.type, 'FAILED', {
74
+ statusManager.updateProcess(step, process.type, 'FAILED', {
83
75
  error: {
84
76
  message: error.message,
85
77
  code: LiFiErrorCode.ChainSwitchError,
@@ -1,5 +1,6 @@
1
1
  import type { ChainId, ExtendedChain } from '@lifi/types'
2
- import type { Address, Chain, Client, Transaction } from 'viem'
2
+ import type { Address, Chain, Client, Hex, Transaction } from 'viem'
3
+ import { pad, toHex } from 'viem'
3
4
  import { getBlock } from 'viem/actions'
4
5
  import { config } from '../../config.js'
5
6
  import { median } from '../../utils/median.js'
@@ -100,3 +101,18 @@ export const retryDelay = ({ count }: { count: number; error: Error }) =>
100
101
  Math.min(~~(1 << count) * 200, 3000)
101
102
 
102
103
  export const retryCount = 30
104
+
105
+ /**
106
+ * Helper function to check if a domain salt matches a chainId.
107
+ * The salt is a padded hex string representation of the chainId.
108
+ */
109
+ export const isSaltMatchingChainId = (
110
+ salt: Hex | undefined,
111
+ chainId: number
112
+ ): boolean => {
113
+ if (!salt) {
114
+ return false
115
+ }
116
+ const paddedChainId = pad(toHex(chainId), { size: 32 })
117
+ return salt.toLowerCase() === paddedChainId.toLowerCase()
118
+ }
@@ -66,7 +66,7 @@ export const resumeRoute = async (
66
66
  }
67
67
  }
68
68
 
69
- await prepareRestart(route)
69
+ prepareRestart(route)
70
70
 
71
71
  return executeRoute(route, executionOptions)
72
72
  }
@@ -1,13 +1,16 @@
1
1
  import type { RouteExtended } from './types.js'
2
2
 
3
- export const prepareRestart = async (route: RouteExtended) => {
3
+ export const prepareRestart = (route: RouteExtended) => {
4
4
  for (let index = 0; index < route.steps.length; index++) {
5
5
  const step = route.steps[index]
6
6
  if (step.execution) {
7
- // Find the index of the last process that has tx hash
7
+ // Find the index of the last process that has tx hash, taskId, or signed messages
8
8
  const lastValidIndex = step.execution.process.findLastIndex(
9
9
  (process) =>
10
- (!!process.txHash || !!process.taskId) && process.status !== 'FAILED'
10
+ (!!process.txHash ||
11
+ !!process.taskId ||
12
+ !!process.signedTypedData?.length) &&
13
+ process.status !== 'FAILED'
11
14
  )
12
15
 
13
16
  // Keep all processes up to the one with tx hash
@@ -10,20 +10,17 @@ const processMessages: Record<
10
10
  PENDING: 'Waiting for token allowance',
11
11
  DONE: 'Token allowance set',
12
12
  },
13
- SWITCH_CHAIN: {
14
- ACTION_REQUIRED: 'Chain switch required',
15
- PENDING: 'Waiting for chain switch',
16
- DONE: 'Chain switched',
17
- },
18
13
  SWAP: {
19
14
  STARTED: 'Preparing swap transaction',
20
- ACTION_REQUIRED: 'Please sign the transaction',
15
+ ACTION_REQUIRED: 'Sign swap transaction',
16
+ MESSAGE_REQUIRED: 'Sign swap message',
21
17
  PENDING: 'Waiting for swap transaction',
22
18
  DONE: 'Swap completed',
23
19
  },
24
20
  CROSS_CHAIN: {
25
21
  STARTED: 'Preparing bridge transaction',
26
- ACTION_REQUIRED: 'Please sign the transaction',
22
+ ACTION_REQUIRED: 'Sign bridge transaction',
23
+ MESSAGE_REQUIRED: 'Sign bridge message',
27
24
  PENDING: 'Waiting for bridge transaction',
28
25
  DONE: 'Bridge transaction confirmed',
29
26
  },
@@ -37,7 +34,6 @@ const processMessages: Record<
37
34
  PENDING: 'Waiting for permit message',
38
35
  DONE: 'Permit message signed',
39
36
  },
40
- TRANSACTION: {},
41
37
  }
42
38
  const substatusMessages: Record<
43
39
  StatusMessage,
package/src/core/types.ts CHANGED
@@ -132,6 +132,7 @@ export type ExecutionStatus = 'ACTION_REQUIRED' | 'PENDING' | 'FAILED' | 'DONE'
132
132
  export type ProcessStatus =
133
133
  | 'STARTED'
134
134
  | 'ACTION_REQUIRED'
135
+ | 'MESSAGE_REQUIRED'
135
136
  | 'PENDING'
136
137
  | 'FAILED'
137
138
  | 'DONE'
@@ -140,11 +141,9 @@ export type ProcessStatus =
140
141
  export type ProcessType =
141
142
  | 'TOKEN_ALLOWANCE'
142
143
  | 'PERMIT'
143
- | 'SWITCH_CHAIN'
144
144
  | 'SWAP'
145
145
  | 'CROSS_CHAIN'
146
146
  | 'RECEIVING_CHAIN'
147
- | 'TRANSACTION'
148
147
 
149
148
  export type Process = {
150
149
  type: ProcessType
@@ -26,6 +26,7 @@ import {
26
26
  type SignedLiFiStep,
27
27
  type StatusResponse,
28
28
  type TokenExtended,
29
+ type TokensExtendedResponse,
29
30
  type TokensRequest,
30
31
  type TokensResponse,
31
32
  type ToolsRequest,
@@ -450,10 +451,18 @@ export const getChains = async (
450
451
  * @param options - Request options
451
452
  * @returns The tokens that are available on the requested chains
452
453
  */
453
- export const getTokens = async (
454
+ export async function getTokens(
455
+ params?: TokensRequest & { extended?: false | undefined },
456
+ options?: RequestOptions
457
+ ): Promise<TokensResponse>
458
+ export async function getTokens(
459
+ params: TokensRequest & { extended: true },
460
+ options?: RequestOptions
461
+ ): Promise<TokensExtendedResponse>
462
+ export async function getTokens(
454
463
  params?: TokensRequest,
455
464
  options?: RequestOptions
456
- ): Promise<TokensResponse> => {
465
+ ): Promise<TokensResponse> {
457
466
  if (params) {
458
467
  for (const key of Object.keys(params)) {
459
468
  if (!params[key as keyof TokensRequest]) {
@@ -464,14 +473,14 @@ export const getTokens = async (
464
473
  const urlSearchParams = new URLSearchParams(
465
474
  params as Record<string, string>
466
475
  ).toString()
476
+ const isExtended = params?.extended === true
467
477
  const response = await withDedupe(
468
478
  () =>
469
- request<TokensResponse>(
470
- `${config.get().apiUrl}/tokens?${urlSearchParams}`,
471
- {
472
- signal: options?.signal,
473
- }
474
- ),
479
+ request<
480
+ typeof isExtended extends true ? TokensExtendedResponse : TokensResponse
481
+ >(`${config.get().apiUrl}/tokens?${urlSearchParams}`, {
482
+ signal: options?.signal,
483
+ }),
475
484
  { id: `${getTokens.name}.${urlSearchParams}` }
476
485
  )
477
486
  return response
@@ -3,6 +3,8 @@ import type {
3
3
  RequestOptions,
4
4
  Token,
5
5
  TokenAmount,
6
+ TokenAmountExtended,
7
+ TokenExtended,
6
8
  WalletTokenExtended,
7
9
  } from '@lifi/types'
8
10
  import { config } from '../config.js'
@@ -26,16 +28,20 @@ export const getTokenBalance = async (
26
28
  }
27
29
 
28
30
  /**
29
- * Returns the balances for a list tokens a wallet holds across all aggregated chains.
31
+ * Returns the balances for a list tokens a wallet holds across all aggregated chains.
30
32
  * @param walletAddress - A wallet address.
31
- * @param tokens - A list of Token objects.
33
+ * @param tokens - A list of Token (or TokenExtended) objects.
32
34
  * @returns A list of objects containing the tokens and the amounts on different chains.
33
35
  * @throws {BaseError} Throws a ValidationError if parameters are invalid.
34
36
  */
35
- export const getTokenBalances = async (
37
+ export async function getTokenBalances(
36
38
  walletAddress: string,
37
39
  tokens: Token[]
38
- ): Promise<TokenAmount[]> => {
40
+ ): Promise<TokenAmount[]>
41
+ export async function getTokenBalances(
42
+ walletAddress: string,
43
+ tokens: TokenExtended[]
44
+ ): Promise<TokenAmountExtended[]> {
39
45
  // split by chain
40
46
  const tokensByChain = tokens.reduce(
41
47
  (tokens, token) => {
@@ -45,7 +51,7 @@ export const getTokenBalances = async (
45
51
  tokens[token.chainId].push(token)
46
52
  return tokens
47
53
  },
48
- {} as { [chainId: number]: Token[] }
54
+ {} as { [chainId: number]: Token[] | TokenExtended[] }
49
55
  )
50
56
 
51
57
  const tokenAmountsByChain = await getTokenBalancesByChain(
@@ -62,10 +68,14 @@ export const getTokenBalances = async (
62
68
  * @returns A list of objects containing the tokens and the amounts on different chains organized by the chosen chains.
63
69
  * @throws {BaseError} Throws a ValidationError if parameters are invalid.
64
70
  */
65
- export const getTokenBalancesByChain = async (
71
+ export async function getTokenBalancesByChain(
66
72
  walletAddress: string,
67
73
  tokensByChain: { [chainId: number]: Token[] }
68
- ): Promise<{ [chainId: number]: TokenAmount[] }> => {
74
+ ): Promise<{ [chainId: number]: TokenAmount[] }>
75
+ export async function getTokenBalancesByChain(
76
+ walletAddress: string,
77
+ tokensByChain: { [chainId: number]: TokenExtended[] }
78
+ ): Promise<{ [chainId: number]: TokenAmountExtended[] }> {
69
79
  if (!walletAddress) {
70
80
  throw new ValidationError('Missing walletAddress.')
71
81
  }
@@ -83,7 +93,9 @@ export const getTokenBalancesByChain = async (
83
93
  throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)
84
94
  }
85
95
 
86
- const tokenAmountsByChain: { [chainId: number]: TokenAmount[] } = {}
96
+ const tokenAmountsByChain: {
97
+ [chainId: number]: TokenAmount[] | TokenAmountExtended[]
98
+ } = {}
87
99
  const tokenAmountsSettled = await Promise.allSettled(
88
100
  Object.keys(tokensByChain).map(async (chainIdStr) => {
89
101
  const chainId = Number.parseInt(chainIdStr, 10)
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/sdk'
2
- export const version = '3.10.1'
2
+ export const version = '3.11.0'