@lifi/sdk 4.8.2 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/actions/getChains.js +7 -1
  3. package/dist/cjs/actions/getChains.js.map +1 -1
  4. package/dist/cjs/actions/getContractCallsQuote.js +1 -1
  5. package/dist/cjs/actions/getGasRecommendation.js +1 -1
  6. package/dist/cjs/actions/getQuote.js +1 -1
  7. package/dist/cjs/actions/getRelayedTransactionStatus.js +1 -1
  8. package/dist/cjs/actions/getRelayerQuote.js +1 -1
  9. package/dist/cjs/actions/getRoutes.js +1 -1
  10. package/dist/cjs/actions/getStatus.js +1 -1
  11. package/dist/cjs/actions/getToken.js +1 -1
  12. package/dist/cjs/actions/getTokens.js +7 -1
  13. package/dist/cjs/actions/getTokens.js.map +1 -1
  14. package/dist/cjs/actions/relayTransaction.js +1 -1
  15. package/dist/cjs/client/createClient.js +23 -2
  16. package/dist/cjs/client/createClient.js.map +1 -1
  17. package/dist/cjs/core/tasks/helpers/checkBalance.js +6 -3
  18. package/dist/cjs/core/tasks/helpers/checkBalance.js.map +1 -1
  19. package/dist/cjs/index.d.ts +4 -3
  20. package/dist/cjs/index.js +4 -3
  21. package/dist/cjs/types/core.d.ts +56 -1
  22. package/dist/cjs/utils/abort.d.ts +6 -0
  23. package/dist/cjs/utils/abort.js +10 -0
  24. package/dist/cjs/utils/abort.js.map +1 -0
  25. package/dist/cjs/utils/checkPackageUpdates.js +1 -1
  26. package/dist/cjs/utils/convertQuoteToRoute.js +1 -1
  27. package/dist/cjs/utils/formatUnits.js +1 -0
  28. package/dist/cjs/utils/formatUnits.js.map +1 -1
  29. package/dist/cjs/utils/lru.d.ts +14 -0
  30. package/dist/cjs/utils/lru.js +35 -0
  31. package/dist/cjs/utils/lru.js.map +1 -0
  32. package/dist/cjs/utils/parseUnits.js +30 -16
  33. package/dist/cjs/utils/parseUnits.js.map +1 -1
  34. package/dist/cjs/utils/request.js +2 -2
  35. package/dist/cjs/utils/sleep.d.ts +7 -1
  36. package/dist/cjs/utils/sleep.js +22 -3
  37. package/dist/cjs/utils/sleep.js.map +1 -1
  38. package/dist/cjs/utils/waitForResult.d.ts +4 -4
  39. package/dist/cjs/utils/waitForResult.js +4 -4
  40. package/dist/cjs/utils/waitForResult.js.map +1 -1
  41. package/dist/cjs/utils/withDedupe.d.ts +15 -12
  42. package/dist/cjs/utils/withDedupe.js +69 -25
  43. package/dist/cjs/utils/withDedupe.js.map +1 -1
  44. package/dist/cjs/utils/withTimeout.js +6 -2
  45. package/dist/cjs/utils/withTimeout.js.map +1 -1
  46. package/dist/cjs/version.d.ts +1 -1
  47. package/dist/cjs/version.js +1 -1
  48. package/dist/cjs/version.js.map +1 -1
  49. package/dist/esm/actions/getChains.d.ts.map +1 -1
  50. package/dist/esm/actions/getChains.js +7 -1
  51. package/dist/esm/actions/getChains.js.map +1 -1
  52. package/dist/esm/actions/getContractCallsQuote.js +1 -1
  53. package/dist/esm/actions/getGasRecommendation.js +1 -1
  54. package/dist/esm/actions/getQuote.js +1 -1
  55. package/dist/esm/actions/getRelayedTransactionStatus.js +1 -1
  56. package/dist/esm/actions/getRelayerQuote.js +1 -1
  57. package/dist/esm/actions/getRoutes.js +1 -1
  58. package/dist/esm/actions/getStatus.js +1 -1
  59. package/dist/esm/actions/getToken.js +1 -1
  60. package/dist/esm/actions/getTokens.d.ts.map +1 -1
  61. package/dist/esm/actions/getTokens.js +7 -1
  62. package/dist/esm/actions/getTokens.js.map +1 -1
  63. package/dist/esm/actions/relayTransaction.js +1 -1
  64. package/dist/esm/client/createClient.d.ts.map +1 -1
  65. package/dist/esm/client/createClient.js +23 -2
  66. package/dist/esm/client/createClient.js.map +1 -1
  67. package/dist/esm/core/tasks/helpers/checkBalance.js +6 -3
  68. package/dist/esm/core/tasks/helpers/checkBalance.js.map +1 -1
  69. package/dist/esm/index.d.ts +4 -3
  70. package/dist/esm/index.js +4 -3
  71. package/dist/esm/types/core.d.ts +56 -1
  72. package/dist/esm/types/core.d.ts.map +1 -1
  73. package/dist/esm/utils/abort.d.ts +6 -0
  74. package/dist/esm/utils/abort.d.ts.map +1 -0
  75. package/dist/esm/utils/abort.js +8 -0
  76. package/dist/esm/utils/abort.js.map +1 -0
  77. package/dist/esm/utils/checkPackageUpdates.js +1 -1
  78. package/dist/esm/utils/convertQuoteToRoute.js +1 -1
  79. package/dist/esm/utils/formatUnits.js +1 -0
  80. package/dist/esm/utils/formatUnits.js.map +1 -1
  81. package/dist/esm/utils/lru.d.ts +14 -0
  82. package/dist/esm/utils/lru.d.ts.map +1 -0
  83. package/dist/esm/utils/lru.js +34 -0
  84. package/dist/esm/utils/lru.js.map +1 -0
  85. package/dist/esm/utils/parseUnits.js +30 -16
  86. package/dist/esm/utils/parseUnits.js.map +1 -1
  87. package/dist/esm/utils/request.js +2 -2
  88. package/dist/esm/utils/sleep.d.ts +7 -1
  89. package/dist/esm/utils/sleep.d.ts.map +1 -1
  90. package/dist/esm/utils/sleep.js +22 -3
  91. package/dist/esm/utils/sleep.js.map +1 -1
  92. package/dist/esm/utils/waitForResult.d.ts +4 -4
  93. package/dist/esm/utils/waitForResult.js +4 -4
  94. package/dist/esm/utils/waitForResult.js.map +1 -1
  95. package/dist/esm/utils/withDedupe.d.ts +15 -12
  96. package/dist/esm/utils/withDedupe.d.ts.map +1 -1
  97. package/dist/esm/utils/withDedupe.js +71 -25
  98. package/dist/esm/utils/withDedupe.js.map +1 -1
  99. package/dist/esm/utils/withTimeout.d.ts.map +1 -1
  100. package/dist/esm/utils/withTimeout.js +6 -2
  101. package/dist/esm/utils/withTimeout.js.map +1 -1
  102. package/dist/esm/version.d.ts +1 -1
  103. package/dist/esm/version.js +1 -1
  104. package/dist/esm/version.js.map +1 -1
  105. package/package.json +2 -2
  106. package/src/actions/getChains.ts +12 -4
  107. package/src/actions/getTokens.ts +12 -4
  108. package/src/client/createClient.ts +35 -2
  109. package/src/core/tasks/helpers/checkBalance.ts +11 -3
  110. package/src/index.ts +4 -1
  111. package/src/types/core.ts +59 -1
  112. package/src/utils/abort.ts +10 -0
  113. package/src/utils/formatUnits.ts +6 -0
  114. package/src/utils/lru.ts +38 -0
  115. package/src/utils/parseUnits.ts +53 -20
  116. package/src/utils/sleep.ts +27 -3
  117. package/src/utils/waitForResult.ts +4 -4
  118. package/src/utils/withDedupe.ts +121 -27
  119. package/src/utils/withTimeout.ts +7 -2
  120. package/src/version.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"getTokens.js","names":[],"sources":["../../../src/actions/getTokens.ts"],"sourcesContent":["import type {\n RequestOptions,\n TokensExtendedResponse,\n TokensRequest,\n TokensResponse,\n} from '@lifi/types'\nimport type { SDKClient } from '../types/core.js'\nimport { request } from '../utils/request.js'\nimport { withDedupe } from '../utils/withDedupe.js'\n\n/**\n * Get all known tokens.\n * @param client - The SDK client\n * @param params - The configuration of the requested tokens\n * @param options - Request options\n * @returns The tokens that are available on the requested chains\n */\nexport async function getTokens(\n client: SDKClient,\n params?: TokensRequest & { extended?: false | undefined },\n options?: RequestOptions\n): Promise<TokensResponse>\nexport async function getTokens(\n client: SDKClient,\n params: TokensRequest & { extended: true },\n options?: RequestOptions\n): Promise<TokensExtendedResponse>\nexport async function getTokens(\n client: SDKClient,\n params?: TokensRequest,\n options?: RequestOptions\n): Promise<TokensResponse> {\n if (params) {\n for (const key of Object.keys(params)) {\n const value = params[key as keyof TokensRequest]\n if (value === undefined || value === null) {\n delete params[key as keyof TokensRequest]\n }\n }\n }\n const urlSearchParams = new URLSearchParams(\n params as Record<string, string>\n ).toString()\n const _isExtended = params?.extended === true\n const response = await withDedupe(\n () =>\n request<\n typeof _isExtended extends true\n ? TokensExtendedResponse\n : TokensResponse\n >(client.config, `${client.config.apiUrl}/tokens?${urlSearchParams}`, {\n signal: options?.signal,\n }),\n { id: `${getTokens.name}.${client.config.apiUrl}.${urlSearchParams}` }\n )\n return response\n}\n"],"mappings":";;;AA2BA,eAAsB,UACpB,QACA,QACA,SACyB;CACzB,IAAI,QACF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACrC,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO,OAAO;CAElB;CAEF,MAAM,kBAAkB,IAAI,gBAC1B,MACF,CAAC,CAAC,SAAS;CACS,QAAQ;CAY5B,OAAO,MAXgB,iBAEnB,QAIE,OAAO,QAAQ,GAAG,OAAO,OAAO,OAAO,UAAU,mBAAmB,EACpE,QAAQ,SAAS,OACnB,CAAC,GACH,EAAE,IAAI,GAAG,UAAU,KAAK,GAAG,OAAO,OAAO,OAAO,GAAG,kBAAkB,CACvE;AAEF"}
