@gibs/bridge-sdk 1.19.0 → 1.20.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 (111) hide show
  1. package/dist/abis.js +0 -2
  2. package/dist/bridge-limits.js +0 -38
  3. package/dist/chain-info.js +0 -116
  4. package/dist/chain-names.js +0 -151
  5. package/dist/chains.js +0 -198
  6. package/dist/cjs/abis.js +0 -2
  7. package/dist/cjs/bridge-limits.js +0 -38
  8. package/dist/cjs/chain-info.js +0 -116
  9. package/dist/cjs/chain-names.js +0 -151
  10. package/dist/cjs/chains.js +0 -198
  11. package/dist/cjs/claims.js +0 -60
  12. package/dist/cjs/config.js +1 -208
  13. package/dist/cjs/consolidated-quote.js +0 -465
  14. package/dist/cjs/destination-calldata.js +0 -71
  15. package/dist/cjs/ecosystems.js +0 -183
  16. package/dist/cjs/execution.js +0 -92
  17. package/dist/cjs/fee-type.js +0 -1
  18. package/dist/cjs/hyperliquid-execution.js +0 -98
  19. package/dist/cjs/image-links.js +0 -108
  20. package/dist/cjs/lifi-execution-guard.js +65 -172
  21. package/dist/cjs/near-intents-conformance.js +0 -110
  22. package/dist/cjs/near-intents-route.js +0 -149
  23. package/dist/cjs/near-intents.js +0 -562
  24. package/dist/cjs/origin-deposit.js +0 -56
  25. package/dist/cjs/pre-quote.js +0 -80
  26. package/dist/cjs/prices.js +0 -113
  27. package/dist/cjs/providers.js +0 -83
  28. package/dist/cjs/quote-fees.js +0 -80
  29. package/dist/cjs/referral.js +0 -75
  30. package/dist/cjs/relay-currencies.js +0 -31
  31. package/dist/cjs/relay-depository.js +52 -77
  32. package/dist/cjs/relay-quote.js +0 -394
  33. package/dist/cjs/route-steps.js +0 -100
  34. package/dist/cjs/routing/amounts.js +0 -83
  35. package/dist/cjs/routing/candidate-route.js +0 -73
  36. package/dist/cjs/routing/candidate.js +0 -46
  37. package/dist/cjs/routing/carrier-provider-key.js +0 -27
  38. package/dist/cjs/routing/carriers/omnibridge.js +0 -120
  39. package/dist/cjs/routing/carriers/pulsex-paths.js +0 -57
  40. package/dist/cjs/routing/carriers/pulsex-split.js +0 -92
  41. package/dist/cjs/routing/carriers/pulsex.js +0 -279
  42. package/dist/cjs/routing/carriers/wrap.js +0 -57
  43. package/dist/cjs/routing/composed-route-quality.js +0 -95
  44. package/dist/cjs/routing/entry-chain-anchor.js +0 -27
  45. package/dist/cjs/routing/format.js +0 -50
  46. package/dist/cjs/routing/index.js +0 -28
  47. package/dist/cjs/routing/producers.js +0 -854
  48. package/dist/cjs/routing/providers.js +0 -27
  49. package/dist/cjs/routing/search/compose.js +0 -56
  50. package/dist/cjs/routing/search/graph.js +0 -364
  51. package/dist/cjs/routing/search/plan-route-data.js +0 -508
  52. package/dist/cjs/routing/search/score.js +0 -344
  53. package/dist/cjs/routing/swap-transactions.js +0 -74
  54. package/dist/cjs/routing/usd-value.js +0 -12
  55. package/dist/cjs/settings.js +1 -456
  56. package/dist/cjs/swap-venues.js +0 -17
  57. package/dist/cjs/ton-connect.js +0 -82
  58. package/dist/cjs/ton-execution.js +0 -6
  59. package/dist/cjs/ton.js +0 -42
  60. package/dist/cjs/tron-execution.js +0 -145
  61. package/dist/claims.js +0 -60
  62. package/dist/config.js +1 -211
  63. package/dist/consolidated-quote.js +0 -465
  64. package/dist/destination-calldata.js +0 -71
  65. package/dist/ecosystems.js +0 -183
  66. package/dist/execution.js +0 -92
  67. package/dist/fee-type.js +0 -2
  68. package/dist/hyperliquid-execution.js +0 -98
  69. package/dist/image-links.js +0 -108
  70. package/dist/lifi-execution-guard.js +65 -172
  71. package/dist/near-intents-conformance.js +0 -110
  72. package/dist/near-intents-route.js +0 -149
  73. package/dist/near-intents.js +0 -562
  74. package/dist/origin-deposit.js +0 -56
  75. package/dist/pre-quote.js +0 -80
  76. package/dist/prices.js +0 -113
  77. package/dist/providers.js +0 -83
  78. package/dist/quote-fees.js +0 -80
  79. package/dist/referral.js +0 -75
  80. package/dist/relay-currencies.js +0 -31
  81. package/dist/relay-depository.js +52 -77
  82. package/dist/relay-quote.js +0 -394
  83. package/dist/route-steps.js +0 -100
  84. package/dist/routing/amounts.js +0 -83
  85. package/dist/routing/candidate-route.js +0 -73
  86. package/dist/routing/candidate.js +0 -46
  87. package/dist/routing/carrier-provider-key.js +0 -27
  88. package/dist/routing/carriers/omnibridge.js +0 -120
  89. package/dist/routing/carriers/pulsex-paths.js +0 -57
  90. package/dist/routing/carriers/pulsex-split.js +0 -92
  91. package/dist/routing/carriers/pulsex.js +0 -279
  92. package/dist/routing/carriers/wrap.js +0 -57
  93. package/dist/routing/composed-route-quality.js +0 -95
  94. package/dist/routing/entry-chain-anchor.js +0 -27
  95. package/dist/routing/format.js +0 -50
  96. package/dist/routing/index.js +0 -28
  97. package/dist/routing/producers.js +0 -854
  98. package/dist/routing/providers.js +0 -27
  99. package/dist/routing/search/compose.js +0 -56
  100. package/dist/routing/search/graph.js +0 -364
  101. package/dist/routing/search/plan-route-data.js +0 -508
  102. package/dist/routing/search/score.js +0 -344
  103. package/dist/routing/swap-transactions.js +0 -74
  104. package/dist/routing/usd-value.js +0 -12
  105. package/dist/settings.js +1 -456
  106. package/dist/swap-venues.js +0 -17
  107. package/dist/ton-connect.js +0 -82
  108. package/dist/ton-execution.js +0 -6
  109. package/dist/ton.js +0 -42
  110. package/dist/tron-execution.js +0 -145
  111. package/package.json +3 -3
