@lifi/sdk 4.0.0-alpha.6 → 4.0.0-alpha.8

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 (105) hide show
  1. package/dist/cjs/actions/index.d.ts +3 -1
  2. package/dist/cjs/actions/index.js +2 -0
  3. package/dist/cjs/actions/index.js.map +1 -1
  4. package/dist/cjs/actions/patchContractCalls.d.ts +10 -0
  5. package/dist/cjs/actions/patchContractCalls.js +16 -0
  6. package/dist/cjs/actions/patchContractCalls.js.map +1 -0
  7. package/dist/cjs/core/checkBalance.js +3 -3
  8. package/dist/cjs/core/checkBalance.js.map +1 -1
  9. package/dist/cjs/core/utils.d.ts +2 -2
  10. package/dist/cjs/core/utils.js +0 -5
  11. package/dist/cjs/core/utils.js.map +1 -1
  12. package/dist/cjs/errors/constants.d.ts +4 -2
  13. package/dist/cjs/errors/constants.js +2 -0
  14. package/dist/cjs/errors/constants.js.map +1 -1
  15. package/dist/cjs/index.d.ts +5 -4
  16. package/dist/cjs/index.js +10 -8
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/types/core.d.ts +28 -4
  19. package/dist/cjs/utils/convertQuoteToRoute.d.ts +6 -1
  20. package/dist/cjs/utils/convertQuoteToRoute.js +73 -5
  21. package/dist/cjs/utils/convertQuoteToRoute.js.map +1 -1
  22. package/dist/cjs/utils/formatUnits.d.ts +1 -0
  23. package/dist/cjs/utils/formatUnits.js +18 -0
  24. package/dist/cjs/utils/formatUnits.js.map +1 -0
  25. package/dist/cjs/utils/isHex.d.ts +4 -0
  26. package/dist/cjs/utils/isHex.js +13 -0
  27. package/dist/cjs/utils/isHex.js.map +1 -0
  28. package/dist/cjs/utils/parseUnits.d.ts +1 -0
  29. package/dist/cjs/utils/parseUnits.js +44 -0
  30. package/dist/cjs/utils/parseUnits.js.map +1 -0
  31. package/dist/cjs/utils/request.js +7 -6
  32. package/dist/cjs/utils/request.js.map +1 -1
  33. package/dist/cjs/version.d.ts +1 -1
  34. package/dist/cjs/version.js +1 -1
  35. package/dist/esm/actions/index.d.ts +9 -1
  36. package/dist/esm/actions/index.js +2 -0
  37. package/dist/esm/actions/index.js.map +1 -1
  38. package/dist/esm/actions/patchContractCalls.d.ts +10 -0
  39. package/dist/esm/actions/patchContractCalls.js +12 -0
  40. package/dist/esm/actions/patchContractCalls.js.map +1 -0
  41. package/dist/esm/core/checkBalance.js +1 -1
  42. package/dist/esm/core/checkBalance.js.map +1 -1
  43. package/dist/esm/core/utils.d.ts +2 -10
  44. package/dist/esm/core/utils.js +0 -11
  45. package/dist/esm/core/utils.js.map +1 -1
  46. package/dist/esm/errors/constants.d.ts +4 -2
  47. package/dist/esm/errors/constants.js +2 -0
  48. package/dist/esm/errors/constants.js.map +1 -1
  49. package/dist/esm/index.d.ts +5 -4
  50. package/dist/esm/index.js +4 -2
  51. package/dist/esm/index.js.map +1 -1
  52. package/dist/esm/types/core.d.ts +28 -4
  53. package/dist/esm/utils/convertQuoteToRoute.d.ts +11 -3
  54. package/dist/esm/utils/convertQuoteToRoute.js +76 -7
  55. package/dist/esm/utils/convertQuoteToRoute.js.map +1 -1
  56. package/dist/esm/utils/formatUnits.d.ts +4 -0
  57. package/dist/esm/utils/formatUnits.js +18 -0
  58. package/dist/esm/utils/formatUnits.js.map +1 -0
  59. package/dist/esm/utils/isHex.d.ts +4 -0
  60. package/dist/esm/utils/isHex.js +10 -0
  61. package/dist/esm/utils/isHex.js.map +1 -0
  62. package/dist/esm/utils/parseUnits.d.ts +4 -0
  63. package/dist/esm/utils/parseUnits.js +46 -0
  64. package/dist/esm/utils/parseUnits.js.map +1 -0
  65. package/dist/esm/utils/request.js +7 -6
  66. package/dist/esm/utils/request.js.map +1 -1
  67. package/dist/esm/version.d.ts +1 -1
  68. package/dist/esm/version.js +1 -1
  69. package/dist/types/actions/index.d.ts +9 -1
  70. package/dist/types/actions/index.d.ts.map +1 -1
  71. package/dist/types/actions/patchContractCalls.d.ts +11 -0
  72. package/dist/types/actions/patchContractCalls.d.ts.map +1 -0
  73. package/dist/types/core/checkBalance.d.ts.map +1 -1
  74. package/dist/types/core/utils.d.ts +2 -10
  75. package/dist/types/core/utils.d.ts.map +1 -1
  76. package/dist/types/errors/constants.d.ts +4 -2
  77. package/dist/types/errors/constants.d.ts.map +1 -1
  78. package/dist/types/index.d.ts +5 -4
  79. package/dist/types/index.d.ts.map +1 -1
  80. package/dist/types/types/core.d.ts +28 -4
  81. package/dist/types/types/core.d.ts.map +1 -1
  82. package/dist/types/utils/convertQuoteToRoute.d.ts +11 -3
  83. package/dist/types/utils/convertQuoteToRoute.d.ts.map +1 -1
  84. package/dist/types/utils/formatUnits.d.ts +5 -0
  85. package/dist/types/utils/formatUnits.d.ts.map +1 -0
  86. package/dist/types/utils/isHex.d.ts +5 -0
  87. package/dist/types/utils/isHex.d.ts.map +1 -0
  88. package/dist/types/utils/parseUnits.d.ts +5 -0
  89. package/dist/types/utils/parseUnits.d.ts.map +1 -0
  90. package/dist/types/utils/request.d.ts.map +1 -1
  91. package/dist/types/version.d.ts +1 -1
  92. package/package.json +2 -3
  93. package/src/actions/index.ts +18 -0
  94. package/src/actions/patchContractCalls.ts +30 -0
  95. package/src/core/checkBalance.ts +1 -1
  96. package/src/core/utils.ts +1 -13
  97. package/src/errors/constants.ts +2 -0
  98. package/src/index.ts +9 -4
  99. package/src/types/core.ts +37 -4
  100. package/src/utils/convertQuoteToRoute.ts +117 -8
  101. package/src/utils/formatUnits.ts +22 -0
  102. package/src/utils/isHex.ts +14 -0
  103. package/src/utils/parseUnits.ts +50 -0
  104. package/src/utils/request.ts +9 -6
  105. package/src/version.ts +1 -1
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  // biome-ignore lint/performance/noBarrelFile: module entrypoint
2
2
  // biome-ignore lint/performance/noReExportAll: types