1
+ {"version":3,"file":"getTokens.js","names":[],"sources":["../../../src/actions/getTokens.ts"],"sourcesContent":["import type {\n RequestOptions,\n TokensExtendedResponse,\n TokensRequest,\n TokensResponse,\n} from '@lifi/types'\nimport { SDKError } from '../errors/SDKError.js'\nimport type { SDKClient } from '../types/core.js'\nimport { request } from '../utils/request.js'\nimport { withDedupe } from '../utils/withDedupe.js'\n\n/**\n * Get all known tokens.\n * @param client - The SDK client\n * @param params - The configuration of the requested tokens\n * @param options - Request options\n * @returns The tokens that are available on the requested chains\n */\nexport async function getTokens(\n client: SDKClient,\n params?: TokensRequest & { extended?: false | undefined },\n options?: RequestOptions\n): Promise<TokensResponse>\nexport async function getTokens(\n client: SDKClient,\n params: TokensRequest & { extended: true },\n options?: RequestOptions\n): Promise<TokensExtendedResponse>\nexport async function getTokens(\n client: SDKClient,\n params?: TokensRequest,\n options?: RequestOptions\n): Promise<TokensResponse> {\n if (params) {\n for (const key of Object.keys(params)) {\n const value = params[key as keyof TokensRequest]\n if (value === undefined || value === null) {\n delete params[key as keyof TokensRequest]\n }\n }\n }\n const urlSearchParams = new URLSearchParams(\n params as Record<string, string>\n ).toString()\n const _isExtended = params?.extended === true\n const response = await withDedupe(\n (signal) =>\n request<\n typeof _isExtended extends true\n ? TokensExtendedResponse\n : TokensResponse\n >(client.config, `${client.config.apiUrl}/tokens?${urlSearchParams}`, {\n signal,\n }),\n {\n id: `${getTokens.name}.${client.config.apiUrl}.${urlSearchParams}`,\n signal: options?.signal,\n }\n ).catch((error) => {\n // A caller that aborts leaves before the request settles; keep its error\n // shaped like one from `request`.\n throw error instanceof SDKError ? error : new SDKError(error)\n })\n return response\n}\n"],"mappings":";;;;AA4BA,eAAsB,UACpB,QACA,QACA,SACyB;CACzB,IAAI,QACF,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,GAAG;EACrC,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO,OAAO;CAElB;CAEF,MAAM,kBAAkB,IAAI,gBAC1B,MACF,CAAC,CAAC,SAAS;CACS,QAAQ;CAmB5B,OAAO,MAlBgB,YACpB,WACC,QAIE,OAAO,QAAQ,GAAG,OAAO,OAAO,OAAO,UAAU,mBAAmB,EACpE,OACF,CAAC,GACH;EACE,IAAI,GAAG,UAAU,KAAK,GAAG,OAAO,OAAO,OAAO,GAAG;EACjD,QAAQ,SAAS;CACnB,CACF,CAAC,CAAC,OAAO,UAAU;EAGjB,MAAM,iBAAiB,WAAW,QAAQ,IAAI,SAAS,KAAK;CAC9D,CAAC;AAEH"}
@@ -1,7 +1,7 @@
1
+ import { SDKError } from "../errors/SDKError.js";
1
2
  import { BaseError } from "../errors/baseError.js";
2
3
  import "../errors/constants.js";
3
4
  import { ValidationError } from "../errors/errors.js";
4
- import { SDKError } from "../errors/SDKError.js";
5
5
  import { request } from "../utils/request.js";
