@lifi/sdk 3.0.2-beta.0 → 3.1.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.
- package/package.json +3 -3
- package/src/_cjs/core/EVM/EVMStepExecutor.js +12 -11
- package/src/_cjs/core/EVM/EVMStepExecutor.js.map +1 -1
- package/src/_cjs/core/EVM/checkAllowance.js +3 -4
- package/src/_cjs/core/EVM/checkAllowance.js.map +1 -1
- package/src/_cjs/core/EVM/multisig.js +4 -3
- package/src/_cjs/core/EVM/multisig.js.map +1 -1
- package/src/_cjs/core/EVM/parseEVMErrors.js +38 -0
- package/src/_cjs/core/EVM/parseEVMErrors.js.map +1 -0
- package/src/_cjs/core/EVM/publicClient.js +8 -3
- package/src/_cjs/core/EVM/publicClient.js.map +1 -1
- package/src/_cjs/core/EVM/switchChain.js +4 -3
- package/src/_cjs/core/EVM/switchChain.js.map +1 -1
- package/src/_cjs/core/Solana/SolanaStepExecutor.js +42 -24
- package/src/_cjs/core/Solana/SolanaStepExecutor.js.map +1 -1
- package/src/_cjs/core/Solana/connection.js.map +1 -1
- package/src/_cjs/core/Solana/parseSolanaErrors.js +33 -0
- package/src/_cjs/core/Solana/parseSolanaErrors.js.map +1 -0
- package/src/_cjs/core/checkBalance.js +2 -2
- package/src/_cjs/core/checkBalance.js.map +1 -1
- package/src/_cjs/core/stepComparison.js +3 -3
- package/src/_cjs/core/stepComparison.js.map +1 -1
- package/src/_cjs/core/waitForReceivingTransaction.js +1 -1
- package/src/_cjs/core/waitForReceivingTransaction.js.map +1 -1
- package/src/_cjs/errors/SDKError.js +48 -0
- package/src/_cjs/errors/SDKError.js.map +1 -0
- package/src/_cjs/errors/baseError.js +30 -0
- package/src/_cjs/errors/baseError.js.map +1 -0
- package/src/_cjs/errors/constants.js +48 -0
- package/src/_cjs/errors/constants.js.map +1 -0
- package/src/_cjs/errors/errors.js +48 -0
- package/src/_cjs/errors/errors.js.map +1 -0
- package/src/_cjs/errors/httpError.js +101 -0
- package/src/_cjs/errors/httpError.js.map +1 -0
- package/src/_cjs/errors/index.js +11 -0
- package/src/_cjs/errors/index.js.map +1 -0
- package/src/_cjs/errors/utils/baseErrorRootCause.js +21 -0
- package/src/_cjs/errors/utils/baseErrorRootCause.js.map +1 -0
- package/src/_cjs/errors/utils/rootCause.js +12 -0
- package/src/_cjs/errors/utils/rootCause.js.map +1 -0
- package/src/_cjs/helpers.js +13 -8
- package/src/_cjs/helpers.js.map +1 -1
- package/src/_cjs/index.js +2 -4
- package/src/_cjs/index.js.map +1 -1
- package/src/_cjs/request.js +37 -34
- package/src/_cjs/request.js.map +1 -1
- package/src/_cjs/services/api.js +61 -132
- package/src/_cjs/services/api.js.map +1 -1
- package/src/_cjs/services/balance.js +1 -1
- package/src/_cjs/services/balance.js.map +1 -1
- package/src/_cjs/types/request.js +3 -0
- package/src/_cjs/types/request.js.map +1 -0
- package/src/_cjs/utils/errors.js +0 -183
- package/src/_cjs/utils/errors.js.map +1 -1
- package/src/_cjs/utils/index.js +1 -2
- package/src/_cjs/utils/index.js.map +1 -1
- package/src/_cjs/version.js +1 -1
- package/src/_esm/core/EVM/EVMStepExecutor.js +8 -7
- package/src/_esm/core/EVM/EVMStepExecutor.js.map +1 -1
- package/src/_esm/core/EVM/checkAllowance.js +3 -4
- package/src/_esm/core/EVM/checkAllowance.js.map +1 -1
- package/src/_esm/core/EVM/multisig.js +2 -1
- package/src/_esm/core/EVM/multisig.js.map +1 -1
- package/src/_esm/core/EVM/parseEVMErrors.js +35 -0
- package/src/_esm/core/EVM/parseEVMErrors.js.map +1 -0
- package/src/_esm/core/EVM/publicClient.js +9 -4
- package/src/_esm/core/EVM/publicClient.js.map +1 -1
- package/src/_esm/core/EVM/switchChain.js +2 -1
- package/src/_esm/core/EVM/switchChain.js.map +1 -1
- package/src/_esm/core/Solana/SolanaStepExecutor.js +52 -25
- package/src/_esm/core/Solana/SolanaStepExecutor.js.map +1 -1
- package/src/_esm/core/Solana/connection.js +2 -3
- package/src/_esm/core/Solana/connection.js.map +1 -1
- package/src/_esm/core/Solana/parseSolanaErrors.js +29 -0
- package/src/_esm/core/Solana/parseSolanaErrors.js.map +1 -0
- package/src/_esm/core/checkBalance.js +2 -2
- package/src/_esm/core/checkBalance.js.map +1 -1
- package/src/_esm/core/stepComparison.js +3 -3
- package/src/_esm/core/stepComparison.js.map +1 -1
- package/src/_esm/core/waitForReceivingTransaction.js +1 -1
- package/src/_esm/core/waitForReceivingTransaction.js.map +1 -1
- package/src/_esm/errors/SDKError.js +47 -0
- package/src/_esm/errors/SDKError.js.map +1 -0
- package/src/_esm/errors/baseError.js +28 -0
- package/src/_esm/errors/baseError.js.map +1 -0
- package/src/_esm/errors/constants.js +45 -0
- package/src/_esm/errors/constants.js.map +1 -0
- package/src/_esm/errors/errors.js +38 -0
- package/src/_esm/errors/errors.js.map +1 -0
- package/src/_esm/errors/httpError.js +97 -0
- package/src/_esm/errors/httpError.js.map +1 -0
- package/src/_esm/errors/index.js +8 -0
- package/src/_esm/errors/index.js.map +1 -0
- package/src/_esm/errors/utils/baseErrorRootCause.js +16 -0
- package/src/_esm/errors/utils/baseErrorRootCause.js.map +1 -0
- package/src/_esm/errors/utils/rootCause.js +8 -0
- package/src/_esm/errors/utils/rootCause.js.map +1 -0
- package/src/_esm/helpers.js +16 -9
- package/src/_esm/helpers.js.map +1 -1
- package/src/_esm/index.js +2 -2
- package/src/_esm/index.js.map +1 -1
- package/src/_esm/request.js +38 -35
- package/src/_esm/request.js.map +1 -1
- package/src/_esm/services/api.js +61 -133
- package/src/_esm/services/api.js.map +1 -1
- package/src/_esm/services/balance.js +4 -4
- package/src/_esm/services/balance.js.map +1 -1
- package/src/_esm/types/request.js +2 -0
- package/src/_esm/types/request.js.map +1 -0
- package/src/_esm/utils/errors.js +1 -173
- package/src/_esm/utils/errors.js.map +1 -1
- package/src/_esm/utils/index.js +1 -2
- package/src/_esm/utils/index.js.map +1 -1
- package/src/_esm/version.js +1 -1
- package/src/_types/core/EVM/EVMStepExecutor.d.ts.map +1 -1
- package/src/_types/core/EVM/checkAllowance.d.ts.map +1 -1
- package/src/_types/core/EVM/multisig.d.ts.map +1 -1
- package/src/_types/core/EVM/parseEVMErrors.d.ts +4 -0
- package/src/_types/core/EVM/parseEVMErrors.d.ts.map +1 -0
- package/src/_types/core/EVM/publicClient.d.ts.map +1 -1
- package/src/_types/core/EVM/switchChain.d.ts.map +1 -1
- package/src/_types/core/Solana/SolanaStepExecutor.d.ts.map +1 -1
- package/src/_types/core/Solana/connection.d.ts +2 -3
- package/src/_types/core/Solana/connection.d.ts.map +1 -1
- package/src/_types/core/Solana/parseSolanaErrors.d.ts +4 -0
- package/src/_types/core/Solana/parseSolanaErrors.d.ts.map +1 -0
- package/src/_types/core/stepComparison.d.ts.map +1 -1
- package/src/_types/errors/SDKError.d.ts +12 -0
- package/src/_types/errors/SDKError.d.ts.map +1 -0
- package/src/_types/errors/baseError.d.ts +7 -0
- package/src/_types/errors/baseError.d.ts.map +1 -0
- package/src/_types/errors/constants.d.ts +43 -0
- package/src/_types/errors/constants.d.ts.map +1 -0
- package/src/_types/errors/errors.d.ts +24 -0
- package/src/_types/errors/errors.d.ts.map +1 -0
- package/src/_types/errors/httpError.d.ts +21 -0
- package/src/_types/errors/httpError.d.ts.map +1 -0
- package/src/_types/errors/index.d.ts +8 -0
- package/src/_types/errors/index.d.ts.map +1 -0
- package/src/_types/errors/utils/baseErrorRootCause.d.ts +4 -0
- package/src/_types/errors/utils/baseErrorRootCause.d.ts.map +1 -0
- package/src/_types/errors/utils/rootCause.d.ts +2 -0
- package/src/_types/errors/utils/rootCause.d.ts.map +1 -0
- package/src/_types/helpers.d.ts +5 -4
- package/src/_types/helpers.d.ts.map +1 -1
- package/src/_types/index.d.ts +2 -2
- package/src/_types/index.d.ts.map +1 -1
- package/src/_types/request.d.ts +1 -5
- package/src/_types/request.d.ts.map +1 -1
- package/src/_types/services/api.d.ts.map +1 -1
- package/src/_types/services/balance.d.ts +3 -3
- package/src/_types/types/request.d.ts +4 -0
- package/src/_types/types/request.d.ts.map +1 -0
- package/src/_types/utils/errors.d.ts +0 -109
- package/src/_types/utils/errors.d.ts.map +1 -1
- package/src/_types/utils/index.d.ts +1 -2
- package/src/_types/utils/index.d.ts.map +1 -1
- package/src/_types/version.d.ts +1 -1
- package/src/core/EVM/EVMStepExecutor.ts +14 -12
- package/src/core/EVM/checkAllowance.ts +3 -4
- package/src/core/EVM/multisig.ts +2 -1
- package/src/core/EVM/parseEVMErrors.ts +60 -0
- package/src/core/EVM/publicClient.ts +9 -4
- package/src/core/EVM/switchChain.ts +2 -1
- package/src/core/Solana/SolanaStepExecutor.ts +67 -34
- package/src/core/Solana/connection.ts +2 -3
- package/src/core/Solana/parseSolanaErrors.ts +45 -0
- package/src/core/checkBalance.ts +2 -2
- package/src/core/stepComparison.ts +3 -4
- package/src/core/waitForReceivingTransaction.ts +1 -1
- package/src/errors/SDKError.ts +25 -0
- package/src/errors/baseError.ts +22 -0
- package/src/errors/constants.ts +45 -0
- package/src/errors/errors.ts +44 -0
- package/src/errors/httpError.ts +93 -0
- package/src/errors/index.ts +7 -0
- package/src/errors/utils/baseErrorRootCause.ts +18 -0
- package/src/errors/utils/rootCause.ts +7 -0
- package/src/helpers.ts +23 -16
- package/src/index.ts +2 -2
- package/src/request.ts +52 -38
- package/src/services/api.ts +109 -157
- package/src/services/balance.ts +4 -4
- package/src/types/request.ts +3 -0
- package/src/utils/errors.ts +0 -233
- package/src/utils/index.ts +1 -2
- package/src/version.ts +1 -1
- package/src/_cjs/utils/parseBackendError.js +0 -27
- package/src/_cjs/utils/parseBackendError.js.map +0 -1
- package/src/_cjs/utils/parseError.js +0 -69
- package/src/_cjs/utils/parseError.js.map +0 -1
- package/src/_esm/utils/parseBackendError.js +0 -24
- package/src/_esm/utils/parseBackendError.js.map +0 -1
- package/src/_esm/utils/parseError.js +0 -100
- package/src/_esm/utils/parseError.js.map +0 -1
- package/src/_types/utils/parseBackendError.d.ts +0 -3
- package/src/_types/utils/parseBackendError.d.ts.map +0 -1
- package/src/_types/utils/parseError.d.ts +0 -35
- package/src/_types/utils/parseError.d.ts.map +0 -1
- package/src/utils/parseBackendError.ts +0 -50
- package/src/utils/parseError.ts +0 -210
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type LiFiStep, type Process } from '@lifi/types'
|
|
2
|
+
import { TransactionError, UnknownError } from '../../errors/errors.js'
|
|
3
|
+
import { SDKError } from '../../errors/SDKError.js'
|
|
4
|
+
import { ErrorMessage, LiFiErrorCode } from '../../errors/constants.js'
|
|
5
|
+
import { BaseError } from '../../utils/index.js'
|
|
6
|
+
import { fetchTxErrorDetails } from '../../helpers.js'
|
|
7
|
+
|
|
8
|
+
export const parseEVMErrors = async (
|
|
9
|
+
e: Error,
|
|
10
|
+
step?: LiFiStep,
|
|
11
|
+
process?: Process
|
|
12
|
+
): Promise<SDKError> => {
|
|
13
|
+
if (e instanceof SDKError) {
|
|
14
|
+
e.step = e.step ?? step
|
|
15
|
+
e.process = e.process ?? process
|
|
16
|
+
return e
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseError = await handleSpecificErrors(e, step, process)
|
|
20
|
+
|
|
21
|
+
return new SDKError(baseError, step, process)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handleSpecificErrors = async (
|
|
25
|
+
e: any,
|
|
26
|
+
step?: LiFiStep,
|
|
27
|
+
process?: Process
|
|
28
|
+
) => {
|
|
29
|
+
if (e.cause?.name === 'UserRejectedRequestError') {
|
|
30
|
+
return new TransactionError(LiFiErrorCode.SignatureRejected, e.message, e)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
step &&
|
|
35
|
+
process?.txHash &&
|
|
36
|
+
e.code === LiFiErrorCode.TransactionFailed &&
|
|
37
|
+
e.message === ErrorMessage.TransactionReverted
|
|
38
|
+
) {
|
|
39
|
+
const response = await fetchTxErrorDetails(
|
|
40
|
+
process.txHash,
|
|
41
|
+
step.action.fromChainId
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const errorMessage = response?.error_message
|
|
45
|
+
|
|
46
|
+
if (errorMessage?.toLowerCase().includes('out of gas')) {
|
|
47
|
+
return new TransactionError(
|
|
48
|
+
LiFiErrorCode.GasLimitError,
|
|
49
|
+
ErrorMessage.GasLimitLow,
|
|
50
|
+
e
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (e instanceof BaseError) {
|
|
56
|
+
return e
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new UnknownError(e.message || ErrorMessage.UnknownError, e)
|
|
60
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChainId } from '@lifi/types'
|
|
2
2
|
import type { PublicClient } from 'viem'
|
|
3
|
-
import { createPublicClient, fallback, http } from 'viem'
|
|
3
|
+
import { createPublicClient, fallback, http, webSocket } from 'viem'
|
|
4
4
|
import { mainnet, type Chain } from 'viem/chains'
|
|
5
5
|
import { config } from '../../config.js'
|
|
6
6
|
import { getRpcUrls } from '../rpc.js'
|
|
@@ -19,9 +19,11 @@ export const getPublicClient = async (
|
|
|
19
19
|
if (!publicClients[chainId]) {
|
|
20
20
|
const urls = await getRpcUrls(chainId)
|
|
21
21
|
const fallbackTransports = urls.map((url) =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
url.startsWith('wss')
|
|
23
|
+
? webSocket(url)
|
|
24
|
+
: http(url, {
|
|
25
|
+
batch: true,
|
|
26
|
+
})
|
|
25
27
|
)
|
|
26
28
|
const _chain = await config.getChainById(chainId)
|
|
27
29
|
const chain: Chain = {
|
|
@@ -43,6 +45,9 @@ export const getPublicClient = async (
|
|
|
43
45
|
publicClients[chainId] = createPublicClient({
|
|
44
46
|
chain: chain,
|
|
45
47
|
transport: fallback(fallbackTransports),
|
|
48
|
+
batch: {
|
|
49
|
+
multicall: true,
|
|
50
|
+
},
|
|
46
51
|
})
|
|
47
52
|
}
|
|
48
53
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { WalletClient } from 'viem'
|
|
2
|
-
import { LiFiErrorCode
|
|
2
|
+
import { LiFiErrorCode } from '../../errors/constants.js'
|
|
3
|
+
import { ProviderError } from '../../errors/errors.js'
|
|
3
4
|
import type { StatusManager } from '../StatusManager.js'
|
|
4
5
|
import type { LiFiStepExtended, SwitchChainHook } from '../types.js'
|
|
5
6
|
|
|
@@ -6,15 +6,13 @@ import {
|
|
|
6
6
|
type SendOptions,
|
|
7
7
|
type SignatureResult,
|
|
8
8
|
} from '@solana/web3.js'
|
|
9
|
+
import bs58 from 'bs58'
|
|
9
10
|
import { config } from '../../config.js'
|
|
11
|
+
import { LiFiErrorCode } from '../../errors/constants.js'
|
|
12
|
+
import { TransactionError } from '../../errors/errors.js'
|
|
10
13
|
import { getStepTransaction } from '../../services/api.js'
|
|
11
14
|
import { base64ToUint8Array } from '../../utils/base64ToUint8Array.js'
|
|
12
|
-
import {
|
|
13
|
-
LiFiErrorCode,
|
|
14
|
-
TransactionError,
|
|
15
|
-
getTransactionFailedMessage,
|
|
16
|
-
parseError,
|
|
17
|
-
} from '../../utils/index.js'
|
|
15
|
+
import { getTransactionFailedMessage } from '../../utils/index.js'
|
|
18
16
|
import { BaseStepExecutor } from '../BaseStepExecutor.js'
|
|
19
17
|
import { checkBalance } from '../checkBalance.js'
|
|
20
18
|
import { getSubstatusMessage } from '../processMessages.js'
|
|
@@ -27,15 +25,12 @@ import type {
|
|
|
27
25
|
import { sleep } from '../utils.js'
|
|
28
26
|
import { waitForReceivingTransaction } from '../waitForReceivingTransaction.js'
|
|
29
27
|
import { getSolanaConnection } from './connection.js'
|
|
28
|
+
import { parseSolanaErrors } from './parseSolanaErrors.js'
|
|
30
29
|
|
|
31
30
|
export interface SolanaStepExecutorOptions extends StepExecutorOptions {
|
|
32
31
|
walletAdapter: SignerWalletAdapter
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
const TX_RETRY_INTERVAL = 1000
|
|
36
|
-
// https://solana.com/docs/advanced/confirmation
|
|
37
|
-
const TIMEOUT_PERIOD = 60_000
|
|
38
|
-
|
|
39
34
|
export class SolanaStepExecutor extends BaseStepExecutor {
|
|
40
35
|
private walletAdapter: SignerWalletAdapter
|
|
41
36
|
|
|
@@ -149,8 +144,23 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
149
144
|
|
|
150
145
|
this.checkWalletAdapter(step)
|
|
151
146
|
|
|
152
|
-
const
|
|
153
|
-
|
|
147
|
+
const signedTxPromise =
|
|
148
|
+
this.walletAdapter.signTransaction(versionedTransaction)
|
|
149
|
+
|
|
150
|
+
// We give users 2 minutes to sign the transaction or it should be considered expired
|
|
151
|
+
const signedTx = await Promise.race([
|
|
152
|
+
signedTxPromise,
|
|
153
|
+
// https://solana.com/docs/advanced/confirmation#transaction-expiration
|
|
154
|
+
// Use 2 minutes to account for fluctuations
|
|
155
|
+
sleep(120_000),
|
|
156
|
+
])
|
|
157
|
+
|
|
158
|
+
if (!signedTx) {
|
|
159
|
+
throw new TransactionError(
|
|
160
|
+
LiFiErrorCode.TransactionExpired,
|
|
161
|
+
'Transaction has expired: blockhash is no longer recent enough.'
|
|
162
|
+
)
|
|
163
|
+
}
|
|
154
164
|
|
|
155
165
|
process = this.statusManager.updateProcess(
|
|
156
166
|
step,
|
|
@@ -158,21 +168,31 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
158
168
|
'PENDING'
|
|
159
169
|
)
|
|
160
170
|
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
171
|
+
const simulationResult = await connection.simulateTransaction(
|
|
172
|
+
signedTx,
|
|
173
|
+
{
|
|
174
|
+
commitment: 'processed',
|
|
175
|
+
replaceRecentBlockhash: true,
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if (simulationResult.value.err) {
|
|
180
|
+
throw new TransactionError(
|
|
181
|
+
LiFiErrorCode.TransactionSimulationFailed,
|
|
182
|
+
'Transaction simulation failed'
|
|
183
|
+
)
|
|
170
184
|
}
|
|
171
185
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
// Create transaction hash (signature)
|
|
187
|
+
const txSignature = bs58.encode(signedTx.signatures[0])
|
|
188
|
+
|
|
189
|
+
// A known weirdness - MAX_RECENT_BLOCKHASHES is 300
|
|
190
|
+
// https://github.com/solana-labs/solana/blob/master/sdk/program/src/clock.rs#L123
|
|
191
|
+
// but MAX_PROCESSING_AGE is 150
|
|
192
|
+
// https://github.com/solana-labs/solana/blob/master/sdk/program/src/clock.rs#L129
|
|
193
|
+
// the blockhash queue in the bank tells you 300 + current slot, but it won't be accepted 150 blocks later.
|
|
194
|
+
// https://solana.com/docs/advanced/confirmation#transaction-expiration
|
|
195
|
+
const lastValidBlockHeight = blockhashResult.lastValidBlockHeight - 150
|
|
176
196
|
|
|
177
197
|
// In the following section, we wait and constantly check for the transaction to be confirmed
|
|
178
198
|
// and resend the transaction if it is not confirmed within a certain time interval
|
|
@@ -183,7 +203,7 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
183
203
|
{
|
|
184
204
|
signature: txSignature,
|
|
185
205
|
blockhash: blockhashResult.blockhash,
|
|
186
|
-
lastValidBlockHeight:
|
|
206
|
+
lastValidBlockHeight: lastValidBlockHeight,
|
|
187
207
|
abortSignal: abortController.signal,
|
|
188
208
|
},
|
|
189
209
|
'confirmed'
|
|
@@ -191,20 +211,34 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
191
211
|
.then((result) => result.value)
|
|
192
212
|
|
|
193
213
|
let confirmedTx: SignatureResult | null = null
|
|
194
|
-
|
|
214
|
+
let blockHeight = await connection.getBlockHeight()
|
|
215
|
+
|
|
216
|
+
const rawTransactionOptions: SendOptions = {
|
|
217
|
+
// We can skip preflight check after the first transaction has been sent
|
|
218
|
+
// https://solana.com/docs/advanced/retry#the-cost-of-skipping-preflight
|
|
219
|
+
skipPreflight: true,
|
|
220
|
+
// Setting max retries to 0 as we are handling retries manually
|
|
221
|
+
maxRetries: 0,
|
|
222
|
+
// https://solana.com/docs/advanced/confirmation#use-an-appropriate-preflight-commitment-level
|
|
223
|
+
preflightCommitment: 'confirmed',
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const signedTxSerialized = signedTx.serialize()
|
|
195
227
|
|
|
196
|
-
|
|
228
|
+
// https://solana.com/docs/advanced/retry#customizing-rebroadcast-logic
|
|
229
|
+
while (!confirmedTx && blockHeight < lastValidBlockHeight) {
|
|
197
230
|
await connection.sendRawTransaction(
|
|
198
|
-
|
|
231
|
+
signedTxSerialized,
|
|
199
232
|
rawTransactionOptions
|
|
200
233
|
)
|
|
201
234
|
confirmedTx = await Promise.race([
|
|
202
235
|
confirmTransactionPromise,
|
|
203
|
-
sleep(
|
|
236
|
+
sleep(1000),
|
|
204
237
|
])
|
|
205
238
|
if (confirmedTx) {
|
|
206
239
|
break
|
|
207
240
|
}
|
|
241
|
+
blockHeight = await connection.getBlockHeight()
|
|
208
242
|
}
|
|
209
243
|
|
|
210
244
|
// Stop waiting for tx confirmation
|
|
@@ -233,7 +267,7 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
233
267
|
if (!confirmedTx) {
|
|
234
268
|
throw new TransactionError(
|
|
235
269
|
LiFiErrorCode.TransactionExpired,
|
|
236
|
-
'
|
|
270
|
+
'Transaction has expired: The block height has exceeded the maximum allowed limit.'
|
|
237
271
|
)
|
|
238
272
|
}
|
|
239
273
|
|
|
@@ -252,15 +286,14 @@ export class SolanaStepExecutor extends BaseStepExecutor {
|
|
|
252
286
|
process = this.statusManager.updateProcess(step, process.type, 'DONE')
|
|
253
287
|
}
|
|
254
288
|
} catch (e: any) {
|
|
255
|
-
const error = await
|
|
289
|
+
const error = await parseSolanaErrors(e, step, process)
|
|
256
290
|
process = this.statusManager.updateProcess(
|
|
257
291
|
step,
|
|
258
292
|
process.type,
|
|
259
293
|
'FAILED',
|
|
260
294
|
{
|
|
261
295
|
error: {
|
|
262
|
-
message: error.message,
|
|
263
|
-
htmlMessage: error.htmlMessage,
|
|
296
|
+
message: error.cause.message,
|
|
264
297
|
code: error.code,
|
|
265
298
|
},
|
|
266
299
|
}
|
|
@@ -5,9 +5,8 @@ import { getRpcUrl } from '../rpc.js'
|
|
|
5
5
|
let connection: Connection | undefined = undefined
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* getSolanaConnection is just a thin wrapper around getting the
|
|
9
|
-
*
|
|
10
|
-
* @returns - Solana rpc connection
|
|
8
|
+
* getSolanaConnection is just a thin wrapper around getting the connection (RPC provider) for Solana
|
|
9
|
+
* @returns - Solana RPC connection
|
|
11
10
|
*/
|
|
12
11
|
export const getSolanaConnection = async (): Promise<Connection> => {
|
|
13
12
|
if (!connection) {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { LiFiStep, Process } from '@lifi/types'
|
|
2
|
+
import { BaseError } from '../../errors/baseError.js'
|
|
3
|
+
import { ErrorMessage, LiFiErrorCode } from '../../errors/constants.js'
|
|
4
|
+
import { TransactionError, UnknownError } from '../../errors/errors.js'
|
|
5
|
+
import { SDKError } from '../../errors/SDKError.js'
|
|
6
|
+
|
|
7
|
+
export const parseSolanaErrors = async (
|
|
8
|
+
e: Error,
|
|
9
|
+
step?: LiFiStep,
|
|
10
|
+
process?: Process
|
|
11
|
+
): Promise<SDKError> => {
|
|
12
|
+
if (e instanceof SDKError) {
|
|
13
|
+
e.step = e.step ?? step
|
|
14
|
+
e.process = e.process ?? process
|
|
15
|
+
return e
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const baseError = handleSpecificErrors(e)
|
|
19
|
+
|
|
20
|
+
return new SDKError(baseError, step, process)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleSpecificErrors = (e: any) => {
|
|
24
|
+
if (e.name === 'WalletSignTransactionError') {
|
|
25
|
+
return new TransactionError(LiFiErrorCode.SignatureRejected, e.message, e)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (e.name === 'SendTransactionError') {
|
|
29
|
+
return new TransactionError(LiFiErrorCode.TransactionFailed, e.message, e)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (e.message?.includes('simulate')) {
|
|
33
|
+
return new TransactionError(
|
|
34
|
+
LiFiErrorCode.TransactionSimulationFailed,
|
|
35
|
+
e.message,
|
|
36
|
+
e
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (e instanceof BaseError) {
|
|
41
|
+
return e
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return new UnknownError(e.message || ErrorMessage.UnknownError, e)
|
|
45
|
+
}
|
package/src/core/checkBalance.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LiFiStep } from '@lifi/types'
|
|
2
2
|
import { formatUnits } from 'viem'
|
|
3
3
|
import { getTokenBalance } from '../services/balance.js'
|
|
4
|
-
import { BalanceError } from '../
|
|
4
|
+
import { BalanceError } from '../errors/errors.js'
|
|
5
5
|
import { sleep } from './utils.js'
|
|
6
6
|
|
|
7
7
|
export const checkBalance = async (
|
|
@@ -40,7 +40,7 @@ export const checkBalance = async (
|
|
|
40
40
|
`start a new one with a maximum of ${current} ${token.symbol}.`
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
throw new BalanceError('The balance is too low.'
|
|
43
|
+
throw new BalanceError('The balance is too low.')
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LiFiStep } from '@lifi/types'
|
|
2
|
-
import { LiFiErrorCode
|
|
2
|
+
import { LiFiErrorCode } from '../errors/constants.js'
|
|
3
|
+
import { TransactionError } from '../errors/errors.js'
|
|
3
4
|
import type { StatusManager } from './StatusManager.js'
|
|
4
5
|
import type { ExecutionOptions } from './types.js'
|
|
5
6
|
import { checkStepSlippageThreshold } from './utils.js'
|
|
@@ -41,9 +42,7 @@ export const stepComparison = async (
|
|
|
41
42
|
// The user declined the new exchange rate, so we are not going to proceed
|
|
42
43
|
throw new TransactionError(
|
|
43
44
|
LiFiErrorCode.ExchangeRateUpdateCanceled,
|
|
44
|
-
|
|
45
|
-
`Transaction was not sent, your funds are still in your wallet.
|
|
46
|
-
The exchange rate has changed and the previous estimation can not be fulfilled due to value loss.`
|
|
45
|
+
`Exchange rate has changed!\nTransaction was not sent, your funds are still in your wallet.\nThe exchange rate has changed and the previous estimation can not be fulfilled due to value loss.`
|
|
47
46
|
)
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
StatusResponse,
|
|
6
6
|
} from '@lifi/types'
|
|
7
7
|
import { getStatus } from '../services/api.js'
|
|
8
|
-
import { ServerError } from '../
|
|
8
|
+
import { ServerError } from '../errors/errors.js'
|
|
9
9
|
import { repeatUntilDone } from '../utils/utils.js'
|
|
10
10
|
import type { StatusManager } from './StatusManager.js'
|
|
11
11
|
import { getSubstatusMessage } from './processMessages.js'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LiFiStep, Process } from '@lifi/types'
|
|
2
|
+
import { version } from '../version.js'
|
|
3
|
+
import type { BaseError } from './baseError.js'
|
|
4
|
+
import { type ErrorCode } from './constants.js'
|
|
5
|
+
|
|
6
|
+
// Note: SDKError is used to wrapper and present errors at the top level
|
|
7
|
+
// Where opportunity allows we also add the step and the process related to the error
|
|
8
|
+
export class SDKError extends Error {
|
|
9
|
+
step?: LiFiStep
|
|
10
|
+
process?: Process
|
|
11
|
+
code: ErrorCode
|
|
12
|
+
override name = 'SDKError'
|
|
13
|
+
override cause: BaseError
|
|
14
|
+
|
|
15
|
+
constructor(cause: BaseError, step?: LiFiStep, process?: Process) {
|
|
16
|
+
const errorMessage = `${cause.message ? `[${cause.name}] ${cause.message}` : 'Unknown error occurred'}\nLI.FI SDK version: ${version}`
|
|
17
|
+
super(errorMessage)
|
|
18
|
+
this.name = 'SDKError'
|
|
19
|
+
this.step = step
|
|
20
|
+
this.process = process
|
|
21
|
+
this.cause = cause
|
|
22
|
+
this.stack = this.cause.stack
|
|
23
|
+
this.code = cause.code
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ErrorCode, ErrorName } from './constants.js'
|
|
2
|
+
import { getRootCause } from './utils/rootCause.js'
|
|
3
|
+
|
|
4
|
+
// Note: we use the BaseErrors to capture errors at specific points in the code
|
|
5
|
+
// they can carry addition to help give more context
|
|
6
|
+
export class BaseError extends Error {
|
|
7
|
+
code: ErrorCode
|
|
8
|
+
override cause?: Error
|
|
9
|
+
|
|
10
|
+
constructor(name: ErrorName, code: number, message: string, cause?: Error) {
|
|
11
|
+
super(message)
|
|
12
|
+
|
|
13
|
+
this.name = name
|
|
14
|
+
this.code = code
|
|
15
|
+
this.cause = cause
|
|
16
|
+
|
|
17
|
+
const rootCause = getRootCause(this.cause)
|
|
18
|
+
if (rootCause && rootCause.stack) {
|
|
19
|
+
this.stack = rootCause.stack
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export enum ErrorName {
|
|
2
|
+
RPCError = 'RPCError',
|
|
3
|
+
ProviderError = 'ProviderError',
|
|
4
|
+
ServerError = 'ServerError',
|
|
5
|
+
TransactionError = 'TransactionError',
|
|
6
|
+
ValidationError = 'ValidationError',
|
|
7
|
+
BalanceError = 'BalanceError',
|
|
8
|
+
NotFoundError = 'NotFoundError',
|
|
9
|
+
UnknownError = 'UnknownError',
|
|
10
|
+
SlippageError = 'SlippageError',
|
|
11
|
+
HTTPError = 'HTTPError',
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type ErrorCode = LiFiErrorCode
|
|
15
|
+
|
|
16
|
+
export enum LiFiErrorCode {
|
|
17
|
+
InternalError = 1000,
|
|
18
|
+
ValidationError = 1001,
|
|
19
|
+
TransactionUnderpriced = 1002,
|
|
20
|
+
TransactionFailed = 1003,
|
|
21
|
+
Timeout = 1004,
|
|
22
|
+
ProviderUnavailable = 1005,
|
|
23
|
+
NotFound = 1006,
|
|
24
|
+
ChainSwitchError = 1007,
|
|
25
|
+
TransactionUnprepared = 1008,
|
|
26
|
+
GasLimitError = 1009,
|
|
27
|
+
TransactionCanceled = 1010,
|
|
28
|
+
SlippageError = 1011,
|
|
29
|
+
SignatureRejected = 1012,
|
|
30
|
+
BalanceError = 1013,
|
|
31
|
+
AllowanceRequired = 1014,
|
|
32
|
+
InsufficientFunds = 1015,
|
|
33
|
+
ExchangeRateUpdateCanceled = 1016,
|
|
34
|
+
WalletChangedDuringExecution = 1017,
|
|
35
|
+
TransactionExpired = 1018,
|
|
36
|
+
TransactionSimulationFailed = 1019,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export enum ErrorMessage {
|
|
40
|
+
UnknownError = 'Unknown error occurred.',
|
|
41
|
+
SlippageError = 'The slippage is larger than the defined threshold. Please request a new route to get a fresh quote.',
|
|
42
|
+
GasLimitLow = 'Gas limit is too low.',
|
|
43
|
+
TransactionUnderpriced = 'Transaction is underpriced.',
|
|
44
|
+
TransactionReverted = 'Transaction was reverted.',
|
|
45
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ErrorName, LiFiErrorCode } from './constants.js'
|
|
2
|
+
import { BaseError } from './baseError.js'
|
|
3
|
+
|
|
4
|
+
export class RPCError extends BaseError {
|
|
5
|
+
constructor(code: LiFiErrorCode, message: string, cause?: Error) {
|
|
6
|
+
super(ErrorName.RPCError, code, message, cause)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ProviderError extends BaseError {
|
|
11
|
+
constructor(code: LiFiErrorCode, message: string, cause?: Error) {
|
|
12
|
+
super(ErrorName.ProviderError, code, message, cause)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TransactionError extends BaseError {
|
|
17
|
+
constructor(code: LiFiErrorCode, message: string, cause?: Error) {
|
|
18
|
+
super(ErrorName.TransactionError, code, message, cause)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class UnknownError extends BaseError {
|
|
23
|
+
constructor(message: string, cause?: Error) {
|
|
24
|
+
super(ErrorName.UnknownError, LiFiErrorCode.InternalError, message, cause)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class BalanceError extends BaseError {
|
|
29
|
+
constructor(message: string, cause?: Error) {
|
|
30
|
+
super(ErrorName.BalanceError, LiFiErrorCode.BalanceError, message, cause)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ServerError extends BaseError {
|
|
35
|
+
constructor(message: string) {
|
|
36
|
+
super(ErrorName.ServerError, LiFiErrorCode.InternalError, message)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ValidationError extends BaseError {
|
|
41
|
+
constructor(message: string) {
|
|
42
|
+
super(ErrorName.ValidationError, LiFiErrorCode.ValidationError, message)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { UnavailableRoutes } from '@lifi/types'
|
|
2
|
+
import { LiFiErrorCode } from './constants.js'
|
|
3
|
+
import { BaseError } from './baseError.js'
|
|
4
|
+
import type { ExtendedRequestInit } from '../types/request.js'
|
|
5
|
+
import { ErrorName, ErrorMessage } from './constants.js'
|
|
6
|
+
|
|
7
|
+
interface ServerErrorResponseBody {
|
|
8
|
+
code: number
|
|
9
|
+
message: string
|
|
10
|
+
errors?: UnavailableRoutes
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const statusCodeToErrorClassificationMap = new Map([
|
|
14
|
+
[
|
|
15
|
+
400,
|
|
16
|
+
{ type: ErrorName.ValidationError, code: LiFiErrorCode.ValidationError },
|
|
17
|
+
],
|
|
18
|
+
[404, { type: ErrorName.NotFoundError, code: LiFiErrorCode.NotFound }],
|
|
19
|
+
[
|
|
20
|
+
409,
|
|
21
|
+
{
|
|
22
|
+
type: ErrorName.SlippageError,
|
|
23
|
+
code: LiFiErrorCode.SlippageError,
|
|
24
|
+
message: ErrorMessage.SlippageError,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[500, { type: ErrorName.ServerError, code: LiFiErrorCode.InternalError }],
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
const getErrorClassificationFromStatusCode = (code: number) =>
|
|
31
|
+
statusCodeToErrorClassificationMap.get(code) ?? {
|
|
32
|
+
type: ErrorName.ServerError,
|
|
33
|
+
code: LiFiErrorCode.InternalError,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const createInitialMessage = (response: Response) => {
|
|
37
|
+
const statusCode =
|
|
38
|
+
response.status || response.status === 0 ? response.status : ''
|
|
39
|
+
const title = response.statusText || ''
|
|
40
|
+
const status = `${statusCode} ${title}`.trim()
|
|
41
|
+
const reason = status ? `status code ${status}` : 'an unknown error'
|
|
42
|
+
return `Request failed with ${reason}`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class HTTPError extends BaseError {
|
|
46
|
+
public response: Response
|
|
47
|
+
public status: number
|
|
48
|
+
public url: RequestInfo | URL
|
|
49
|
+
public fetchOptions: ExtendedRequestInit
|
|
50
|
+
public type?: ErrorName
|
|
51
|
+
public responseBody?: ServerErrorResponseBody
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
response: Response,
|
|
55
|
+
url: RequestInfo | URL,
|
|
56
|
+
options: ExtendedRequestInit
|
|
57
|
+
) {
|
|
58
|
+
const errorClassification = getErrorClassificationFromStatusCode(
|
|
59
|
+
response.status
|
|
60
|
+
)
|
|
61
|
+
const additionalMessage = errorClassification?.message
|
|
62
|
+
? `\n${errorClassification.message}`
|
|
63
|
+
: ''
|
|
64
|
+
const message = createInitialMessage(response) + additionalMessage
|
|
65
|
+
|
|
66
|
+
super(ErrorName.HTTPError, errorClassification.code, message)
|
|
67
|
+
|
|
68
|
+
this.type = errorClassification.type
|
|
69
|
+
this.response = response
|
|
70
|
+
this.status = response.status
|
|
71
|
+
this.message = message
|
|
72
|
+
this.url = url
|
|
73
|
+
this.fetchOptions = options
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async buildAdditionalDetails() {
|
|
77
|
+
if (this.type) {
|
|
78
|
+
this.message = `[${this.type}] ${this.message}`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
this.responseBody = await this.response.json()
|
|
83
|
+
|
|
84
|
+
const spacer = '\n '
|
|
85
|
+
|
|
86
|
+
if (this.responseBody) {
|
|
87
|
+
this.message += `${spacer}responseMessage: ${this.responseBody?.message.toString().replaceAll('\n', spacer)}`
|
|
88
|
+
}
|
|
89
|
+
} catch {}
|
|
90
|
+
|
|
91
|
+
return this
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseError } from '../baseError.js'
|
|
2
|
+
import { HTTPError } from '../httpError.js'
|
|
3
|
+
|
|
4
|
+
export const getRootCauseBaseError = (e: Error) => {
|
|
5
|
+
let rootCause = e
|
|
6
|
+
while (rootCause.cause && rootCause.cause instanceof BaseError) {
|
|
7
|
+
rootCause = rootCause.cause as BaseError
|
|
8
|
+
}
|
|
9
|
+
return rootCause as BaseError
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getRootCauseBaseErrorMessage = (e: Error) => {
|
|
13
|
+
const rootCause = getRootCauseBaseError(e)
|
|
14
|
+
|
|
15
|
+
return rootCause instanceof HTTPError
|
|
16
|
+
? (rootCause as HTTPError).responseBody?.message || rootCause.message
|
|
17
|
+
: rootCause.message
|
|
18
|
+
}
|