3
3
  export * from '@lifi/types'
4
- export type { Client } from 'viem'
5
- export { formatUnits, isHex, parseUnits } from 'viem/utils'
6
4
  export { getChains } from './actions/getChains.js'
7
5
  export { getConnections } from './actions/getConnections.js'
8
6
  export { getContractCallsQuote } from './actions/getContractCallsQuote.js'
@@ -23,6 +21,7 @@ export { getTools } from './actions/getTools.js'
23
21
  export { getTransactionHistory } from './actions/getTransactionHistory.js'
24
22
  export { getWalletBalances } from './actions/getWalletBalances.js'
25
23
  export { actions } from './actions/index.js'
24
+ export { patchContractCalls } from './actions/patchContractCalls.js'
26
25
  export { relayTransaction } from './actions/relayTransaction.js'
27
26
  export { createClient } from './client/createClient.js'
28
27
  export { BaseStepExecutor } from './core/BaseStepExecutor.js'
@@ -37,7 +36,6 @@ export {
37
36
  } from './core/execution.js'
38
37
  export { StatusManager } from './core/StatusManager.js'
39
38
  export { stepComparison } from './core/stepComparison.js'
40
- export { isTokenMessageSigningAllowed } from './core/utils.js'
41
39
  export { waitForDestinationChainTransaction } from './core/waitForDestinationChainTransaction.js'