@@ -46,25 +46,13 @@ const amounts_js_1 = require("../amounts.js");
46
46
  const entry_chain_anchor_js_1 = require("../entry-chain-anchor.js");
47
47
  const producers_js_1 = require("../producers.js");
48
48
  const graph_js_1 = require("../search/graph.js");
49
- /** Builds an {@link AssetNode} for an Ethereum-Virtual-Machine token. */
50
49
  const evmNode = (token) => ({
51
50
  chainId: token.chainId,
52
51
  address: token.address,
53
52
  decimals: token.decimals,
54
53
  ecosystem: 'evm',
55
54
  });
56
- /**
57
- * A directed pair of node keys, joined for use as a `Map` key. Never leaves
58
- * this module — see `capabilities.ts`'s former `directedPairKey` doc for why
59
- * a plain `|` join is enough here.
60
- */
61
55
  const directedPairKey = (from, to) => `${(0, graph_js_1.assetNodeKey)(from)}|${(0, graph_js_1.assetNodeKey)(to)}`;
62
- /**
63
- * Finds which omnibridge provider, if any, has a live pathway carrying funds
64
- * from `fromChainId` to `toChainId`. Checks BOTH omnibridge providers rather
65
- * than assuming one: `Providers.PULSECHAIN` carries the Ethereum pathway,
66
- * `Providers.TOKENSEX` the Binance Smart Chain one.
67
- */
68
56
  const omnibridgeCarrierBetween = (fromChainId, toChainId, isProductionNetwork) => {
69
57
  const fromChain = (0, config_js_1.toChain)(fromChainId);
70
58
  const toChainKey = (0, config_js_1.toChain)(toChainId);
@@ -77,12 +65,6 @@ const omnibridgeCarrierBetween = (fromChainId, toChainId, isProductionNetwork) =
77
65
  }
78
66
  return null;
79
67
  };