6
6
  //#region src/actions/relayTransaction.ts
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"createClient.d.ts","names":[],"sources":["../../../src/client/createClient.ts"],"mappings":";;wBAWgB,aAAa,SAAS,YAAY"}
1
+ {"version":3,"file":"createClient.d.ts","names":[],"sources":["../../../src/client/createClient.ts"],"mappings":";;wBAYgB,aAAa,SAAS,YAAY"}
@@ -5,11 +5,26 @@ import { getClientStorage } from "./getClientStorage.js";
5
5
  function createClient(options) {
6
6
  if (!options.integrator) throw new Error("Integrator not found. Please see documentation https://docs.li.fi/integrate-li.fi-js-sdk/set-up-the-sdk");
7
7
  if (!options.disableVersionCheck && process.env.NODE_ENV === "development") checkPackageUpdates(name, version);
8
- const { providers, ...configOptions } = options;
8
+ const { providers, rpcUrls, ...configOptions } = options;
9
+ const readRpcUrls = {};
10
+ const writeRpcUrls = {};
11
+ const bundleRpcUrls = {};
12
+ let hasRoles = false;
13
+ for (const key in rpcUrls) {
14
+ const chainId = Number(key);
15
+ const entry = rpcUrls[chainId];
16
+ if (Array.isArray(entry)) readRpcUrls[chainId] = entry;
17
+ else if (entry) {
18
+ hasRoles = true;
19
+ if (entry.read) readRpcUrls[chainId] = entry.read;
20
+ if (entry.write?.length) writeRpcUrls[chainId] = entry.write;
21
+ if (entry.bundle?.length) bundleRpcUrls[chainId] = entry.bundle;
22
+ }
23
+ }
9
24
  const _config = {
10
25
  ...configOptions,
11
26
  apiUrl: configOptions?.apiUrl ?? "https://li.quest/v1",
12
- rpcUrls: configOptions?.rpcUrls ?? {},
27
+ rpcUrls: hasRoles ? readRpcUrls : rpcUrls ?? {},
13
28
  debug: configOptions?.debug ?? false,
14
29
  preloadChains: configOptions?.preloadChains ?? true,
15
30
  integrator: configOptions?.integrator ?? "lifi-sdk"
@@ -49,6 +64,12 @@ function createClient(options) {
49
64
  const chainRpcUrls = (await this.getRpcUrls())[chainId];
50
65
  if (!chainRpcUrls?.length) throw new Error(`RPC URL not found for chainId: ${chainId}`);
51
66
  return chainRpcUrls;
67
+ },
68
+ async getWriteRpcUrlsByChainId(chainId) {
69
+ return writeRpcUrls[chainId] ?? [];
70
+ },
71
+ async getBundleRpcUrlsByChainId(chainId) {
72
+ return bundleRpcUrls[chainId] ?? [];
52
73
  }
53
74
  };
54
75
  function extend(base) {
@@ -1 +1 @@
1
- {"version":3,"file":"createClient.js","names":[],"sources":["../../../src/client/createClient.ts"],"sourcesContent":["import type { ChainId, ChainType, ExtendedChain } from '@lifi/types'\nimport type {\n SDKBaseConfig,\n SDKClient,\n SDKConfig,\n SDKProvider,\n} from '../types/core.js'\nimport { checkPackageUpdates } from '../utils/checkPackageUpdates.js'\nimport { name, version } from '../version.js'\nimport { getClientStorage } from './getClientStorage.js'\n\nexport function createClient(options: SDKConfig): SDKClient {\n if (!options.integrator) {\n throw new Error(\n 'Integrator not found. Please see documentation https://docs.li.fi/integrate-li.fi-js-sdk/set-up-the-sdk'\n )\n }\n\n if (!options.disableVersionCheck && process.env.NODE_ENV === 'development') {\n checkPackageUpdates(name, version)\n }\n\n const { providers, ...configOptions } = options\n\n const _config: SDKBaseConfig = {\n ...configOptions,\n apiUrl: configOptions?.apiUrl ?? 'https://li.quest/v1',\n rpcUrls: configOptions?.rpcUrls ?? {},\n debug: configOptions?.debug ?? false,\n preloadChains: configOptions?.preloadChains ?? true,\n integrator: configOptions?.integrator ?? 'lifi-sdk',\n }\n\n let _providers: SDKProvider[] = providers ?? []\n const _storage = getClientStorage(_config)\n\n const client: SDKClient = {\n get config() {\n return _config\n },\n get providers() {\n return _providers\n },\n getProvider(type: ChainType) {\n return this.providers.find((provider) => provider.type === type)\n },\n setProviders(newProviders: SDKProvider[]) {\n const providerMap = new Map(\n this.providers.map((provider) => [provider.type, provider])\n )\n for (const provider of newProviders) {\n providerMap.set(provider.type, provider)\n }\n _providers = Array.from(providerMap.values())\n },\n setChains(chains: ExtendedChain[]) {\n _storage.setChains(chains)\n },\n async getChains() {\n return await _storage.getChains()\n },\n async getChainById(chainId: ChainId) {\n const chains = await this.getChains()\n const chain = chains?.find((chain) => chain.id === chainId)\n if (!chain) {\n throw new Error(`ChainId ${chainId} not found`)\n }\n return chain\n },\n async getRpcUrls() {\n return await _storage.getRpcUrls()\n },\n async getRpcUrlsByChainId(chainId: ChainId) {\n const rpcUrls = await this.getRpcUrls()\n const chainRpcUrls = rpcUrls[chainId]\n if (!chainRpcUrls?.length) {\n throw new Error(`RPC URL not found for chainId: ${chainId}`)\n }\n return chainRpcUrls\n },\n }\n\n function extend<TClient extends SDKClient>(\n base: TClient\n ): <TExtensions extends Record<string, any>>(\n extendFn: (client: TClient) => TExtensions\n ) => TClient & TExtensions {\n return (extendFn) => {\n const extensions = extendFn(base)\n // Copy descriptors rather than spreading. `config` and `providers` are\n // accessors over closure variables, and `setProviders` reassigns\n // `_providers` — so a spread would freeze the extension on the values\n // those getters happened to return at extend time, leaving it with a\n // provider list that never updates.\n const extended = Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(base),\n ...Object.getOwnPropertyDescriptors(extensions),\n }\n ) as TClient & typeof extensions\n\n // Preserve the extend function for further extensions\n return Object.assign(extended, {\n extend: extend(extended),\n })\n }\n }\n\n return Object.assign(client, { extend: extend(client) })\n}\n"],"mappings":";;;;AAWA,SAAgB,aAAa,SAA+B;CAC1D,IAAI,CAAC,QAAQ,YACX,MAAM,IAAI,MACR,yGACF;CAGF,IAAI,CAAC,QAAQ,uBAAuB,QAAQ,IAAI,aAAa,eAC3D,oBAAoB,MAAM,OAAO;CAGnC,MAAM,EAAE,WAAW,GAAG,kBAAkB;CAExC,MAAM,UAAyB;EAC7B,GAAG;EACH,QAAQ,eAAe,UAAU;EACjC,SAAS,eAAe,WAAW,CAAC;EACpC,OAAO,eAAe,SAAS;EAC/B,eAAe,eAAe,iBAAiB;EAC/C,YAAY,eAAe,cAAc;CAC3C;CAEA,IAAI,aAA4B,aAAa,CAAC;CAC9C,MAAM,WAAW,iBAAiB,OAAO;CAEzC,MAAM,SAAoB;EACxB,IAAI,SAAS;GACX,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO;EACT;EACA,YAAY,MAAiB;GAC3B,OAAO,KAAK,UAAU,MAAM,aAAa,SAAS,SAAS,IAAI;EACjE;EACA,aAAa,cAA6B;GACxC,MAAM,cAAc,IAAI,IACtB,KAAK,UAAU,KAAK,aAAa,CAAC,SAAS,MAAM,QAAQ,CAAC,CAC5D;GACA,KAAK,MAAM,YAAY,cACrB,YAAY,IAAI,SAAS,MAAM,QAAQ;GAEzC,aAAa,MAAM,KAAK,YAAY,OAAO,CAAC;EAC9C;EACA,UAAU,QAAyB;GACjC,SAAS,UAAU,MAAM;EAC3B;EACA,MAAM,YAAY;GAChB,OAAO,MAAM,SAAS,UAAU;EAClC;EACA,MAAM,aAAa,SAAkB;GAEnC,MAAM,SAAQ,MADO,KAAK,UAAU,EAAA,EACd,MAAM,UAAU,MAAM,OAAO,OAAO;GAC1D,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,WAAW,QAAQ,WAAW;GAEhD,OAAO;EACT;EACA,MAAM,aAAa;GACjB,OAAO,MAAM,SAAS,WAAW;EACnC;EACA,MAAM,oBAAoB,SAAkB;GAE1C,MAAM,gBAAe,MADC,KAAK,WAAW,EAAA,CACT;GAC7B,IAAI,CAAC,cAAc,QACjB,MAAM,IAAI,MAAM,kCAAkC,SAAS;GAE7D,OAAO;EACT;CACF;CAEA,SAAS,OACP,MAGyB;EACzB,QAAQ,aAAa;GACnB,MAAM,aAAa,SAAS,IAAI;GAMhC,MAAM,WAAW,OAAO,iBACtB,CAAC,GACD;IACE,GAAG,OAAO,0BAA0B,IAAI;IACxC,GAAG,OAAO,0BAA0B,UAAU;GAChD,CACF;GAGA,OAAO,OAAO,OAAO,UAAU,EAC7B,QAAQ,OAAO,QAAQ,EACzB,CAAC;EACH;CACF;CAEA,OAAO,OAAO,OAAO,QAAQ,EAAE,QAAQ,OAAO,MAAM,EAAE,CAAC;AACzD"}
1
+ {"version":3,"file":"createClient.js","names":[],"sources":["../../../src/client/createClient.ts"],"sourcesContent":["import type { ChainId, ChainType, ExtendedChain } from '@lifi/types'\nimport type {\n RPCUrls,\n SDKBaseConfig,\n SDKClient,\n SDKConfig,\n SDKProvider,\n} from '../types/core.js'\nimport { checkPackageUpdates } from '../utils/checkPackageUpdates.js'\nimport { name, version } from '../version.js'\nimport { getClientStorage } from './getClientStorage.js'\n\nexport function createClient(options: SDKConfig): SDKClient {\n if (!options.integrator) {\n throw new Error(\n 'Integrator not found. Please see documentation https://docs.li.fi/integrate-li.fi-js-sdk/set-up-the-sdk'\n )\n }\n\n if (!options.disableVersionCheck && process.env.NODE_ENV === 'development') {\n checkPackageUpdates(name, version)\n }\n\n const { providers, rpcUrls, ...configOptions } = options\n\n // Role entries split once, here: the config keeps plain read lists, so\n // everything that reads `config.rpcUrls` sees `string[]` as before.\n const readRpcUrls: RPCUrls = {}\n const writeRpcUrls: RPCUrls = {}\n const bundleRpcUrls: RPCUrls = {}\n let hasRoles = false\n for (const key in rpcUrls) {\n const chainId = Number(key) as ChainId\n const entry = rpcUrls[chainId]\n if (Array.isArray(entry)) {\n readRpcUrls[chainId] = entry\n } else if (entry) {\n hasRoles = true\n if (entry.read) {\n readRpcUrls[chainId] = entry.read\n }\n if (entry.write?.length) {\n writeRpcUrls[chainId] = entry.write\n }\n if (entry.bundle?.length) {\n bundleRpcUrls[chainId] = entry.bundle\n }\n }\n }\n\n const _config: SDKBaseConfig = {\n ...configOptions,\n apiUrl: configOptions?.apiUrl ?? 'https://li.quest/v1',\n // Plain lists only: the caller's own object, exactly as before.\n rpcUrls: hasRoles ? readRpcUrls : ((rpcUrls ?? {}) as RPCUrls),\n debug: configOptions?.debug ?? false,\n preloadChains: configOptions?.preloadChains ?? true,\n integrator: configOptions?.integrator ?? 'lifi-sdk',\n }\n\n let _providers: SDKProvider[] = providers ?? []\n const _storage = getClientStorage(_config)\n\n const client: SDKClient = {\n get config() {\n return _config\n },\n get providers() {\n return _providers\n },\n getProvider(type: ChainType) {\n return this.providers.find((provider) => provider.type === type)\n },\n setProviders(newProviders: SDKProvider[]) {\n const providerMap = new Map(\n this.providers.map((provider) => [provider.type, provider])\n )\n for (const provider of newProviders) {\n providerMap.set(provider.type, provider)\n }\n _providers = Array.from(providerMap.values())\n },\n setChains(chains: ExtendedChain[]) {\n _storage.setChains(chains)\n },\n async getChains() {\n return await _storage.getChains()\n },\n async getChainById(chainId: ChainId) {\n const chains = await this.getChains()\n const chain = chains?.find((chain) => chain.id === chainId)\n if (!chain) {\n throw new Error(`ChainId ${chainId} not found`)\n }\n return chain\n },\n async getRpcUrls() {\n return await _storage.getRpcUrls()\n },\n async getRpcUrlsByChainId(chainId: ChainId) {\n const rpcUrls = await this.getRpcUrls()\n const chainRpcUrls = rpcUrls[chainId]\n if (!chainRpcUrls?.length) {\n throw new Error(`RPC URL not found for chainId: ${chainId}`)\n }\n return chainRpcUrls\n },\n async getWriteRpcUrlsByChainId(chainId: ChainId) {\n return writeRpcUrls[chainId] ?? []\n },\n async getBundleRpcUrlsByChainId(chainId: ChainId) {\n return bundleRpcUrls[chainId] ?? []\n },\n }\n\n function extend<TClient extends SDKClient>(\n base: TClient\n ): <TExtensions extends Record<string, any>>(\n extendFn: (client: TClient) => TExtensions\n ) => TClient & TExtensions {\n return (extendFn) => {\n const extensions = extendFn(base)\n // Copy descriptors rather than spreading. `config` and `providers` are\n // accessors over closure variables, and `setProviders` reassigns\n // `_providers` — so a spread would freeze the extension on the values\n // those getters happened to return at extend time, leaving it with a\n // provider list that never updates.\n const extended = Object.defineProperties(\n {},\n {\n ...Object.getOwnPropertyDescriptors(base),\n ...Object.getOwnPropertyDescriptors(extensions),\n }\n ) as TClient & typeof extensions\n\n // Preserve the extend function for further extensions\n return Object.assign(extended, {\n extend: extend(extended),\n })\n }\n }\n\n return Object.assign(client, { extend: extend(client) })\n}\n"],"mappings":";;;;AAYA,SAAgB,aAAa,SAA+B;CAC1D,IAAI,CAAC,QAAQ,YACX,MAAM,IAAI,MACR,yGACF;CAGF,IAAI,CAAC,QAAQ,uBAAuB,QAAQ,IAAI,aAAa,eAC3D,oBAAoB,MAAM,OAAO;CAGnC,MAAM,EAAE,WAAW,SAAS,GAAG,kBAAkB;CAIjD,MAAM,cAAuB,CAAC;CAC9B,MAAM,eAAwB,CAAC;CAC/B,MAAM,gBAAyB,CAAC;CAChC,IAAI,WAAW;CACf,KAAK,MAAM,OAAO,SAAS;EACzB,MAAM,UAAU,OAAO,GAAG;EAC1B,MAAM,QAAQ,QAAQ;EACtB,IAAI,MAAM,QAAQ,KAAK,GACrB,YAAY,WAAW;OAClB,IAAI,OAAO;GAChB,WAAW;GACX,IAAI,MAAM,MACR,YAAY,WAAW,MAAM;GAE/B,IAAI,MAAM,OAAO,QACf,aAAa,WAAW,MAAM;GAEhC,IAAI,MAAM,QAAQ,QAChB,cAAc,WAAW,MAAM;EAEnC;CACF;CAEA,MAAM,UAAyB;EAC7B,GAAG;EACH,QAAQ,eAAe,UAAU;EAEjC,SAAS,WAAW,cAAgB,WAAW,CAAC;EAChD,OAAO,eAAe,SAAS;EAC/B,eAAe,eAAe,iBAAiB;EAC/C,YAAY,eAAe,cAAc;CAC3C;CAEA,IAAI,aAA4B,aAAa,CAAC;CAC9C,MAAM,WAAW,iBAAiB,OAAO;CAEzC,MAAM,SAAoB;EACxB,IAAI,SAAS;GACX,OAAO;EACT;EACA,IAAI,YAAY;GACd,OAAO;EACT;EACA,YAAY,MAAiB;GAC3B,OAAO,KAAK,UAAU,MAAM,aAAa,SAAS,SAAS,IAAI;EACjE;EACA,aAAa,cAA6B;GACxC,MAAM,cAAc,IAAI,IACtB,KAAK,UAAU,KAAK,aAAa,CAAC,SAAS,MAAM,QAAQ,CAAC,CAC5D;GACA,KAAK,MAAM,YAAY,cACrB,YAAY,IAAI,SAAS,MAAM,QAAQ;GAEzC,aAAa,MAAM,KAAK,YAAY,OAAO,CAAC;EAC9C;EACA,UAAU,QAAyB;GACjC,SAAS,UAAU,MAAM;EAC3B;EACA,MAAM,YAAY;GAChB,OAAO,MAAM,SAAS,UAAU;EAClC;EACA,MAAM,aAAa,SAAkB;GAEnC,MAAM,SAAQ,MADO,KAAK,UAAU,EAAA,EACd,MAAM,UAAU,MAAM,OAAO,OAAO;GAC1D,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,WAAW,QAAQ,WAAW;GAEhD,OAAO;EACT;EACA,MAAM,aAAa;GACjB,OAAO,MAAM,SAAS,WAAW;EACnC;EACA,MAAM,oBAAoB,SAAkB;GAE1C,MAAM,gBAAe,MADC,KAAK,WAAW,EAAA,CACT;GAC7B,IAAI,CAAC,cAAc,QACjB,MAAM,IAAI,MAAM,kCAAkC,SAAS;GAE7D,OAAO;EACT;EACA,MAAM,yBAAyB,SAAkB;GAC/C,OAAO,aAAa,YAAY,CAAC;EACnC;EACA,MAAM,0BAA0B,SAAkB;GAChD,OAAO,cAAc,YAAY,CAAC;EACpC;CACF;CAEA,SAAS,OACP,MAGyB;EACzB,QAAQ,aAAa;GACnB,MAAM,aAAa,SAAS,IAAI;GAMhC,MAAM,WAAW,OAAO,iBACtB,CAAC,GACD;IACE,GAAG,OAAO,0BAA0B,IAAI;IACxC,GAAG,OAAO,0BAA0B,UAAU;GAChD,CACF;GAGA,OAAO,OAAO,OAAO,UAAU,EAC7B,QAAQ,OAAO,QAAQ,EACzB,CAAC;EACH;CACF;CAEA,OAAO,OAAO,OAAO,QAAQ,EAAE,QAAQ,OAAO,MAAM,EAAE,CAAC;AACzD"}
@@ -51,6 +51,8 @@ const checkBalance = async (client, walletAddress, step, options = {}) => {
51
51
  const tokens = reqs.map((r) => r.token);
52
52
  const slippage = step.action.slippage ?? 0;
53
53
  const slippageScaled = BigInt(Math.floor((1 - slippage) * Number(SLIPPAGE_PRECISION)));
54
+ const controller = new AbortController();
55
+ const { signal } = controller;
54
56
  await withTimeout(async () => {
55
57
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
56
58
  const isFinal = attempt === 5;
@@ -59,9 +61,10 @@ const checkBalance = async (client, walletAddress, step, options = {}) => {
59
61
  balances = await provider.getBalance(client, walletAddress, tokens);
60
62
  } catch (error) {
61
63
  if (isFinal) throw new BalanceError("Could not read wallet balance.", error);
62
- await sleep(BACKOFF_BASE_MS * 2 ** attempt);
64
+ await sleep(BACKOFF_BASE_MS * 2 ** attempt, { signal });
63
65
  continue;
64
66
  }
67
+ if (signal.aborted) return;
65
68
  const balanceByAddress = new Map(balances.map((b) => [b.address.toLowerCase(), b.amount]));
66
69
  const unknown = [];
67
70
  const insufficient = [];
@@ -94,12 +97,12 @@ const checkBalance = async (client, walletAddress, step, options = {}) => {
94
97
  });
95
98
  throw new BalanceError("The balance is too low.", /* @__PURE__ */ new Error(`${lines.join(" ")} No funds have been sent.`));
96
99
  }
97
- await sleep(BACKOFF_BASE_MS * 2 ** attempt);
100
+ await sleep(BACKOFF_BASE_MS * 2 ** attempt, { signal });
98
101
  }
99
102
  }, {
100
103
  timeout: OVERALL_TIMEOUT_MS,
101
104
  errorInstance: new BalanceError("Could not read wallet balance.")
102
- });
105
+ }).finally(() => controller.abort());
103
106
  };