42
40
  export { BaseError } from './errors/baseError.js'
43
41
  export type { ErrorCode } from './errors/constants.js'
@@ -57,15 +55,20 @@ export type {
57
55
  AcceptExchangeRateUpdateHook,
58
56
  AcceptSlippageUpdateHook,
59
57
  AcceptSlippageUpdateHookParams,
58
+ ContractCallParams,
59
+ ContractTool,
60
60
  ExchangeRateUpdateParams,
61
61
  Execution,
62
62
  ExecutionOptions,
63
63
  ExecutionStatus,
64
+ GetContractCallsHook,
65
+ GetContractCallsResult,
64
66
  InteractionSettings,
65
67
  LiFiStepExtended,
66
68
  Process,
67
69
  ProcessStatus,
68
70
  ProcessType,
71
+ RequestInterceptor,
69
72
  RouteExecutionData,
70
73
  RouteExecutionDataDictionary,
71
74
  RouteExecutionDictionary,
@@ -78,7 +81,6 @@ export type {
78
81
  StepExecutor,
79
82
  StepExecutorOptions,
80
83
  StepExtended,
81
- SwitchChainHook,
82
84
  TransactionMethodType,
83
85
  TransactionParameters,
84
86
  TransactionRequestParameters,
@@ -88,6 +90,9 @@ export type {
88
90
  export { checkPackageUpdates } from './utils/checkPackageUpdates.js'
89
91
  export { convertQuoteToRoute } from './utils/convertQuoteToRoute.js'
90
92
  export { fetchTxErrorDetails } from './utils/fetchTxErrorDetails.js'
93
+ export { formatUnits } from './utils/formatUnits.js'
94
+ export { isHex } from './utils/isHex.js'
95
+ export { parseUnits } from './utils/parseUnits.js'
91
96
  export { sleep } from './utils/sleep.js'
92
97
  export { waitForResult } from './utils/waitForResult.js'
93
98
  export { withDedupe } from './utils/withDedupe.js'
package/src/types/core.ts CHANGED
@@ -2,6 +2,7 @@ import type {
2
2
  ChainId,
3
3
  ChainType,
4
4
  CoinKey,
5
+ ContractCall,
5
6
  ExtendedChain,
6
7
  FeeCost,
7
8
  GasCost,
@@ -13,7 +14,11 @@ import type {
13
14
  Token,
14
15
  TokenAmount,
15
16
  } from '@lifi/types'
16
- import type { Client } from 'viem'
17
+ import type { ExtendedRequestInit } from './request.js'
18
+
19
+ export type RequestInterceptor = (
20
+ request: ExtendedRequestInit
21
+ ) => ExtendedRequestInit | Promise<ExtendedRequestInit>
17
22
 
18
23
  export interface SDKBaseConfig {
19
24
  apiKey?: string
@@ -27,6 +32,8 @@ export interface SDKBaseConfig {
27
32
  widgetVersion?: string
28
33
  debug: boolean
29
34
  preloadChains?: boolean
35
+ chainsRefetchInterval?: number
36
+ requestInterceptor?: RequestInterceptor
30
37
  }
31
38
 
32
39
  export interface SDKConfig extends Partial<Omit<SDKBaseConfig, 'integrator'>> {
@@ -132,8 +139,6 @@ export type TransactionRequestUpdateHook = (
132
139
  updatedTxRequest: TransactionRequestParameters
133
140
  ) => Promise<TransactionParameters>
134
141
 
135
- export type SwitchChainHook = (chainId: number) => Promise<Client | undefined>
136
-
137
142
  export interface AcceptSlippageUpdateHookParams {
138
143
  toToken: Token
139
144
  oldToAmount: string
@@ -156,11 +161,39 @@ export type AcceptExchangeRateUpdateHook = (
156
161
  params: ExchangeRateUpdateParams
157
162
  ) => Promise<boolean | undefined>
158
163
 
164
+ export interface ContractCallParams {
165
+ fromChainId: number
166
+ toChainId: number
167
+ fromTokenAddress: string
168
+ toTokenAddress: string
169
+ fromAddress: string
170
+ toAddress?: string
171
+ fromAmount: bigint
172
+ toAmount: bigint
173
+ slippage?: number
174
+ }
175
+
176
+ export interface ContractTool {
177
+ name: string
178
+ logoURI: string
179
+ }
180
+
181
+ export interface GetContractCallsResult {
182
+ contractCalls: ContractCall[]
183
+ patcher?: boolean
184
+ contractTool?: ContractTool
185
+ }
186
+
187
+ export type GetContractCallsHook = (
188
+ params: ContractCallParams
189
+ ) => Promise<GetContractCallsResult>
190
+
159
191
  export interface ExecutionOptions {
160
192
  acceptExchangeRateUpdateHook?: AcceptExchangeRateUpdateHook
161
- switchChainHook?: SwitchChainHook
162
193
  updateRouteHook?: UpdateRouteHook
163
194
  updateTransactionRequestHook?: TransactionRequestUpdateHook
195
+ getContractCalls?: GetContractCallsHook
196
+ adjustZeroOutputFromPreviousStep?: boolean
164
197
  executeInBackground?: boolean
165
198
  disableMessageSigning?: boolean
166
199
  /**
@@ -1,23 +1,132 @@
1
- import type { LiFiStep, Route } from '@lifi/types'
1
+ import type { LiFiStep, Route, Step } from '@lifi/types'
2
2
  import { ValidationError } from '../errors/errors.js'
3
3
  import { SDKError } from '../errors/SDKError.js'
4
+ import { formatUnits } from './formatUnits.js'
5
+
6
+ interface ConvertQuoteToRouteOptions {
7
+ /**
8
+ * When true, if the quote has zero output values (toAmount, toAmountMin, toAmountUSD),
9
+ * use the values from the previous included step that has non-zero output.
10
+ */
11
+ adjustZeroOutputFromPreviousStep?: boolean
12
+ }
13
+
14
+ const parseBigInt = (value: string | undefined): bigint => {
15
+ if (!value) {
16
+ return 0n
17
+ }
18
+ try {
19
+ return BigInt(value)
20
+ } catch {
21
+ return 0n
22
+ }
23
+ }
24
+
25
+ const parseNumber = (value: string | undefined): number => {
26
+ if (!value) {
27
+ return 0
28
+ }
29
+ const parsed = Number(value)
30
+ return Number.isNaN(parsed) ? 0 : parsed
31
+ }
32
+
33
+ const isZeroOutput = (
34
+ toAmount: string,
35
+ toAmountMin: string,
36
+ toAmountUSD?: string
37
+ ): boolean => {
38
+ return (
39
+ !parseBigInt(toAmount) &&
40
+ !parseBigInt(toAmountMin) &&
41
+ !parseNumber(toAmountUSD)
42
+ )
43
+ }
44
+
45
+ const hasNonZeroOutput = (step: Step): boolean => {
46
+ return (
47
+ !!parseBigInt(step.estimate.toAmount) ||
48
+ !!parseBigInt(step.estimate.toAmountMin)
49
+ )
50
+ }
51
+
52
+ const findPreviousNonZeroStep = (steps: Step[]): Step | undefined => {
53
+ // Find the last step that has non-zero output (the step before the zero output step)
54
+ for (let i = steps.length - 1; i >= 0; i--) {
55
+ const step = steps[i]
56
+ if (hasNonZeroOutput(step)) {
57
+ return step
58
+ }
59
+ }
60
+ return undefined
61
+ }
62
+
63
+ export function formatTokenPrice(
64
+ amount?: string | bigint,
65
+ price?: string,
66
+ decimals?: number
67
+ ) {
68
+ if (!amount || !price) {
69
+ return 0
70
+ }
71
+
72
+ const formattedAmount =
73
+ typeof amount === 'bigint' && decimals !== undefined
74
+ ? formatUnits(amount, decimals)
75
+ : amount.toString()
76
+
77
+ if (Number.isNaN(Number(formattedAmount)) || Number.isNaN(Number(price))) {
78
+ return 0
79
+ }
80
+ return Number.parseFloat(formattedAmount) * Number.parseFloat(price)
81
+ }
4
82
 
5
83
  /**
6
84
  * Converts a quote to Route
7
85
  * @param quote - Step returned from the quote endpoint.
8
- * @param txHash
9
- * @param chainId
86
+ * @param options - Optional configuration for handling edge cases.
10
87
  * @returns - The route to be executed.
11
88
  * @throws {BaseError} Throws a ValidationError if the step has missing values.
12
89
  */
13
- export const convertQuoteToRoute = (quote: LiFiStep): Route => {
90
+ export const convertQuoteToRoute = (
91
+ quote: LiFiStep,
92
+ options?: ConvertQuoteToRouteOptions
93
+ ): Route => {
94
+ let toAmount = quote.estimate.toAmount
95
+ let toAmountMin = quote.estimate.toAmountMin
96
+ let toAmountUSD = quote.estimate.toAmountUSD
97
+
98
+ // Handle zero output values by looking at previous included step
99
+ if (
100
+ options?.adjustZeroOutputFromPreviousStep &&
101
+ quote.includedSteps?.length &&
102
+ isZeroOutput(toAmount, toAmountMin, toAmountUSD)
103
+ ) {
104
+ const previousStep = findPreviousNonZeroStep(quote.includedSteps)
105
+ if (previousStep) {
106
+ toAmount = previousStep.estimate.toAmount
107
+ toAmountMin = previousStep.estimate.toAmountMin
108
+ toAmountUSD = formatTokenPrice(
109
+ parseBigInt(toAmount),
110
+ previousStep.action.toToken.priceUSD,
111
+ previousStep.action.toToken.decimals
112
+ ).toFixed(2)
113
+
114
+ // Update the last included step's estimate with the adjusted values
115
+ const lastStep = quote.includedSteps[quote.includedSteps.length - 1]
116
+ if (lastStep && !hasNonZeroOutput(lastStep)) {
117
+ lastStep.estimate.toAmount = toAmount
118
+ lastStep.estimate.toAmountMin = toAmountMin
119
+ }
120
+ }
121
+ }
122
+
14
123
  if (!quote.estimate.fromAmountUSD) {
15
124
  throw new SDKError(
16
125
  new ValidationError("Missing 'fromAmountUSD' in step estimate.")
17
126
  )
18
127
  }
19
128
 
20
- if (!quote.estimate.toAmountUSD) {
129
+ if (!toAmountUSD) {
21
130
  throw new SDKError(
22
131
  new ValidationError("Missing 'toAmountUSD' in step estimate.")
23
132
  )
@@ -32,9 +141,9 @@ export const convertQuoteToRoute = (quote: LiFiStep): Route => {
32
141
  fromAddress: quote.action.fromAddress,
33
142
  toChainId: quote.action.toToken.chainId,
34
143
  toToken: quote.action.toToken,
35
- toAmount: quote.estimate.toAmount,
36
- toAmountMin: quote.estimate.toAmountMin,
37
- toAmountUSD: quote.estimate.toAmountUSD,
144
+ toAmount,
145
+ toAmountMin,
146
+ toAmountUSD,
38
147
  toAddress: quote.action.toAddress || quote.action.fromAddress,
39
148
  gasCostUSD: quote.estimate.gasCosts?.[0]?.amountUSD || '0',
40
149
  steps: [quote],
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Divides a number by a given exponent of base 10 (10exponent), and formats it into a string representation of the number.
3
+ */
4
+ export function formatUnits(value: bigint, decimals: number) {
5
+ let display = value.toString()
6
+
7
+ const negative = display.startsWith('-')
8
+ if (negative) {
9
+ display = display.slice(1)
10
+ }
11
+
12
+ display = display.padStart(decimals, '0')
13
+
14
+ let [integer, fraction] = [
15
+ display.slice(0, display.length - decimals),
16
+ display.slice(display.length - decimals),
17
+ ]
18
+ fraction = fraction.replace(/(0+)$/, '')
19
+ return `${negative ? '-' : ''}${integer || '0'}${
20
+ fraction ? `.${fraction}` : ''
21
+ }`
22
+ }
@@ -0,0 +1,14 @@
1
+ export type Hex = `0x${string}`
2
+
3
+ export function isHex(
4
+ value: unknown,
5
+ { strict = true }: { strict?: boolean | undefined } = {}
6
+ ): value is Hex {
7
+ if (!value) {
8
+ return false
9
+ }
10
+ if (typeof value !== 'string') {
11
+ return false
12
+ }
13
+ return strict ? /^0x[0-9a-fA-F]*$/.test(value) : value.startsWith('0x')
14
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Multiplies a string representation of a number by a given exponent of base 10 (10exponent).
3
+ */
4
+ export function parseUnits(value: string, decimals: number) {
5
+ if (!/^(-?)([0-9]*)\.?([0-9]*)$/.test(value)) {
6
+ throw new Error(`Number \`${value}\` is not a valid decimal number.`)
7
+ }
8
+
9
+ let [integer, fraction = '0'] = value.split('.')
10
+
11
+ const negative = integer.startsWith('-')
12
+ if (negative) {
13
+ integer = integer.slice(1)
14
+ }
15
+
16
+ // trim trailing zeros.
17
+ fraction = fraction.replace(/(0+)$/, '')
18
+
19
+ // round off if the fraction is larger than the number of decimals.
20
+ if (decimals === 0) {
21
+ if (Math.round(Number(`.${fraction}`)) === 1) {
22
+ integer = `${BigInt(integer) + 1n}`
23
+ }
24
+ fraction = ''
25
+ } else if (fraction.length > decimals) {
26
+ const [left, unit, right] = [
27
+ fraction.slice(0, decimals - 1),
28
+ fraction.slice(decimals - 1, decimals),
29
+ fraction.slice(decimals),
30
+ ]
31
+
32
+ const rounded = Math.round(Number(`${unit}.${right}`))
33
+ if (rounded > 9) {
34
+ fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, '0')
35
+ } else {
36
+ fraction = `${left}${rounded}`
37
+ }
38
+
39
+ if (fraction.length > decimals) {
40
+ fraction = fraction.slice(1)
41
+ integer = `${BigInt(integer) + 1n}`
42
+ }
43
+
44
+ fraction = fraction.slice(0, decimals)
45
+ } else {
46
+ fraction = fraction.padEnd(decimals, '0')
47
+ }
48
+
49
+ return BigInt(`${negative ? '-' : ''}${integer}${fraction}`)
50
+ }
@@ -24,7 +24,8 @@ export const request = async <T = Response>(
24
24
  retries: requestSettings.retries,
25
25
  }
26
26
  ): Promise<T> => {
27
- const { userId, integrator, widgetVersion, apiKey } = config
27
+ const { userId, integrator, widgetVersion, apiKey, requestInterceptor } =
28
+ config
28
29
 
29
30
  if (!integrator) {
30
31
  throw new SDKError(
@@ -71,6 +72,10 @@ export const request = async <T = Response>(
71
72
  'x-lifi-integrator': integrator,
72
73
  }
73
74
 
75
+ if (requestInterceptor) {
76
+ options = await requestInterceptor(options)
77
+ }
78
+
74
79
  const response: Response = await fetch(
75
80
  url,
76
81
  stripExtendRequestInitProperties(options)
@@ -82,12 +87,10 @@ export const request = async <T = Response>(
82
87
 
83
88
  return await response.json()
84
89
  } catch (error) {
85
- if (options.retries > 0 && (error as HTTPError).status === 500) {
90
+ const retries = options.retries ?? 0
91
+ if (retries > 0 && (error as HTTPError).status === 500) {
86
92
  await sleep(500)
87
- return request<T>(config, url, {
88
- ...options,
89
- retries: options.retries - 1,
90
- })
93
+ return request<T>(config, url, { ...options, retries: retries - 1 })
91
94
  }
92
95
 
93
96
  await (error as HTTPError).buildAdditionalDetails?.()
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = '@lifi/sdk'
2
- export const version = '4.0.0-alpha.6'
2
+ export const version = '4.0.0-alpha.8'