80
- /**
81
- * Builds the omnibridge's directed edge map from the bridgeable pairs and the
82
- * live pathway config, in both directions. INBOUND goes through
83
- * {@link entryChainAnchorsForPair} so a native coin and its wrapped form both
84
- * anchor the same PulseChain mint; OUTBOUND needs no aliasing.
85
- */
86
68
  const buildOmnibridgeEdgeMap = (bridgeablePairs, isProductionNetwork) => {
87
69
  const edges = new Map();
88
70
  for (const pair of bridgeablePairs) {
@@ -104,13 +86,6 @@ const buildOmnibridgeEdgeMap = (bridgeablePairs, isProductionNetwork) => {
104
86
  }
105
87
  return edges;
106
88
  };
107
- /**
108
- * Resolves one edge's crossing details from the live pathway config, or null
109
- * when no pathway matches — the same "no live pathway" case `edgesFrom`
110
- * already prunes, kept here as a guard rather than assumed away, because
111
- * `prepare` walks every node pair itself rather than trusting the caller to
112
- * hand it only edges this carrier already offered.
113
- */
114
89
  const resolveCrossing = (edge, isProductionNetwork) => {
115
90
  const bridgeKey = [edge.carrier, (0, config_js_1.toChain)(edge.from.chainId), (0, config_js_1.toChain)(edge.to.chainId)];
116
91
  const wrappedNativeAddress = config_js_1.nativeAssetOut[(0, config_js_1.toChain)(edge.from.chainId)];
@@ -131,7 +106,6 @@ const resolveCrossing = (edge, isProductionNetwork) => {
131
106
  feeTypeKey: path.toHome ? config_js_1.FOREIGN_TO_HOME_FEE : config_js_1.HOME_TO_FOREIGN_FEE,
132
107
  };
133
108
  };
134
- /** Groups values by a computed chain id, preserving the deduped-by-`keyOf` invariant callers rely on. */
135
109
  const groupByChainId = (values, chainIdOf) => {
136
110
  const groups = new Map();
137
111
  for (const value of values) {
@@ -146,7 +120,6 @@ const groupByChainId = (values, chainIdOf) => {
146
120
  }
147
121
  return groups;
148
122
  };
149
- /** Stage-one read: `minPerTx`, `maxAvailablePerTx` (an entry mediator's own bounds) and `feeManager()` (the fee contract's address) — every one a fixed-arity view call, so one multicall per chain covers all three kinds at once. */
150
123
  const readStageOneBatch = async ({ resolvedCrossings, clientForChain, }) => {
151
124
  const limitsByKey = new Map();
152
125
  const feeManagerAddressByKey = new Map();
@@ -202,11 +175,6 @@ const readStageOneBatch = async ({ resolvedCrossings, clientForChain, }) => {
202
175
  }
203
176
  const feeManagerAddress = results[cursor];
204
177
  cursor += 1;
205
- // A DECODED address is exactly 42 characters ("0x" + 40 hex digits).
206
- // `multicallRead` returns the UNDECODED revert bytes on a failed
207
- // `allowFailure` call — also a `string`, usually a different length
208
- // (`'0x'` for a bare revert) — so `typeof` alone cannot tell success
209
- // from failure here the way it does for the `bigint` reads above.
210
178
  if (typeof feeManagerAddress === 'string' && feeManagerAddress.length === 42) {
211
179
  feeManagerAddressByKey.set(request.key, (0, viem_1.getAddress)(feeManagerAddress));
212
180
  }
@@ -214,7 +182,6 @@ const readStageOneBatch = async ({ resolvedCrossings, clientForChain, }) => {
214
182
  }));
215
183
  return { limitsByKey, feeManagerAddressByKey };
216
184
  };