104
107
  //#endregion
105
108
  export { checkBalance };
@@ -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 // Aborted once `withTimeout` settles. A timeout rejects at once, but the\n // loop keeps running; the signal stops it before it can change `step`.\n const controller = new AbortController()\n const { signal } = controller\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, { signal })\n continue\n }\n if (signal.aborted) {\n return\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, { signal })\n }\n },\n {\n timeout: OVERALL_TIMEOUT_MS,\n errorInstance: new BalanceError('Could not read wallet balance.'),\n }\n ).finally(() => controller.abort())\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { InMemoryStorage, LocalStorageAdapter, SDKStorage, createDefaultStorage } from "./core/storage.js";
2
- import { AcceptExchangeRateUpdateHook, AcceptSlippageUpdateHook, AcceptSlippageUpdateHookParams, ContractCallParams, ContractTool, ExchangeRateUpdateParams, ExecuteStepRetryParams, Execution, ExecutionAction, ExecutionActionStatus, ExecutionActionType, ExecutionOptions, ExecutionStatus, GetContractCallsHook, GetContractCallsResult, InteractionSettings, LiFiStepExtended, RPCUrls, RequestInterceptor, RouteExecutionData, RouteExecutionDataDictionary, RouteExecutionDictionary, RouteExtended, SDKBaseConfig, SDKClient, SDKConfig, SDKProvider, StepExecutor, StepExecutorOptions, StepExtended, TransactionMethodType, TransactionParameters, TransactionRequestParameters, TransactionRequestUpdateHook, UpdateRouteHook } from "./types/core.js";
2
+ import { AcceptExchangeRateUpdateHook, AcceptSlippageUpdateHook, AcceptSlippageUpdateHookParams, ContractCallParams, ContractTool, ExchangeRateUpdateParams, ExecuteStepRetryParams, Execution, ExecutionAction, ExecutionActionStatus, ExecutionActionType, ExecutionOptions, ExecutionStatus, GetContractCallsHook, GetContractCallsResult, InteractionSettings, LiFiStepExtended, RPCUrls, RPCUrlsByRole, RPCUrlsConfig, RequestInterceptor, RouteExecutionData, RouteExecutionDataDictionary, RouteExecutionDictionary, RouteExtended, SDKBaseConfig, SDKClient, SDKConfig, SDKProvider, StepExecutor, StepExecutorOptions, StepExtended, TransactionMethodType, TransactionParameters, TransactionRequestParameters, TransactionRequestUpdateHook, UpdateRouteHook } from "./types/core.js";
3
3
  import { getChains } from "./actions/getChains.js";
4
4
  import { getConnections } from "./actions/getConnections.js";
5
5
  import { getContractCallsQuote } from "./actions/getContractCallsQuote.js";
@@ -46,11 +46,12 @@ import { convertQuoteToRoute } from "./utils/convertQuoteToRoute.js";
46
46
  import { fetchTxErrorDetails } from "./utils/fetchTxErrorDetails.js";
47
47
  import { formatUnits } from "./utils/formatUnits.js";
48
48
  import { isHex } from "./utils/isHex.js";
49
+ import { LruMap } from "./utils/lru.js";
49
50
  import { parseUnits } from "./utils/parseUnits.js";
50
51
  import { sleep } from "./utils/sleep.js";
51
52
  import { waitForResult } from "./utils/waitForResult.js";
52
- import { LruMap, withDedupe } from "./utils/withDedupe.js";
53
+ import { withDedupe } from "./utils/withDedupe.js";
53
54
  import { withTimeout } from "./utils/withTimeout.js";
54
55
  import { name, version } from "./version.js";
55
56
  export * from "@lifi/types";
56
- export { type AcceptExchangeRateUpdateHook, type AcceptSlippageUpdateHook, type AcceptSlippageUpdateHookParams, BalanceError, BaseError, BaseStepExecutionTask, BaseStepExecutor, type CheckBalanceOptions, CheckBalanceTask, type ContractCallParams, type ContractTool, type ErrorCode, ErrorMessage, ErrorName, type ExchangeRateUpdateParams, ExecuteStepRetryError, type ExecuteStepRetryParams, type Execution, type ExecutionAction, type ExecutionActionStatus, type ExecutionActionType, type ExecutionOptions, type ExecutionStatus, type GetContractCallsHook, type GetContractCallsResult, type GetStatusRequestExtended, HTTPError, InMemoryStorage, type InteractionSettings, LiFiErrorCode, type LiFiStepExtended, type LiFiStepRequest, LocalStorageAdapter, LruMap, PrepareTransactionTask, ProviderError, type QuoteRequest, type QuoteRequestFromAmount, type QuoteRequestToAmount, RPCError, type RPCUrls, type RequestInterceptor, type RouteExecutionData, type RouteExecutionDataDictionary, type RouteExecutionDictionary, type RouteExtended, type SDKBaseConfig, type SDKClient, type SDKConfig, SDKError, type SDKProvider, type SDKStorage, ServerError, StatusManager, type StepExecutor, type StepExecutorBaseContext, type StepExecutorContext, type StepExecutorOptions, type StepExtended, TaskPipeline, type TaskResult, type TaskStatus, TransactionError, type TransactionMethodType, type TransactionParameters, type TransactionRequestParameters, type TransactionRequestUpdateHook, UnknownError, type UpdateRouteHook, ValidationError, WaitForTransactionStatusTask, actions, checkBalance, checkPackageUpdates, convertQuoteToRoute, createClient, createDefaultStorage, executeRoute, fetchTxErrorDetails, formatUnits, getActionMessage, getActiveRoute, getActiveRoutes, getChains, getConnections, getContractCallsQuote, getGasRecommendation, getNameServiceAddress, getQuote, getRelayedTransactionStatus, getRelayerQuote, getRoutes, getStatus, getStepTransaction, getSubstatusMessage, getToken, getTokenBalance, getTokenBalances, getTokenBalancesByChain, getTokens, getTools, getTransactionHistory, getTransactionRequestData, isHex, name, parseUnits, patchContractCalls, relayTransaction, resumeRoute, sleep, stepComparison, stopRouteExecution, updateRouteExecution, version, waitForResult, withDedupe, withTimeout };
57
+ export { type AcceptExchangeRateUpdateHook, type AcceptSlippageUpdateHook, type AcceptSlippageUpdateHookParams, BalanceError, BaseError, BaseStepExecutionTask, BaseStepExecutor, type CheckBalanceOptions, CheckBalanceTask, type ContractCallParams, type ContractTool, type ErrorCode, ErrorMessage, ErrorName, type ExchangeRateUpdateParams, ExecuteStepRetryError, type ExecuteStepRetryParams, type Execution, type ExecutionAction, type ExecutionActionStatus, type ExecutionActionType, type ExecutionOptions, type ExecutionStatus, type GetContractCallsHook, type GetContractCallsResult, type GetStatusRequestExtended, HTTPError, InMemoryStorage, type InteractionSettings, LiFiErrorCode, type LiFiStepExtended, type LiFiStepRequest, LocalStorageAdapter, LruMap, PrepareTransactionTask, ProviderError, type QuoteRequest, type QuoteRequestFromAmount, type QuoteRequestToAmount, RPCError, type RPCUrls, type RPCUrlsByRole, type RPCUrlsConfig, type RequestInterceptor, type RouteExecutionData, type RouteExecutionDataDictionary, type RouteExecutionDictionary, type RouteExtended, type SDKBaseConfig, type SDKClient, type SDKConfig, SDKError, type SDKProvider, type SDKStorage, ServerError, StatusManager, type StepExecutor, type StepExecutorBaseContext, type StepExecutorContext, type StepExecutorOptions, type StepExtended, TaskPipeline, type TaskResult, type TaskStatus, TransactionError, type TransactionMethodType, type TransactionParameters, type TransactionRequestParameters, type TransactionRequestUpdateHook, UnknownError, type UpdateRouteHook, ValidationError, WaitForTransactionStatusTask, actions, checkBalance, checkPackageUpdates, convertQuoteToRoute, createClient, createDefaultStorage, executeRoute, fetchTxErrorDetails, formatUnits, getActionMessage, getActiveRoute, getActiveRoutes, getChains, getConnections, getContractCallsQuote, getGasRecommendation, getNameServiceAddress, getQuote, getRelayedTransactionStatus, getRelayerQuote, getRoutes, getStatus, getStepTransaction, getSubstatusMessage, getToken, getTokenBalance, getTokenBalances, getTokenBalancesByChain, getTokens, getTools, getTransactionHistory, getTransactionRequestData, isHex, name, parseUnits, patchContractCalls, relayTransaction, resumeRoute, sleep, stepComparison, stopRouteExecution, updateRouteExecution, version, waitForResult, withDedupe, withTimeout };
package/dist/esm/index.js CHANGED
@@ -1,11 +1,12 @@
1
+ import { name, version } from "./version.js";
2
+ import { SDKError } from "./errors/SDKError.js";
1
3
  import { BaseError } from "./errors/baseError.js";
