@lifi/sdk 4.3.0 → 4.4.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/CHANGELOG.md +12 -0
- package/dist/cjs/actions/getContractCallsQuote.js.map +1 -1
- package/dist/cjs/actions/getTokenBalances.js +3 -2
- package/dist/cjs/actions/getTokenBalances.js.map +1 -1
- package/dist/cjs/client/getClientStorage.js +3 -2
- package/dist/cjs/client/getClientStorage.js.map +1 -1
- package/dist/cjs/core/BaseStepExecutor.js +11 -9
- package/dist/cjs/core/BaseStepExecutor.js.map +1 -1
- package/dist/cjs/core/StatusManager.js +1 -4
- package/dist/cjs/core/StatusManager.js.map +1 -1
- package/dist/cjs/core/tasks/CheckBalanceTask.js +2 -1
- package/dist/cjs/core/tasks/CheckBalanceTask.js.map +1 -1
- package/dist/cjs/core/tasks/PrepareTransactionTask.d.ts +7 -0
- package/dist/cjs/core/tasks/PrepareTransactionTask.js +12 -2
- package/dist/cjs/core/tasks/PrepareTransactionTask.js.map +1 -1
- package/dist/cjs/core/tasks/helpers/checkBalance.js +1 -1
- package/dist/cjs/core/tasks/helpers/checkBalance.js.map +1 -1
- package/dist/cjs/utils/checkPackageUpdates.js +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/version.js.map +1 -1
- package/dist/esm/actions/getTokenBalances.js +3 -2
- package/dist/esm/actions/getTokenBalances.js.map +1 -1
- package/dist/esm/client/getClientStorage.js +3 -2
- package/dist/esm/client/getClientStorage.js.map +1 -1
- package/dist/esm/core/BaseStepExecutor.js +11 -9
- package/dist/esm/core/BaseStepExecutor.js.map +1 -1
- package/dist/esm/core/StatusManager.js +1 -4
- package/dist/esm/core/StatusManager.js.map +1 -1
- package/dist/esm/core/tasks/CheckBalanceTask.js +2 -1
- package/dist/esm/core/tasks/CheckBalanceTask.js.map +1 -1
- package/dist/esm/core/tasks/PrepareTransactionTask.d.ts +7 -0
- package/dist/esm/core/tasks/PrepareTransactionTask.d.ts.map +1 -1
- package/dist/esm/core/tasks/PrepareTransactionTask.js +12 -2
- package/dist/esm/core/tasks/PrepareTransactionTask.js.map +1 -1
- package/dist/esm/core/tasks/WaitForTransactionStatusTask.js +2 -1
- package/dist/esm/core/tasks/WaitForTransactionStatusTask.js.map +1 -1
- package/dist/esm/core/tasks/helpers/checkBalance.js +1 -1
- package/dist/esm/core/tasks/helpers/checkBalance.js.map +1 -1
- package/dist/esm/utils/checkPackageUpdates.js +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/package.json +2 -2
- package/src/client/getClientStorage.ts +1 -0
- package/src/core/tasks/PrepareTransactionTask.ts +11 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @lifi/sdk
|
|
2
2
|
|
|
3
|
+
## 4.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`fd1e9b5`](https://github.com/lifinance/sdk/commit/fd1e9b5ff481b35683d7b8557011c9c726446cdd) Thanks [@chybisov](https://github.com/chybisov)! - Add Stellar (STL) ecosystem support. Introduces the `@lifi/sdk-provider-stellar` package — address validation, Federation resolution, SAC-based balance reads, and route execution via `StellarStepExecutor` — and registers `ChainType.STL` in the SDK client's chain fetching so Stellar chains and RPC URLs load.
|
|
8
|
+
|
|
9
|
+
Execution signs the backend-generated Soroban envelope with the connected wallet, submits it over Stellar RPC, waits for on-chain confirmation, then tracks the transfer through the LI.FI status API. Because a Stellar envelope embeds the sender's account sequence number and a short-lived set of timebounds, the provider always requests a freshly built transaction immediately before signing rather than reusing the one attached to the quote. SAC allowances are granted first when a step requires one, and the approval is confirmed on-chain before the route transaction is requested.
|
|
10
|
+
|
|
11
|
+
`PrepareTransactionTask` gains a `shouldRefetchTransaction` hook. It defaults to the previous behaviour — fetch only when the step carries no transaction request — and the Stellar task overrides it, because a Stellar envelope embeds the sender's sequence number and cannot be reused.
|
|
12
|
+
|
|
13
|
+
`StellarProviderOptions.horizonUrl` is gone — nothing read it — and `StellarWallet.signAuthEntry` is now optional, because the router routes use source-account auth and the SDK never calls it. The provider also exports `DEFAULT_NETWORK_PASSPHRASE`, and refuses to sign when the connected wallet's network disagrees with the configured one.
|
|
14
|
+
|
|
3
15
|
## 4.3.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getContractCallsQuote.js","names":["SDKError","ValidationError","request"],"sources":["../../../src/actions/getContractCallsQuote.ts"],"sourcesContent":["import type {\n ContractCallsQuoteRequest,\n LiFiStep,\n RequestOptions,\n} from '@lifi/types'\nimport {\n isContractCallsRequestWithFromAmount,\n isContractCallsRequestWithToAmount,\n} from '@lifi/types'\nimport { ValidationError } from '../errors/errors.js'\nimport { SDKError } from '../errors/SDKError.js'\nimport type { SDKClient } from '../types/core.js'\nimport { request } from '../utils/request.js'\n\n/**\n * Get a quote for a destination contract call\n * @param client - The SDK client\n * @param params - The configuration of the requested destination call\n * @param options - Request options\n * @throws {LiFiError} - Throws a LiFiError if request fails\n * @returns - Returns step.\n */\nexport const getContractCallsQuote = async (\n client: SDKClient,\n params: ContractCallsQuoteRequest,\n options?: RequestOptions\n): Promise<LiFiStep> => {\n // validation\n const requiredParameters: Array<keyof ContractCallsQuoteRequest> = [\n 'fromChain',\n 'fromToken',\n 'fromAddress',\n 'toChain',\n 'toToken',\n 'contractCalls',\n ]\n for (const requiredParameter of requiredParameters) {\n if (!params[requiredParameter]) {\n throw new SDKError(\n new ValidationError(\n `Required parameter \"${requiredParameter}\" is missing.`\n )\n )\n }\n }\n if (\n !isContractCallsRequestWithFromAmount(params) &&\n !isContractCallsRequestWithToAmount(params)\n ) {\n throw new SDKError(\n new ValidationError(\n `Required parameter \"fromAmount\" or \"toAmount\" is missing.`\n )\n )\n }\n // apply defaults\n // option.order is not used in this endpoint\n params.integrator ??= client.config.integrator\n params.slippage ??= client.config.routeOptions?.slippage\n params.referrer ??= client.config.routeOptions?.referrer\n params.fee ??= client.config.routeOptions?.fee\n params.allowBridges ??= client.config.routeOptions?.bridges?.allow\n params.denyBridges ??= client.config.routeOptions?.bridges?.deny\n params.preferBridges ??= client.config.routeOptions?.bridges?.prefer\n params.allowExchanges ??= client.config.routeOptions?.exchanges?.allow\n params.denyExchanges ??= client.config.routeOptions?.exchanges?.deny\n params.preferExchanges ??= client.config.routeOptions?.exchanges?.prefer\n // send request\n return await request<LiFiStep>(\n client.config,\n `${client.config.apiUrl}/quote/contractCalls`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params),\n signal: options?.signal,\n }\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,MAAa,wBAAwB,OACnC,QACA,QACA,YACsB;CAUtB,KAAK,MAAM,qBAAqB;EAP9B;EACA;EACA;EACA;EACA;EACA;CAE+C,GAC/C,IAAI,CAAC,OAAO,oBACV,MAAM,IAAIA,wBAAAA,SACR,IAAIC,sBAAAA,gBACF,uBAAuB,kBAAkB,cAC3C,CACF;CAGJ,IACE,EAAA,
|
|
1
|
+
{"version":3,"file":"getContractCallsQuote.js","names":["SDKError","ValidationError","isContractCallsRequestWithFromAmount","isContractCallsRequestWithToAmount","request"],"sources":["../../../src/actions/getContractCallsQuote.ts"],"sourcesContent":["import type {\n ContractCallsQuoteRequest,\n LiFiStep,\n RequestOptions,\n} from '@lifi/types'\nimport {\n isContractCallsRequestWithFromAmount,\n isContractCallsRequestWithToAmount,\n} from '@lifi/types'\nimport { ValidationError } from '../errors/errors.js'\nimport { SDKError } from '../errors/SDKError.js'\nimport type { SDKClient } from '../types/core.js'\nimport { request } from '../utils/request.js'\n\n/**\n * Get a quote for a destination contract call\n * @param client - The SDK client\n * @param params - The configuration of the requested destination call\n * @param options - Request options\n * @throws {LiFiError} - Throws a LiFiError if request fails\n * @returns - Returns step.\n */\nexport const getContractCallsQuote = async (\n client: SDKClient,\n params: ContractCallsQuoteRequest,\n options?: RequestOptions\n): Promise<LiFiStep> => {\n // validation\n const requiredParameters: Array<keyof ContractCallsQuoteRequest> = [\n 'fromChain',\n 'fromToken',\n 'fromAddress',\n 'toChain',\n 'toToken',\n 'contractCalls',\n ]\n for (const requiredParameter of requiredParameters) {\n if (!params[requiredParameter]) {\n throw new SDKError(\n new ValidationError(\n `Required parameter \"${requiredParameter}\" is missing.`\n )\n )\n }\n }\n if (\n !isContractCallsRequestWithFromAmount(params) &&\n !isContractCallsRequestWithToAmount(params)\n ) {\n throw new SDKError(\n new ValidationError(\n `Required parameter \"fromAmount\" or \"toAmount\" is missing.`\n )\n )\n }\n // apply defaults\n // option.order is not used in this endpoint\n params.integrator ??= client.config.integrator\n params.slippage ??= client.config.routeOptions?.slippage\n params.referrer ??= client.config.routeOptions?.referrer\n params.fee ??= client.config.routeOptions?.fee\n params.allowBridges ??= client.config.routeOptions?.bridges?.allow\n params.denyBridges ??= client.config.routeOptions?.bridges?.deny\n params.preferBridges ??= client.config.routeOptions?.bridges?.prefer\n params.allowExchanges ??= client.config.routeOptions?.exchanges?.allow\n params.denyExchanges ??= client.config.routeOptions?.exchanges?.deny\n params.preferExchanges ??= client.config.routeOptions?.exchanges?.prefer\n // send request\n return await request<LiFiStep>(\n client.config,\n `${client.config.apiUrl}/quote/contractCalls`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(params),\n signal: options?.signal,\n }\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,MAAa,wBAAwB,OACnC,QACA,QACA,YACsB;CAUtB,KAAK,MAAM,qBAAqB;EAP9B;EACA;EACA;EACA;EACA;EACA;CAE+C,GAC/C,IAAI,CAAC,OAAO,oBACV,MAAM,IAAIA,wBAAAA,SACR,IAAIC,sBAAAA,gBACF,uBAAuB,kBAAkB,cAC3C,CACF;CAGJ,IACE,EAAA,GAACC,YAAAA,qCAAAA,CAAqC,MAAM,KAC5C,EAAA,GAACC,YAAAA,mCAAAA,CAAmC,MAAM,GAE1C,MAAM,IAAIH,wBAAAA,SACR,IAAIC,sBAAAA,gBACF,2DACF,CACF;CAIF,OAAO,eAAe,OAAO,OAAO;CACpC,OAAO,aAAa,OAAO,OAAO,cAAc;CAChD,OAAO,aAAa,OAAO,OAAO,cAAc;CAChD,OAAO,QAAQ,OAAO,OAAO,cAAc;CAC3C,OAAO,iBAAiB,OAAO,OAAO,cAAc,SAAS;CAC7D,OAAO,gBAAgB,OAAO,OAAO,cAAc,SAAS;CAC5D,OAAO,kBAAkB,OAAO,OAAO,cAAc,SAAS;CAC9D,OAAO,mBAAmB,OAAO,OAAO,cAAc,WAAW;CACjE,OAAO,kBAAkB,OAAO,OAAO,cAAc,WAAW;CAChE,OAAO,oBAAoB,OAAO,OAAO,cAAc,WAAW;CAElE,OAAO,MAAMG,sBAAAA,QACX,OAAO,QACP,GAAG,OAAO,OAAO,OAAO,uBACxB;EACE,QAAQ;EACR,SAAS,EACP,gBAAgB,mBAClB;EACA,MAAM,KAAK,UAAU,MAAM;EAC3B,QAAQ,SAAS;CACnB,CACF;AACF"}
|
|
@@ -2,11 +2,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_actions_getTokenBalancesByChain = require("./getTokenBalancesByChain.js");
|
|
3
3
|
//#region src/actions/getTokenBalances.ts
|
|
4
4
|
async function getTokenBalances(client, walletAddress, tokens) {
|
|
5
|
-
const
|
|
5
|
+
const tokensByChain = tokens.reduce((tokens, token) => {
|
|
6
6
|
if (!tokens[token.chainId]) tokens[token.chainId] = [];
|
|
7
7
|
tokens[token.chainId].push(token);
|
|
8
8
|
return tokens;
|
|
9
|
-
}, {})
|
|
9
|
+
}, {});
|
|
10
|
+
const tokenAmountsByChain = await require_actions_getTokenBalancesByChain.getTokenBalancesByChain(client, walletAddress, tokensByChain);
|
|
10
11
|
return Object.values(tokenAmountsByChain).flat();
|
|
11
12
|
}
|
|
12
13
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTokenBalances.js","names":["getTokenBalancesByChain"],"sources":["../../../src/actions/getTokenBalances.ts"],"sourcesContent":["import type {\n Token,\n TokenAmount,\n TokenAmountExtended,\n TokenExtended,\n} from '@lifi/types'\nimport type { SDKClient } from '../types/core.js'\nimport { getTokenBalancesByChain } from './getTokenBalancesByChain.js'\n\n/**\n * Returns the balances for a list tokens a wallet holds across all aggregated chains.\n * @param client - The SDK client.\n * @param walletAddress - A wallet address.\n * @param tokens - A list of Token (or TokenExtended) objects.\n * @returns A list of objects containing the tokens and the amounts on different chains.\n * @throws {ValidationError} Throws a ValidationError if validation fails.\n * @throws {Error} Throws an Error if the SDK Provider for the wallet address is not found.\n */\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]>\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: TokenExtended[]\n): Promise<TokenAmountExtended[]> {\n // split by chain\n const tokensByChain = tokens.reduce(\n (tokens, token) => {\n if (!tokens[token.chainId]) {\n tokens[token.chainId] = []\n }\n tokens[token.chainId].push(token)\n return tokens\n },\n {} as { [chainId: number]: Token[] | TokenExtended[] }\n )\n\n const tokenAmountsByChain = await getTokenBalancesByChain(\n client,\n walletAddress,\n tokensByChain\n )\n return Object.values(tokenAmountsByChain).flat()\n}\n"],"mappings":";;;AAuBA,eAAsB,iBACpB,QACA,eACA,QACgC;
|
|
1
|
+
{"version":3,"file":"getTokenBalances.js","names":["getTokenBalancesByChain"],"sources":["../../../src/actions/getTokenBalances.ts"],"sourcesContent":["import type {\n Token,\n TokenAmount,\n TokenAmountExtended,\n TokenExtended,\n} from '@lifi/types'\nimport type { SDKClient } from '../types/core.js'\nimport { getTokenBalancesByChain } from './getTokenBalancesByChain.js'\n\n/**\n * Returns the balances for a list tokens a wallet holds across all aggregated chains.\n * @param client - The SDK client.\n * @param walletAddress - A wallet address.\n * @param tokens - A list of Token (or TokenExtended) objects.\n * @returns A list of objects containing the tokens and the amounts on different chains.\n * @throws {ValidationError} Throws a ValidationError if validation fails.\n * @throws {Error} Throws an Error if the SDK Provider for the wallet address is not found.\n */\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]>\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: TokenExtended[]\n): Promise<TokenAmountExtended[]> {\n // split by chain\n const tokensByChain = tokens.reduce(\n (tokens, token) => {\n if (!tokens[token.chainId]) {\n tokens[token.chainId] = []\n }\n tokens[token.chainId].push(token)\n return tokens\n },\n {} as { [chainId: number]: Token[] | TokenExtended[] }\n )\n\n const tokenAmountsByChain = await getTokenBalancesByChain(\n client,\n walletAddress,\n tokensByChain\n )\n return Object.values(tokenAmountsByChain).flat()\n}\n"],"mappings":";;;AAuBA,eAAsB,iBACpB,QACA,eACA,QACgC;CAEhC,MAAM,gBAAgB,OAAO,QAC1B,QAAQ,UAAU;EACjB,IAAI,CAAC,OAAO,MAAM,UAChB,OAAO,MAAM,WAAW,CAAC;EAE3B,OAAO,MAAM,QAAQ,CAAC,KAAK,KAAK;EAChC,OAAO;CACT,GACA,CAAC,CACH;CAEA,MAAM,sBAAsB,MAAMA,wCAAAA,wBAChC,QACA,eACA,aACF;CACA,OAAO,OAAO,OAAO,mBAAmB,CAAC,CAAC,KAAK;AACjD"}
|
|
@@ -3,7 +3,7 @@ const require_actions_getChains = require("../actions/getChains.js");
|
|
|
3
3
|
const require_core_utils = require("../core/utils.js");
|
|
4
4
|
let _lifi_types = require("@lifi/types");
|
|
5
5
|
//#region src/client/getClientStorage.ts
|
|
6
|
-
const chainsRefreshInterval =
|
|
6
|
+
const chainsRefreshInterval = 216e5;
|
|
7
7
|
const getClientStorage = (config) => {
|
|
8
8
|
let _chains = [];
|
|
9
9
|
let _rpcUrls = { ...config.rpcUrls };
|
|
@@ -29,7 +29,8 @@ const getClientStorage = (config) => {
|
|
|
29
29
|
_lifi_types.ChainType.SVM,
|
|
30
30
|
_lifi_types.ChainType.UTXO,
|
|
31
31
|
_lifi_types.ChainType.MVM,
|
|
32
|
-
_lifi_types.ChainType.TVM
|
|
32
|
+
_lifi_types.ChainType.TVM,
|
|
33
|
+
_lifi_types.ChainType.STL
|
|
33
34
|
] });
|
|
34
35
|
_chainsUpdatedAt = Date.now();
|
|
35
36
|
updateRpcUrls();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getClientStorage.js","names":["getRpcUrlsFromChains","ChainId","_getChains","ChainType"],"sources":["../../../src/client/getClientStorage.ts"],"sourcesContent":["import { ChainId, ChainType, type ExtendedChain } from '@lifi/types'\nimport { _getChains } from '../actions/getChains.js'\nimport { getRpcUrlsFromChains } from '../core/utils.js'\nimport type { RPCUrls, SDKBaseConfig } from '../types/core.js'\n\n// 6 hours in milliseconds\nconst chainsRefreshInterval = 1000 * 60 * 60 * 6\n\nexport interface ClientStorage {\n readonly needReset: boolean\n setChains(chains: ExtendedChain[]): void\n getChains(): Promise<ExtendedChain[]>\n getRpcUrls(): Promise<RPCUrls>\n}\n\nexport const getClientStorage = (config: SDKBaseConfig): ClientStorage => {\n let _chains = [] as ExtendedChain[]\n let _rpcUrls = { ...config.rpcUrls } as RPCUrls\n let _chainsUpdatedAt: number | undefined\n\n const updateRpcUrls = () => {\n _rpcUrls = { ...config.rpcUrls }\n _rpcUrls = getRpcUrlsFromChains(_rpcUrls, _chains, [ChainId.SOL])\n }\n\n return {\n get needReset() {\n return (\n !_chainsUpdatedAt ||\n Date.now() - _chainsUpdatedAt >= chainsRefreshInterval\n )\n },\n setChains(chains: ExtendedChain[]) {\n _chains = chains\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n },\n async getChains() {\n // When preloadChains is false, SDK does not auto-fetch chains\n // External consumer is responsible for calling setChains\n if (!config.preloadChains) {\n return _chains\n }\n\n if (this.needReset || !_chains.length) {\n _chains = await _getChains(config, {\n chainTypes: [\n ChainType.EVM,\n ChainType.SVM,\n ChainType.UTXO,\n ChainType.MVM,\n ChainType.TVM,\n ],\n })\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n }\n return _chains\n },\n async getRpcUrls() {\n await this.getChains() // _rpcUrls is updated when needed\n return _rpcUrls\n },\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"getClientStorage.js","names":["getRpcUrlsFromChains","ChainId","_getChains","ChainType"],"sources":["../../../src/client/getClientStorage.ts"],"sourcesContent":["import { ChainId, ChainType, type ExtendedChain } from '@lifi/types'\nimport { _getChains } from '../actions/getChains.js'\nimport { getRpcUrlsFromChains } from '../core/utils.js'\nimport type { RPCUrls, SDKBaseConfig } from '../types/core.js'\n\n// 6 hours in milliseconds\nconst chainsRefreshInterval = 1000 * 60 * 60 * 6\n\nexport interface ClientStorage {\n readonly needReset: boolean\n setChains(chains: ExtendedChain[]): void\n getChains(): Promise<ExtendedChain[]>\n getRpcUrls(): Promise<RPCUrls>\n}\n\nexport const getClientStorage = (config: SDKBaseConfig): ClientStorage => {\n let _chains = [] as ExtendedChain[]\n let _rpcUrls = { ...config.rpcUrls } as RPCUrls\n let _chainsUpdatedAt: number | undefined\n\n const updateRpcUrls = () => {\n _rpcUrls = { ...config.rpcUrls }\n _rpcUrls = getRpcUrlsFromChains(_rpcUrls, _chains, [ChainId.SOL])\n }\n\n return {\n get needReset() {\n return (\n !_chainsUpdatedAt ||\n Date.now() - _chainsUpdatedAt >= chainsRefreshInterval\n )\n },\n setChains(chains: ExtendedChain[]) {\n _chains = chains\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n },\n async getChains() {\n // When preloadChains is false, SDK does not auto-fetch chains\n // External consumer is responsible for calling setChains\n if (!config.preloadChains) {\n return _chains\n }\n\n if (this.needReset || !_chains.length) {\n _chains = await _getChains(config, {\n chainTypes: [\n ChainType.EVM,\n ChainType.SVM,\n ChainType.UTXO,\n ChainType.MVM,\n ChainType.TVM,\n ChainType.STL,\n ],\n })\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n }\n return _chains\n },\n async getRpcUrls() {\n await this.getChains() // _rpcUrls is updated when needed\n return _rpcUrls\n },\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,wBAAwB;AAS9B,MAAa,oBAAoB,WAAyC;CACxE,IAAI,UAAU,CAAC;CACf,IAAI,WAAW,EAAE,GAAG,OAAO,QAAQ;CACnC,IAAI;CAEJ,MAAM,sBAAsB;EAC1B,WAAW,EAAE,GAAG,OAAO,QAAQ;EAC/B,WAAWA,mBAAAA,qBAAqB,UAAU,SAAS,CAACC,YAAAA,QAAQ,GAAG,CAAC;CAClE;CAEA,OAAO;EACL,IAAI,YAAY;GACd,OACE,CAAC,oBACD,KAAK,IAAI,IAAI,oBAAoB;EAErC;EACA,UAAU,QAAyB;GACjC,UAAU;GACV,mBAAmB,KAAK,IAAI;GAC5B,cAAc;EAChB;EACA,MAAM,YAAY;GAGhB,IAAI,CAAC,OAAO,eACV,OAAO;GAGT,IAAI,KAAK,aAAa,CAAC,QAAQ,QAAQ;IACrC,UAAU,MAAMC,0BAAAA,WAAW,QAAQ,EACjC,YAAY;KACVC,YAAAA,UAAU;KACVA,YAAAA,UAAU;KACVA,YAAAA,UAAU;KACVA,YAAAA,UAAU;KACVA,YAAAA,UAAU;KACVA,YAAAA,UAAU;IACZ,EACF,CAAC;IACD,mBAAmB,KAAK,IAAI;IAC5B,cAAc;GAChB;GACA,OAAO;EACT;EACA,MAAM,aAAa;GACjB,MAAM,KAAK,UAAU;GACrB,OAAO;EACT;CACF;AACF"}
|
|
@@ -50,17 +50,19 @@ var BaseStepExecutor = class {
|
|
|
50
50
|
} catch (error) {
|
|
51
51
|
const action = step.execution?.actions?.at(-1);
|
|
52
52
|
const parsed = await this.parseErrors(error, step, action, retryParams);
|
|
53
|
-
if (!(parsed instanceof require_errors_errors.ExecuteStepRetryError))
|
|
54
|
-
|
|
55
|
-
code: parsed.code
|
|
56
|
-
} });
|
|
57
|
-
else this.statusManager.updateExecution(step, {
|
|
58
|
-
status: "FAILED",
|
|
59
|
-
error: {
|
|
53
|
+
if (!(parsed instanceof require_errors_errors.ExecuteStepRetryError)) {
|
|
54
|
+
if (action) this.statusManager.updateAction(step, action.type, "FAILED", { error: {
|
|
60
55
|
message: parsed.cause?.message,
|
|
61
56
|
code: parsed.code
|
|
62
|
-
}
|
|
63
|
-
|
|
57
|
+
} });
|
|
58
|
+
else this.statusManager.updateExecution(step, {
|
|
59
|
+
status: "FAILED",
|
|
60
|
+
error: {
|
|
61
|
+
message: parsed.cause?.message,
|
|
62
|
+
code: parsed.code
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
64
66
|
throw parsed;
|
|
65
67
|
}
|
|
66
68
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseStepExecutor.js","names":["StatusManager","ExecuteStepRetryError"],"sources":["../../../src/core/BaseStepExecutor.ts"],"sourcesContent":["import { ExecuteStepRetryError } from '../errors/errors.js'\nimport type { SDKError } from '../errors/SDKError.js'\nimport type {\n ExecuteStepRetryParams,\n ExecutionAction,\n ExecutionOptions,\n InteractionSettings,\n LiFiStepExtended,\n SDKClient,\n StepExecutor,\n StepExecutorOptions,\n} from '../types/core.js'\nimport type {\n StepExecutorBaseContext,\n StepExecutorContext,\n} from '../types/execution.js'\nimport { StatusManager } from './StatusManager.js'\nimport type { TaskPipeline } from './TaskPipeline.js'\n\n// Please be careful when changing the defaults as it may break the behavior (e.g., background execution)\nconst defaultInteractionSettings = {\n allowInteraction: true,\n allowUpdates: true,\n allowExecution: true,\n}\n\nexport abstract class BaseStepExecutor implements StepExecutor {\n protected executionOptions?: ExecutionOptions\n protected statusManager: StatusManager\n\n public allowUserInteraction = true\n public allowExecution = true\n\n constructor(options: StepExecutorOptions) {\n this.statusManager = new StatusManager(options.routeId)\n this.executionOptions = options.executionOptions\n }\n\n setInteraction = (settings?: InteractionSettings): void => {\n const interactionSettings = {\n ...defaultInteractionSettings,\n ...settings,\n }\n this.allowUserInteraction = interactionSettings.allowInteraction\n this.statusManager.allowUpdates(interactionSettings.allowUpdates)\n this.allowExecution = interactionSettings.allowExecution\n }\n\n private createBaseContext = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<StepExecutorBaseContext> => {\n const fromChain = await client.getChainById(step.action.fromChainId)\n const toChain = await client.getChainById(step.action.toChainId)\n\n const isBridgeExecution = fromChain.id !== toChain.id\n\n return {\n client,\n step,\n fromChain,\n toChain,\n isBridgeExecution,\n retryParams,\n statusManager: this.statusManager,\n executionOptions: this.executionOptions,\n allowUserInteraction: this.allowUserInteraction,\n }\n }\n\n abstract createContext(\n baseContext: StepExecutorBaseContext\n ): Promise<StepExecutorContext>\n\n abstract createPipeline(context: StepExecutorContext): TaskPipeline\n\n abstract parseErrors(\n error: Error,\n step?: LiFiStepExtended,\n action?: ExecutionAction,\n retryParams?: ExecuteStepRetryParams\n ): Promise<SDKError | ExecuteStepRetryError>\n\n executeStep = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<LiFiStepExtended> => {\n try {\n step.execution = this.statusManager.initializeExecution(step)\n\n const baseContext = await this.createBaseContext(\n client,\n step,\n retryParams\n )\n const context = await this.createContext(baseContext)\n const pipeline = this.createPipeline(context)\n\n await pipeline.run(context)\n\n return step\n } catch (error: any) {\n // Derive failing action from last in execution.actions\n const action = step.execution?.actions?.at(-1)\n const parsed = await this.parseErrors(error, step, action, retryParams)\n if (!(parsed instanceof ExecuteStepRetryError)) {\n if (action) {\n this.statusManager.updateAction(step, action.type, 'FAILED', {\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n } else {\n this.statusManager.updateExecution(step, {\n status: 'FAILED',\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n }\n }\n throw parsed\n }\n }\n}\n"],"mappings":";;;;AAoBA,MAAM,6BAA6B;CACjC,kBAAkB;CAClB,cAAc;CACd,gBAAgB;AAClB;AAEA,IAAsB,mBAAtB,MAA+D;CAC7D;CACA;CAEA,uBAA8B;CAC9B,iBAAwB;CAExB,YAAY,SAA8B;EACxC,KAAK,gBAAgB,IAAIA,2BAAAA,cAAc,QAAQ,OAAO;EACtD,KAAK,mBAAmB,QAAQ;CAClC;CAEA,kBAAkB,aAAyC;EACzD,MAAM,sBAAsB;GAC1B,GAAG;GACH,GAAG;EACL;EACA,KAAK,uBAAuB,oBAAoB;EAChD,KAAK,cAAc,aAAa,oBAAoB,YAAY;EAChE,KAAK,iBAAiB,oBAAoB;CAC5C;CAEA,oBAA4B,OAC1B,QACA,MACA,gBACqC;EACrC,MAAM,YAAY,MAAM,OAAO,aAAa,KAAK,OAAO,WAAW;EACnE,MAAM,UAAU,MAAM,OAAO,aAAa,KAAK,OAAO,SAAS;EAI/D,OAAO;GACL;GACA;GACA;GACA;GACA,mBAPwB,UAAU,OAAO,QAAQ;GAQjD;GACA,eAAe,KAAK;GACpB,kBAAkB,KAAK;GACvB,sBAAsB,KAAK;EAC7B;CACF;CAeA,cAAc,OACZ,QACA,MACA,gBAC8B;EAC9B,IAAI;GACF,KAAK,YAAY,KAAK,cAAc,oBAAoB,IAAI;GAE5D,MAAM,cAAc,MAAM,KAAK,kBAC7B,QACA,MACA,WACF;GACA,MAAM,UAAU,MAAM,KAAK,cAAc,WAAW;GAGpD,MAFiB,KAAK,eAAe,OAExB,CAAC,CAAC,IAAI,OAAO;GAE1B,OAAO;EACT,SAAS,OAAY;GAEnB,MAAM,SAAS,KAAK,WAAW,SAAS,GAAG,EAAE;GAC7C,MAAM,SAAS,MAAM,KAAK,YAAY,OAAO,MAAM,QAAQ,WAAW;GACtE,IAAI,EAAE,kBAAkBC,sBAAAA,
|
|
1
|
+
{"version":3,"file":"BaseStepExecutor.js","names":["StatusManager","ExecuteStepRetryError"],"sources":["../../../src/core/BaseStepExecutor.ts"],"sourcesContent":["import { ExecuteStepRetryError } from '../errors/errors.js'\nimport type { SDKError } from '../errors/SDKError.js'\nimport type {\n ExecuteStepRetryParams,\n ExecutionAction,\n ExecutionOptions,\n InteractionSettings,\n LiFiStepExtended,\n SDKClient,\n StepExecutor,\n StepExecutorOptions,\n} from '../types/core.js'\nimport type {\n StepExecutorBaseContext,\n StepExecutorContext,\n} from '../types/execution.js'\nimport { StatusManager } from './StatusManager.js'\nimport type { TaskPipeline } from './TaskPipeline.js'\n\n// Please be careful when changing the defaults as it may break the behavior (e.g., background execution)\nconst defaultInteractionSettings = {\n allowInteraction: true,\n allowUpdates: true,\n allowExecution: true,\n}\n\nexport abstract class BaseStepExecutor implements StepExecutor {\n protected executionOptions?: ExecutionOptions\n protected statusManager: StatusManager\n\n public allowUserInteraction = true\n public allowExecution = true\n\n constructor(options: StepExecutorOptions) {\n this.statusManager = new StatusManager(options.routeId)\n this.executionOptions = options.executionOptions\n }\n\n setInteraction = (settings?: InteractionSettings): void => {\n const interactionSettings = {\n ...defaultInteractionSettings,\n ...settings,\n }\n this.allowUserInteraction = interactionSettings.allowInteraction\n this.statusManager.allowUpdates(interactionSettings.allowUpdates)\n this.allowExecution = interactionSettings.allowExecution\n }\n\n private createBaseContext = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<StepExecutorBaseContext> => {\n const fromChain = await client.getChainById(step.action.fromChainId)\n const toChain = await client.getChainById(step.action.toChainId)\n\n const isBridgeExecution = fromChain.id !== toChain.id\n\n return {\n client,\n step,\n fromChain,\n toChain,\n isBridgeExecution,\n retryParams,\n statusManager: this.statusManager,\n executionOptions: this.executionOptions,\n allowUserInteraction: this.allowUserInteraction,\n }\n }\n\n abstract createContext(\n baseContext: StepExecutorBaseContext\n ): Promise<StepExecutorContext>\n\n abstract createPipeline(context: StepExecutorContext): TaskPipeline\n\n abstract parseErrors(\n error: Error,\n step?: LiFiStepExtended,\n action?: ExecutionAction,\n retryParams?: ExecuteStepRetryParams\n ): Promise<SDKError | ExecuteStepRetryError>\n\n executeStep = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<LiFiStepExtended> => {\n try {\n step.execution = this.statusManager.initializeExecution(step)\n\n const baseContext = await this.createBaseContext(\n client,\n step,\n retryParams\n )\n const context = await this.createContext(baseContext)\n const pipeline = this.createPipeline(context)\n\n await pipeline.run(context)\n\n return step\n } catch (error: any) {\n // Derive failing action from last in execution.actions\n const action = step.execution?.actions?.at(-1)\n const parsed = await this.parseErrors(error, step, action, retryParams)\n if (!(parsed instanceof ExecuteStepRetryError)) {\n if (action) {\n this.statusManager.updateAction(step, action.type, 'FAILED', {\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n } else {\n this.statusManager.updateExecution(step, {\n status: 'FAILED',\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n }\n }\n throw parsed\n }\n }\n}\n"],"mappings":";;;;AAoBA,MAAM,6BAA6B;CACjC,kBAAkB;CAClB,cAAc;CACd,gBAAgB;AAClB;AAEA,IAAsB,mBAAtB,MAA+D;CAC7D;CACA;CAEA,uBAA8B;CAC9B,iBAAwB;CAExB,YAAY,SAA8B;EACxC,KAAK,gBAAgB,IAAIA,2BAAAA,cAAc,QAAQ,OAAO;EACtD,KAAK,mBAAmB,QAAQ;CAClC;CAEA,kBAAkB,aAAyC;EACzD,MAAM,sBAAsB;GAC1B,GAAG;GACH,GAAG;EACL;EACA,KAAK,uBAAuB,oBAAoB;EAChD,KAAK,cAAc,aAAa,oBAAoB,YAAY;EAChE,KAAK,iBAAiB,oBAAoB;CAC5C;CAEA,oBAA4B,OAC1B,QACA,MACA,gBACqC;EACrC,MAAM,YAAY,MAAM,OAAO,aAAa,KAAK,OAAO,WAAW;EACnE,MAAM,UAAU,MAAM,OAAO,aAAa,KAAK,OAAO,SAAS;EAI/D,OAAO;GACL;GACA;GACA;GACA;GACA,mBAPwB,UAAU,OAAO,QAAQ;GAQjD;GACA,eAAe,KAAK;GACpB,kBAAkB,KAAK;GACvB,sBAAsB,KAAK;EAC7B;CACF;CAeA,cAAc,OACZ,QACA,MACA,gBAC8B;EAC9B,IAAI;GACF,KAAK,YAAY,KAAK,cAAc,oBAAoB,IAAI;GAE5D,MAAM,cAAc,MAAM,KAAK,kBAC7B,QACA,MACA,WACF;GACA,MAAM,UAAU,MAAM,KAAK,cAAc,WAAW;GAGpD,MAFiB,KAAK,eAAe,OAExB,CAAC,CAAC,IAAI,OAAO;GAE1B,OAAO;EACT,SAAS,OAAY;GAEnB,MAAM,SAAS,KAAK,WAAW,SAAS,GAAG,EAAE;GAC7C,MAAM,SAAS,MAAM,KAAK,YAAY,OAAO,MAAM,QAAQ,WAAW;GACtE,IAAI,EAAE,kBAAkBC,sBAAAA,wBAAwB;IAC9C,IAAI,QACF,KAAK,cAAc,aAAa,MAAM,OAAO,MAAM,UAAU,EAC3D,OAAO;KACL,SAAS,OAAO,OAAO;KACvB,MAAM,OAAO;IACf,EACF,CAAC;SAED,KAAK,cAAc,gBAAgB,MAAM;KACvC,QAAQ;KACR,OAAO;MACL,SAAS,OAAO,OAAO;MACvB,MAAM,OAAO;KACf;IACF,CAAC;GAEL;GACA,MAAM;EACR;CACF;AACF"}
|
|
@@ -122,10 +122,7 @@ var StatusManager = class {
|
|
|
122
122
|
break;
|
|
123
123
|
case "RESET_REQUIRED":
|
|
124
124
|
case "MESSAGE_REQUIRED":
|
|
125
|
-
case "ACTION_REQUIRED":
|
|
126
|
-
step.execution.status = "ACTION_REQUIRED";
|
|
127
|
-
break;
|
|
128
|
-
default: break;
|
|
125
|
+
case "ACTION_REQUIRED": step.execution.status = "ACTION_REQUIRED";
|
|
129
126
|
}
|
|
130
127
|
currentAction.status = status;
|
|
131
128
|
currentAction.message = require_core_actionMessages.getActionMessage(type, status);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusManager.js","names":["getActionMessage","executionState"],"sources":["../../../src/core/StatusManager.ts"],"sourcesContent":["import type { ChainId, LiFiStep } from '@lifi/types'\nimport type {\n Execution,\n ExecutionAction,\n ExecutionActionStatus,\n ExecutionActionType,\n LiFiStepExtended,\n} from '../types/core.js'\nimport { getActionMessage } from './actionMessages.js'\nimport { executionState } from './executionState.js'\n\ntype ActionProps = {\n step: LiFiStepExtended\n type: ExecutionActionType\n chainId: ChainId\n status: ExecutionActionStatus\n}\n\n/**\n * Manages status updates of a route and provides various functions for tracking actions.\n */\nexport class StatusManager {\n private readonly routeId: string\n private shouldUpdate = true\n\n constructor(routeId: string) {\n this.routeId = routeId\n }\n\n /**\n * Initializes the execution object of a Step.\n * @param step The current step in execution\n * @returns The initialized execution object for this step\n */\n initializeExecution = (step: LiFiStepExtended): Execution => {\n if (!step.execution) {\n step.execution = {\n startedAt: Date.now(),\n status: 'PENDING',\n actions: [],\n }\n this.updateStepInRoute(step)\n }\n\n // Change status to PENDING after resuming from FAILED\n if (step.execution.status === 'FAILED') {\n step.execution.startedAt = Date.now()\n step.execution.status = 'PENDING'\n step.execution.signedAt = undefined\n step.execution.error = undefined\n this.updateStepInRoute(step)\n }\n\n return step.execution\n }\n\n /**\n * Updates the execution object of a Step.\n * @param step The current step in execution\n * @param execution Partial execution data to merge\n * @returns The step with the updated execution object\n */\n updateExecution(\n step: LiFiStepExtended,\n execution: Partial<Execution>\n ): LiFiStep {\n if (!step.execution) {\n throw Error(\"Can't update empty execution.\")\n }\n step.execution = {\n ...step.execution,\n ...execution,\n }\n this.updateStepInRoute(step)\n return step\n }\n\n /**\n * Finds an action of the specified type in the step's execution\n * @param step The step to search in\n * @param type The action type to find\n * @returns The found action or undefined if not found\n */\n findAction(\n step: LiFiStepExtended,\n type: ExecutionActionType\n ): ExecutionAction | undefined {\n if (!step.execution?.actions) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const action = step.execution.actions.find((p) => p.type === type)\n\n return action\n }\n\n /**\n * Create and push a new action into the execution.\n * Caller is responsible for ensuring an action of this type does not already exist.\n * @param step The step that should contain the new action.\n * @param type Type of the action.\n * @param chainId Chain Id of the action.\n * @param status The initial status for the new action.\n * @returns The created action.\n */\n createAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const newAction: ExecutionAction = {\n type,\n message: getActionMessage(type, status),\n status,\n chainId,\n }\n\n step.execution.actions.push(newAction)\n this.updateStepInRoute(step)\n return newAction\n }\n\n /**\n * Find an existing action by type and update it, or create a new one if none exists.\n * @param step The step that should contain the action.\n * @param type Type of the action. Used to identify already existing actions.\n * @param chainId Chain Id of the action (used when creating).\n * @param status The status to set on the found or newly created action.\n * @returns The updated or newly created action.\n */\n initializeAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n const action = this.findAction(step, type)\n\n if (action) {\n return this.updateAction(step, type, status, {\n error: undefined,\n })\n }\n\n return this.createAction({ step, type, chainId, status })\n }\n\n /**\n * Update an action object.\n * @param step The step where the action should be updated\n * @param type The action type to update\n * @param status The status the action gets.\n * @param [params] Additional parameters to append to the action.\n * @returns The updated action\n */\n updateAction = (\n step: LiFiStepExtended,\n type: ExecutionActionType,\n status: ExecutionActionStatus,\n params?: Partial<ExecutionAction & { signedAt?: number }>\n ): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Can't update an empty step execution.\")\n }\n const currentAction = this.findAction(step, type)\n\n if (!currentAction) {\n throw new Error(\"Can't find an action for the given type.\")\n }\n\n switch (status) {\n case 'CANCELLED':\n break\n case 'FAILED':\n step.execution.status = 'FAILED'\n if (params?.error) {\n step.execution.error = params.error\n }\n break\n case 'DONE':\n break\n case 'PENDING':\n step.execution.status = 'PENDING'\n if (params?.signedAt) {\n step.execution.signedAt = params.signedAt\n }\n break\n case 'RESET_REQUIRED':\n case 'MESSAGE_REQUIRED':\n case 'ACTION_REQUIRED':\n step.execution.status = 'ACTION_REQUIRED'\n break\n default:\n break\n }\n\n currentAction.status = status\n currentAction.message = getActionMessage(type, status)\n // set extra parameters or overwrite the standard params set in the switch statement\n if (params) {\n const { signedAt: _signedAt, ...rest } = params\n Object.assign(currentAction, rest)\n }\n // Sort actions, the ones with DONE status go first\n step.execution.actions = [\n ...step.execution.actions.filter((action) => action.status === 'DONE'),\n ...step.execution.actions.filter((action) => action.status !== 'DONE'),\n ]\n this.updateStepInRoute(step) // updates the step in the route\n return currentAction\n }\n\n updateStepInRoute = (step: LiFiStep): LiFiStep => {\n if (!this.shouldUpdate) {\n return step\n }\n const data = executionState.get(this.routeId)\n\n if (!data) {\n throw new Error('Execution data not found.')\n }\n\n const stepIndex = data.route.steps.findIndex(\n (routeStep) => routeStep.id === step.id\n )\n\n if (stepIndex === -1) {\n throw new Error(\"Couldn't find a step to update.\")\n }\n\n data.route.steps[stepIndex] = { ...data.route.steps[stepIndex], ...step }\n\n data.executionOptions?.updateRouteHook?.(data.route)\n return data.route.steps[stepIndex]\n }\n\n allowUpdates(value: boolean): void {\n this.shouldUpdate = value\n }\n}\n"],"mappings":";;;;;;;AAqBA,IAAa,gBAAb,MAA2B;CACzB;CACA,eAAuB;CAEvB,YAAY,SAAiB;EAC3B,KAAK,UAAU;CACjB;;;;;;CAOA,uBAAuB,SAAsC;EAC3D,IAAI,CAAC,KAAK,WAAW;GACnB,KAAK,YAAY;IACf,WAAW,KAAK,IAAI;IACpB,QAAQ;IACR,SAAS,CAAC;GACZ;GACA,KAAK,kBAAkB,IAAI;EAC7B;EAGA,IAAI,KAAK,UAAU,WAAW,UAAU;GACtC,KAAK,UAAU,YAAY,KAAK,IAAI;GACpC,KAAK,UAAU,SAAS;GACxB,KAAK,UAAU,WAAW,KAAA;GAC1B,KAAK,UAAU,QAAQ,KAAA;GACvB,KAAK,kBAAkB,IAAI;EAC7B;EAEA,OAAO,KAAK;CACd;;;;;;;CAQA,gBACE,MACA,WACU;EACV,IAAI,CAAC,KAAK,WACR,MAAM,MAAM,+BAA+B;EAE7C,KAAK,YAAY;GACf,GAAG,KAAK;GACR,GAAG;EACL;EACA,KAAK,kBAAkB,IAAI;EAC3B,OAAO;CACT;;;;;;;CAQA,WACE,MACA,MAC6B;EAC7B,IAAI,CAAC,KAAK,WAAW,SACnB,MAAM,IAAI,MAAM,oCAAoC;EAKtD,OAFe,KAAK,UAAU,QAAQ,MAAM,MAAM,EAAE,SAAS,IAEjD;CACd;;;;;;;;;;CAWA,gBAAgB,EACd,MACA,MACA,SACA,aACkC;EAClC,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MAAM,oCAAoC;EAGtD,MAAM,YAA6B;GACjC;GACA,SAASA,4BAAAA,iBAAiB,MAAM,MAAM;GACtC;GACA;EACF;EAEA,KAAK,UAAU,QAAQ,KAAK,SAAS;EACrC,KAAK,kBAAkB,IAAI;EAC3B,OAAO;CACT;;;;;;;;;CAUA,oBAAoB,EAClB,MACA,MACA,SACA,aACkC;EAGlC,IAFe,KAAK,WAAW,MAAM,IAE5B,GACP,OAAO,KAAK,aAAa,MAAM,MAAM,QAAQ,EAC3C,OAAO,KAAA,EACT,CAAC;EAGH,OAAO,KAAK,aAAa;GAAE;GAAM;GAAM;GAAS;EAAO,CAAC;CAC1D;;;;;;;;;CAUA,gBACE,MACA,MACA,QACA,WACoB;EACpB,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MAAM,uCAAuC;EAEzD,MAAM,gBAAgB,KAAK,WAAW,MAAM,IAAI;EAEhD,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,0CAA0C;EAG5D,QAAQ,QAAR;GACE,KAAK,aACH;GACF,KAAK;IACH,KAAK,UAAU,SAAS;IACxB,IAAI,QAAQ,OACV,KAAK,UAAU,QAAQ,OAAO;IAEhC;GACF,KAAK,QACH;GACF,KAAK;IACH,KAAK,UAAU,SAAS;IACxB,IAAI,QAAQ,UACV,KAAK,UAAU,WAAW,OAAO;IAEnC;GACF,KAAK;GACL,KAAK;GACL,KAAK
|
|
1
|
+
{"version":3,"file":"StatusManager.js","names":["getActionMessage","executionState"],"sources":["../../../src/core/StatusManager.ts"],"sourcesContent":["import type { ChainId, LiFiStep } from '@lifi/types'\nimport type {\n Execution,\n ExecutionAction,\n ExecutionActionStatus,\n ExecutionActionType,\n LiFiStepExtended,\n} from '../types/core.js'\nimport { getActionMessage } from './actionMessages.js'\nimport { executionState } from './executionState.js'\n\ntype ActionProps = {\n step: LiFiStepExtended\n type: ExecutionActionType\n chainId: ChainId\n status: ExecutionActionStatus\n}\n\n/**\n * Manages status updates of a route and provides various functions for tracking actions.\n */\nexport class StatusManager {\n private readonly routeId: string\n private shouldUpdate = true\n\n constructor(routeId: string) {\n this.routeId = routeId\n }\n\n /**\n * Initializes the execution object of a Step.\n * @param step The current step in execution\n * @returns The initialized execution object for this step\n */\n initializeExecution = (step: LiFiStepExtended): Execution => {\n if (!step.execution) {\n step.execution = {\n startedAt: Date.now(),\n status: 'PENDING',\n actions: [],\n }\n this.updateStepInRoute(step)\n }\n\n // Change status to PENDING after resuming from FAILED\n if (step.execution.status === 'FAILED') {\n step.execution.startedAt = Date.now()\n step.execution.status = 'PENDING'\n step.execution.signedAt = undefined\n step.execution.error = undefined\n this.updateStepInRoute(step)\n }\n\n return step.execution\n }\n\n /**\n * Updates the execution object of a Step.\n * @param step The current step in execution\n * @param execution Partial execution data to merge\n * @returns The step with the updated execution object\n */\n updateExecution(\n step: LiFiStepExtended,\n execution: Partial<Execution>\n ): LiFiStep {\n if (!step.execution) {\n throw Error(\"Can't update empty execution.\")\n }\n step.execution = {\n ...step.execution,\n ...execution,\n }\n this.updateStepInRoute(step)\n return step\n }\n\n /**\n * Finds an action of the specified type in the step's execution\n * @param step The step to search in\n * @param type The action type to find\n * @returns The found action or undefined if not found\n */\n findAction(\n step: LiFiStepExtended,\n type: ExecutionActionType\n ): ExecutionAction | undefined {\n if (!step.execution?.actions) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const action = step.execution.actions.find((p) => p.type === type)\n\n return action\n }\n\n /**\n * Create and push a new action into the execution.\n * Caller is responsible for ensuring an action of this type does not already exist.\n * @param step The step that should contain the new action.\n * @param type Type of the action.\n * @param chainId Chain Id of the action.\n * @param status The initial status for the new action.\n * @returns The created action.\n */\n createAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const newAction: ExecutionAction = {\n type,\n message: getActionMessage(type, status),\n status,\n chainId,\n }\n\n step.execution.actions.push(newAction)\n this.updateStepInRoute(step)\n return newAction\n }\n\n /**\n * Find an existing action by type and update it, or create a new one if none exists.\n * @param step The step that should contain the action.\n * @param type Type of the action. Used to identify already existing actions.\n * @param chainId Chain Id of the action (used when creating).\n * @param status The status to set on the found or newly created action.\n * @returns The updated or newly created action.\n */\n initializeAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n const action = this.findAction(step, type)\n\n if (action) {\n return this.updateAction(step, type, status, {\n error: undefined,\n })\n }\n\n return this.createAction({ step, type, chainId, status })\n }\n\n /**\n * Update an action object.\n * @param step The step where the action should be updated\n * @param type The action type to update\n * @param status The status the action gets.\n * @param [params] Additional parameters to append to the action.\n * @returns The updated action\n */\n updateAction = (\n step: LiFiStepExtended,\n type: ExecutionActionType,\n status: ExecutionActionStatus,\n params?: Partial<ExecutionAction & { signedAt?: number }>\n ): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Can't update an empty step execution.\")\n }\n const currentAction = this.findAction(step, type)\n\n if (!currentAction) {\n throw new Error(\"Can't find an action for the given type.\")\n }\n\n switch (status) {\n case 'CANCELLED':\n break\n case 'FAILED':\n step.execution.status = 'FAILED'\n if (params?.error) {\n step.execution.error = params.error\n }\n break\n case 'DONE':\n break\n case 'PENDING':\n step.execution.status = 'PENDING'\n if (params?.signedAt) {\n step.execution.signedAt = params.signedAt\n }\n break\n case 'RESET_REQUIRED':\n case 'MESSAGE_REQUIRED':\n case 'ACTION_REQUIRED':\n step.execution.status = 'ACTION_REQUIRED'\n break\n default:\n break\n }\n\n currentAction.status = status\n currentAction.message = getActionMessage(type, status)\n // set extra parameters or overwrite the standard params set in the switch statement\n if (params) {\n const { signedAt: _signedAt, ...rest } = params\n Object.assign(currentAction, rest)\n }\n // Sort actions, the ones with DONE status go first\n step.execution.actions = [\n ...step.execution.actions.filter((action) => action.status === 'DONE'),\n ...step.execution.actions.filter((action) => action.status !== 'DONE'),\n ]\n this.updateStepInRoute(step) // updates the step in the route\n return currentAction\n }\n\n updateStepInRoute = (step: LiFiStep): LiFiStep => {\n if (!this.shouldUpdate) {\n return step\n }\n const data = executionState.get(this.routeId)\n\n if (!data) {\n throw new Error('Execution data not found.')\n }\n\n const stepIndex = data.route.steps.findIndex(\n (routeStep) => routeStep.id === step.id\n )\n\n if (stepIndex === -1) {\n throw new Error(\"Couldn't find a step to update.\")\n }\n\n data.route.steps[stepIndex] = { ...data.route.steps[stepIndex], ...step }\n\n data.executionOptions?.updateRouteHook?.(data.route)\n return data.route.steps[stepIndex]\n }\n\n allowUpdates(value: boolean): void {\n this.shouldUpdate = value\n }\n}\n"],"mappings":"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"}
|
|
@@ -25,7 +25,8 @@ var CheckBalanceTask = class extends require_core_BaseStepExecutionTask.BaseStep
|
|
|
25
25
|
});
|
|
26
26
|
const walletAddress = step.action.fromAddress;
|
|
27
27
|
if (!walletAddress) throw new require_errors_errors.TransactionError(1e3, "The wallet address is undefined.");
|
|
28
|
-
|
|
28
|
+
const options = await this.getCheckBalanceOptions(context);
|
|
29
|
+
await require_core_tasks_helpers_checkBalance.checkBalance(client, walletAddress, step, options);
|
|
29
30
|
return { status: "COMPLETED" };
|
|
30
31
|
}
|
|
31
32
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckBalanceTask.js","names":["BaseStepExecutionTask","TransactionError","checkBalance"],"sources":["../../../../src/core/tasks/CheckBalanceTask.ts"],"sourcesContent":["import { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport {\n type CheckBalanceOptions,\n checkBalance,\n} from './helpers/checkBalance.js'\n\nexport class CheckBalanceTask extends BaseStepExecutionTask {\n /**\n * Per-step options hook for chain-specific subclasses (e.g. skip the\n * gas check for smart-contract wallets or relayed steps). Default `{}`\n * keeps behavior unchanged for every provider that doesn't override.\n * Resolved lazily inside `run()` — pipelines that resume past\n * `CheckBalance` pay no cost.\n */\n protected getCheckBalanceOptions(\n _context: StepExecutorContext\n ): Promise<CheckBalanceOptions> {\n return Promise.resolve({})\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const { client, step, statusManager, isBridgeExecution } = context\n\n statusManager.initializeAction({\n step,\n type: isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP',\n chainId: step.action.fromChainId,\n status: 'STARTED',\n })\n\n const walletAddress = step.action.fromAddress\n if (!walletAddress) {\n throw new TransactionError(\n LiFiErrorCode.InternalError,\n 'The wallet address is undefined.'\n )\n }\n\n const options = await this.getCheckBalanceOptions(context)\n await checkBalance(client, walletAddress, step, options)\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;AASA,IAAa,mBAAb,cAAsCA,mCAAAA,sBAAsB;;;;;;;;CAQ1D,uBACE,UAC8B;EAC9B,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAC3B;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EAAE,QAAQ,MAAM,eAAe,sBAAsB;EAE3D,cAAc,iBAAiB;GAC7B;GACA,MAAM,oBAAoB,gBAAgB;GAC1C,SAAS,KAAK,OAAO;GACrB,QAAQ;EACV,CAAC;EAED,MAAM,gBAAgB,KAAK,OAAO;EAClC,IAAI,CAAC,eACH,MAAM,IAAIC,sBAAAA,iBAAAA,KAER,kCACF;
|
|
1
|
+
{"version":3,"file":"CheckBalanceTask.js","names":["BaseStepExecutionTask","TransactionError","checkBalance"],"sources":["../../../../src/core/tasks/CheckBalanceTask.ts"],"sourcesContent":["import { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport {\n type CheckBalanceOptions,\n checkBalance,\n} from './helpers/checkBalance.js'\n\nexport class CheckBalanceTask extends BaseStepExecutionTask {\n /**\n * Per-step options hook for chain-specific subclasses (e.g. skip the\n * gas check for smart-contract wallets or relayed steps). Default `{}`\n * keeps behavior unchanged for every provider that doesn't override.\n * Resolved lazily inside `run()` — pipelines that resume past\n * `CheckBalance` pay no cost.\n */\n protected getCheckBalanceOptions(\n _context: StepExecutorContext\n ): Promise<CheckBalanceOptions> {\n return Promise.resolve({})\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const { client, step, statusManager, isBridgeExecution } = context\n\n statusManager.initializeAction({\n step,\n type: isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP',\n chainId: step.action.fromChainId,\n status: 'STARTED',\n })\n\n const walletAddress = step.action.fromAddress\n if (!walletAddress) {\n throw new TransactionError(\n LiFiErrorCode.InternalError,\n 'The wallet address is undefined.'\n )\n }\n\n const options = await this.getCheckBalanceOptions(context)\n await checkBalance(client, walletAddress, step, options)\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;AASA,IAAa,mBAAb,cAAsCA,mCAAAA,sBAAsB;;;;;;;;CAQ1D,uBACE,UAC8B;EAC9B,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAC3B;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EAAE,QAAQ,MAAM,eAAe,sBAAsB;EAE3D,cAAc,iBAAiB;GAC7B;GACA,MAAM,oBAAoB,gBAAgB;GAC1C,SAAS,KAAK,OAAO;GACrB,QAAQ;EACV,CAAC;EAED,MAAM,gBAAgB,KAAK,OAAO;EAClC,IAAI,CAAC,eACH,MAAM,IAAIC,sBAAAA,iBAAAA,KAER,kCACF;EAGF,MAAM,UAAU,MAAM,KAAK,uBAAuB,OAAO;EACzD,MAAMC,wCAAAA,aAAa,QAAQ,eAAe,MAAM,OAAO;EACvD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
|
|
@@ -2,6 +2,13 @@ import { StepExecutorContext, TaskResult } from "../../types/execution.js";
|
|
|
2
2
|
import { BaseStepExecutionTask } from "../BaseStepExecutionTask.js";
|
|
3
3
|
//#region src/core/tasks/PrepareTransactionTask.d.ts
|
|
4
4
|
declare class PrepareTransactionTask extends BaseStepExecutionTask {
|
|
5
|
+
/**
|
|
6
|
+
* Whether `run()` has to fetch a fresh transaction request. The default asks
|
|
7
|
+
* only when the step carries none, which keeps every existing provider
|
|
8
|
+
* unchanged. Chains whose payload cannot be reused override it — see
|
|
9
|
+
* `StellarPrepareTransactionTask`.
|
|
10
|
+
*/
|
|
11
|
+
protected shouldRefetchTransaction(context: StepExecutorContext): boolean;
|
|
5
12
|
run(context: StepExecutorContext): Promise<TaskResult>;
|
|
6
13
|
}
|
|
7
14
|
//#endregion
|
|
@@ -6,13 +6,23 @@ const require_core_BaseStepExecutionTask = require("../BaseStepExecutionTask.js"
|
|
|
6
6
|
const require_core_tasks_helpers_stepComparison = require("./helpers/stepComparison.js");
|
|
7
7
|
//#region src/core/tasks/PrepareTransactionTask.ts
|
|
8
8
|
var PrepareTransactionTask = class extends require_core_BaseStepExecutionTask.BaseStepExecutionTask {
|
|
9
|
+
/**
|
|
10
|
+
* Whether `run()` has to fetch a fresh transaction request. The default asks
|
|
11
|
+
* only when the step carries none, which keeps every existing provider
|
|
12
|
+
* unchanged. Chains whose payload cannot be reused override it — see
|
|
13
|
+
* `StellarPrepareTransactionTask`.
|
|
14
|
+
*/
|
|
15
|
+
shouldRefetchTransaction(context) {
|
|
16
|
+
return !context.step.transactionRequest;
|
|
17
|
+
}
|
|
9
18
|
async run(context) {
|
|
10
19
|
const { client, step, statusManager, allowUserInteraction, executionOptions, isBridgeExecution } = context;
|
|
11
20
|
const action = statusManager.findAction(step, isBridgeExecution ? "CROSS_CHAIN" : "SWAP");
|
|
12
21
|
if (!action) throw new require_errors_errors.TransactionError(1008, "Unable to prepare transaction. Action not found.");
|
|
13
|
-
if (
|
|
22
|
+
if (this.shouldRefetchTransaction(context)) {
|
|
14
23
|
const { execution, ...stepBase } = step;
|
|
15
|
-
const
|
|
24
|
+
const updatedStep = await require_actions_getStepTransaction.getStepTransaction(client, stepBase);
|
|
25
|
+
const comparedStep = await require_core_tasks_helpers_stepComparison.stepComparison(statusManager, step, updatedStep, allowUserInteraction, executionOptions);
|
|
16
26
|
Object.assign(step, {
|
|
17
27
|
...comparedStep,
|
|
18
28
|
execution: step.execution
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrepareTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","
|
|
1
|
+
{"version":3,"file":"PrepareTransactionTask.js","names":["BaseStepExecutionTask","TransactionError","getStepTransaction","stepComparison"],"sources":["../../../../src/core/tasks/PrepareTransactionTask.ts"],"sourcesContent":["import { getStepTransaction } from '../../actions/getStepTransaction.js'\nimport { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport { stepComparison } from './helpers/stepComparison.js'\n\nexport class PrepareTransactionTask extends BaseStepExecutionTask {\n /**\n * Whether `run()` has to fetch a fresh transaction request. The default asks\n * only when the step carries none, which keeps every existing provider\n * unchanged. Chains whose payload cannot be reused override it — see\n * `StellarPrepareTransactionTask`.\n */\n protected shouldRefetchTransaction(context: StepExecutorContext): boolean {\n return !context.step.transactionRequest\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n allowUserInteraction,\n executionOptions,\n isBridgeExecution,\n } = context\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (this.shouldRefetchTransaction(context)) {\n const { execution, ...stepBase } = step\n const updatedStep = await getStepTransaction(client, stepBase)\n const comparedStep = await stepComparison(\n statusManager,\n step,\n updatedStep,\n allowUserInteraction,\n executionOptions\n )\n Object.assign(step, {\n ...comparedStep,\n execution: step.execution,\n })\n }\n\n if (!step.transactionRequest?.data) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Transaction request data is not found.'\n )\n }\n\n statusManager.updateAction(step, action.type, 'ACTION_REQUIRED')\n\n if (!allowUserInteraction) {\n return { status: 'PAUSED' }\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;;AAOA,IAAa,yBAAb,cAA4CA,mCAAAA,sBAAsB;;;;;;;CAOhE,yBAAmC,SAAuC;EACxE,OAAO,CAAC,QAAQ,KAAK;CACvB;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EACJ,QACA,MACA,eACA,sBACA,kBACA,sBACE;EAEJ,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EAEA,IAAI,CAAC,QACH,MAAM,IAAIC,sBAAAA,iBAAAA,MAER,kDACF;EAGF,IAAI,KAAK,yBAAyB,OAAO,GAAG;GAC1C,MAAM,EAAE,WAAW,GAAG,aAAa;GACnC,MAAM,cAAc,MAAMC,mCAAAA,mBAAmB,QAAQ,QAAQ;GAC7D,MAAM,eAAe,MAAMC,0CAAAA,eACzB,eACA,MACA,aACA,sBACA,gBACF;GACA,OAAO,OAAO,MAAM;IAClB,GAAG;IACH,WAAW,KAAK;GAClB,CAAC;EACH;EAEA,IAAI,CAAC,KAAK,oBAAoB,MAC5B,MAAM,IAAIF,sBAAAA,iBAAAA,MAER,uEACF;EAGF,cAAc,aAAa,MAAM,OAAO,MAAM,iBAAiB;EAE/D,IAAI,CAAC,sBACH,OAAO,EAAE,QAAQ,SAAS;EAG5B,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
|
|
@@ -54,7 +54,7 @@ const checkBalance = async (client, walletAddress, step, options = {}) => {
|
|
|
54
54
|
const slippageScaled = BigInt(Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION)));
|
|
55
55
|
await require_utils_withTimeout.withTimeout(async () => {
|
|
56
56
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
57
|
-
const isFinal = attempt ===
|
|
57
|
+
const isFinal = attempt === 5;
|
|
58
58
|
let balances;
|
|
59
59
|
try {
|
|
60
60
|
balances = await provider.getBalance(client, walletAddress, tokens);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkBalance.js","names":["withTimeout","BalanceError","sleep","formatUnits"],"sources":["../../../../../src/core/tasks/helpers/checkBalance.ts"],"sourcesContent":["import type { LiFiStep, Token, TokenAmount } from '@lifi/types'\nimport { BalanceError } from '../../../errors/errors.js'\nimport type { SDKClient } from '../../../types/core.js'\nimport { formatUnits } from '../../../utils/formatUnits.js'\nimport { sleep } from '../../../utils/sleep.js'\nimport { withTimeout } from '../../../utils/withTimeout.js'\n\nconst MAX_ATTEMPTS = 6\n// Exponential backoff: 150, 300, 600, 1200, 2400 → ≈4.65s of sleep total.\nconst BACKOFF_BASE_MS = 150\nconst OVERALL_TIMEOUT_MS = 10_000\nconst SLIPPAGE_PRECISION = 1_000_000_000n\n\ntype Bucket = 'source' | 'gas' | 'fee'\n\ntype Requirement = {\n token: Token\n sourcePart: bigint // step.action.fromAmount\n gasPart: bigint // step.estimate.gasCosts in this token\n feePart: bigint // non-included step.estimate.feeCosts in this token\n}\n\nexport type CheckBalanceOptions = {\n /**\n * Set to `false` when outer-tx gas is paid by something other than\n * `walletAddress` (SCA executor, 4337 bundler / paymaster, relayer):\n * `gasPart` is excluded from the sufficiency check and slippage rescue.\n * Source amount and non-included fees (e.g. LZ `msg.value`) are still\n * verified. Defaults to `true` (strict, today's behavior).\n */\n walletPaysGas?: boolean\n}\n\n/**\n * Verifies that the wallet holds enough of every token required to execute\n * the step on its source chain — the source-token amount, any gas costs, and\n * any non-included fee costs. Reads all balances in one batched provider\n * call, retries within a bounded budget to absorb transient RPC failures and\n * post-confirmation propagation lag, and applies slippage to the source-token\n * portion only as a last resort (overhead is never trimmed).\n *\n * Throws BalanceError(\"The balance is too low.\") on a genuine shortfall, or\n * BalanceError(\"Could not read wallet balance.\") if the balance can't be read\n * after retries.\n */\nexport const checkBalance = async (\n client: SDKClient,\n walletAddress: string,\n step: LiFiStep,\n options: CheckBalanceOptions = {}\n): Promise<void> => {\n const fromChainId = step.action.fromChainId\n // Chains with non-standard native decimals (e.g. Tempo, Stable) report\n // gas costs in units that don't align with wallet balances — drop the\n // gas portion of the check on those chains.\n const fromChain = await client.getChainById(fromChainId)\n const walletPaysGas =\n (options.walletPaysGas ?? true) && !fromChain.nonStandardNativeDecimals\n const requirements = new Map<string, Requirement>()\n const add = (token: Token, amount: bigint, bucket: Bucket): void => {\n if (token.chainId !== fromChainId || amount === 0n) {\n return\n }\n const key = token.address.toLowerCase()\n const req = requirements.get(key) ?? {\n token,\n sourcePart: 0n,\n gasPart: 0n,\n feePart: 0n,\n }\n if (bucket === 'source') {\n req.sourcePart += amount\n } else if (bucket === 'gas') {\n req.gasPart += amount\n } else {\n req.feePart += amount\n }\n requirements.set(key, req)\n }\n add(step.action.fromToken, BigInt(step.action.fromAmount), 'source')\n for (const gas of step.estimate?.gasCosts ?? []) {\n add(gas.token, BigInt(gas.amount), 'gas')\n }\n for (const fee of step.estimate?.feeCosts ?? []) {\n // Included fees are already part of fromAmount — don't count twice.\n if (!fee.included) {\n add(fee.token, BigInt(fee.amount), 'fee')\n }\n }\n\n const reservedOverhead = (r: Requirement): bigint =>\n r.feePart + (walletPaysGas ? r.gasPart : 0n)\n const need = (r: Requirement): bigint => r.sourcePart + reservedOverhead(r)\n\n // Drop pure-gas entries when `walletPaysGas` is false (e.g. native ETH\n // on Safe Apps with only `gasPart`) — saves one balance read each.\n for (const [key, req] of requirements) {\n if (need(req) === 0n) {\n requirements.delete(key)\n }\n }\n\n if (requirements.size === 0) {\n return\n }\n\n // Provider is dispatched by wallet address; all requirements share the\n // source chain, which matches this provider by virtue of the address.\n const provider = client.providers.find((p) => p.isAddress(walletAddress))\n if (!provider) {\n throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)\n }\n\n const reqs = Array.from(requirements.values())\n const tokens = reqs.map((r) => r.token)\n const slippage = step.action.slippage ?? 0\n const slippageScaled = BigInt(\n Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION))\n )\n\n await withTimeout(\n async () => {\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const isFinal = attempt === MAX_ATTEMPTS - 1\n\n let balances: TokenAmount[]\n try {\n balances = await provider.getBalance(client, walletAddress, tokens)\n } catch (error) {\n if (isFinal) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n error as Error\n )\n }\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n continue\n }\n\n const balanceByAddress = new Map(\n balances.map((b) => [b.address.toLowerCase(), b.amount] as const)\n )\n\n const unknown: Token[] = []\n const insufficient: { req: Requirement; have: bigint }[] = []\n for (const req of reqs) {\n const have = balanceByAddress.get(req.token.address.toLowerCase())\n if (have === undefined) {\n unknown.push(req.token)\n } else if (have < need(req)) {\n insufficient.push({ req, have })\n }\n }\n\n if (unknown.length === 0 && insufficient.length === 0) {\n return\n }\n\n // Final-attempt slippage rescue: only when the sole shortfall is\n // the source-token portion. Trim source to (balance − reserved\n // overhead) so the overhead reserve is preserved.\n if (\n isFinal &&\n unknown.length === 0 &&\n insufficient.length === 1 &&\n insufficient[0].req.sourcePart > 0n\n ) {\n const { req, have } = insufficient[0]\n const reserved = reservedOverhead(req)\n const minAcceptable =\n (req.sourcePart * slippageScaled) / SLIPPAGE_PRECISION + reserved\n if (have >= minAcceptable) {\n const newFromAmount = (have - reserved).toString()\n step.action.fromAmount = newFromAmount\n if (step.includedSteps?.length) {\n step.includedSteps[0].action.fromAmount = newFromAmount\n }\n return\n }\n }\n\n if (isFinal) {\n if (unknown.length > 0) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n new Error(\n `Could not read balance for: ${unknown\n .map((t) => t.symbol || t.address)\n .join(', ')}.`\n )\n )\n }\n const lines = insufficient.map(({ req, have }) => {\n const needed = formatUnits(need(req), req.token.decimals)\n const current = formatUnits(have, req.token.decimals)\n const symbol = req.token.symbol\n // The \"fees\" branch covers pure-overhead tokens; with\n // walletPaysGas=false, gas is excluded from `need(req)` so it\n // only fires for genuine fee shortfalls.\n return req.sourcePart > 0n\n ? `Your ${symbol} balance is too low, you try to transfer ${needed} ${symbol}, but your wallet only holds ${current} ${symbol}.`\n : `Insufficient ${symbol} for fees: need ${needed} ${symbol}, have ${current} ${symbol}.`\n })\n throw new BalanceError(\n 'The balance is too low.',\n new Error(`${lines.join(' ')} No funds have been sent.`)\n )\n }\n\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n }\n },\n {\n timeout: OVERALL_TIMEOUT_MS,\n errorInstance: new BalanceError('Could not read wallet balance.'),\n }\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checkBalance.js","names":["withTimeout","BalanceError","sleep","formatUnits"],"sources":["../../../../../src/core/tasks/helpers/checkBalance.ts"],"sourcesContent":["import type { LiFiStep, Token, TokenAmount } from '@lifi/types'\nimport { BalanceError } from '../../../errors/errors.js'\nimport type { SDKClient } from '../../../types/core.js'\nimport { formatUnits } from '../../../utils/formatUnits.js'\nimport { sleep } from '../../../utils/sleep.js'\nimport { withTimeout } from '../../../utils/withTimeout.js'\n\nconst MAX_ATTEMPTS = 6\n// Exponential backoff: 150, 300, 600, 1200, 2400 → ≈4.65s of sleep total.\nconst BACKOFF_BASE_MS = 150\nconst OVERALL_TIMEOUT_MS = 10_000\nconst SLIPPAGE_PRECISION = 1_000_000_000n\n\ntype Bucket = 'source' | 'gas' | 'fee'\n\ntype Requirement = {\n token: Token\n sourcePart: bigint // step.action.fromAmount\n gasPart: bigint // step.estimate.gasCosts in this token\n feePart: bigint // non-included step.estimate.feeCosts in this token\n}\n\nexport type CheckBalanceOptions = {\n /**\n * Set to `false` when outer-tx gas is paid by something other than\n * `walletAddress` (SCA executor, 4337 bundler / paymaster, relayer):\n * `gasPart` is excluded from the sufficiency check and slippage rescue.\n * Source amount and non-included fees (e.g. LZ `msg.value`) are still\n * verified. Defaults to `true` (strict, today's behavior).\n */\n walletPaysGas?: boolean\n}\n\n/**\n * Verifies that the wallet holds enough of every token required to execute\n * the step on its source chain — the source-token amount, any gas costs, and\n * any non-included fee costs. Reads all balances in one batched provider\n * call, retries within a bounded budget to absorb transient RPC failures and\n * post-confirmation propagation lag, and applies slippage to the source-token\n * portion only as a last resort (overhead is never trimmed).\n *\n * Throws BalanceError(\"The balance is too low.\") on a genuine shortfall, or\n * BalanceError(\"Could not read wallet balance.\") if the balance can't be read\n * after retries.\n */\nexport const checkBalance = async (\n client: SDKClient,\n walletAddress: string,\n step: LiFiStep,\n options: CheckBalanceOptions = {}\n): Promise<void> => {\n const fromChainId = step.action.fromChainId\n // Chains with non-standard native decimals (e.g. Tempo, Stable) report\n // gas costs in units that don't align with wallet balances — drop the\n // gas portion of the check on those chains.\n const fromChain = await client.getChainById(fromChainId)\n const walletPaysGas =\n (options.walletPaysGas ?? true) && !fromChain.nonStandardNativeDecimals\n const requirements = new Map<string, Requirement>()\n const add = (token: Token, amount: bigint, bucket: Bucket): void => {\n if (token.chainId !== fromChainId || amount === 0n) {\n return\n }\n const key = token.address.toLowerCase()\n const req = requirements.get(key) ?? {\n token,\n sourcePart: 0n,\n gasPart: 0n,\n feePart: 0n,\n }\n if (bucket === 'source') {\n req.sourcePart += amount\n } else if (bucket === 'gas') {\n req.gasPart += amount\n } else {\n req.feePart += amount\n }\n requirements.set(key, req)\n }\n add(step.action.fromToken, BigInt(step.action.fromAmount), 'source')\n for (const gas of step.estimate?.gasCosts ?? []) {\n add(gas.token, BigInt(gas.amount), 'gas')\n }\n for (const fee of step.estimate?.feeCosts ?? []) {\n // Included fees are already part of fromAmount — don't count twice.\n if (!fee.included) {\n add(fee.token, BigInt(fee.amount), 'fee')\n }\n }\n\n const reservedOverhead = (r: Requirement): bigint =>\n r.feePart + (walletPaysGas ? r.gasPart : 0n)\n const need = (r: Requirement): bigint => r.sourcePart + reservedOverhead(r)\n\n // Drop pure-gas entries when `walletPaysGas` is false (e.g. native ETH\n // on Safe Apps with only `gasPart`) — saves one balance read each.\n for (const [key, req] of requirements) {\n if (need(req) === 0n) {\n requirements.delete(key)\n }\n }\n\n if (requirements.size === 0) {\n return\n }\n\n // Provider is dispatched by wallet address; all requirements share the\n // source chain, which matches this provider by virtue of the address.\n const provider = client.providers.find((p) => p.isAddress(walletAddress))\n if (!provider) {\n throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)\n }\n\n const reqs = Array.from(requirements.values())\n const tokens = reqs.map((r) => r.token)\n const slippage = step.action.slippage ?? 0\n const slippageScaled = BigInt(\n Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION))\n )\n\n await withTimeout(\n async () => {\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const isFinal = attempt === MAX_ATTEMPTS - 1\n\n let balances: TokenAmount[]\n try {\n balances = await provider.getBalance(client, walletAddress, tokens)\n } catch (error) {\n if (isFinal) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n error as Error\n )\n }\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n continue\n }\n\n const balanceByAddress = new Map(\n balances.map((b) => [b.address.toLowerCase(), b.amount] as const)\n )\n\n const unknown: Token[] = []\n const insufficient: { req: Requirement; have: bigint }[] = []\n for (const req of reqs) {\n const have = balanceByAddress.get(req.token.address.toLowerCase())\n if (have === undefined) {\n unknown.push(req.token)\n } else if (have < need(req)) {\n insufficient.push({ req, have })\n }\n }\n\n if (unknown.length === 0 && insufficient.length === 0) {\n return\n }\n\n // Final-attempt slippage rescue: only when the sole shortfall is\n // the source-token portion. Trim source to (balance − reserved\n // overhead) so the overhead reserve is preserved.\n if (\n isFinal &&\n unknown.length === 0 &&\n insufficient.length === 1 &&\n insufficient[0].req.sourcePart > 0n\n ) {\n const { req, have } = insufficient[0]\n const reserved = reservedOverhead(req)\n const minAcceptable =\n (req.sourcePart * slippageScaled) / SLIPPAGE_PRECISION + reserved\n if (have >= minAcceptable) {\n const newFromAmount = (have - reserved).toString()\n step.action.fromAmount = newFromAmount\n if (step.includedSteps?.length) {\n step.includedSteps[0].action.fromAmount = newFromAmount\n }\n return\n }\n }\n\n if (isFinal) {\n if (unknown.length > 0) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n new Error(\n `Could not read balance for: ${unknown\n .map((t) => t.symbol || t.address)\n .join(', ')}.`\n )\n )\n }\n const lines = insufficient.map(({ req, have }) => {\n const needed = formatUnits(need(req), req.token.decimals)\n const current = formatUnits(have, req.token.decimals)\n const symbol = req.token.symbol\n // The \"fees\" branch covers pure-overhead tokens; with\n // walletPaysGas=false, gas is excluded from `need(req)` so it\n // only fires for genuine fee shortfalls.\n return req.sourcePart > 0n\n ? `Your ${symbol} balance is too low, you try to transfer ${needed} ${symbol}, but your wallet only holds ${current} ${symbol}.`\n : `Insufficient ${symbol} for fees: need ${needed} ${symbol}, have ${current} ${symbol}.`\n })\n throw new BalanceError(\n 'The balance is too low.',\n new Error(`${lines.join(' ')} No funds have been sent.`)\n )\n }\n\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n }\n },\n {\n timeout: OVERALL_TIMEOUT_MS,\n errorInstance: new BalanceError('Could not read wallet balance.'),\n }\n )\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ const checkPackageUpdates = async (packageName, packageVersion) => {
|
|
|
5
5
|
try {
|
|
6
6
|
const pkgName = packageName ?? "@lifi/sdk";
|
|
7
7
|
const latestVersion = (await (await fetch(`https://registry.npmjs.org/${pkgName}/latest`)).json()).version;
|
|
8
|
-
const currentVersion = packageVersion ?? "4.
|
|
8
|
+
const currentVersion = packageVersion ?? "4.4.0";
|
|
9
9
|
if (latestVersion > currentVersion) console.warn(`${pkgName}: new package version is available. Please update as soon as possible to enjoy the newest features. Current version: ${currentVersion}. Latest version: ${latestVersion}.`);
|
|
10
10
|
} catch (_error) {}
|
|
11
11
|
};
|
package/dist/cjs/version.d.ts
CHANGED
package/dist/cjs/version.js
CHANGED
package/dist/cjs/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk'\nexport const version = '4.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk'\nexport const version = '4.4.0'\n"],"mappings":";;AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getTokenBalancesByChain } from "./getTokenBalancesByChain.js";
|
|
2
2
|
//#region src/actions/getTokenBalances.ts
|
|
3
3
|
async function getTokenBalances(client, walletAddress, tokens) {
|
|
4
|
-
const
|
|
4
|
+
const tokensByChain = tokens.reduce((tokens, token) => {
|
|
5
5
|
if (!tokens[token.chainId]) tokens[token.chainId] = [];
|
|
6
6
|
tokens[token.chainId].push(token);
|
|
7
7
|
return tokens;
|
|
8
|
-
}, {})
|
|
8
|
+
}, {});
|
|
9
|
+
const tokenAmountsByChain = await getTokenBalancesByChain(client, walletAddress, tokensByChain);
|
|
9
10
|
return Object.values(tokenAmountsByChain).flat();
|
|
10
11
|
}
|
|
11
12
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTokenBalances.js","names":[],"sources":["../../../src/actions/getTokenBalances.ts"],"sourcesContent":["import type {\n Token,\n TokenAmount,\n TokenAmountExtended,\n TokenExtended,\n} from '@lifi/types'\nimport type { SDKClient } from '../types/core.js'\nimport { getTokenBalancesByChain } from './getTokenBalancesByChain.js'\n\n/**\n * Returns the balances for a list tokens a wallet holds across all aggregated chains.\n * @param client - The SDK client.\n * @param walletAddress - A wallet address.\n * @param tokens - A list of Token (or TokenExtended) objects.\n * @returns A list of objects containing the tokens and the amounts on different chains.\n * @throws {ValidationError} Throws a ValidationError if validation fails.\n * @throws {Error} Throws an Error if the SDK Provider for the wallet address is not found.\n */\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]>\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: TokenExtended[]\n): Promise<TokenAmountExtended[]> {\n // split by chain\n const tokensByChain = tokens.reduce(\n (tokens, token) => {\n if (!tokens[token.chainId]) {\n tokens[token.chainId] = []\n }\n tokens[token.chainId].push(token)\n return tokens\n },\n {} as { [chainId: number]: Token[] | TokenExtended[] }\n )\n\n const tokenAmountsByChain = await getTokenBalancesByChain(\n client,\n walletAddress,\n tokensByChain\n )\n return Object.values(tokenAmountsByChain).flat()\n}\n"],"mappings":";;AAuBA,eAAsB,iBACpB,QACA,eACA,QACgC;
|
|
1
|
+
{"version":3,"file":"getTokenBalances.js","names":[],"sources":["../../../src/actions/getTokenBalances.ts"],"sourcesContent":["import type {\n Token,\n TokenAmount,\n TokenAmountExtended,\n TokenExtended,\n} from '@lifi/types'\nimport type { SDKClient } from '../types/core.js'\nimport { getTokenBalancesByChain } from './getTokenBalancesByChain.js'\n\n/**\n * Returns the balances for a list tokens a wallet holds across all aggregated chains.\n * @param client - The SDK client.\n * @param walletAddress - A wallet address.\n * @param tokens - A list of Token (or TokenExtended) objects.\n * @returns A list of objects containing the tokens and the amounts on different chains.\n * @throws {ValidationError} Throws a ValidationError if validation fails.\n * @throws {Error} Throws an Error if the SDK Provider for the wallet address is not found.\n */\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: Token[]\n): Promise<TokenAmount[]>\nexport async function getTokenBalances(\n client: SDKClient,\n walletAddress: string,\n tokens: TokenExtended[]\n): Promise<TokenAmountExtended[]> {\n // split by chain\n const tokensByChain = tokens.reduce(\n (tokens, token) => {\n if (!tokens[token.chainId]) {\n tokens[token.chainId] = []\n }\n tokens[token.chainId].push(token)\n return tokens\n },\n {} as { [chainId: number]: Token[] | TokenExtended[] }\n )\n\n const tokenAmountsByChain = await getTokenBalancesByChain(\n client,\n walletAddress,\n tokensByChain\n )\n return Object.values(tokenAmountsByChain).flat()\n}\n"],"mappings":";;AAuBA,eAAsB,iBACpB,QACA,eACA,QACgC;CAEhC,MAAM,gBAAgB,OAAO,QAC1B,QAAQ,UAAU;EACjB,IAAI,CAAC,OAAO,MAAM,UAChB,OAAO,MAAM,WAAW,CAAC;EAE3B,OAAO,MAAM,QAAQ,CAAC,KAAK,KAAK;EAChC,OAAO;CACT,GACA,CAAC,CACH;CAEA,MAAM,sBAAsB,MAAM,wBAChC,QACA,eACA,aACF;CACA,OAAO,OAAO,OAAO,mBAAmB,CAAC,CAAC,KAAK;AACjD"}
|
|
@@ -2,7 +2,7 @@ import { _getChains } from "../actions/getChains.js";
|
|
|
2
2
|
import { getRpcUrlsFromChains } from "../core/utils.js";
|
|
3
3
|
import { ChainId, ChainType } from "@lifi/types";
|
|
4
4
|
//#region src/client/getClientStorage.ts
|
|
5
|
-
const chainsRefreshInterval =
|
|
5
|
+
const chainsRefreshInterval = 216e5;
|
|
6
6
|
const getClientStorage = (config) => {
|
|
7
7
|
let _chains = [];
|
|
8
8
|
let _rpcUrls = { ...config.rpcUrls };
|
|
@@ -28,7 +28,8 @@ const getClientStorage = (config) => {
|
|
|
28
28
|
ChainType.SVM,
|
|
29
29
|
ChainType.UTXO,
|
|
30
30
|
ChainType.MVM,
|
|
31
|
-
ChainType.TVM
|
|
31
|
+
ChainType.TVM,
|
|
32
|
+
ChainType.STL
|
|
32
33
|
] });
|
|
33
34
|
_chainsUpdatedAt = Date.now();
|
|
34
35
|
updateRpcUrls();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getClientStorage.js","names":[],"sources":["../../../src/client/getClientStorage.ts"],"sourcesContent":["import { ChainId, ChainType, type ExtendedChain } from '@lifi/types'\nimport { _getChains } from '../actions/getChains.js'\nimport { getRpcUrlsFromChains } from '../core/utils.js'\nimport type { RPCUrls, SDKBaseConfig } from '../types/core.js'\n\n// 6 hours in milliseconds\nconst chainsRefreshInterval = 1000 * 60 * 60 * 6\n\nexport interface ClientStorage {\n readonly needReset: boolean\n setChains(chains: ExtendedChain[]): void\n getChains(): Promise<ExtendedChain[]>\n getRpcUrls(): Promise<RPCUrls>\n}\n\nexport const getClientStorage = (config: SDKBaseConfig): ClientStorage => {\n let _chains = [] as ExtendedChain[]\n let _rpcUrls = { ...config.rpcUrls } as RPCUrls\n let _chainsUpdatedAt: number | undefined\n\n const updateRpcUrls = () => {\n _rpcUrls = { ...config.rpcUrls }\n _rpcUrls = getRpcUrlsFromChains(_rpcUrls, _chains, [ChainId.SOL])\n }\n\n return {\n get needReset() {\n return (\n !_chainsUpdatedAt ||\n Date.now() - _chainsUpdatedAt >= chainsRefreshInterval\n )\n },\n setChains(chains: ExtendedChain[]) {\n _chains = chains\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n },\n async getChains() {\n // When preloadChains is false, SDK does not auto-fetch chains\n // External consumer is responsible for calling setChains\n if (!config.preloadChains) {\n return _chains\n }\n\n if (this.needReset || !_chains.length) {\n _chains = await _getChains(config, {\n chainTypes: [\n ChainType.EVM,\n ChainType.SVM,\n ChainType.UTXO,\n ChainType.MVM,\n ChainType.TVM,\n ],\n })\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n }\n return _chains\n },\n async getRpcUrls() {\n await this.getChains() // _rpcUrls is updated when needed\n return _rpcUrls\n },\n }\n}\n"],"mappings":";;;;AAMA,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"getClientStorage.js","names":[],"sources":["../../../src/client/getClientStorage.ts"],"sourcesContent":["import { ChainId, ChainType, type ExtendedChain } from '@lifi/types'\nimport { _getChains } from '../actions/getChains.js'\nimport { getRpcUrlsFromChains } from '../core/utils.js'\nimport type { RPCUrls, SDKBaseConfig } from '../types/core.js'\n\n// 6 hours in milliseconds\nconst chainsRefreshInterval = 1000 * 60 * 60 * 6\n\nexport interface ClientStorage {\n readonly needReset: boolean\n setChains(chains: ExtendedChain[]): void\n getChains(): Promise<ExtendedChain[]>\n getRpcUrls(): Promise<RPCUrls>\n}\n\nexport const getClientStorage = (config: SDKBaseConfig): ClientStorage => {\n let _chains = [] as ExtendedChain[]\n let _rpcUrls = { ...config.rpcUrls } as RPCUrls\n let _chainsUpdatedAt: number | undefined\n\n const updateRpcUrls = () => {\n _rpcUrls = { ...config.rpcUrls }\n _rpcUrls = getRpcUrlsFromChains(_rpcUrls, _chains, [ChainId.SOL])\n }\n\n return {\n get needReset() {\n return (\n !_chainsUpdatedAt ||\n Date.now() - _chainsUpdatedAt >= chainsRefreshInterval\n )\n },\n setChains(chains: ExtendedChain[]) {\n _chains = chains\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n },\n async getChains() {\n // When preloadChains is false, SDK does not auto-fetch chains\n // External consumer is responsible for calling setChains\n if (!config.preloadChains) {\n return _chains\n }\n\n if (this.needReset || !_chains.length) {\n _chains = await _getChains(config, {\n chainTypes: [\n ChainType.EVM,\n ChainType.SVM,\n ChainType.UTXO,\n ChainType.MVM,\n ChainType.TVM,\n ChainType.STL,\n ],\n })\n _chainsUpdatedAt = Date.now()\n updateRpcUrls()\n }\n return _chains\n },\n async getRpcUrls() {\n await this.getChains() // _rpcUrls is updated when needed\n return _rpcUrls\n },\n }\n}\n"],"mappings":";;;;AAMA,MAAM,wBAAwB;AAS9B,MAAa,oBAAoB,WAAyC;CACxE,IAAI,UAAU,CAAC;CACf,IAAI,WAAW,EAAE,GAAG,OAAO,QAAQ;CACnC,IAAI;CAEJ,MAAM,sBAAsB;EAC1B,WAAW,EAAE,GAAG,OAAO,QAAQ;EAC/B,WAAW,qBAAqB,UAAU,SAAS,CAAC,QAAQ,GAAG,CAAC;CAClE;CAEA,OAAO;EACL,IAAI,YAAY;GACd,OACE,CAAC,oBACD,KAAK,IAAI,IAAI,oBAAoB;EAErC;EACA,UAAU,QAAyB;GACjC,UAAU;GACV,mBAAmB,KAAK,IAAI;GAC5B,cAAc;EAChB;EACA,MAAM,YAAY;GAGhB,IAAI,CAAC,OAAO,eACV,OAAO;GAGT,IAAI,KAAK,aAAa,CAAC,QAAQ,QAAQ;IACrC,UAAU,MAAM,WAAW,QAAQ,EACjC,YAAY;KACV,UAAU;KACV,UAAU;KACV,UAAU;KACV,UAAU;KACV,UAAU;KACV,UAAU;IACZ,EACF,CAAC;IACD,mBAAmB,KAAK,IAAI;IAC5B,cAAc;GAChB;GACA,OAAO;EACT;EACA,MAAM,aAAa;GACjB,MAAM,KAAK,UAAU;GACrB,OAAO;EACT;CACF;AACF"}
|
|
@@ -49,17 +49,19 @@ var BaseStepExecutor = class {
|
|
|
49
49
|
} catch (error) {
|
|
50
50
|
const action = step.execution?.actions?.at(-1);
|
|
51
51
|
const parsed = await this.parseErrors(error, step, action, retryParams);
|
|
52
|
-
if (!(parsed instanceof ExecuteStepRetryError))
|
|
53
|
-
|
|
54
|
-
code: parsed.code
|
|
55
|
-
} });
|
|
56
|
-
else this.statusManager.updateExecution(step, {
|
|
57
|
-
status: "FAILED",
|
|
58
|
-
error: {
|
|
52
|
+
if (!(parsed instanceof ExecuteStepRetryError)) {
|
|
53
|
+
if (action) this.statusManager.updateAction(step, action.type, "FAILED", { error: {
|
|
59
54
|
message: parsed.cause?.message,
|
|
60
55
|
code: parsed.code
|
|
61
|
-
}
|
|
62
|
-
|
|
56
|
+
} });
|
|
57
|
+
else this.statusManager.updateExecution(step, {
|
|
58
|
+
status: "FAILED",
|
|
59
|
+
error: {
|
|
60
|
+
message: parsed.cause?.message,
|
|
61
|
+
code: parsed.code
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
63
65
|
throw parsed;
|
|
64
66
|
}
|
|
65
67
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseStepExecutor.js","names":[],"sources":["../../../src/core/BaseStepExecutor.ts"],"sourcesContent":["import { ExecuteStepRetryError } from '../errors/errors.js'\nimport type { SDKError } from '../errors/SDKError.js'\nimport type {\n ExecuteStepRetryParams,\n ExecutionAction,\n ExecutionOptions,\n InteractionSettings,\n LiFiStepExtended,\n SDKClient,\n StepExecutor,\n StepExecutorOptions,\n} from '../types/core.js'\nimport type {\n StepExecutorBaseContext,\n StepExecutorContext,\n} from '../types/execution.js'\nimport { StatusManager } from './StatusManager.js'\nimport type { TaskPipeline } from './TaskPipeline.js'\n\n// Please be careful when changing the defaults as it may break the behavior (e.g., background execution)\nconst defaultInteractionSettings = {\n allowInteraction: true,\n allowUpdates: true,\n allowExecution: true,\n}\n\nexport abstract class BaseStepExecutor implements StepExecutor {\n protected executionOptions?: ExecutionOptions\n protected statusManager: StatusManager\n\n public allowUserInteraction = true\n public allowExecution = true\n\n constructor(options: StepExecutorOptions) {\n this.statusManager = new StatusManager(options.routeId)\n this.executionOptions = options.executionOptions\n }\n\n setInteraction = (settings?: InteractionSettings): void => {\n const interactionSettings = {\n ...defaultInteractionSettings,\n ...settings,\n }\n this.allowUserInteraction = interactionSettings.allowInteraction\n this.statusManager.allowUpdates(interactionSettings.allowUpdates)\n this.allowExecution = interactionSettings.allowExecution\n }\n\n private createBaseContext = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<StepExecutorBaseContext> => {\n const fromChain = await client.getChainById(step.action.fromChainId)\n const toChain = await client.getChainById(step.action.toChainId)\n\n const isBridgeExecution = fromChain.id !== toChain.id\n\n return {\n client,\n step,\n fromChain,\n toChain,\n isBridgeExecution,\n retryParams,\n statusManager: this.statusManager,\n executionOptions: this.executionOptions,\n allowUserInteraction: this.allowUserInteraction,\n }\n }\n\n abstract createContext(\n baseContext: StepExecutorBaseContext\n ): Promise<StepExecutorContext>\n\n abstract createPipeline(context: StepExecutorContext): TaskPipeline\n\n abstract parseErrors(\n error: Error,\n step?: LiFiStepExtended,\n action?: ExecutionAction,\n retryParams?: ExecuteStepRetryParams\n ): Promise<SDKError | ExecuteStepRetryError>\n\n executeStep = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<LiFiStepExtended> => {\n try {\n step.execution = this.statusManager.initializeExecution(step)\n\n const baseContext = await this.createBaseContext(\n client,\n step,\n retryParams\n )\n const context = await this.createContext(baseContext)\n const pipeline = this.createPipeline(context)\n\n await pipeline.run(context)\n\n return step\n } catch (error: any) {\n // Derive failing action from last in execution.actions\n const action = step.execution?.actions?.at(-1)\n const parsed = await this.parseErrors(error, step, action, retryParams)\n if (!(parsed instanceof ExecuteStepRetryError)) {\n if (action) {\n this.statusManager.updateAction(step, action.type, 'FAILED', {\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n } else {\n this.statusManager.updateExecution(step, {\n status: 'FAILED',\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n }\n }\n throw parsed\n }\n }\n}\n"],"mappings":";;;AAoBA,MAAM,6BAA6B;CACjC,kBAAkB;CAClB,cAAc;CACd,gBAAgB;AAClB;AAEA,IAAsB,mBAAtB,MAA+D;CAC7D;CACA;CAEA,uBAA8B;CAC9B,iBAAwB;CAExB,YAAY,SAA8B;EACxC,KAAK,gBAAgB,IAAI,cAAc,QAAQ,OAAO;EACtD,KAAK,mBAAmB,QAAQ;CAClC;CAEA,kBAAkB,aAAyC;EACzD,MAAM,sBAAsB;GAC1B,GAAG;GACH,GAAG;EACL;EACA,KAAK,uBAAuB,oBAAoB;EAChD,KAAK,cAAc,aAAa,oBAAoB,YAAY;EAChE,KAAK,iBAAiB,oBAAoB;CAC5C;CAEA,oBAA4B,OAC1B,QACA,MACA,gBACqC;EACrC,MAAM,YAAY,MAAM,OAAO,aAAa,KAAK,OAAO,WAAW;EACnE,MAAM,UAAU,MAAM,OAAO,aAAa,KAAK,OAAO,SAAS;EAI/D,OAAO;GACL;GACA;GACA;GACA;GACA,mBAPwB,UAAU,OAAO,QAAQ;GAQjD;GACA,eAAe,KAAK;GACpB,kBAAkB,KAAK;GACvB,sBAAsB,KAAK;EAC7B;CACF;CAeA,cAAc,OACZ,QACA,MACA,gBAC8B;EAC9B,IAAI;GACF,KAAK,YAAY,KAAK,cAAc,oBAAoB,IAAI;GAE5D,MAAM,cAAc,MAAM,KAAK,kBAC7B,QACA,MACA,WACF;GACA,MAAM,UAAU,MAAM,KAAK,cAAc,WAAW;GAGpD,MAFiB,KAAK,eAAe,OAExB,CAAC,CAAC,IAAI,OAAO;GAE1B,OAAO;EACT,SAAS,OAAY;GAEnB,MAAM,SAAS,KAAK,WAAW,SAAS,GAAG,EAAE;GAC7C,MAAM,SAAS,MAAM,KAAK,YAAY,OAAO,MAAM,QAAQ,WAAW;GACtE,IAAI,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"BaseStepExecutor.js","names":[],"sources":["../../../src/core/BaseStepExecutor.ts"],"sourcesContent":["import { ExecuteStepRetryError } from '../errors/errors.js'\nimport type { SDKError } from '../errors/SDKError.js'\nimport type {\n ExecuteStepRetryParams,\n ExecutionAction,\n ExecutionOptions,\n InteractionSettings,\n LiFiStepExtended,\n SDKClient,\n StepExecutor,\n StepExecutorOptions,\n} from '../types/core.js'\nimport type {\n StepExecutorBaseContext,\n StepExecutorContext,\n} from '../types/execution.js'\nimport { StatusManager } from './StatusManager.js'\nimport type { TaskPipeline } from './TaskPipeline.js'\n\n// Please be careful when changing the defaults as it may break the behavior (e.g., background execution)\nconst defaultInteractionSettings = {\n allowInteraction: true,\n allowUpdates: true,\n allowExecution: true,\n}\n\nexport abstract class BaseStepExecutor implements StepExecutor {\n protected executionOptions?: ExecutionOptions\n protected statusManager: StatusManager\n\n public allowUserInteraction = true\n public allowExecution = true\n\n constructor(options: StepExecutorOptions) {\n this.statusManager = new StatusManager(options.routeId)\n this.executionOptions = options.executionOptions\n }\n\n setInteraction = (settings?: InteractionSettings): void => {\n const interactionSettings = {\n ...defaultInteractionSettings,\n ...settings,\n }\n this.allowUserInteraction = interactionSettings.allowInteraction\n this.statusManager.allowUpdates(interactionSettings.allowUpdates)\n this.allowExecution = interactionSettings.allowExecution\n }\n\n private createBaseContext = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<StepExecutorBaseContext> => {\n const fromChain = await client.getChainById(step.action.fromChainId)\n const toChain = await client.getChainById(step.action.toChainId)\n\n const isBridgeExecution = fromChain.id !== toChain.id\n\n return {\n client,\n step,\n fromChain,\n toChain,\n isBridgeExecution,\n retryParams,\n statusManager: this.statusManager,\n executionOptions: this.executionOptions,\n allowUserInteraction: this.allowUserInteraction,\n }\n }\n\n abstract createContext(\n baseContext: StepExecutorBaseContext\n ): Promise<StepExecutorContext>\n\n abstract createPipeline(context: StepExecutorContext): TaskPipeline\n\n abstract parseErrors(\n error: Error,\n step?: LiFiStepExtended,\n action?: ExecutionAction,\n retryParams?: ExecuteStepRetryParams\n ): Promise<SDKError | ExecuteStepRetryError>\n\n executeStep = async (\n client: SDKClient,\n step: LiFiStepExtended,\n retryParams?: ExecuteStepRetryParams\n ): Promise<LiFiStepExtended> => {\n try {\n step.execution = this.statusManager.initializeExecution(step)\n\n const baseContext = await this.createBaseContext(\n client,\n step,\n retryParams\n )\n const context = await this.createContext(baseContext)\n const pipeline = this.createPipeline(context)\n\n await pipeline.run(context)\n\n return step\n } catch (error: any) {\n // Derive failing action from last in execution.actions\n const action = step.execution?.actions?.at(-1)\n const parsed = await this.parseErrors(error, step, action, retryParams)\n if (!(parsed instanceof ExecuteStepRetryError)) {\n if (action) {\n this.statusManager.updateAction(step, action.type, 'FAILED', {\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n } else {\n this.statusManager.updateExecution(step, {\n status: 'FAILED',\n error: {\n message: parsed.cause?.message,\n code: parsed.code,\n },\n })\n }\n }\n throw parsed\n }\n }\n}\n"],"mappings":";;;AAoBA,MAAM,6BAA6B;CACjC,kBAAkB;CAClB,cAAc;CACd,gBAAgB;AAClB;AAEA,IAAsB,mBAAtB,MAA+D;CAC7D;CACA;CAEA,uBAA8B;CAC9B,iBAAwB;CAExB,YAAY,SAA8B;EACxC,KAAK,gBAAgB,IAAI,cAAc,QAAQ,OAAO;EACtD,KAAK,mBAAmB,QAAQ;CAClC;CAEA,kBAAkB,aAAyC;EACzD,MAAM,sBAAsB;GAC1B,GAAG;GACH,GAAG;EACL;EACA,KAAK,uBAAuB,oBAAoB;EAChD,KAAK,cAAc,aAAa,oBAAoB,YAAY;EAChE,KAAK,iBAAiB,oBAAoB;CAC5C;CAEA,oBAA4B,OAC1B,QACA,MACA,gBACqC;EACrC,MAAM,YAAY,MAAM,OAAO,aAAa,KAAK,OAAO,WAAW;EACnE,MAAM,UAAU,MAAM,OAAO,aAAa,KAAK,OAAO,SAAS;EAI/D,OAAO;GACL;GACA;GACA;GACA;GACA,mBAPwB,UAAU,OAAO,QAAQ;GAQjD;GACA,eAAe,KAAK;GACpB,kBAAkB,KAAK;GACvB,sBAAsB,KAAK;EAC7B;CACF;CAeA,cAAc,OACZ,QACA,MACA,gBAC8B;EAC9B,IAAI;GACF,KAAK,YAAY,KAAK,cAAc,oBAAoB,IAAI;GAE5D,MAAM,cAAc,MAAM,KAAK,kBAC7B,QACA,MACA,WACF;GACA,MAAM,UAAU,MAAM,KAAK,cAAc,WAAW;GAGpD,MAFiB,KAAK,eAAe,OAExB,CAAC,CAAC,IAAI,OAAO;GAE1B,OAAO;EACT,SAAS,OAAY;GAEnB,MAAM,SAAS,KAAK,WAAW,SAAS,GAAG,EAAE;GAC7C,MAAM,SAAS,MAAM,KAAK,YAAY,OAAO,MAAM,QAAQ,WAAW;GACtE,IAAI,EAAE,kBAAkB,wBAAwB;IAC9C,IAAI,QACF,KAAK,cAAc,aAAa,MAAM,OAAO,MAAM,UAAU,EAC3D,OAAO;KACL,SAAS,OAAO,OAAO;KACvB,MAAM,OAAO;IACf,EACF,CAAC;SAED,KAAK,cAAc,gBAAgB,MAAM;KACvC,QAAQ;KACR,OAAO;MACL,SAAS,OAAO,OAAO;MACvB,MAAM,OAAO;KACf;IACF,CAAC;GAEL;GACA,MAAM;EACR;CACF;AACF"}
|
|
@@ -121,10 +121,7 @@ var StatusManager = class {
|
|
|
121
121
|
break;
|
|
122
122
|
case "RESET_REQUIRED":
|
|
123
123
|
case "MESSAGE_REQUIRED":
|
|
124
|
-
case "ACTION_REQUIRED":
|
|
125
|
-
step.execution.status = "ACTION_REQUIRED";
|
|
126
|
-
break;
|
|
127
|
-
default: break;
|
|
124
|
+
case "ACTION_REQUIRED": step.execution.status = "ACTION_REQUIRED";
|
|
128
125
|
}
|
|
129
126
|
currentAction.status = status;
|
|
130
127
|
currentAction.message = getActionMessage(type, status);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusManager.js","names":[],"sources":["../../../src/core/StatusManager.ts"],"sourcesContent":["import type { ChainId, LiFiStep } from '@lifi/types'\nimport type {\n Execution,\n ExecutionAction,\n ExecutionActionStatus,\n ExecutionActionType,\n LiFiStepExtended,\n} from '../types/core.js'\nimport { getActionMessage } from './actionMessages.js'\nimport { executionState } from './executionState.js'\n\ntype ActionProps = {\n step: LiFiStepExtended\n type: ExecutionActionType\n chainId: ChainId\n status: ExecutionActionStatus\n}\n\n/**\n * Manages status updates of a route and provides various functions for tracking actions.\n */\nexport class StatusManager {\n private readonly routeId: string\n private shouldUpdate = true\n\n constructor(routeId: string) {\n this.routeId = routeId\n }\n\n /**\n * Initializes the execution object of a Step.\n * @param step The current step in execution\n * @returns The initialized execution object for this step\n */\n initializeExecution = (step: LiFiStepExtended): Execution => {\n if (!step.execution) {\n step.execution = {\n startedAt: Date.now(),\n status: 'PENDING',\n actions: [],\n }\n this.updateStepInRoute(step)\n }\n\n // Change status to PENDING after resuming from FAILED\n if (step.execution.status === 'FAILED') {\n step.execution.startedAt = Date.now()\n step.execution.status = 'PENDING'\n step.execution.signedAt = undefined\n step.execution.error = undefined\n this.updateStepInRoute(step)\n }\n\n return step.execution\n }\n\n /**\n * Updates the execution object of a Step.\n * @param step The current step in execution\n * @param execution Partial execution data to merge\n * @returns The step with the updated execution object\n */\n updateExecution(\n step: LiFiStepExtended,\n execution: Partial<Execution>\n ): LiFiStep {\n if (!step.execution) {\n throw Error(\"Can't update empty execution.\")\n }\n step.execution = {\n ...step.execution,\n ...execution,\n }\n this.updateStepInRoute(step)\n return step\n }\n\n /**\n * Finds an action of the specified type in the step's execution\n * @param step The step to search in\n * @param type The action type to find\n * @returns The found action or undefined if not found\n */\n findAction(\n step: LiFiStepExtended,\n type: ExecutionActionType\n ): ExecutionAction | undefined {\n if (!step.execution?.actions) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const action = step.execution.actions.find((p) => p.type === type)\n\n return action\n }\n\n /**\n * Create and push a new action into the execution.\n * Caller is responsible for ensuring an action of this type does not already exist.\n * @param step The step that should contain the new action.\n * @param type Type of the action.\n * @param chainId Chain Id of the action.\n * @param status The initial status for the new action.\n * @returns The created action.\n */\n createAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const newAction: ExecutionAction = {\n type,\n message: getActionMessage(type, status),\n status,\n chainId,\n }\n\n step.execution.actions.push(newAction)\n this.updateStepInRoute(step)\n return newAction\n }\n\n /**\n * Find an existing action by type and update it, or create a new one if none exists.\n * @param step The step that should contain the action.\n * @param type Type of the action. Used to identify already existing actions.\n * @param chainId Chain Id of the action (used when creating).\n * @param status The status to set on the found or newly created action.\n * @returns The updated or newly created action.\n */\n initializeAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n const action = this.findAction(step, type)\n\n if (action) {\n return this.updateAction(step, type, status, {\n error: undefined,\n })\n }\n\n return this.createAction({ step, type, chainId, status })\n }\n\n /**\n * Update an action object.\n * @param step The step where the action should be updated\n * @param type The action type to update\n * @param status The status the action gets.\n * @param [params] Additional parameters to append to the action.\n * @returns The updated action\n */\n updateAction = (\n step: LiFiStepExtended,\n type: ExecutionActionType,\n status: ExecutionActionStatus,\n params?: Partial<ExecutionAction & { signedAt?: number }>\n ): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Can't update an empty step execution.\")\n }\n const currentAction = this.findAction(step, type)\n\n if (!currentAction) {\n throw new Error(\"Can't find an action for the given type.\")\n }\n\n switch (status) {\n case 'CANCELLED':\n break\n case 'FAILED':\n step.execution.status = 'FAILED'\n if (params?.error) {\n step.execution.error = params.error\n }\n break\n case 'DONE':\n break\n case 'PENDING':\n step.execution.status = 'PENDING'\n if (params?.signedAt) {\n step.execution.signedAt = params.signedAt\n }\n break\n case 'RESET_REQUIRED':\n case 'MESSAGE_REQUIRED':\n case 'ACTION_REQUIRED':\n step.execution.status = 'ACTION_REQUIRED'\n break\n default:\n break\n }\n\n currentAction.status = status\n currentAction.message = getActionMessage(type, status)\n // set extra parameters or overwrite the standard params set in the switch statement\n if (params) {\n const { signedAt: _signedAt, ...rest } = params\n Object.assign(currentAction, rest)\n }\n // Sort actions, the ones with DONE status go first\n step.execution.actions = [\n ...step.execution.actions.filter((action) => action.status === 'DONE'),\n ...step.execution.actions.filter((action) => action.status !== 'DONE'),\n ]\n this.updateStepInRoute(step) // updates the step in the route\n return currentAction\n }\n\n updateStepInRoute = (step: LiFiStep): LiFiStep => {\n if (!this.shouldUpdate) {\n return step\n }\n const data = executionState.get(this.routeId)\n\n if (!data) {\n throw new Error('Execution data not found.')\n }\n\n const stepIndex = data.route.steps.findIndex(\n (routeStep) => routeStep.id === step.id\n )\n\n if (stepIndex === -1) {\n throw new Error(\"Couldn't find a step to update.\")\n }\n\n data.route.steps[stepIndex] = { ...data.route.steps[stepIndex], ...step }\n\n data.executionOptions?.updateRouteHook?.(data.route)\n return data.route.steps[stepIndex]\n }\n\n allowUpdates(value: boolean): void {\n this.shouldUpdate = value\n }\n}\n"],"mappings":";;;;;;AAqBA,IAAa,gBAAb,MAA2B;CACzB;CACA,eAAuB;CAEvB,YAAY,SAAiB;EAC3B,KAAK,UAAU;CACjB;;;;;;CAOA,uBAAuB,SAAsC;EAC3D,IAAI,CAAC,KAAK,WAAW;GACnB,KAAK,YAAY;IACf,WAAW,KAAK,IAAI;IACpB,QAAQ;IACR,SAAS,CAAC;GACZ;GACA,KAAK,kBAAkB,IAAI;EAC7B;EAGA,IAAI,KAAK,UAAU,WAAW,UAAU;GACtC,KAAK,UAAU,YAAY,KAAK,IAAI;GACpC,KAAK,UAAU,SAAS;GACxB,KAAK,UAAU,WAAW,KAAA;GAC1B,KAAK,UAAU,QAAQ,KAAA;GACvB,KAAK,kBAAkB,IAAI;EAC7B;EAEA,OAAO,KAAK;CACd;;;;;;;CAQA,gBACE,MACA,WACU;EACV,IAAI,CAAC,KAAK,WACR,MAAM,MAAM,+BAA+B;EAE7C,KAAK,YAAY;GACf,GAAG,KAAK;GACR,GAAG;EACL;EACA,KAAK,kBAAkB,IAAI;EAC3B,OAAO;CACT;;;;;;;CAQA,WACE,MACA,MAC6B;EAC7B,IAAI,CAAC,KAAK,WAAW,SACnB,MAAM,IAAI,MAAM,oCAAoC;EAKtD,OAFe,KAAK,UAAU,QAAQ,MAAM,MAAM,EAAE,SAAS,IAEjD;CACd;;;;;;;;;;CAWA,gBAAgB,EACd,MACA,MACA,SACA,aACkC;EAClC,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MAAM,oCAAoC;EAGtD,MAAM,YAA6B;GACjC;GACA,SAAS,iBAAiB,MAAM,MAAM;GACtC;GACA;EACF;EAEA,KAAK,UAAU,QAAQ,KAAK,SAAS;EACrC,KAAK,kBAAkB,IAAI;EAC3B,OAAO;CACT;;;;;;;;;CAUA,oBAAoB,EAClB,MACA,MACA,SACA,aACkC;EAGlC,IAFe,KAAK,WAAW,MAAM,IAE5B,GACP,OAAO,KAAK,aAAa,MAAM,MAAM,QAAQ,EAC3C,OAAO,KAAA,EACT,CAAC;EAGH,OAAO,KAAK,aAAa;GAAE;GAAM;GAAM;GAAS;EAAO,CAAC;CAC1D;;;;;;;;;CAUA,gBACE,MACA,MACA,QACA,WACoB;EACpB,IAAI,CAAC,KAAK,WACR,MAAM,IAAI,MAAM,uCAAuC;EAEzD,MAAM,gBAAgB,KAAK,WAAW,MAAM,IAAI;EAEhD,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,0CAA0C;EAG5D,QAAQ,QAAR;GACE,KAAK,aACH;GACF,KAAK;IACH,KAAK,UAAU,SAAS;IACxB,IAAI,QAAQ,OACV,KAAK,UAAU,QAAQ,OAAO;IAEhC;GACF,KAAK,QACH;GACF,KAAK;IACH,KAAK,UAAU,SAAS;IACxB,IAAI,QAAQ,UACV,KAAK,UAAU,WAAW,OAAO;IAEnC;GACF,KAAK;GACL,KAAK;GACL,KAAK
|
|
1
|
+
{"version":3,"file":"StatusManager.js","names":[],"sources":["../../../src/core/StatusManager.ts"],"sourcesContent":["import type { ChainId, LiFiStep } from '@lifi/types'\nimport type {\n Execution,\n ExecutionAction,\n ExecutionActionStatus,\n ExecutionActionType,\n LiFiStepExtended,\n} from '../types/core.js'\nimport { getActionMessage } from './actionMessages.js'\nimport { executionState } from './executionState.js'\n\ntype ActionProps = {\n step: LiFiStepExtended\n type: ExecutionActionType\n chainId: ChainId\n status: ExecutionActionStatus\n}\n\n/**\n * Manages status updates of a route and provides various functions for tracking actions.\n */\nexport class StatusManager {\n private readonly routeId: string\n private shouldUpdate = true\n\n constructor(routeId: string) {\n this.routeId = routeId\n }\n\n /**\n * Initializes the execution object of a Step.\n * @param step The current step in execution\n * @returns The initialized execution object for this step\n */\n initializeExecution = (step: LiFiStepExtended): Execution => {\n if (!step.execution) {\n step.execution = {\n startedAt: Date.now(),\n status: 'PENDING',\n actions: [],\n }\n this.updateStepInRoute(step)\n }\n\n // Change status to PENDING after resuming from FAILED\n if (step.execution.status === 'FAILED') {\n step.execution.startedAt = Date.now()\n step.execution.status = 'PENDING'\n step.execution.signedAt = undefined\n step.execution.error = undefined\n this.updateStepInRoute(step)\n }\n\n return step.execution\n }\n\n /**\n * Updates the execution object of a Step.\n * @param step The current step in execution\n * @param execution Partial execution data to merge\n * @returns The step with the updated execution object\n */\n updateExecution(\n step: LiFiStepExtended,\n execution: Partial<Execution>\n ): LiFiStep {\n if (!step.execution) {\n throw Error(\"Can't update empty execution.\")\n }\n step.execution = {\n ...step.execution,\n ...execution,\n }\n this.updateStepInRoute(step)\n return step\n }\n\n /**\n * Finds an action of the specified type in the step's execution\n * @param step The step to search in\n * @param type The action type to find\n * @returns The found action or undefined if not found\n */\n findAction(\n step: LiFiStepExtended,\n type: ExecutionActionType\n ): ExecutionAction | undefined {\n if (!step.execution?.actions) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const action = step.execution.actions.find((p) => p.type === type)\n\n return action\n }\n\n /**\n * Create and push a new action into the execution.\n * Caller is responsible for ensuring an action of this type does not already exist.\n * @param step The step that should contain the new action.\n * @param type Type of the action.\n * @param chainId Chain Id of the action.\n * @param status The initial status for the new action.\n * @returns The created action.\n */\n createAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Execution hasn't been initialized.\")\n }\n\n const newAction: ExecutionAction = {\n type,\n message: getActionMessage(type, status),\n status,\n chainId,\n }\n\n step.execution.actions.push(newAction)\n this.updateStepInRoute(step)\n return newAction\n }\n\n /**\n * Find an existing action by type and update it, or create a new one if none exists.\n * @param step The step that should contain the action.\n * @param type Type of the action. Used to identify already existing actions.\n * @param chainId Chain Id of the action (used when creating).\n * @param status The status to set on the found or newly created action.\n * @returns The updated or newly created action.\n */\n initializeAction = ({\n step,\n type,\n chainId,\n status,\n }: ActionProps): ExecutionAction => {\n const action = this.findAction(step, type)\n\n if (action) {\n return this.updateAction(step, type, status, {\n error: undefined,\n })\n }\n\n return this.createAction({ step, type, chainId, status })\n }\n\n /**\n * Update an action object.\n * @param step The step where the action should be updated\n * @param type The action type to update\n * @param status The status the action gets.\n * @param [params] Additional parameters to append to the action.\n * @returns The updated action\n */\n updateAction = (\n step: LiFiStepExtended,\n type: ExecutionActionType,\n status: ExecutionActionStatus,\n params?: Partial<ExecutionAction & { signedAt?: number }>\n ): ExecutionAction => {\n if (!step.execution) {\n throw new Error(\"Can't update an empty step execution.\")\n }\n const currentAction = this.findAction(step, type)\n\n if (!currentAction) {\n throw new Error(\"Can't find an action for the given type.\")\n }\n\n switch (status) {\n case 'CANCELLED':\n break\n case 'FAILED':\n step.execution.status = 'FAILED'\n if (params?.error) {\n step.execution.error = params.error\n }\n break\n case 'DONE':\n break\n case 'PENDING':\n step.execution.status = 'PENDING'\n if (params?.signedAt) {\n step.execution.signedAt = params.signedAt\n }\n break\n case 'RESET_REQUIRED':\n case 'MESSAGE_REQUIRED':\n case 'ACTION_REQUIRED':\n step.execution.status = 'ACTION_REQUIRED'\n break\n default:\n break\n }\n\n currentAction.status = status\n currentAction.message = getActionMessage(type, status)\n // set extra parameters or overwrite the standard params set in the switch statement\n if (params) {\n const { signedAt: _signedAt, ...rest } = params\n Object.assign(currentAction, rest)\n }\n // Sort actions, the ones with DONE status go first\n step.execution.actions = [\n ...step.execution.actions.filter((action) => action.status === 'DONE'),\n ...step.execution.actions.filter((action) => action.status !== 'DONE'),\n ]\n this.updateStepInRoute(step) // updates the step in the route\n return currentAction\n }\n\n updateStepInRoute = (step: LiFiStep): LiFiStep => {\n if (!this.shouldUpdate) {\n return step\n }\n const data = executionState.get(this.routeId)\n\n if (!data) {\n throw new Error('Execution data not found.')\n }\n\n const stepIndex = data.route.steps.findIndex(\n (routeStep) => routeStep.id === step.id\n )\n\n if (stepIndex === -1) {\n throw new Error(\"Couldn't find a step to update.\")\n }\n\n data.route.steps[stepIndex] = { ...data.route.steps[stepIndex], ...step }\n\n data.executionOptions?.updateRouteHook?.(data.route)\n return data.route.steps[stepIndex]\n }\n\n allowUpdates(value: boolean): void {\n this.shouldUpdate = value\n }\n}\n"],"mappings":"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"}
|
|
@@ -24,7 +24,8 @@ var CheckBalanceTask = class extends BaseStepExecutionTask {
|
|
|
24
24
|
});
|
|
25
25
|
const walletAddress = step.action.fromAddress;
|
|
26
26
|
if (!walletAddress) throw new TransactionError(1e3, "The wallet address is undefined.");
|
|
27
|
-
|
|
27
|
+
const options = await this.getCheckBalanceOptions(context);
|
|
28
|
+
await checkBalance(client, walletAddress, step, options);
|
|
28
29
|
return { status: "COMPLETED" };
|
|
29
30
|
}
|
|
30
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckBalanceTask.js","names":[],"sources":["../../../../src/core/tasks/CheckBalanceTask.ts"],"sourcesContent":["import { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport {\n type CheckBalanceOptions,\n checkBalance,\n} from './helpers/checkBalance.js'\n\nexport class CheckBalanceTask extends BaseStepExecutionTask {\n /**\n * Per-step options hook for chain-specific subclasses (e.g. skip the\n * gas check for smart-contract wallets or relayed steps). Default `{}`\n * keeps behavior unchanged for every provider that doesn't override.\n * Resolved lazily inside `run()` — pipelines that resume past\n * `CheckBalance` pay no cost.\n */\n protected getCheckBalanceOptions(\n _context: StepExecutorContext\n ): Promise<CheckBalanceOptions> {\n return Promise.resolve({})\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const { client, step, statusManager, isBridgeExecution } = context\n\n statusManager.initializeAction({\n step,\n type: isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP',\n chainId: step.action.fromChainId,\n status: 'STARTED',\n })\n\n const walletAddress = step.action.fromAddress\n if (!walletAddress) {\n throw new TransactionError(\n LiFiErrorCode.InternalError,\n 'The wallet address is undefined.'\n )\n }\n\n const options = await this.getCheckBalanceOptions(context)\n await checkBalance(client, walletAddress, step, options)\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;AASA,IAAa,mBAAb,cAAsC,sBAAsB;;;;;;;;CAQ1D,uBACE,UAC8B;EAC9B,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAC3B;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EAAE,QAAQ,MAAM,eAAe,sBAAsB;EAE3D,cAAc,iBAAiB;GAC7B;GACA,MAAM,oBAAoB,gBAAgB;GAC1C,SAAS,KAAK,OAAO;GACrB,QAAQ;EACV,CAAC;EAED,MAAM,gBAAgB,KAAK,OAAO;EAClC,IAAI,CAAC,eACH,MAAM,IAAI,iBAAA,KAER,kCACF;
|
|
1
|
+
{"version":3,"file":"CheckBalanceTask.js","names":[],"sources":["../../../../src/core/tasks/CheckBalanceTask.ts"],"sourcesContent":["import { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport {\n type CheckBalanceOptions,\n checkBalance,\n} from './helpers/checkBalance.js'\n\nexport class CheckBalanceTask extends BaseStepExecutionTask {\n /**\n * Per-step options hook for chain-specific subclasses (e.g. skip the\n * gas check for smart-contract wallets or relayed steps). Default `{}`\n * keeps behavior unchanged for every provider that doesn't override.\n * Resolved lazily inside `run()` — pipelines that resume past\n * `CheckBalance` pay no cost.\n */\n protected getCheckBalanceOptions(\n _context: StepExecutorContext\n ): Promise<CheckBalanceOptions> {\n return Promise.resolve({})\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const { client, step, statusManager, isBridgeExecution } = context\n\n statusManager.initializeAction({\n step,\n type: isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP',\n chainId: step.action.fromChainId,\n status: 'STARTED',\n })\n\n const walletAddress = step.action.fromAddress\n if (!walletAddress) {\n throw new TransactionError(\n LiFiErrorCode.InternalError,\n 'The wallet address is undefined.'\n )\n }\n\n const options = await this.getCheckBalanceOptions(context)\n await checkBalance(client, walletAddress, step, options)\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;AASA,IAAa,mBAAb,cAAsC,sBAAsB;;;;;;;;CAQ1D,uBACE,UAC8B;EAC9B,OAAO,QAAQ,QAAQ,CAAC,CAAC;CAC3B;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EAAE,QAAQ,MAAM,eAAe,sBAAsB;EAE3D,cAAc,iBAAiB;GAC7B;GACA,MAAM,oBAAoB,gBAAgB;GAC1C,SAAS,KAAK,OAAO;GACrB,QAAQ;EACV,CAAC;EAED,MAAM,gBAAgB,KAAK,OAAO;EAClC,IAAI,CAAC,eACH,MAAM,IAAI,iBAAA,KAER,kCACF;EAGF,MAAM,UAAU,MAAM,KAAK,uBAAuB,OAAO;EACzD,MAAM,aAAa,QAAQ,eAAe,MAAM,OAAO;EACvD,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
|
|
@@ -2,6 +2,13 @@ import { StepExecutorContext, TaskResult } from "../../types/execution.js";
|
|
|
2
2
|
import { BaseStepExecutionTask } from "../BaseStepExecutionTask.js";
|
|
3
3
|
//#region src/core/tasks/PrepareTransactionTask.d.ts
|
|
4
4
|
declare class PrepareTransactionTask extends BaseStepExecutionTask {
|
|
5
|
+
/**
|
|
6
|
+
* Whether `run()` has to fetch a fresh transaction request. The default asks
|
|
7
|
+
* only when the step carries none, which keeps every existing provider
|
|
8
|
+
* unchanged. Chains whose payload cannot be reused override it — see
|
|
9
|
+
* `StellarPrepareTransactionTask`.
|
|
10
|
+
*/
|
|
11
|
+
protected shouldRefetchTransaction(context: StepExecutorContext): boolean;
|
|
5
12
|
run(context: StepExecutorContext): Promise<TaskResult>;
|
|
6
13
|
}
|
|
7
14
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrepareTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/PrepareTransactionTask.ts"],"mappings":";;;cAOa,+BAA+B;
|
|
1
|
+
{"version":3,"file":"PrepareTransactionTask.d.ts","names":[],"sources":["../../../../src/core/tasks/PrepareTransactionTask.ts"],"mappings":";;;cAOa,+BAA+B;;;;;;;YAOhC,yBAAyB,SAAS;EAI5C,IAAU,SAAS,sBAAsB,QAAQ"}
|
|
@@ -5,13 +5,23 @@ import { BaseStepExecutionTask } from "../BaseStepExecutionTask.js";
|
|
|
5
5
|
import { stepComparison } from "./helpers/stepComparison.js";
|
|
6
6
|
//#region src/core/tasks/PrepareTransactionTask.ts
|
|
7
7
|
var PrepareTransactionTask = class extends BaseStepExecutionTask {
|
|
8
|
+
/**
|
|
9
|
+
* Whether `run()` has to fetch a fresh transaction request. The default asks
|
|
10
|
+
* only when the step carries none, which keeps every existing provider
|
|
11
|
+
* unchanged. Chains whose payload cannot be reused override it — see
|
|
12
|
+
* `StellarPrepareTransactionTask`.
|
|
13
|
+
*/
|
|
14
|
+
shouldRefetchTransaction(context) {
|
|
15
|
+
return !context.step.transactionRequest;
|
|
16
|
+
}
|
|
8
17
|
async run(context) {
|
|
9
18
|
const { client, step, statusManager, allowUserInteraction, executionOptions, isBridgeExecution } = context;
|
|
10
19
|
const action = statusManager.findAction(step, isBridgeExecution ? "CROSS_CHAIN" : "SWAP");
|
|
11
20
|
if (!action) throw new TransactionError(1008, "Unable to prepare transaction. Action not found.");
|
|
12
|
-
if (
|
|
21
|
+
if (this.shouldRefetchTransaction(context)) {
|
|
13
22
|
const { execution, ...stepBase } = step;
|
|
14
|
-
const
|
|
23
|
+
const updatedStep = await getStepTransaction(client, stepBase);
|
|
24
|
+
const comparedStep = await stepComparison(statusManager, step, updatedStep, allowUserInteraction, executionOptions);
|
|
15
25
|
Object.assign(step, {
|
|
16
26
|
...comparedStep,
|
|
17
27
|
execution: step.execution
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PrepareTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/PrepareTransactionTask.ts"],"sourcesContent":["import { getStepTransaction } from '../../actions/getStepTransaction.js'\nimport { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport { stepComparison } from './helpers/stepComparison.js'\n\nexport class PrepareTransactionTask extends BaseStepExecutionTask {\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n allowUserInteraction,\n executionOptions,\n isBridgeExecution,\n } = context\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (
|
|
1
|
+
{"version":3,"file":"PrepareTransactionTask.js","names":[],"sources":["../../../../src/core/tasks/PrepareTransactionTask.ts"],"sourcesContent":["import { getStepTransaction } from '../../actions/getStepTransaction.js'\nimport { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport { stepComparison } from './helpers/stepComparison.js'\n\nexport class PrepareTransactionTask extends BaseStepExecutionTask {\n /**\n * Whether `run()` has to fetch a fresh transaction request. The default asks\n * only when the step carries none, which keeps every existing provider\n * unchanged. Chains whose payload cannot be reused override it — see\n * `StellarPrepareTransactionTask`.\n */\n protected shouldRefetchTransaction(context: StepExecutorContext): boolean {\n return !context.step.transactionRequest\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n allowUserInteraction,\n executionOptions,\n isBridgeExecution,\n } = context\n\n const action = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n\n if (!action) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Action not found.'\n )\n }\n\n if (this.shouldRefetchTransaction(context)) {\n const { execution, ...stepBase } = step\n const updatedStep = await getStepTransaction(client, stepBase)\n const comparedStep = await stepComparison(\n statusManager,\n step,\n updatedStep,\n allowUserInteraction,\n executionOptions\n )\n Object.assign(step, {\n ...comparedStep,\n execution: step.execution,\n })\n }\n\n if (!step.transactionRequest?.data) {\n throw new TransactionError(\n LiFiErrorCode.TransactionUnprepared,\n 'Unable to prepare transaction. Transaction request data is not found.'\n )\n }\n\n statusManager.updateAction(step, action.type, 'ACTION_REQUIRED')\n\n if (!allowUserInteraction) {\n return { status: 'PAUSED' }\n }\n\n return { status: 'COMPLETED' }\n }\n}\n"],"mappings":";;;;;;AAOA,IAAa,yBAAb,cAA4C,sBAAsB;;;;;;;CAOhE,yBAAmC,SAAuC;EACxE,OAAO,CAAC,QAAQ,KAAK;CACvB;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EACJ,QACA,MACA,eACA,sBACA,kBACA,sBACE;EAEJ,MAAM,SAAS,cAAc,WAC3B,MACA,oBAAoB,gBAAgB,MACtC;EAEA,IAAI,CAAC,QACH,MAAM,IAAI,iBAAA,MAER,kDACF;EAGF,IAAI,KAAK,yBAAyB,OAAO,GAAG;GAC1C,MAAM,EAAE,WAAW,GAAG,aAAa;GACnC,MAAM,cAAc,MAAM,mBAAmB,QAAQ,QAAQ;GAC7D,MAAM,eAAe,MAAM,eACzB,eACA,MACA,aACA,sBACA,gBACF;GACA,OAAO,OAAO,MAAM;IAClB,GAAG;IACH,WAAW,KAAK;GAClB,CAAC;EACH;EAEA,IAAI,CAAC,KAAK,oBAAoB,MAC5B,MAAM,IAAI,iBAAA,MAER,uEACF;EAGF,cAAc,aAAa,MAAM,OAAO,MAAM,iBAAiB;EAE/D,IAAI,CAAC,sBACH,OAAO,EAAE,QAAQ,SAAS;EAG5B,OAAO,EAAE,QAAQ,YAAY;CAC/B;AACF"}
|
|
@@ -51,7 +51,8 @@ var WaitForTransactionStatusTask = class extends BaseStepExecutionTask {
|
|
|
51
51
|
});
|
|
52
52
|
return { status: "COMPLETED" };
|
|
53
53
|
} catch (e) {
|
|
54
|
-
|
|
54
|
+
const htmlMessage = await getTransactionFailedMessage(client, step, `${toChain.metamask.blockExplorerUrls[0]}tx/${transactionHash}`);
|
|
55
|
+
throw new TransactionError(1003, htmlMessage ?? "Failed while waiting for status of destination chain transaction.", e);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WaitForTransactionStatusTask.js","names":[],"sources":["../../../../src/core/tasks/WaitForTransactionStatusTask.ts"],"sourcesContent":["import type { ExtendedTransactionInfo, FullStatusData } from '@lifi/types'\nimport { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { ExecutionActionType } from '../../types/core.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { getTransactionFailedMessage } from '../../utils/getTransactionMessage.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport { waitForTransactionStatus } from './helpers/waitForTransactionStatus.js'\n\nexport class WaitForTransactionStatusTask extends BaseStepExecutionTask {\n readonly actionType: ExecutionActionType\n\n constructor(actionType: ExecutionActionType) {\n super()\n this.actionType = actionType\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n pollingIntervalMs,\n toChain,\n isBridgeExecution,\n } = context\n\n // At this point, we should have a txHash or taskId\n // taskId is used for custom integrations that don't use the standard transaction hash\n let transactionHash: string | undefined\n try {\n const swapOrBridgeAction = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n transactionHash = swapOrBridgeAction?.txHash || swapOrBridgeAction?.taskId\n\n // Wait for the transaction status on the destination chain\n if (!transactionHash) {\n throw new Error('Transaction hash is undefined.')\n }\n\n const action = statusManager.initializeAction({\n step,\n type: this.actionType,\n chainId:\n this.actionType === 'RECEIVING_CHAIN'\n ? step.action.toChainId\n : step.action.fromChainId,\n status: 'PENDING',\n })\n\n const statusResponse = (await waitForTransactionStatus(\n client,\n statusManager,\n transactionHash,\n step,\n action.type,\n pollingIntervalMs\n )) as FullStatusData\n\n const statusReceiving =\n statusResponse.receiving as ExtendedTransactionInfo\n\n // Update action status\n statusManager.updateAction(step, action.type, 'DONE', {\n chainId: statusReceiving?.chainId || toChain.id,\n substatus: statusResponse.substatus,\n substatusMessage: statusResponse.substatusMessage,\n txHash: statusReceiving?.txHash,\n txLink:\n statusReceiving?.txLink ||\n `${toChain.metamask.blockExplorerUrls[0]}tx/${statusReceiving?.txHash}`,\n })\n\n // Update execution status\n statusManager.updateExecution(step, {\n status: 'DONE',\n ...(statusResponse.sending.amount && {\n fromAmount: statusResponse.sending.amount,\n }),\n ...(statusReceiving?.amount && { toAmount: statusReceiving.amount }),\n ...(statusReceiving?.token && { toToken: statusReceiving.token }),\n internalTxLink: statusResponse?.lifiExplorerLink,\n externalTxLink: statusResponse?.bridgeExplorerLink,\n gasCosts: [\n {\n amount: statusResponse.sending.gasAmount,\n amountUSD: statusResponse.sending.gasAmountUSD,\n token: statusResponse.sending.gasToken,\n estimate: statusResponse.sending.gasUsed,\n limit: statusResponse.sending.gasUsed,\n price: statusResponse.sending.gasPrice,\n type: 'SEND',\n },\n ],\n })\n\n return { status: 'COMPLETED' }\n } catch (e: any) {\n const htmlMessage = await getTransactionFailedMessage(\n client,\n step,\n `${toChain.metamask.blockExplorerUrls[0]}tx/${transactionHash}`\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n htmlMessage ??\n 'Failed while waiting for status of destination chain transaction.',\n e\n )\n }\n }\n}\n"],"mappings":";;;;;;AASA,IAAa,+BAAb,cAAkD,sBAAsB;CACtE;CAEA,YAAY,YAAiC;EAC3C,MAAM;EACN,KAAK,aAAa;CACpB;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EACJ,QACA,MACA,eACA,mBACA,SACA,sBACE;EAIJ,IAAI;EACJ,IAAI;GACF,MAAM,qBAAqB,cAAc,WACvC,MACA,oBAAoB,gBAAgB,MACtC;GACA,kBAAkB,oBAAoB,UAAU,oBAAoB;GAGpE,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,gCAAgC;GAGlD,MAAM,SAAS,cAAc,iBAAiB;IAC5C;IACA,MAAM,KAAK;IACX,SACE,KAAK,eAAe,oBAChB,KAAK,OAAO,YACZ,KAAK,OAAO;IAClB,QAAQ;GACV,CAAC;GAED,MAAM,iBAAkB,MAAM,yBAC5B,QACA,eACA,iBACA,MACA,OAAO,MACP,iBACF;GAEA,MAAM,kBACJ,eAAe;GAGjB,cAAc,aAAa,MAAM,OAAO,MAAM,QAAQ;IACpD,SAAS,iBAAiB,WAAW,QAAQ;IAC7C,WAAW,eAAe;IAC1B,kBAAkB,eAAe;IACjC,QAAQ,iBAAiB;IACzB,QACE,iBAAiB,UACjB,GAAG,QAAQ,SAAS,kBAAkB,GAAG,KAAK,iBAAiB;GACnE,CAAC;GAGD,cAAc,gBAAgB,MAAM;IAClC,QAAQ;IACR,GAAI,eAAe,QAAQ,UAAU,EACnC,YAAY,eAAe,QAAQ,OACrC;IACA,GAAI,iBAAiB,UAAU,EAAE,UAAU,gBAAgB,OAAO;IAClE,GAAI,iBAAiB,SAAS,EAAE,SAAS,gBAAgB,MAAM;IAC/D,gBAAgB,gBAAgB;IAChC,gBAAgB,gBAAgB;IAChC,UAAU,CACR;KACE,QAAQ,eAAe,QAAQ;KAC/B,WAAW,eAAe,QAAQ;KAClC,OAAO,eAAe,QAAQ;KAC9B,UAAU,eAAe,QAAQ;KACjC,OAAO,eAAe,QAAQ;KAC9B,OAAO,eAAe,QAAQ;KAC9B,MAAM;IACR,CACF;GACF,CAAC;GAED,OAAO,EAAE,QAAQ,YAAY;EAC/B,SAAS,GAAQ;
|
|
1
|
+
{"version":3,"file":"WaitForTransactionStatusTask.js","names":[],"sources":["../../../../src/core/tasks/WaitForTransactionStatusTask.ts"],"sourcesContent":["import type { ExtendedTransactionInfo, FullStatusData } from '@lifi/types'\nimport { LiFiErrorCode } from '../../errors/constants.js'\nimport { TransactionError } from '../../errors/errors.js'\nimport type { ExecutionActionType } from '../../types/core.js'\nimport type { StepExecutorContext, TaskResult } from '../../types/execution.js'\nimport { getTransactionFailedMessage } from '../../utils/getTransactionMessage.js'\nimport { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'\nimport { waitForTransactionStatus } from './helpers/waitForTransactionStatus.js'\n\nexport class WaitForTransactionStatusTask extends BaseStepExecutionTask {\n readonly actionType: ExecutionActionType\n\n constructor(actionType: ExecutionActionType) {\n super()\n this.actionType = actionType\n }\n\n async run(context: StepExecutorContext): Promise<TaskResult> {\n const {\n client,\n step,\n statusManager,\n pollingIntervalMs,\n toChain,\n isBridgeExecution,\n } = context\n\n // At this point, we should have a txHash or taskId\n // taskId is used for custom integrations that don't use the standard transaction hash\n let transactionHash: string | undefined\n try {\n const swapOrBridgeAction = statusManager.findAction(\n step,\n isBridgeExecution ? 'CROSS_CHAIN' : 'SWAP'\n )\n transactionHash = swapOrBridgeAction?.txHash || swapOrBridgeAction?.taskId\n\n // Wait for the transaction status on the destination chain\n if (!transactionHash) {\n throw new Error('Transaction hash is undefined.')\n }\n\n const action = statusManager.initializeAction({\n step,\n type: this.actionType,\n chainId:\n this.actionType === 'RECEIVING_CHAIN'\n ? step.action.toChainId\n : step.action.fromChainId,\n status: 'PENDING',\n })\n\n const statusResponse = (await waitForTransactionStatus(\n client,\n statusManager,\n transactionHash,\n step,\n action.type,\n pollingIntervalMs\n )) as FullStatusData\n\n const statusReceiving =\n statusResponse.receiving as ExtendedTransactionInfo\n\n // Update action status\n statusManager.updateAction(step, action.type, 'DONE', {\n chainId: statusReceiving?.chainId || toChain.id,\n substatus: statusResponse.substatus,\n substatusMessage: statusResponse.substatusMessage,\n txHash: statusReceiving?.txHash,\n txLink:\n statusReceiving?.txLink ||\n `${toChain.metamask.blockExplorerUrls[0]}tx/${statusReceiving?.txHash}`,\n })\n\n // Update execution status\n statusManager.updateExecution(step, {\n status: 'DONE',\n ...(statusResponse.sending.amount && {\n fromAmount: statusResponse.sending.amount,\n }),\n ...(statusReceiving?.amount && { toAmount: statusReceiving.amount }),\n ...(statusReceiving?.token && { toToken: statusReceiving.token }),\n internalTxLink: statusResponse?.lifiExplorerLink,\n externalTxLink: statusResponse?.bridgeExplorerLink,\n gasCosts: [\n {\n amount: statusResponse.sending.gasAmount,\n amountUSD: statusResponse.sending.gasAmountUSD,\n token: statusResponse.sending.gasToken,\n estimate: statusResponse.sending.gasUsed,\n limit: statusResponse.sending.gasUsed,\n price: statusResponse.sending.gasPrice,\n type: 'SEND',\n },\n ],\n })\n\n return { status: 'COMPLETED' }\n } catch (e: any) {\n const htmlMessage = await getTransactionFailedMessage(\n client,\n step,\n `${toChain.metamask.blockExplorerUrls[0]}tx/${transactionHash}`\n )\n throw new TransactionError(\n LiFiErrorCode.TransactionFailed,\n htmlMessage ??\n 'Failed while waiting for status of destination chain transaction.',\n e\n )\n }\n }\n}\n"],"mappings":";;;;;;AASA,IAAa,+BAAb,cAAkD,sBAAsB;CACtE;CAEA,YAAY,YAAiC;EAC3C,MAAM;EACN,KAAK,aAAa;CACpB;CAEA,MAAM,IAAI,SAAmD;EAC3D,MAAM,EACJ,QACA,MACA,eACA,mBACA,SACA,sBACE;EAIJ,IAAI;EACJ,IAAI;GACF,MAAM,qBAAqB,cAAc,WACvC,MACA,oBAAoB,gBAAgB,MACtC;GACA,kBAAkB,oBAAoB,UAAU,oBAAoB;GAGpE,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,gCAAgC;GAGlD,MAAM,SAAS,cAAc,iBAAiB;IAC5C;IACA,MAAM,KAAK;IACX,SACE,KAAK,eAAe,oBAChB,KAAK,OAAO,YACZ,KAAK,OAAO;IAClB,QAAQ;GACV,CAAC;GAED,MAAM,iBAAkB,MAAM,yBAC5B,QACA,eACA,iBACA,MACA,OAAO,MACP,iBACF;GAEA,MAAM,kBACJ,eAAe;GAGjB,cAAc,aAAa,MAAM,OAAO,MAAM,QAAQ;IACpD,SAAS,iBAAiB,WAAW,QAAQ;IAC7C,WAAW,eAAe;IAC1B,kBAAkB,eAAe;IACjC,QAAQ,iBAAiB;IACzB,QACE,iBAAiB,UACjB,GAAG,QAAQ,SAAS,kBAAkB,GAAG,KAAK,iBAAiB;GACnE,CAAC;GAGD,cAAc,gBAAgB,MAAM;IAClC,QAAQ;IACR,GAAI,eAAe,QAAQ,UAAU,EACnC,YAAY,eAAe,QAAQ,OACrC;IACA,GAAI,iBAAiB,UAAU,EAAE,UAAU,gBAAgB,OAAO;IAClE,GAAI,iBAAiB,SAAS,EAAE,SAAS,gBAAgB,MAAM;IAC/D,gBAAgB,gBAAgB;IAChC,gBAAgB,gBAAgB;IAChC,UAAU,CACR;KACE,QAAQ,eAAe,QAAQ;KAC/B,WAAW,eAAe,QAAQ;KAClC,OAAO,eAAe,QAAQ;KAC9B,UAAU,eAAe,QAAQ;KACjC,OAAO,eAAe,QAAQ;KAC9B,OAAO,eAAe,QAAQ;KAC9B,MAAM;IACR,CACF;GACF,CAAC;GAED,OAAO,EAAE,QAAQ,YAAY;EAC/B,SAAS,GAAQ;GACf,MAAM,cAAc,MAAM,4BACxB,QACA,MACA,GAAG,QAAQ,SAAS,kBAAkB,GAAG,KAAK,iBAChD;GACA,MAAM,IAAI,iBAAA,MAER,eACE,qEACF,CACF;EACF;CACF;AACF"}
|
|
@@ -53,7 +53,7 @@ const checkBalance = async (client, walletAddress, step, options = {}) => {
|
|
|
53
53
|
const slippageScaled = BigInt(Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION)));
|
|
54
54
|
await withTimeout(async () => {
|
|
55
55
|
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
56
|
-
const isFinal = attempt ===
|
|
56
|
+
const isFinal = attempt === 5;
|
|
57
57
|
let balances;
|
|
58
58
|
try {
|
|
59
59
|
balances = await provider.getBalance(client, walletAddress, tokens);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkBalance.js","names":[],"sources":["../../../../../src/core/tasks/helpers/checkBalance.ts"],"sourcesContent":["import type { LiFiStep, Token, TokenAmount } from '@lifi/types'\nimport { BalanceError } from '../../../errors/errors.js'\nimport type { SDKClient } from '../../../types/core.js'\nimport { formatUnits } from '../../../utils/formatUnits.js'\nimport { sleep } from '../../../utils/sleep.js'\nimport { withTimeout } from '../../../utils/withTimeout.js'\n\nconst MAX_ATTEMPTS = 6\n// Exponential backoff: 150, 300, 600, 1200, 2400 → ≈4.65s of sleep total.\nconst BACKOFF_BASE_MS = 150\nconst OVERALL_TIMEOUT_MS = 10_000\nconst SLIPPAGE_PRECISION = 1_000_000_000n\n\ntype Bucket = 'source' | 'gas' | 'fee'\n\ntype Requirement = {\n token: Token\n sourcePart: bigint // step.action.fromAmount\n gasPart: bigint // step.estimate.gasCosts in this token\n feePart: bigint // non-included step.estimate.feeCosts in this token\n}\n\nexport type CheckBalanceOptions = {\n /**\n * Set to `false` when outer-tx gas is paid by something other than\n * `walletAddress` (SCA executor, 4337 bundler / paymaster, relayer):\n * `gasPart` is excluded from the sufficiency check and slippage rescue.\n * Source amount and non-included fees (e.g. LZ `msg.value`) are still\n * verified. Defaults to `true` (strict, today's behavior).\n */\n walletPaysGas?: boolean\n}\n\n/**\n * Verifies that the wallet holds enough of every token required to execute\n * the step on its source chain — the source-token amount, any gas costs, and\n * any non-included fee costs. Reads all balances in one batched provider\n * call, retries within a bounded budget to absorb transient RPC failures and\n * post-confirmation propagation lag, and applies slippage to the source-token\n * portion only as a last resort (overhead is never trimmed).\n *\n * Throws BalanceError(\"The balance is too low.\") on a genuine shortfall, or\n * BalanceError(\"Could not read wallet balance.\") if the balance can't be read\n * after retries.\n */\nexport const checkBalance = async (\n client: SDKClient,\n walletAddress: string,\n step: LiFiStep,\n options: CheckBalanceOptions = {}\n): Promise<void> => {\n const fromChainId = step.action.fromChainId\n // Chains with non-standard native decimals (e.g. Tempo, Stable) report\n // gas costs in units that don't align with wallet balances — drop the\n // gas portion of the check on those chains.\n const fromChain = await client.getChainById(fromChainId)\n const walletPaysGas =\n (options.walletPaysGas ?? true) && !fromChain.nonStandardNativeDecimals\n const requirements = new Map<string, Requirement>()\n const add = (token: Token, amount: bigint, bucket: Bucket): void => {\n if (token.chainId !== fromChainId || amount === 0n) {\n return\n }\n const key = token.address.toLowerCase()\n const req = requirements.get(key) ?? {\n token,\n sourcePart: 0n,\n gasPart: 0n,\n feePart: 0n,\n }\n if (bucket === 'source') {\n req.sourcePart += amount\n } else if (bucket === 'gas') {\n req.gasPart += amount\n } else {\n req.feePart += amount\n }\n requirements.set(key, req)\n }\n add(step.action.fromToken, BigInt(step.action.fromAmount), 'source')\n for (const gas of step.estimate?.gasCosts ?? []) {\n add(gas.token, BigInt(gas.amount), 'gas')\n }\n for (const fee of step.estimate?.feeCosts ?? []) {\n // Included fees are already part of fromAmount — don't count twice.\n if (!fee.included) {\n add(fee.token, BigInt(fee.amount), 'fee')\n }\n }\n\n const reservedOverhead = (r: Requirement): bigint =>\n r.feePart + (walletPaysGas ? r.gasPart : 0n)\n const need = (r: Requirement): bigint => r.sourcePart + reservedOverhead(r)\n\n // Drop pure-gas entries when `walletPaysGas` is false (e.g. native ETH\n // on Safe Apps with only `gasPart`) — saves one balance read each.\n for (const [key, req] of requirements) {\n if (need(req) === 0n) {\n requirements.delete(key)\n }\n }\n\n if (requirements.size === 0) {\n return\n }\n\n // Provider is dispatched by wallet address; all requirements share the\n // source chain, which matches this provider by virtue of the address.\n const provider = client.providers.find((p) => p.isAddress(walletAddress))\n if (!provider) {\n throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)\n }\n\n const reqs = Array.from(requirements.values())\n const tokens = reqs.map((r) => r.token)\n const slippage = step.action.slippage ?? 0\n const slippageScaled = BigInt(\n Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION))\n )\n\n await withTimeout(\n async () => {\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const isFinal = attempt === MAX_ATTEMPTS - 1\n\n let balances: TokenAmount[]\n try {\n balances = await provider.getBalance(client, walletAddress, tokens)\n } catch (error) {\n if (isFinal) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n error as Error\n )\n }\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n continue\n }\n\n const balanceByAddress = new Map(\n balances.map((b) => [b.address.toLowerCase(), b.amount] as const)\n )\n\n const unknown: Token[] = []\n const insufficient: { req: Requirement; have: bigint }[] = []\n for (const req of reqs) {\n const have = balanceByAddress.get(req.token.address.toLowerCase())\n if (have === undefined) {\n unknown.push(req.token)\n } else if (have < need(req)) {\n insufficient.push({ req, have })\n }\n }\n\n if (unknown.length === 0 && insufficient.length === 0) {\n return\n }\n\n // Final-attempt slippage rescue: only when the sole shortfall is\n // the source-token portion. Trim source to (balance − reserved\n // overhead) so the overhead reserve is preserved.\n if (\n isFinal &&\n unknown.length === 0 &&\n insufficient.length === 1 &&\n insufficient[0].req.sourcePart > 0n\n ) {\n const { req, have } = insufficient[0]\n const reserved = reservedOverhead(req)\n const minAcceptable =\n (req.sourcePart * slippageScaled) / SLIPPAGE_PRECISION + reserved\n if (have >= minAcceptable) {\n const newFromAmount = (have - reserved).toString()\n step.action.fromAmount = newFromAmount\n if (step.includedSteps?.length) {\n step.includedSteps[0].action.fromAmount = newFromAmount\n }\n return\n }\n }\n\n if (isFinal) {\n if (unknown.length > 0) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n new Error(\n `Could not read balance for: ${unknown\n .map((t) => t.symbol || t.address)\n .join(', ')}.`\n )\n )\n }\n const lines = insufficient.map(({ req, have }) => {\n const needed = formatUnits(need(req), req.token.decimals)\n const current = formatUnits(have, req.token.decimals)\n const symbol = req.token.symbol\n // The \"fees\" branch covers pure-overhead tokens; with\n // walletPaysGas=false, gas is excluded from `need(req)` so it\n // only fires for genuine fee shortfalls.\n return req.sourcePart > 0n\n ? `Your ${symbol} balance is too low, you try to transfer ${needed} ${symbol}, but your wallet only holds ${current} ${symbol}.`\n : `Insufficient ${symbol} for fees: need ${needed} ${symbol}, have ${current} ${symbol}.`\n })\n throw new BalanceError(\n 'The balance is too low.',\n new Error(`${lines.join(' ')} No funds have been sent.`)\n )\n }\n\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n }\n },\n {\n timeout: OVERALL_TIMEOUT_MS,\n errorInstance: new BalanceError('Could not read wallet balance.'),\n }\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checkBalance.js","names":[],"sources":["../../../../../src/core/tasks/helpers/checkBalance.ts"],"sourcesContent":["import type { LiFiStep, Token, TokenAmount } from '@lifi/types'\nimport { BalanceError } from '../../../errors/errors.js'\nimport type { SDKClient } from '../../../types/core.js'\nimport { formatUnits } from '../../../utils/formatUnits.js'\nimport { sleep } from '../../../utils/sleep.js'\nimport { withTimeout } from '../../../utils/withTimeout.js'\n\nconst MAX_ATTEMPTS = 6\n// Exponential backoff: 150, 300, 600, 1200, 2400 → ≈4.65s of sleep total.\nconst BACKOFF_BASE_MS = 150\nconst OVERALL_TIMEOUT_MS = 10_000\nconst SLIPPAGE_PRECISION = 1_000_000_000n\n\ntype Bucket = 'source' | 'gas' | 'fee'\n\ntype Requirement = {\n token: Token\n sourcePart: bigint // step.action.fromAmount\n gasPart: bigint // step.estimate.gasCosts in this token\n feePart: bigint // non-included step.estimate.feeCosts in this token\n}\n\nexport type CheckBalanceOptions = {\n /**\n * Set to `false` when outer-tx gas is paid by something other than\n * `walletAddress` (SCA executor, 4337 bundler / paymaster, relayer):\n * `gasPart` is excluded from the sufficiency check and slippage rescue.\n * Source amount and non-included fees (e.g. LZ `msg.value`) are still\n * verified. Defaults to `true` (strict, today's behavior).\n */\n walletPaysGas?: boolean\n}\n\n/**\n * Verifies that the wallet holds enough of every token required to execute\n * the step on its source chain — the source-token amount, any gas costs, and\n * any non-included fee costs. Reads all balances in one batched provider\n * call, retries within a bounded budget to absorb transient RPC failures and\n * post-confirmation propagation lag, and applies slippage to the source-token\n * portion only as a last resort (overhead is never trimmed).\n *\n * Throws BalanceError(\"The balance is too low.\") on a genuine shortfall, or\n * BalanceError(\"Could not read wallet balance.\") if the balance can't be read\n * after retries.\n */\nexport const checkBalance = async (\n client: SDKClient,\n walletAddress: string,\n step: LiFiStep,\n options: CheckBalanceOptions = {}\n): Promise<void> => {\n const fromChainId = step.action.fromChainId\n // Chains with non-standard native decimals (e.g. Tempo, Stable) report\n // gas costs in units that don't align with wallet balances — drop the\n // gas portion of the check on those chains.\n const fromChain = await client.getChainById(fromChainId)\n const walletPaysGas =\n (options.walletPaysGas ?? true) && !fromChain.nonStandardNativeDecimals\n const requirements = new Map<string, Requirement>()\n const add = (token: Token, amount: bigint, bucket: Bucket): void => {\n if (token.chainId !== fromChainId || amount === 0n) {\n return\n }\n const key = token.address.toLowerCase()\n const req = requirements.get(key) ?? {\n token,\n sourcePart: 0n,\n gasPart: 0n,\n feePart: 0n,\n }\n if (bucket === 'source') {\n req.sourcePart += amount\n } else if (bucket === 'gas') {\n req.gasPart += amount\n } else {\n req.feePart += amount\n }\n requirements.set(key, req)\n }\n add(step.action.fromToken, BigInt(step.action.fromAmount), 'source')\n for (const gas of step.estimate?.gasCosts ?? []) {\n add(gas.token, BigInt(gas.amount), 'gas')\n }\n for (const fee of step.estimate?.feeCosts ?? []) {\n // Included fees are already part of fromAmount — don't count twice.\n if (!fee.included) {\n add(fee.token, BigInt(fee.amount), 'fee')\n }\n }\n\n const reservedOverhead = (r: Requirement): bigint =>\n r.feePart + (walletPaysGas ? r.gasPart : 0n)\n const need = (r: Requirement): bigint => r.sourcePart + reservedOverhead(r)\n\n // Drop pure-gas entries when `walletPaysGas` is false (e.g. native ETH\n // on Safe Apps with only `gasPart`) — saves one balance read each.\n for (const [key, req] of requirements) {\n if (need(req) === 0n) {\n requirements.delete(key)\n }\n }\n\n if (requirements.size === 0) {\n return\n }\n\n // Provider is dispatched by wallet address; all requirements share the\n // source chain, which matches this provider by virtue of the address.\n const provider = client.providers.find((p) => p.isAddress(walletAddress))\n if (!provider) {\n throw new Error(`SDK Token Provider for ${walletAddress} is not found.`)\n }\n\n const reqs = Array.from(requirements.values())\n const tokens = reqs.map((r) => r.token)\n const slippage = step.action.slippage ?? 0\n const slippageScaled = BigInt(\n Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION))\n )\n\n await withTimeout(\n async () => {\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const isFinal = attempt === MAX_ATTEMPTS - 1\n\n let balances: TokenAmount[]\n try {\n balances = await provider.getBalance(client, walletAddress, tokens)\n } catch (error) {\n if (isFinal) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n error as Error\n )\n }\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n continue\n }\n\n const balanceByAddress = new Map(\n balances.map((b) => [b.address.toLowerCase(), b.amount] as const)\n )\n\n const unknown: Token[] = []\n const insufficient: { req: Requirement; have: bigint }[] = []\n for (const req of reqs) {\n const have = balanceByAddress.get(req.token.address.toLowerCase())\n if (have === undefined) {\n unknown.push(req.token)\n } else if (have < need(req)) {\n insufficient.push({ req, have })\n }\n }\n\n if (unknown.length === 0 && insufficient.length === 0) {\n return\n }\n\n // Final-attempt slippage rescue: only when the sole shortfall is\n // the source-token portion. Trim source to (balance − reserved\n // overhead) so the overhead reserve is preserved.\n if (\n isFinal &&\n unknown.length === 0 &&\n insufficient.length === 1 &&\n insufficient[0].req.sourcePart > 0n\n ) {\n const { req, have } = insufficient[0]\n const reserved = reservedOverhead(req)\n const minAcceptable =\n (req.sourcePart * slippageScaled) / SLIPPAGE_PRECISION + reserved\n if (have >= minAcceptable) {\n const newFromAmount = (have - reserved).toString()\n step.action.fromAmount = newFromAmount\n if (step.includedSteps?.length) {\n step.includedSteps[0].action.fromAmount = newFromAmount\n }\n return\n }\n }\n\n if (isFinal) {\n if (unknown.length > 0) {\n throw new BalanceError(\n 'Could not read wallet balance.',\n new Error(\n `Could not read balance for: ${unknown\n .map((t) => t.symbol || t.address)\n .join(', ')}.`\n )\n )\n }\n const lines = insufficient.map(({ req, have }) => {\n const needed = formatUnits(need(req), req.token.decimals)\n const current = formatUnits(have, req.token.decimals)\n const symbol = req.token.symbol\n // The \"fees\" branch covers pure-overhead tokens; with\n // walletPaysGas=false, gas is excluded from `need(req)` so it\n // only fires for genuine fee shortfalls.\n return req.sourcePart > 0n\n ? `Your ${symbol} balance is too low, you try to transfer ${needed} ${symbol}, but your wallet only holds ${current} ${symbol}.`\n : `Insufficient ${symbol} for fees: need ${needed} ${symbol}, have ${current} ${symbol}.`\n })\n throw new BalanceError(\n 'The balance is too low.',\n new Error(`${lines.join(' ')} No funds have been sent.`)\n )\n }\n\n await sleep(BACKOFF_BASE_MS * 2 ** attempt)\n }\n },\n {\n timeout: OVERALL_TIMEOUT_MS,\n errorInstance: new BalanceError('Could not read wallet balance.'),\n }\n )\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ const checkPackageUpdates = async (packageName, packageVersion) => {
|
|
|
4
4
|
try {
|
|
5
5
|
const pkgName = packageName ?? "@lifi/sdk";
|
|
6
6
|
const latestVersion = (await (await fetch(`https://registry.npmjs.org/${pkgName}/latest`)).json()).version;
|
|
7
|
-
const currentVersion = packageVersion ?? "4.
|
|
7
|
+
const currentVersion = packageVersion ?? "4.4.0";
|
|
8
8
|
if (latestVersion > currentVersion) console.warn(`${pkgName}: new package version is available. Please update as soon as possible to enjoy the newest features. Current version: ${currentVersion}. Latest version: ${latestVersion}.`);
|
|
9
9
|
} catch (_error) {}
|
|
10
10
|
};
|
package/dist/esm/version.d.ts
CHANGED
package/dist/esm/version.js
CHANGED
package/dist/esm/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk'\nexport const version = '4.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../src/version.ts"],"sourcesContent":["export const name = '@lifi/sdk'\nexport const version = '4.4.0'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "LI.FI SDK for Any-to-Any Cross-Chain-Swap",
|
|
5
5
|
"homepage": "https://github.com/lifinance/sdk",
|
|
6
6
|
"bugs": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"./package.json": "./package.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@lifi/types": "
|
|
31
|
+
"@lifi/types": "18.2.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
@@ -6,6 +6,16 @@ import { BaseStepExecutionTask } from '../BaseStepExecutionTask.js'
|
|
|
6
6
|
import { stepComparison } from './helpers/stepComparison.js'
|
|
7
7
|
|
|
8
8
|
export class PrepareTransactionTask extends BaseStepExecutionTask {
|
|
9
|
+
/**
|
|
10
|
+
* Whether `run()` has to fetch a fresh transaction request. The default asks
|
|
11
|
+
* only when the step carries none, which keeps every existing provider
|
|
12
|
+
* unchanged. Chains whose payload cannot be reused override it — see
|
|
13
|
+
* `StellarPrepareTransactionTask`.
|
|
14
|
+
*/
|
|
15
|
+
protected shouldRefetchTransaction(context: StepExecutorContext): boolean {
|
|
16
|
+
return !context.step.transactionRequest
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
async run(context: StepExecutorContext): Promise<TaskResult> {
|
|
10
20
|
const {
|
|
11
21
|
client,
|
|
@@ -28,7 +38,7 @@ export class PrepareTransactionTask extends BaseStepExecutionTask {
|
|
|
28
38
|
)
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
if (
|
|
41
|
+
if (this.shouldRefetchTransaction(context)) {
|
|
32
42
|
const { execution, ...stepBase } = step
|
|
33
43
|
const updatedStep = await getStepTransaction(client, stepBase)
|
|
34
44
|
const comparedStep = await stepComparison(
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/sdk'
|
|
2
|
-
export const version = '4.
|
|
2
|
+
export const version = '4.4.0'
|