217
- /** Stage-two read: `getFee(feeTypeKey, address(0))` on every fee manager contract stage one resolved — needs the address stage one just learned, so it cannot join that multicall. */
218
185
  const readStageTwoBatch = async ({ resolvedCrossings, feeManagerAddressByKey, clientForChain, }) => {
219
186
  const feeRateByKey = new Map();
220
187
  const feeRateRequests = new Map();
@@ -257,35 +224,6 @@ const limitsKey = (crossing) => `${crossing.limitsChainId}|${crossing.entryMedia
257
224
  const feeManagerHostKey = (crossing) => `${crossing.feeManagerHostChainId}|${crossing.feeManagerHostAddress}`;
258
225
  const feeRateKey = (crossing, feeManagerAddress) => `${crossing.feeManagerHostChainId}|${feeManagerAddress}|${crossing.feeTypeKey}`;
259
226
  const preparedEdgeKey = (edge) => `${edge.carrier}|${(0, graph_js_1.assetNodeKey)(edge.from)}|${(0, graph_js_1.assetNodeKey)(edge.to)}`;
260
- /**
261
- * Batch-reads every entry-mediator bound and fee rate `nodes` implies, once,
262
- * so {@link createOmnibridgeCarrier}'s `exact` can answer every crossing
263
- * SYNCHRONOUSLY afterward — the owner's own model, "each individual step and
264
- * its associated fees can be known ahead of time."
265
- *
266
- * TWO MULTICALLS PER CHAIN, NOT ONE: `getFee` needs the fee manager's
267
- * address, which stage one is what LEARNS — the same two-stage shape
268
- * `chain-info.ts`'s `loadBridgeFees` already uses for one pathway at a time,
269
- * generalized here to batch across every crossing the search's node universe
270
- * touches. `HOME_TO_FOREIGN_FEE`/`FOREIGN_TO_HOME_FEE` are not re-read from
271
- * the chain — they are fixed `keccak256` constants (`config.ts`), so reading
272
- * them per pathway (as `loadBridgeFees` still does, defensively) would only
273
- * spend a third multicall confirming a value that cannot change.
274
- *
275
- * WATCH PAYLOAD SIZE (request-payload-size-traps.md). `nodes` is the search
276
- * graph's own hub-token-restricted universe, not a full token list — this
277
- * function reads one `minPerTx`/`maxAvailablePerTx` pair per DISTINCT
278
- * (mediator, token) crossing in that universe, never per token a chain
279
- * lists. `multicallRead` (`@gibs/common`) chunks by measured encoded size on
280
- * top of that, so a universe large enough to matter still cannot repeat the
281
- * 8.5 MB trap that memory records.
282
- *
283
- * @param nodes - every node the search might branch through
284
- * @param edgesFrom - this carrier's own edge enumerator, reused to find every candidate crossing among `nodes`
285
- * @param isProductionNetwork - see `CreateOmnibridgeCarrierOptions`'s own field
286
- * @param clientForChain - see `CreateOmnibridgeCarrierOptions`'s own field
287
- * @returns every resolved crossing's edge key, mapped to what `exact` needs — never thrown; a chain this host has no client for is simply left unanswered
288
- */
289
227
  const prepareOmnibridgeReads = async ({ nodes, edgesFrom, isProductionNetwork, clientForChain, }) => {
290
228
  const edges = [];
291
229
  for (const from of nodes) {
@@ -313,15 +251,6 @@ const prepareOmnibridgeReads = async ({ nodes, edgesFrom, isProductionNetwork, c
313
251
  }
314
252
  return preparedByEdgeKey;
315
253
  };
316
- /**
317
- * The refusal an entry mediator's own bound produces, carrying the bound
318
- * itself as a figure: `minimumAmount` below the floor, `availableAmount`
319
- * above what is left today. A reader is told the number to type, so the
320
- * number must survive here rather than live only inside the reason string.
321
- *
322
- * @param verdict - the crossed bound, in the entry token's base units
323
- * @returns the refusal, in `edge.from`'s base units
324
- */
325
254
  const entryAmountRefusal = (verdict) => verdict.reason === 'below-minimum'
326
255
  ? {
327
256
  kind: 'below-minimum',
@@ -334,14 +263,6 @@ const entryAmountRefusal = (verdict) => verdict.reason === 'below-minimum'
334
263
  minimumAmount: null,
335
264
  availableAmount: verdict.availablePerTransaction,
336
265
  };
337
- /**
338
- * Builds the omnibridge crossing as a plug-in carrier.
339
- *
340
- * @param options - see {@link CreateOmnibridgeCarrierOptions}
341
- * @returns the carrier — `id: 'omnibridge'`; every EDGE it offers carries the
342
- * real `'pulsechain'`/`'tokensex'` provider id, since one registration can
343
- * speak for either entry chain depending on the pair
344
- */
345
266
  const createOmnibridgeCarrier = (options) => {
346
267
  const { bridgeablePairs, isProductionNetwork, readEntryLimits, readFeePerEther, clientForChain } = options;
347
268
  const edgeMap = buildOmnibridgeEdgeMap(bridgeablePairs, isProductionNetwork);
@@ -353,11 +274,6 @@ const createOmnibridgeCarrier = (options) => {
353
274
  return [];
354
275
  return [{ carrier, from, to, fusesNext: false, origin: 'wallet', floors: [] }];
355
276
  };
356
- // Populated by `prepare`, read by `exact` — see that method's own doc for
357
- // why a mutable closure here is safe: this carrier is a client-side,
358
- // one-search-at-a-time object (quote-service.md: the omnibridge crossing
359
- // is "never built into" the shared, multi-tenant display-quote service),
360
- // so nothing else can be mutating this map while a search reads it.
361
277
  let preparedByEdgeKey = new Map();
362
278
  const prepare = async (nodes) => {
363
279
  if (clientForChain === undefined)
@@ -385,7 +301,6 @@ const createOmnibridgeCarrier = (options) => {
385
301
  return {
386
302
  ok: true,
387
303
  deliveredAmount,
388
- // An exact local computation: the guaranteed figure IS the expected one.
389
304
  minimumDeliveredAmount: deliveredAmount,
390
305
  claimedDurationSeconds: producers_js_1.BRIDGE_SETTLEMENT_ETA_MINUTES * 60,
391
306
  };
@@ -397,47 +312,12 @@ const createOmnibridgeCarrier = (options) => {
397
312
  prepare,
398
313
  exact,
399
314
  isRateLimited: false,
400
- // Crossing requires the reader's own wallet signature on the entry
401
- // chain — never a by-hand deposit.
402
315
  depositMode: 'wallet-signed',
403
316
  sender: 'omnibridge',
404
317
  settlementSeconds: producers_js_1.BRIDGE_SETTLEMENT_ETA_MINUTES * 60,
405
318
  };
406
319
  };
407
320
  exports.createOmnibridgeCarrier = createOmnibridgeCarrier;
408
- /**
409
- * Wraps ANY carrier-shaped object (built-in or a host's own) with the
410
- * omnibridge's OWN fuse rule: whether THIS carrier's execution, landing on
411
- * arrival, also performs the omnibridge's entry call — so the two hops
412
- * together cost the reader one signature instead of two.
413
- *
414
- * EVERY CONDITION IS A FACT THE PROVIDER'S OWN DESCRIPTOR STATES
415
- * (`../../providers.ts`), never a name compared here:
416
- *
417
- * - it executes destination calls at all (`executesDestinationCalls` — true
418
- * for LI.FI and Relay, false for NEAR Intents, which delivers to the
419
- * reader's own wallet and stops);
420
- * - it can do so FROM this hop's origin chain (`destinationCallOriginChainIds`
421
- * — LI.FI's contract-calls endpoint is Ethereum only, so from Base or
422
- * Arbitrum a LI.FI hop is an unfused delivery followed by a crossing the
423
- * reader signs);
424
- * - when the entry is the omnibridge's NATIVE one (the provider lands a
425
- * native coin, and `wrapAndRelayTokens` is payable), it can attach value to
426
- * the call (`destinationCallCarriesNativeValue`);
427
- * - and the next hop really IS the omnibridge entry, landing on PulseChain
428
- * (`relayTokens`/`wrapAndRelayTokens` both MINT there; any other
429
- * destination is the omnibridge's OUTBOUND leg, which no arrival can make).
430
- *
431
- * THIS IS THE ONE PLACE `'pulsechain'`/`'tokensex'`/PulseChain'S CHAIN ID
432
- * APPEAR IN A `fusesWithNext` CALLBACK — gibs's own registration
433
- * (`packages/ui/src/lib/state/useRouteSearch.ts`) wraps `createLifiCarrier`/
434
- * `createRelayCarrier` with this before registering them, so the generic
435
- * search engine (`@gibs/quotes`'s `capabilities.ts`) never has to know what
436
- * "the bridge" means.
437
- *
438
- * @param carrier - the carrier to wrap; its own shape is preserved, only `fusesWithNext` is added/replaced
439
- * @returns the same carrier, with a `fusesWithNext` that recognizes the omnibridge entry call
440
- */
441
321
  const withOmnibridgeEntryFusing = (carrier) => {
442
322
  const descriptor = (0, providers_js_1.findProviderById)(carrier.id);
443
323
  return {
@@ -4,24 +4,8 @@ exports.bestPulseXPathQuote = exports.candidatePulseXPaths = exports.pulseXHubTo
4
4
  const viem_1 = require("viem");
5
5
  const config_js_1 = require("../../config.js");
6
6
  const prices_js_1 = require("../../prices.js");
7
- /** Wrapped PLS: the first hub, and the middle token of every two-hub path. */
8
7
  const wrappedPulseAddress = (0, viem_1.getAddress)(config_js_1.nativeAssetOut[config_js_1.Chains.PLS]);
9
- /** The Ethereum-bridged WETH on PulseChain, read off the Ethereum pathway's own config. Undefined only if that pathway is removed. */
10
8
  const bridgedWrappedEtherAddress = config_js_1.pathways[config_js_1.Providers.PULSECHAIN][config_js_1.Chains.PLS]?.[config_js_1.Chains.ETH]?.bridgedNativeAssetOut;
11
- /**
12
- * The tokens a PulseX swap may route through, Wrapped PLS first. Each address
13
- * comes from a constant this package already holds; none is written here:
14
- *
15
- * - WPLS — `nativeAssetOut[Chains.PLS]` (`config.ts`).
16
- * - PLSX, HEX, USDC, USDT — `pulsechainTokenAddresses` (`config.ts`).
17
- * - DAI (from Ethereum) — `PULSECHAIN_USD_ANCHOR_ADDRESS` (`prices.ts`).
18
- * - WETH (from Ethereum) — the Ethereum pathway's `bridgedNativeAssetOut` (`config.ts`).
19
- *
20
- * KEPT SMALL ON PURPOSE. Each hub adds pools to the one batched reserve read
21
- * the carrier makes per search, and adds paths to every price it computes.
22
- * Seven hubs give at most 20 candidate paths per hop (see
23
- * {@link candidatePulseXPaths}).
24
- */
25
9
  exports.pulseXHubTokens = Object.freeze([
26
10
  { symbol: 'WPLS', address: wrappedPulseAddress },
27
11
  { symbol: 'PLSX', address: config_js_1.pulsechainTokenAddresses.PLSX },
@@ -31,32 +15,8 @@ exports.pulseXHubTokens = Object.freeze([
31
15
  { symbol: 'USDT', address: config_js_1.pulsechainTokenAddresses.USDT },
32
16
  ...(bridgedWrappedEtherAddress === undefined ? [] : [{ symbol: 'WETH', address: (0, viem_1.getAddress)(bridgedWrappedEtherAddress) }]),
33
17
  ].map((hub) => Object.freeze(hub)));
34
- /** Whether a path names any token twice, compared without regard to letter case. */
35
18
  const repeatsAToken = (path) => new Set(path.map((address) => address.toLowerCase())).size !== path.length;
36
- /** Whether two addresses are the same token, compared without regard to letter case. */
37
19
  const sameToken = (left, right) => left.toLowerCase() === right.toLowerCase();
38
- /**
39
- * Every swap path a PulseX hop from `from` to `to` may take:
40
- *
41
- * - direct: `[from, to]`;
42
- * - one hub: `[from, hub, to]` for each hub, Wrapped PLS among them;
43
- * - two hubs: `[from, wrappedNative, hub, to]` and `[from, hub, wrappedNative, to]`
44
- * for each other hub — Wrapped PLS holds the deepest pools on PulseX, so it
45
- * is the one middle token worth pairing with every other hub.
46
- *
47
- * A path that names a token twice is dropped. That removes every path whose
48
- * hub IS `from` or `to`, and the same-token hop when `from` equals `to`.
49
- *
50
- * The fixed path this carrier used before — `[from, WPLS, to]`, or
51
- * `[from, to]` when one side is WPLS — is always in the result, so the pick
52
- * can only match or beat it.
53
- *
54
- * @param options.from - the pool token the swap spends
55
- * @param options.to - the pool token the swap buys
56
- * @param options.wrappedNative - Wrapped PLS
57
- * @param options.hubs - the hub token addresses, Wrapped PLS included
58
- * @returns the candidate paths, direct first, each at least two tokens long
59
- */
60
20
  const candidatePulseXPaths = (options) => {
61
21
  const { from, to, wrappedNative, hubs } = options;
62
22
  const oneHubPaths = hubs.map((hub) => [from, hub, to]);
@@ -69,28 +29,11 @@ const candidatePulseXPaths = (options) => {
69
29
  return [[from, to], ...oneHubPaths, ...twoHubPaths].filter((path) => !repeatsAToken(path));
70
30
  };
71
31
  exports.candidatePulseXPaths = candidatePulseXPaths;
72
- /**
73
- * Whether `candidate` should replace `best`: it delivers more, or it delivers
74
- * the same and crosses fewer pools. A tie goes to the shorter path because
75
- * each extra pool costs gas, and nothing else separates the two.
76
- */
77
32
  const beats = (candidate, best) => {
78
33
  if (candidate.deliveredAmount !== best.deliveredAmount)
79
34
  return candidate.deliveredAmount > best.deliveredAmount;
80
35
  return candidate.path.length < best.path.length;
81
36
  };
82
- /**
83
- * The priced path that delivers the most. A path that delivers nothing is
84
- * never chosen. Among equal outputs the path with fewer pools wins, and among
85
- * equal lengths the earlier candidate wins, so the pick is deterministic.
86
- *
87
- * Gas is not weighed beyond that tie-break: the search charges one flat gas
88
- * figure per swap signature, whatever its path length (`route-search-gas.ts`
89
- * in the interface), so there is no per-pool gas figure to subtract here.
90
- *
91
- * @param quotes - the priced candidates; paths with a missing pool are already left out
92
- * @returns the best candidate, or null when none delivers anything
93
- */
94
37
  const bestPulseXPathQuote = (quotes) => quotes
95
38
  .filter((quote) => quote.deliveredAmount > 0n)
96
39
  .reduce((best, quote) => (best === null || beats(quote, best) ? quote : best), null);
@@ -3,42 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bestPulseXSplitQuote = exports.splitPriceImpact = exports.allocatePulseXSplit = exports.rankPulseXSplitCandidates = exports.executePulseXSplitInOrder = exports.PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS = exports.PULSEX_SPLIT_CANDIDATE_LIMIT = exports.PULSEX_SPLIT_MAX_LEGS = exports.PULSEX_SPLIT_CHUNKS = void 0;
4
4
  const producers_js_1 = require("../producers.js");
5
5
  const pulsex_js_1 = require("./pulsex.js");
6
- /**
7
- * How many equal chunks the search divides the trade into: 20, so legs move
8
- * in 5% steps. Finer steps buy a little output for proportionally more work.
9
- */
10
6
  exports.PULSEX_SPLIT_CHUNKS = 20;
11
- /** The most legs one split may have. Each leg is one more router call to sign or batch, and one more swap's gas. */
12
7
  exports.PULSEX_SPLIT_MAX_LEGS = 3;
13
- /** How many candidate paths, best single-path output first, the search may spread the trade over. */
14
8
  exports.PULSEX_SPLIT_CANDIDATE_LIMIT = 4;
15
- /**
16
- * The gain a split must show over the best single path before it is chosen,
17
- * in basis points of the single path's output: 25, or 0.25%.
18
- *
19
- * GAS IS REAL. Each extra leg costs roughly one more swap's gas, and a
20
- * batching contract adds its own overhead. A split that wins by a rounding
21
- * margin buys the reader nothing but a larger fee. The figure is a judgement,
22
- * not a measurement; revisit it once an execution path has a real gas cost.
23
- */
24
9
  exports.PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS = 25n;
25
10
  const BASIS_POINTS = 10000n;
26
- /** The key one token's reserve in one pool is held under while a split executes. */
27
11
  const reserveKey = (pool, token) => `${pool}|${token.toLowerCase()}`;
28
- /**
29
- * Executes legs one after another against shared pool state, exactly as
30
- * sequential router calls would. A leg that crosses a pool an earlier leg
31
- * already traded sees that pool's moved reserves, in whichever direction the
32
- * earlier leg traded it.
33
- *
34
- * Each trade adds its full input to the pool and removes its output, the
35
- * Uniswap-V2 accounting. See the open risk in the report: if PulseX moves
36
- * part of its fee out of the pool during a swap, a shared pool is a few
37
- * hundredths of a percent shallower here than on chain.
38
- *
39
- * @param legs - each candidate with the amount it spends, in execution order
40
- * @returns what each leg delivers, in the same order
41
- */
42
12
  const executePulseXSplitInOrder = (legs) => {
43
13
  const reserves = new Map();
44
14
  const reserveOf = (pool, token, initial) => reserves.get(reserveKey(pool, token)) ?? initial;
@@ -52,18 +22,7 @@ const executePulseXSplitInOrder = (legs) => {
52
22
  }, amountIn));
53
23
  };
54
24
  exports.executePulseXSplitInOrder = executePulseXSplitInOrder;
55
- /** What one candidate alone delivers for the whole trade. */
56
25
  const singlePathOutput = (candidate, amountIn) => (0, exports.executePulseXSplitInOrder)([{ candidate, amountIn }])[0] ?? 0n;
57
- /**
58
- * The candidates a split may use: the {@link PULSEX_SPLIT_CANDIDATE_LIMIT}
59
- * best by single-path output for the whole trade. A candidate that delivers
60
- * nothing is dropped. Ties go to the shorter path, then the earlier
61
- * candidate, so the ranking is deterministic.
62
- *
63
- * @param candidates - every priced path, on every venue
64
- * @param amountIn - the whole trade
65
- * @returns the ranked candidates, best first, each with its single-path output
66
- */
67
26
  const rankPulseXSplitCandidates = (candidates, amountIn) => candidates
68
27
  .map((candidate, index) => ({ candidate, index, deliveredAmount: singlePathOutput(candidate, amountIn) }))
69
28
  .filter((ranked) => ranked.deliveredAmount > 0n)
@@ -77,42 +36,16 @@ const rankPulseXSplitCandidates = (candidates, amountIn) => candidates
77
36
  .slice(0, exports.PULSEX_SPLIT_CANDIDATE_LIMIT)
78
37
  .map(({ candidate, deliveredAmount }) => ({ candidate, deliveredAmount }));
79
38
  exports.rankPulseXSplitCandidates = rankPulseXSplitCandidates;
80
- /** The trade divided into {@link PULSEX_SPLIT_CHUNKS} chunks; the last one takes the remainder, so the chunks sum to the trade exactly. */
81
39
  const chunksOf = (amountIn) => {
82
40
  const count = BigInt(exports.PULSEX_SPLIT_CHUNKS);
83
41
  const chunk = amountIn / count;
84
42
  return Array.from({ length: exports.PULSEX_SPLIT_CHUNKS }, (_, index) => index === exports.PULSEX_SPLIT_CHUNKS - 1 ? amountIn - chunk * (count - 1n) : chunk);
85
43
  };
86
- /** Each candidate with a positive allocation, in candidate order — the order the legs execute in. */
87
44
  const legsOf = (candidates, allocation) => candidates.flatMap((candidate, index) => {
88
45
  const amountIn = allocation[index] ?? 0n;
89
46
  return amountIn > 0n ? [{ candidate, amountIn }] : [];
90
47
  });
91
- /** What an allocation delivers in total, executed in candidate order. */
92
48
  const allocationOutput = (candidates, allocation) => (0, exports.executePulseXSplitInOrder)(legsOf(candidates, allocation)).reduce((total, amount) => total + amount, 0n);
93
- /**
94
- * Spreads a trade over ranked candidates, one chunk at a time. Each chunk goes
95
- * to the candidate whose share, once raised by that chunk, makes the WHOLE
96
- * split deliver the most — the split is re-executed in full each time
97
- * ({@link executePulseXSplitInOrder}), so a shared pool is always priced with
98
- * the reserves every leg leaves behind. A candidate not yet used is skipped
99
- * once {@link PULSEX_SPLIT_MAX_LEGS} legs exist. Ties go to the earlier
100
- * candidate.
101
- *
102
- * BOUNDED AND DETERMINISTIC. Work per quote is at most
103
- * `PULSEX_SPLIT_CHUNKS × PULSEX_SPLIT_CANDIDATE_LIMIT` full executions (80),
104
- * each of at most `PULSEX_SPLIT_MAX_LEGS` legs of at most three pools: under
105
- * 750 constant-product evaluations, no network call.
106
- *
107
- * On pools that share nothing, each candidate's output is concave in its
108
- * share, so this greedy fill reaches the best allocation on the chunk grid.
109
- * Shared pools break that guarantee; {@link bestPulseXSplitQuote} still never
110
- * returns a split worse than the best single path.
111
- *
112
- * @param candidates - ranked candidates, best first
113
- * @param amountIn - the whole trade
114
- * @returns the share of the trade each candidate spends, in candidate order
115
- */
116
49
  const allocatePulseXSplit = (candidates, amountIn) => chunksOf(amountIn).reduce((allocation, chunk) => {
117
50
  const usedLegs = allocation.filter((amount) => amount > 0n).length;
118
51
  const trials = candidates.flatMap((_, index) => {
@@ -126,20 +59,6 @@ const allocatePulseXSplit = (candidates, amountIn) => chunksOf(amountIn).reduce(
126
59
  return best === null ? allocation : best.trial;
127
60
  }, candidates.map(() => 0n));
128
61
  exports.allocatePulseXSplit = allocatePulseXSplit;
129
- /**
130
- * Price impact of a combined trade, in percent, measured with the same
131
- * reference size the single-path figure uses
132
- * (`amountIn / PULSEX_MARGINAL_REFERENCE_DIVISOR`).
133
- *
134
- * THE REFERENCE IS THE BEST MARGINAL RATE OF ANY CANDIDATE, not a blend of
135
- * the legs' own rates. A split that leans on a path with a worse spot price
136
- * pays for that, and the figure shows it. A blend would hide it.
137
- *
138
- * @param options.candidates - every ranked candidate
139
- * @param options.amountIn - the whole trade
140
- * @param options.deliveredAmount - what the combined trade delivers
141
- * @returns the impact in percent, or null when the reference trade rounds away
142
- */
143
62
  const splitPriceImpact = (options) => {
144
63
  const { candidates, amountIn, deliveredAmount } = options;
145
64
  const referenceAmountIn = amountIn / producers_js_1.PULSEX_MARGINAL_REFERENCE_DIVISOR;
@@ -150,17 +69,6 @@ const splitPriceImpact = (options) => {
150
69
  return (0, pulsex_js_1.priceImpactAgainstMarginal)({ amountIn, amountOut: deliveredAmount, referenceAmountIn, referenceAmountOut });
151
70
  };
152
71
  exports.splitPriceImpact = splitPriceImpact;
153
- /**
154
- * The best split of a PulseX trade, or null when no split beats the best
155
- * single path by {@link PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS}.
156
- *
157
- * NEVER WORSE THAN ONE PATH. A null answer means "take the best single path";
158
- * this never returns a split that delivers less than that path.
159
- *
160
- * @param candidates - every priced path, on every venue
161
- * @param amountIn - the whole trade
162
- * @returns the split with its legs in execution order, or null
163
- */
164
72
  const bestPulseXSplitQuote = (candidates, amountIn) => {
165
73
  if (amountIn <= 0n)
166
74
  return null;