2
4
  import { ErrorMessage, ErrorName, LiFiErrorCode } from "./errors/constants.js";
3
5
  import { BalanceError, ExecuteStepRetryError, ProviderError, RPCError, ServerError, TransactionError, UnknownError, ValidationError } from "./errors/errors.js";
4
6
  import { HTTPError } from "./errors/httpError.js";
5
- import { name, version } from "./version.js";
6
- import { SDKError } from "./errors/SDKError.js";
7
7
  import { sleep } from "./utils/sleep.js";
8
- import { LruMap, withDedupe } from "./utils/withDedupe.js";
8
+ import { LruMap } from "./utils/lru.js";
9
+ import { withDedupe } from "./utils/withDedupe.js";
9
10
  import { getChains } from "./actions/getChains.js";
10
11
  import { getConnections } from "./actions/getConnections.js";
11
12
  import { getContractCallsQuote } from "./actions/getContractCallsQuote.js";
@@ -19,10 +19,51 @@ export interface SDKBaseConfig {
19
19
  requestInterceptor?: RequestInterceptor;
20
20
  storage?: SDKStorage;
21
21
  }
22
- export interface SDKConfig extends Partial<Omit<SDKBaseConfig, "integrator">> {
22
+ export interface SDKConfig extends Partial<Omit<SDKBaseConfig, "integrator" | "rpcUrls">> {
23
23
  integrator: string;
24
+ /**
25
+ * Per chain, one list for reads and sends, or lists by role. The client
26
+ * keeps the read lists in `config.rpcUrls` and serves the write and bundle
27
+ * lists through `getWriteRpcUrlsByChainId` and `getBundleRpcUrlsByChainId`.
28
+ */
29
+ rpcUrls?: RPCUrlsConfig;
24
30
  providers?: SDKProvider[];
25
31
  }
32
+ /**
33
+ * RPC URLs for one chain, split by what they are used for.
34
+ */
35
+ export interface RPCUrlsByRole {
36
+ /**
37
+ * Reads: balances, simulation and confirmation. Unset, the chain's own RPC
38
+ * URLs serve reads, as for a chain with no `rpcUrls` entry.
39
+ */
40
+ read?: string[];
41
+ /**
42
+ * Sends. Unset or empty, the read URLs send as well. While `write` is set,
43
+ * nothing is sent through the read URLs: when `bundle` is unset, the write
44
+ * URLs that support bundles also submit bundles, and with none of them, a
45
+ * route that needs a bundle fails.
46
+ *
47
+ * Only `@lifi/sdk-provider-solana` uses this today; other providers ignore
48
+ * it.
49
+ */
50
+ write?: string[];
51
+ /**
52
+ * Bundle submissions (Jito `sendBundle`). Unset, empty, or with no URL that
53
+ * supports bundles, the write URLs that do submit them. While `bundle` or
54
+ * `write` is set, bundles never go to the read URLs.
55
+ *
56
+ * Only `@lifi/sdk-provider-solana` uses this today; other providers ignore
57
+ * it.
58
+ */
59
+ bundle?: string[];
60
+ }
61
+ /**
62
+ * The `rpcUrls` option of `createClient`: per chain, either one list for reads
63
+ * and sends, or lists by role.
64
+ */
65
+ export type RPCUrlsConfig = Partial<Record<ChainId, string[] | RPCUrlsByRole>>;
66
+ /** RPC URL lists per chain. `client.config.rpcUrls` holds the read lists. */
26
67
  export type RPCUrls = Partial<Record<ChainId, string[]>>;
27
68
  export interface SDKProvider {
28
69
  readonly type: ChainType;
@@ -47,6 +88,20 @@ export interface SDKClient {
47
88
  getChainById(chainId: ChainId): Promise<ExtendedChain>;
48
89
  getRpcUrls(): Promise<RPCUrls>;
49
90
  getRpcUrlsByChainId(chainId: ChainId): Promise<string[]>;
91
+ /**
92
+ * The chain's dedicated write RPC URLs (`rpcUrls[chainId].write`). Empty
93
+ * when the chain has none.
94
+ *
95
+ * Optional so clients from other SDK versions, and hand-written ones, still
96
+ * satisfy `SDKClient`. Providers treat a missing method as no write list.
97
+ */
98
+ getWriteRpcUrlsByChainId?(chainId: ChainId): Promise<string[]>;
99
+ /**
100
+ * The chain's dedicated bundle RPC URLs (`rpcUrls[chainId].bundle`). Empty
101
+ * when the chain has none. Optional for the same reason as
102
+ * `getWriteRpcUrlsByChainId`.
103
+ */
104
+ getBundleRpcUrlsByChainId?(chainId: ChainId): Promise<string[]>;
50
105
  }
51
106
  export interface StepExecutorOptions {
52
107
  routeId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","names":[],"sources":["../../../src/types/core.ts"],"mappings":";;;;YAmBY,sBACV,SAAS,wBACN,sBAAsB,QAAQ;iBAElB;EACf;EACA;EACA;EACA;EACA,eAAe;EACf,mBAAmB;EACnB,SAAS;EACT;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB,UAAU;;iBAGK,kBAAkB,QAAQ,KAAK;EAC9C;EACA,YAAY;;YAGF,UAAU,QAAQ,OAAO;iBAEpB;WACN,MAAM;EACf,UAAU;;;;;;EAMV,gBAAgB;EAChB,eACE,cACA,QAAQ,WACR,UAAU,SACV,QAAQ,UACP;EACH,gBAAgB,SAAS,sBAAsB,QAAQ;EACvD,WACE,QAAQ,WACR,uBACA,QAAQ,UACP,QAAQ;;iBAGI;EACf,QAAQ;EACR,WAAW;EACX,YAAY,MAAM,YAAY;EAC9B,aAAa,WAAW;EACxB,UAAU,QAAQ;EAClB,aAAa,QAAQ;EACrB,aAAa,SAAS,UAAU,QAAQ;EACxC,cAAc,QAAQ;EACtB,oBAAoB,SAAS,UAAU;;iBAGxB;EACf;EACA,mBAAmB;;iBAGJ;EACf;EACA;EACA;;;;;;YAOU,yBAAyB;iBAEpB;EACf;EACA;EACA,eAAe,WAAW;EAC1B,YACE,QAAQ,WACR,MAAM,kBACN,cAAc,yBACb,QAAQ;;iBAGI;EACf,OAAO;EACP,WAAW;EACX,mBAAmB;;YAGT,+BAA+B,QACzC,eAAe;iBAGA,sBAAsB,KAAK;EAC1C,OAAO;;iBAGQ,yBAAyB;EACxC,YAAY;;YAGF,eAAe;EACzB,YAAY;;YAGF;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAGU,2BAA2B,QAAQ,eAAe,QAAQ;YAE1D,mBAAmB,cAAc;iBAE5B,qCAAqC;EACpD;;YAGU,gCACV,kBAAkB,iCACf,QAAQ;iBAEI;EACf,SAAS;EACT;EACA;EACA;EACA;;YAGU,4BACV,QAAQ,mCACL;iBAEY;EACf,SAAS;EACT;EACA;;YAGU,gCACV,QAAQ,6BACL;iBAEY;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf,eAAe;EACf;EACA,eAAe;;YAGL,wBACV,QAAQ,uBACL,QAAQ;iBAEI;EACf,+BAA+B;EAC/B,kBAAkB;EAClB,+BAA+B;EAC/B,mBAAmB;EACnB;EACA;;YAGU;YAEA;YAUA;YAUA;EACV,MAAM;EACN,QAAQ;EACR;EACA,YAAY;EACZ;EACA;EACA;EACA;EACA;EACA,SAAS;EACT;EAEA;IAAU;IAAuB;IAAiB;;;iBAGnC;EACf;EACA;EACA,QAAQ;EACR,SAAS,MAAM;EACf;EACA;EACA,UAAU;EACV,WAAW;EACX,WAAW;EACX;EACA;EAEA;IAAU;IAAuB;IAAiB;;;YAGxC"}
1
+ {"version":3,"file":"core.d.ts","names":[],"sources":["../../../src/types/core.ts"],"mappings":";;;;YAmBY,sBACV,SAAS,wBACN,sBAAsB,QAAQ;iBAElB;EACf;EACA;EACA;EACA;EACA,eAAe;EACf,mBAAmB;EACnB,SAAS;EACT;EACA;EACA;EACA;EACA;EACA,qBAAqB;EACrB,UAAU;;iBAGK,kBACP,QAAQ,KAAK;EACrB;;;;;;EAMA,UAAU;EACV,YAAY;;;;;iBAMG;;;;;EAKf;;;;;;;;;;EAUA;;;;;;;;;EASA;;;;;;YAOU,gBAAgB,QAAQ,OAAO,oBAAoB;;YAGnD,UAAU,QAAQ,OAAO;iBAEpB;WACN,MAAM;EACf,UAAU;;;;;;EAMV,gBAAgB;EAChB,eACE,cACA,QAAQ,WACR,UAAU,SACV,QAAQ,UACP;EACH,gBAAgB,SAAS,sBAAsB,QAAQ;EACvD,WACE,QAAQ,WACR,uBACA,QAAQ,UACP,QAAQ;;iBAGI;EACf,QAAQ;EACR,WAAW;EACX,YAAY,MAAM,YAAY;EAC9B,aAAa,WAAW;EACxB,UAAU,QAAQ;EAClB,aAAa,QAAQ;EACrB,aAAa,SAAS,UAAU,QAAQ;EACxC,cAAc,QAAQ;EACtB,oBAAoB,SAAS,UAAU;;;;;;;;EAQvC,0BAA0B,SAAS,UAAU;;;;;;EAM7C,2BAA2B,SAAS,UAAU;;iBAG/B;EACf;EACA,mBAAmB;;iBAGJ;EACf;EACA;EACA;;;;;;YAOU,yBAAyB;iBAEpB;EACf;EACA;EACA,eAAe,WAAW;EAC1B,YACE,QAAQ,WACR,MAAM,kBACN,cAAc,yBACb,QAAQ;;iBAGI;EACf,OAAO;EACP,WAAW;EACX,mBAAmB;;YAGT,+BAA+B,QACzC,eAAe;iBAGA,sBAAsB,KAAK;EAC1C,OAAO;;iBAGQ,yBAAyB;EACxC,YAAY;;YAGF,eAAe;EACzB,YAAY;;YAGF;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;YAGU,2BAA2B,QAAQ,eAAe,QAAQ;YAE1D,mBAAmB,cAAc;iBAE5B,qCAAqC;EACpD;;YAGU,gCACV,kBAAkB,iCACf,QAAQ;iBAEI;EACf,SAAS;EACT;EACA;EACA;EACA;;YAGU,4BACV,QAAQ,mCACL;iBAEY;EACf,SAAS;EACT;EACA;;YAGU,gCACV,QAAQ,6BACL;iBAEY;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;;iBAGe;EACf,eAAe;EACf;EACA,eAAe;;YAGL,wBACV,QAAQ,uBACL,QAAQ;iBAEI;EACf,+BAA+B;EAC/B,kBAAkB;EAClB,+BAA+B;EAC/B,mBAAmB;EACnB;EACA;;YAGU;YAEA;YAUA;YAUA;EACV,MAAM;EACN,QAAQ;EACR;EACA,YAAY;EACZ;EACA;EACA;EACA;EACA;EACA,SAAS;EACT;EAEA;IAAU;IAAuB;IAAiB;;;iBAGnC;EACf;EACA;EACA,QAAQ;EACR,SAAS,MAAM;EACf;EACA;EACA,UAAU;EACV,WAAW;EACX,WAAW;EACX;EACA;EAEA;IAAU;IAAuB;IAAiB;;;YAGxC"}
@@ -0,0 +1,6 @@
1
+ //#region src/utils/abort.d.ts
2
+ /** Older runtimes and polyfills can abort a signal without a `reason`. */
3
+ export declare const getAbortError: (signal: AbortSignal) => unknown;
4
+ export declare const isAbortError: (error: unknown) => boolean;
5
+ //#endregion
6
+ //# sourceMappingURL=abort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.d.ts","names":[],"sources":["../../../src/utils/abort.ts"],"mappings":";;qBACa,gBAAiB,QAAQ;qBAIzB,eAAgB"}
@@ -0,0 +1,8 @@
1
+ //#region src/utils/abort.ts
2
+ /** Older runtimes and polyfills can abort a signal without a `reason`. */
3
+ const getAbortError = (signal) => signal.reason ?? Object.assign(/* @__PURE__ */ new Error("This operation was aborted"), { name: "AbortError" });
4
+ const isAbortError = (error) => typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
5
+ //#endregion
6
+ export { getAbortError, isAbortError };
7
+
8
+ //# sourceMappingURL=abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort.js","names":[],"sources":["../../../src/utils/abort.ts"],"sourcesContent":["/** Older runtimes and polyfills can abort a signal without a `reason`. */\nexport const getAbortError = (signal: AbortSignal): unknown =>\n signal.reason ??\n Object.assign(new Error('This operation was aborted'), { name: 'AbortError' })\n\nexport const isAbortError = (error: unknown): boolean =>\n typeof error === 'object' &&\n error !== null &&\n 'name' in error &&\n error.name === 'AbortError'\n"],"mappings":";;AACA,MAAa,iBAAiB,WAC5B,OAAO,UACP,OAAO,uBAAO,IAAI,MAAM,4BAA4B,GAAG,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAa,gBAAgB,UAC3B,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS"}
@@ -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.8.2";
7
+ const currentVersion = packageVersion ?? "4.10.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
  };
@@ -1,5 +1,5 @@
1
- import { ValidationError } from "../errors/errors.js";
2
1
  import { SDKError } from "../errors/SDKError.js";
2
+ import { ValidationError } from "../errors/errors.js";
3
3
  import { formatUnits } from "./formatUnits.js";
4
4
  //#region src/utils/convertQuoteToRoute.ts
5
5
  const parseBigInt = (value) => {
@@ -3,6 +3,7 @@
3
3
  * Divides a number by a given exponent of base 10 (10exponent), and formats it into a string representation of the number.
4
4
  */
5
5
  function formatUnits(value, decimals) {
6
+ if (!Number.isInteger(decimals) || decimals < 0) throw new Error(`\`decimals\` must be a non-negative integer. Got \`${decimals}\`.`);
6
7
  let display = value.toString();
7
8
  const negative = display.startsWith("-");
8
9
  if (negative) display = display.slice(1);
@@ -1 +1 @@
1
- {"version":3,"file":"formatUnits.js","names":[],"sources":["../../../src/utils/formatUnits.ts"],"sourcesContent":["/**\n * Divides a number by a given exponent of base 10 (10exponent), and formats it into a string representation of the number.\n */\nexport function formatUnits(value: bigint, decimals: number): string {\n let display = value.toString()\n\n const negative = display.startsWith('-')\n if (negative) {\n display = display.slice(1)\n }\n\n display = display.padStart(decimals, '0')\n\n let [integer, fraction] = [\n display.slice(0, display.length - decimals),\n display.slice(display.length - decimals),\n ]\n fraction = fraction.replace(/(0+)$/, '')\n return `${negative ? '-' : ''}${integer || '0'}${\n fraction ? `.${fraction}` : ''\n }`\n}\n"],"mappings":";;;;AAGA,SAAgB,YAAY,OAAe,UAA0B;CACnE,IAAI,UAAU,MAAM,SAAS;CAE7B,MAAM,WAAW,QAAQ,WAAW,GAAG;CACvC,IAAI,UACF,UAAU,QAAQ,MAAM,CAAC;CAG3B,UAAU,QAAQ,SAAS,UAAU,GAAG;CAExC,IAAI,CAAC,SAAS,YAAY,CACxB,QAAQ,MAAM,GAAG,QAAQ,SAAS,QAAQ,GAC1C,QAAQ,MAAM,QAAQ,SAAS,QAAQ,CACzC;CACA,WAAW,SAAS,QAAQ,SAAS,EAAE;CACvC,OAAO,GAAG,WAAW,MAAM,KAAK,WAAW,MACzC,WAAW,IAAI,aAAa;AAEhC"}
1
+ {"version":3,"file":"formatUnits.js","names":[],"sources":["../../../src/utils/formatUnits.ts"],"sourcesContent":["/**\n * Divides a number by a given exponent of base 10 (10exponent), and formats it into a string representation of the number.\n */\nexport function formatUnits(value: bigint, decimals: number): string {\n if (!Number.isInteger(decimals) || decimals < 0) {\n throw new Error(\n `\\`decimals\\` must be a non-negative integer. Got \\`${decimals}\\`.`\n )\n }\n\n let display = value.toString()\n\n const negative = display.startsWith('-')\n if (negative) {\n display = display.slice(1)\n }\n\n display = display.padStart(decimals, '0')\n\n let [integer, fraction] = [\n display.slice(0, display.length - decimals),\n display.slice(display.length - decimals),\n ]\n fraction = fraction.replace(/(0+)$/, '')\n return `${negative ? '-' : ''}${integer || '0'}${\n fraction ? `.${fraction}` : ''\n }`\n}\n"],"mappings":";;;;AAGA,SAAgB,YAAY,OAAe,UAA0B;CACnE,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,GAC5C,MAAM,IAAI,MACR,sDAAsD,SAAS,IACjE;CAGF,IAAI,UAAU,MAAM,SAAS;CAE7B,MAAM,WAAW,QAAQ,WAAW,GAAG;CACvC,IAAI,UACF,UAAU,QAAQ,MAAM,CAAC;CAG3B,UAAU,QAAQ,SAAS,UAAU,GAAG;CAExC,IAAI,CAAC,SAAS,YAAY,CACxB,QAAQ,MAAM,GAAG,QAAQ,SAAS,QAAQ,GAC1C,QAAQ,MAAM,QAAQ,SAAS,QAAQ,CACzC;CACA,WAAW,SAAS,QAAQ,SAAS,EAAE;CACvC,OAAO,GAAG,WAAW,MAAM,KAAK,WAAW,MACzC,WAAW,IAAI,aAAa;AAEhC"}
@@ -0,0 +1,14 @@
1
+ //#region src/utils/lru.d.ts
2
+ /**
3
+ * Map with a LRU (Least recently used) policy.
4
+ *
5
+ * https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU
6
+ */
7
+ export declare class LruMap<value = unknown> extends Map<string, value> {
8
+ maxSize: number;
9
+ constructor(size: number);
10
+ override get(key: string): value | undefined;
11
+ override set(key: string, value: value): this;
12
+ }
13
+ //#endregion
14
+ //# sourceMappingURL=lru.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru.d.ts","names":[],"sources":["../../../src/utils/lru.ts"],"mappings":";;;;;;qBAKa,OAAO,yBAAyB,YAAY;EACvD;EAEA,YAAY;WAKH,IAAI,cAAc;WASlB,IAAI,aAAa,OAAO"}
@@ -0,0 +1,34 @@
1
+ //#region src/utils/lru.ts
2
+ /**
3
+ * Map with a LRU (Least recently used) policy.
4
+ *
5
+ * https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU
6
+ */
7
+ var LruMap = class extends Map {
8
+ maxSize;
9
+ constructor(size) {
10
+ super();
11
+ this.maxSize = size;
12
+ }
13
+ get(key) {
14
+ const value = super.get(key);
15
+ if (super.has(key)) {
16
+ super.delete(key);
17
+ super.set(key, value);
18
+ }
19
+ return value;
20
+ }
21
+ set(key, value) {
22
+ if (super.has(key)) super.delete(key);
23
+ super.set(key, value);
24
+ if (this.maxSize && this.size > this.maxSize) {
25
+ const firstKey = super.keys().next().value;
26
+ if (firstKey !== void 0) super.delete(firstKey);
27
+ }
28
+ return this;
29
+ }
30
+ };
31
+ //#endregion
32
+ export { LruMap };
33
+
34
+ //# sourceMappingURL=lru.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lru.js","names":[],"sources":["../../../src/utils/lru.ts"],"sourcesContent":["/**\n * Map with a LRU (Least recently used) policy.\n *\n * https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU\n */\nexport class LruMap<value = unknown> extends Map<string, value> {\n maxSize: number\n\n constructor(size: number) {\n super()\n this.maxSize = size\n }\n\n override get(key: string): value | undefined {\n const value = super.get(key)\n if (super.has(key)) {\n super.delete(key)\n super.set(key, value as value)\n }\n return value\n }\n\n override set(key: string, value: value): this {\n if (super.has(key)) {\n super.delete(key)\n }\n super.set(key, value)\n if (this.maxSize && this.size > this.maxSize) {\n // `super.keys()`, not `this.keys()`: on iOS 18 the subclass iterator\n // can yield `undefined`, and the map then grows without limit.\n const firstKey = super.keys().next().value\n if (firstKey !== undefined) {\n super.delete(firstKey)\n }\n }\n return this\n }\n}\n"],"mappings":";;;;;;AAKA,IAAa,SAAb,cAA6C,IAAmB;CAC9D;CAEA,YAAY,MAAc;EACxB,MAAM;EACN,KAAK,UAAU;CACjB;CAEA,IAAa,KAAgC;EAC3C,MAAM,QAAQ,MAAM,IAAI,GAAG;EAC3B,IAAI,MAAM,IAAI,GAAG,GAAG;GAClB,MAAM,OAAO,GAAG;GAChB,MAAM,IAAI,KAAK,KAAc;EAC/B;EACA,OAAO;CACT;CAEA,IAAa,KAAa,OAAoB;EAC5C,IAAI,MAAM,IAAI,GAAG,GACf,MAAM,OAAO,GAAG;EAElB,MAAM,IAAI,KAAK,KAAK;EACpB,IAAI,KAAK,WAAW,KAAK,OAAO,KAAK,SAAS;GAG5C,MAAM,WAAW,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GACrC,IAAI,aAAa,KAAA,GACf,MAAM,OAAO,QAAQ;EAEzB;EACA,OAAO;CACT;AACF"}
@@ -3,31 +3,45 @@
3
3
  * Multiplies a string representation of a number by a given exponent of base 10 (10exponent).
4
4
  */
5
5
  function parseUnits(value, decimals) {
6
- if (!/^(-?)([0-9]*)\.?([0-9]*)$/.test(value)) throw new Error(`Number \`${value}\` is not a valid decimal number.`);
7
- let [integer, fraction = "0"] = value.split(".");
6
+ if (!Number.isInteger(decimals) || decimals < 0) throw new Error(`\`decimals\` must be a non-negative integer. Got \`${decimals}\`.`);
7
+ if (/^-?\.?$/.test(value)) return 0n;
8
+ if (!/^-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$/.test(value)) throw new Error(`Number \`${value}\` is not a valid decimal number.`);
9
+ let [integer = "", fraction = "0"] = value.split(".");
8
10
  const negative = integer.startsWith("-");
9
11
  if (negative) integer = integer.slice(1);
12
+ if (integer === "") integer = "0";
10
13
  fraction = fraction.replace(/(0+)$/, "");
11
14
  if (decimals === 0) {
12
- if (Math.round(Number(`.${fraction}`)) === 1) integer = `${BigInt(integer) + 1n}`;
15
+ if (fraction.length > 0 && Number.parseInt(fraction[0], 10) >= 5) integer = `${BigInt(integer) + 1n}`;
13
16
  fraction = "";
14
17
  } else if (fraction.length > decimals) {
15
- const [left, unit, right] = [
16
- fraction.slice(0, decimals - 1),
17
- fraction.slice(decimals - 1, decimals),
18
- fraction.slice(decimals)
19
- ];
20
- const rounded = Math.round(Number(`${unit}.${right}`));
21
- if (rounded > 9) fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, "0");
22
- else fraction = `${left}${rounded}`;
23
- if (fraction.length > decimals) {
24
- fraction = fraction.slice(1);
25
- integer = `${BigInt(integer) + 1n}`;
26
- }
27
- fraction = fraction.slice(0, decimals);
18
+ const left = fraction.slice(0, decimals);
19
+ if (Number.parseInt(fraction.slice(decimals, decimals + 1), 10) >= 5) {
20
+ const carried = carry(left);
21
+ if (carried.length > decimals) {
22
+ fraction = carried.slice(1);
23
+ integer = `${BigInt(integer) + 1n}`;
24
+ } else fraction = carried;
25
+ } else fraction = left;
28
26
  } else fraction = fraction.padEnd(decimals, "0");
29
27
  return BigInt(`${negative ? "-" : ""}${integer}${fraction}`);
30
28
  }
29
+ /**
30
+ * Adds 1 to a digit string. The result has the same length, unless the carry
31
+ * passes the most significant digit: then it is one digit longer.
32
+ */
33
+ function carry(digits) {
34
+ const out = digits.split("");
35
+ for (let i = out.length - 1; i >= 0; i--) {
36
+ const digit = Number.parseInt(out[i], 10) + 1;
37
+ if (digit < 10) {
38
+ out[i] = String(digit);
39
+ return out.join("");
40
+ }
41
+ out[i] = "0";
42
+ }
43
+ return `1${out.join("")}`;
44
+ }
31
45
  //#endregion
32
46
  export { parseUnits };
33
47
 
@@ -1 +1 @@
1
- {"version":3,"file":"parseUnits.js","names":[],"sources":["../../../src/utils/parseUnits.ts"],"sourcesContent":["/**\n * Multiplies a string representation of a number by a given exponent of base 10 (10exponent).\n */\nexport function parseUnits(value: string, decimals: number): bigint {\n if (!/^(-?)([0-9]*)\\.?([0-9]*)$/.test(value)) {\n throw new Error(`Number \\`${value}\\` is not a valid decimal number.`)\n }\n\n let [integer, fraction = '0'] = value.split('.')\n\n const negative = integer.startsWith('-')\n if (negative) {\n integer = integer.slice(1)\n }\n\n // trim trailing zeros.\n fraction = fraction.replace(/(0+)$/, '')\n\n // round off if the fraction is larger than the number of decimals.\n if (decimals === 0) {\n if (Math.round(Number(`.${fraction}`)) === 1) {\n integer = `${BigInt(integer) + 1n}`\n }\n fraction = ''\n } else if (fraction.length > decimals) {\n const [left, unit, right] = [\n fraction.slice(0, decimals - 1),\n fraction.slice(decimals - 1, decimals),\n fraction.slice(decimals),\n ]\n\n const rounded = Math.round(Number(`${unit}.${right}`))\n if (rounded > 9) {\n fraction = `${BigInt(left) + BigInt(1)}0`.padStart(left.length + 1, '0')\n } else {\n fraction = `${left}${rounded}`\n }\n\n if (fraction.length > decimals) {\n fraction = fraction.slice(1)\n integer = `${BigInt(integer) + 1n}`\n }\n\n fraction = fraction.slice(0, decimals)\n } else {\n fraction = fraction.padEnd(decimals, '0')\n }\n\n return BigInt(`${negative ? '-' : ''}${integer}${fraction}`)\n}\n"],"mappings":";;;;AAGA,SAAgB,WAAW,OAAe,UAA0B;CAClE,IAAI,CAAC,4BAA4B,KAAK,KAAK,GACzC,MAAM,IAAI,MAAM,YAAY,MAAM,kCAAkC;CAGtE,IAAI,CAAC,SAAS,WAAW,OAAO,MAAM,MAAM,GAAG;CAE/C,MAAM,WAAW,QAAQ,WAAW,GAAG;CACvC,IAAI,UACF,UAAU,QAAQ,MAAM,CAAC;CAI3B,WAAW,SAAS,QAAQ,SAAS,EAAE;CAGvC,IAAI,aAAa,GAAG;EAClB,IAAI,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,MAAM,GACzC,UAAU,GAAG,OAAO,OAAO,IAAI;EAEjC,WAAW;CACb,OAAO,IAAI,SAAS,SAAS,UAAU;EACrC,MAAM,CAAC,MAAM,MAAM,SAAS;GAC1B,SAAS,MAAM,GAAG,WAAW,CAAC;GAC9B,SAAS,MAAM,WAAW,GAAG,QAAQ;GACrC,SAAS,MAAM,QAAQ;EACzB;EAEA,MAAM,UAAU,KAAK,MAAM,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;EACrD,IAAI,UAAU,GACZ,WAAW,GAAG,OAAO,IAAI,IAAI,OAAO,CAAC,EAAE,GAAG,SAAS,KAAK,SAAS,GAAG,GAAG;OAEvE,WAAW,GAAG,OAAO;EAGvB,IAAI,SAAS,SAAS,UAAU;GAC9B,WAAW,SAAS,MAAM,CAAC;GAC3B,UAAU,GAAG,OAAO,OAAO,IAAI;EACjC;EAEA,WAAW,SAAS,MAAM,GAAG,QAAQ;CACvC,OACE,WAAW,SAAS,OAAO,UAAU,GAAG;CAG1C,OAAO,OAAO,GAAG,WAAW,MAAM,KAAK,UAAU,UAAU;AAC7D"}
1
+ {"version":3,"file":"parseUnits.js","names":[],"sources":["../../../src/utils/parseUnits.ts"],"sourcesContent":["/**\n * Multiplies a string representation of a number by a given exponent of base 10 (10exponent).\n */\nexport function parseUnits(value: string, decimals: number): bigint {\n if (!Number.isInteger(decimals) || decimals < 0) {\n throw new Error(\n `\\`decimals\\` must be a non-negative integer. Got \\`${decimals}\\`.`\n )\n }\n\n // An input with no digit is 0n: the widget parses an empty amount field.\n if (/^-?\\.?$/.test(value)) {\n return 0n\n }\n\n if (!/^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)$/.test(value)) {\n throw new Error(`Number \\`${value}\\` is not a valid decimal number.`)\n }\n\n let [integer = '', fraction = '0'] = value.split('.')\n\n const negative = integer.startsWith('-')\n if (negative) {\n integer = integer.slice(1)\n }\n if (integer === '') {\n integer = '0'\n }\n\n // trim trailing zeros.\n fraction = fraction.replace(/(0+)$/, '')\n\n // round off if the fraction is larger than the number of decimals.\n if (decimals === 0) {\n // Round half away from zero by the first fractional digit.\n if (fraction.length > 0 && Number.parseInt(fraction[0], 10) >= 5) {\n integer = `${BigInt(integer) + 1n}`\n }\n fraction = ''\n } else if (fraction.length > decimals) {\n const left = fraction.slice(0, decimals)\n const roundDigit = Number.parseInt(\n fraction.slice(decimals, decimals + 1),\n 10\n )\n\n if (roundDigit >= 5) {\n // Carry in decimal space, not through a JS number: a float loses\n // precision on long fractions and can round the wrong way.\n const carried = carry(left)\n if (carried.length > decimals) {\n // The carry overflowed into the integer part.\n fraction = carried.slice(1)\n integer = `${BigInt(integer) + 1n}`\n } else {\n fraction = carried\n }\n } else {\n fraction = left\n }\n } else {\n fraction = fraction.padEnd(decimals, '0')\n }\n\n return BigInt(`${negative ? '-' : ''}${integer}${fraction}`)\n}\n\n/**\n * Adds 1 to a digit string. The result has the same length, unless the carry\n * passes the most significant digit: then it is one digit longer.\n */\nfunction carry(digits: string): string {\n const out = digits.split('')\n for (let i = out.length - 1; i >= 0; i--) {\n const digit = Number.parseInt(out[i], 10) + 1\n if (digit < 10) {\n out[i] = String(digit)\n return out.join('')\n }\n out[i] = '0'\n }\n return `1${out.join('')}`\n}\n"],"mappings":";;;;AAGA,SAAgB,WAAW,OAAe,UAA0B;CAClE,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,WAAW,GAC5C,MAAM,IAAI,MACR,sDAAsD,SAAS,IACjE;CAIF,IAAI,UAAU,KAAK,KAAK,GACtB,OAAO;CAGT,IAAI,CAAC,uCAAuC,KAAK,KAAK,GACpD,MAAM,IAAI,MAAM,YAAY,MAAM,kCAAkC;CAGtE,IAAI,CAAC,UAAU,IAAI,WAAW,OAAO,MAAM,MAAM,GAAG;CAEpD,MAAM,WAAW,QAAQ,WAAW,GAAG;CACvC,IAAI,UACF,UAAU,QAAQ,MAAM,CAAC;CAE3B,IAAI,YAAY,IACd,UAAU;CAIZ,WAAW,SAAS,QAAQ,SAAS,EAAE;CAGvC,IAAI,aAAa,GAAG;EAElB,IAAI,SAAS,SAAS,KAAK,OAAO,SAAS,SAAS,IAAI,EAAE,KAAK,GAC7D,UAAU,GAAG,OAAO,OAAO,IAAI;EAEjC,WAAW;CACb,OAAO,IAAI,SAAS,SAAS,UAAU;EACrC,MAAM,OAAO,SAAS,MAAM,GAAG,QAAQ;EAMvC,IALmB,OAAO,SACxB,SAAS,MAAM,UAAU,WAAW,CAAC,GACrC,EAGW,KAAK,GAAG;GAGnB,MAAM,UAAU,MAAM,IAAI;GAC1B,IAAI,QAAQ,SAAS,UAAU;IAE7B,WAAW,QAAQ,MAAM,CAAC;IAC1B,UAAU,GAAG,OAAO,OAAO,IAAI;GACjC,OACE,WAAW;EAEf,OACE,WAAW;CAEf,OACE,WAAW,SAAS,OAAO,UAAU,GAAG;CAG1C,OAAO,OAAO,GAAG,WAAW,MAAM,KAAK,UAAU,UAAU;AAC7D;;;;;AAMA,SAAS,MAAM,QAAwB;CACrC,MAAM,MAAM,OAAO,MAAM,EAAE;CAC3B,KAAK,IAAI,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK;EACxC,MAAM,QAAQ,OAAO,SAAS,IAAI,IAAI,EAAE,IAAI;EAC5C,IAAI,QAAQ,IAAI;GACd,IAAI,KAAK,OAAO,KAAK;GACrB,OAAO,IAAI,KAAK,EAAE;EACpB;EACA,IAAI,KAAK;CACX;CACA,OAAO,IAAI,IAAI,KAAK,EAAE;AACxB"}
@@ -1,7 +1,7 @@
1
- import { ValidationError } from "../errors/errors.js";
2
- import { HTTPError } from "../errors/httpError.js";
3
1
  import { version } from "../version.js";
4
2
  import { SDKError } from "../errors/SDKError.js";
3
+ import { ValidationError } from "../errors/errors.js";
4
+ import { HTTPError } from "../errors/httpError.js";
5
5
  import { sleep } from "./sleep.js";
6
6
  //#region src/utils/request.ts
7
7
  const requestSettings = { retries: 1 };
@@ -1,4 +1,10 @@
1
1
  //#region src/utils/sleep.d.ts
2
- export declare function sleep(ms: number): Promise<null>;
2
+ /**
3
+ * Resolves with `null` after `ms`. With a `signal`, it rejects with the abort
4
+ * reason as soon as the signal aborts, and clears its timer.
5
+ */
6
+ export declare function sleep(ms: number, { signal }?: {
7
+ signal?: AbortSignal | undefined;
8
+ }): Promise<null>;
3
9
  //#endregion
4
10
  //# sourceMappingURL=sleep.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.d.ts","names":[],"sources":["../../../src/utils/sleep.ts"],"mappings":";wBAAgB,MAAM,aAAa"}
1
+ {"version":3,"file":"sleep.d.ts","names":[],"sources":["../../../src/utils/sleep.ts"],"mappings":";;;;;wBAMgB,MACd,cACE;EAAY,SAAS;IACtB"}
@@ -1,7 +1,26 @@
1
+ import { getAbortError } from "./abort.js";
1
2
  //#region src/utils/sleep.ts
2
- function sleep(ms) {
3
- return new Promise((resolve) => {
4
- setTimeout(() => resolve(null), ms);
3
+ /**
4
+ * Resolves with `null` after `ms`. With a `signal`, it rejects with the abort
5
+ * reason as soon as the signal aborts, and clears its timer.
6
+ */
7
+ function sleep(ms, { signal } = {}) {
8
+ return new Promise((resolve, reject) => {
9
+ if (signal?.aborted) {
10
+ reject(getAbortError(signal));
11
+ return;
12
+ }
13
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
14
+ const timer = setTimeout(() => {
15
+ cleanup();
16
+ resolve(null);
17
+ }, ms);
18
+ const onAbort = () => {
19
+ clearTimeout(timer);
20
+ cleanup();
21
+ reject(getAbortError(signal));
22
+ };
23
+ signal?.addEventListener("abort", onAbort, { once: true });
5
24
  });
6
25
  }
7
26
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.js","names":[],"sources":["../../../src/utils/sleep.ts"],"sourcesContent":["export function sleep(ms: number): Promise<null> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(null), ms)\n })\n}\n"],"mappings":";AAAA,SAAgB,MAAM,IAA2B;CAC/C,OAAO,IAAI,SAAS,YAAY;EAC9B,iBAAiB,QAAQ,IAAI,GAAG,EAAE;CACpC,CAAC;AACH"}
1
+ {"version":3,"file":"sleep.js","names":[],"sources":["../../../src/utils/sleep.ts"],"sourcesContent":["import { getAbortError } from './abort.js'\n\n/**\n * Resolves with `null` after `ms`. With a `signal`, it rejects with the abort\n * reason as soon as the signal aborts, and clears its timer.\n */\nexport function sleep(\n ms: number,\n { signal }: { signal?: AbortSignal | undefined } = {}\n): Promise<null> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(getAbortError(signal))\n return\n }\n\n const cleanup = (): void => signal?.removeEventListener('abort', onAbort)\n const timer = setTimeout(() => {\n cleanup()\n resolve(null)\n }, ms)\n const onAbort = (): void => {\n clearTimeout(timer)\n cleanup()\n reject(getAbortError(signal!))\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n"],"mappings":";;;;;;AAMA,SAAgB,MACd,IACA,EAAE,WAAiD,CAAC,GACrC;CACf,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,QAAQ,SAAS;GACnB,OAAO,cAAc,MAAM,CAAC;GAC5B;EACF;EAEA,MAAM,gBAAsB,QAAQ,oBAAoB,SAAS,OAAO;EACxE,MAAM,QAAQ,iBAAiB;GAC7B,QAAQ;GACR,QAAQ,IAAI;EACd,GAAG,EAAE;EACL,MAAM,gBAAsB;GAC1B,aAAa,KAAK;GAClB,QAAQ;GACR,OAAO,cAAc,MAAO,CAAC;EAC/B;EACA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC3D,CAAC;AACH"}