@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
@@ -45,60 +45,18 @@ const providers_js_1 = require("../providers.js");
45
45
  const producers_js_1 = require("../producers.js");
46
46
  const graph_js_1 = require("../search/graph.js");
47
47
  const pulsex_paths_js_1 = require("./pulsex-paths.js");
48
- /**
49
- * PulseX (the PulseChain-native decentralized exchange, used here for a
50
- * conversion swap leg) as a plug-in `Carrier` for `@gibs/quotes`'s
51
- * route-search engine. GIBS-SPECIFIC — see `docs/quote-service.md`'s
52
- * "Carriers" section: "never built in ... PulseX." Registered from
53
- * `packages/ui/src/lib/state/useRouteSearch.ts` / `route-search-quotes.ts`.
54
- *
55
- * See `omnibridge.ts`'s head doc for why this returns a plain object shaped
56
- * like `Carrier` rather than importing that type.
57
- */
58
- /** The hub addresses every candidate path on PulseChain is built from — see {@link pulseXHubTokens}. */
59
48
  const hubAddresses = pulsex_paths_js_1.pulseXHubTokens.map((hub) => hub.address);
60
- /**
61
- * The wrapped-native token a chain's swaps route through, or undefined when
62
- * this package does not know one for the chain. Without it a path on that
63
- * chain can only be direct.
64
- *
65
- * @param chainId - the numeric chain id
66
- */
67
49
  const wrappedNativeFor = (chainId) => {
68
50
  const wrapped = config_js_1.nativeAssetOut[(0, config_js_1.toChain)(chainId)];
69
51
  return wrapped === undefined ? undefined : (0, viem_1.getAddress)(wrapped);
70
52
  };
71
- /**
72
- * The hub tokens a path on `chainId` may pass through. PulseChain has the
73
- * full list; any other chain has only its wrapped-native token, because this
74
- * package knows no other hubs.
75
- */
76
53
  const hubsFor = (chainId) => {
77
54
  if (chainId === producers_js_1.pulsechainChainId)
78
55
  return hubAddresses;
79
56
  const wrapped = wrappedNativeFor(chainId);
80
57
  return wrapped === undefined ? [] : [wrapped];
81
58
  };
82
- /**
83
- * Every PulseX deployment on PulseChain, V1 first: the PulseChain entry of
84
- * {@link defaultSwapVenues}. Frozen. It lists the DEFAULTS only — a carrier
85
- * built with its own `venues` offers those instead.
86
- */
87
59
  exports.pulseXVenues = config_js_1.defaultSwapVenues[producers_js_1.pulsechainChainId] ?? [];
88
- /**
89
- * The {@link SwapVenue} a swap edge runs on: the one its figure was quoted
90
- * against. Reads the edge's own {@link HopEdge.venue}; an edge with none runs
91
- * on the FIRST venue of its chain (V1 for PulseX) — every swap edge built
92
- * before venues existed was quoted and signed there, and the same-chain swap
93
- * sender's own hand-built edge still is.
94
- *
95
- * NEVER FALLS BACK TO ANOTHER CHAIN OR ANOTHER ROUTER. An edge that names a
96
- * router `venues` does not list gets null, so the caller can refuse it.
97
- *
98
- * @param venues - the venues the caller allows
99
- * @param edge - the edge to sign; `from` names the chain, and defaults to PulseChain when absent
100
- * @returns the venue, or null when the edge is not a swap edge or names a router that is not in `venues`
101
- */
102
60
  const venueForEdge = (venues, edge) => {
103
61
  if (edge.carrier !== 'PulseX')
104
62
  return null;
@@ -108,51 +66,9 @@ const venueForEdge = (venues, edge) => {
108
66
  return (0, swap_venues_js_1.findSwapVenue)(venues, chainId, edge.venue);
109
67
  };
110
68
  exports.venueForEdge = venueForEdge;
111
- /**
112
- * The router a PulseX edge must be signed on, judged against the DEFAULT
113
- * venues only. Prefer {@link venueForEdge} with the venues your host passes.
114
- *
115
- * @param edge - the edge to sign
116
- * @returns the router, or null when the edge is not a swap edge or names a router that is not a default
117
- */
118
69
  const pulseXRouterForEdge = (edge) => (0, exports.venueForEdge)(config_js_1.defaultSwapVenues, edge)?.router ?? null;
119
70
  exports.pulseXRouterForEdge = pulseXRouterForEdge;
120
- /**
121
- * A node's own address for a pool lookup: native (the zero address) is not a
122
- * token the router or its factory ever holds a pool for — only the chain's
123
- * wrapped-native token is — so a native node resolves to that wrapped token
124
- * before it ever reaches {@link pulseXConversionSwapPath}. Every other node
125
- * is unchanged. A chain with no known wrapped-native token leaves a native
126
- * node as it is; it has no pool, so no path prices.
127
- *
128
- * MIRRORS `useRouteSearch.ts`'s `quotePathAddress` (the UI's async `ask`
129
- * already resolves native this way through that function) — this is the
130
- * SAME rule, restated here because `prepare`/`exact` never go through the
131
- * UI's `readAmountOut` at all; see {@link pulseXCandidatePaths}'s own doc.
132
- *
133
- * @param node - the node to resolve
134
- * @returns the node's own address, or its chain's wrapped-native address for a native node
135
- */
136
71
  const poolTokenAddress = (node) => (0, ecosystems_js_1.sameAddress)(node.address, viem_1.zeroAddress, 'evm') ? (wrappedNativeFor(node.chainId) ?? viem_1.zeroAddress) : node.address;
137
- /**
138
- * The fixed Wrapped-PLS path from `from` to `to` — `[from, WPLS, to]`, or
139
- * `[from, to]` when one side is WPLS — or null when both ends resolve to the
140
- * same pool token (PLS<->WPLS is the wrap carrier's job).
141
- *
142
- * NOT THE PATH A HOP IS SIGNED ALONG. The carrier's own `exact` picks the
143
- * best of many paths ({@link candidatePulseXPaths}) and records the winner on
144
- * the hop's outcome; the signer reads it back with {@link pulseXSwapPathForHop}.
145
- * This fixed path serves only the carrier's asynchronous `ask` — the
146
- * interface's `getAmountsOut` read, which the route search never calls for a
147
- * PulseX hop, because the search only asks aggregators live.
148
- *
149
- * Native PLS resolves to WPLS first ({@link poolTokenAddress}), so a path
150
- * never starts or ends on the zero address, which has no pool.
151
- *
152
- * @param from - the hop's origin node
153
- * @param to - the hop's destination node
154
- * @returns the fixed path, or null when there is nothing for PulseX to swap
155
- */
156
72
  const pulseXWrappedNativePath = (from, to) => {
157
73
  const fromAddress = poolTokenAddress(from);
158
74
  const toAddress = poolTokenAddress(to);
@@ -162,38 +78,16 @@ const pulseXWrappedNativePath = (from, to) => {
162
78
  return wrappedNative === undefined ? [fromAddress, toAddress] : (0, providers_js_1.pulseXConversionSwapPath)(fromAddress, toAddress, wrappedNative);
163
79
  };
164
80
  exports.pulseXWrappedNativePath = pulseXWrappedNativePath;
165
- /**
166
- * Every path a PulseX hop from `from` to `to` may be priced along, or none
167
- * when both ends resolve to the same pool token. EVERY PULSEX PRICE AND
168
- * POOL READ IN THIS FILE GOES THROUGH THIS ONE FUNCTION, so `prepare` reads
169
- * exactly the pools `exact` will look up, and native PLS resolves to WPLS
170
- * the same way in both ({@link poolTokenAddress} — a path that started on
171
- * the zero address once left every native-PLS hop unpriced).
172
- *
173
- * @param from - the hop's origin node
174
- * @param to - the hop's destination node
175
- * @returns the candidate paths, direct first; empty when there is nothing to swap
176
- */
177
81
  const pulseXCandidatePaths = (from, to) => {
178
82
  const fromAddress = poolTokenAddress(from);
179
83
  const toAddress = poolTokenAddress(to);
180
84
  if ((0, ecosystems_js_1.sameAddress)(fromAddress, toAddress, 'evm'))
181
85
  return [];
182
86
  const wrappedNative = wrappedNativeFor(from.chainId);
183
- // No known wrapped-native token: the direct pool is the only path.
184
87
  if (wrappedNative === undefined)
185
88
  return [[fromAddress, toAddress]];
186
89
  return (0, pulsex_paths_js_1.candidatePulseXPaths)({ from: fromAddress, to: toAddress, wrappedNative, hubs: hubsFor(from.chainId) });
187
90
  };
188
- /**
189
- * Whether a swap venue in `venues` can carry funds directly from `from` to
190
- * `to`: both must be Ethereum-Virtual-Machine tokens on the SAME chain, that
191
- * chain must have at least one venue, and the two must resolve to different
192
- * pool tokens, so there is at least one path to price.
193
- *
194
- * NO TOKEN ALLOWLIST IS CONSULTED, on purpose: whether a pool exists is the
195
- * reserve read's question, answered in `prepare`, not this one's.
196
- */
197
91
  const pulseXCarries = (venues, from, to) => {
198
92
  if (from.ecosystem !== 'evm' || to.ecosystem !== 'evm')
199
93
  return false;
@@ -203,21 +97,6 @@ const pulseXCarries = (venues, from, to) => {
203
97
  return false;
204
98
  return pulseXCandidatePaths(from, to).length > 0;
205
99
  };
206
- /**
207
- * The swap path a planned PulseX hop must be signed along: the path its
208
- * outcome records ({@link HopOutcome.swapPath}), which is the path the
209
- * carrier's `exact` priced the figure on. NEVER REBUILT HERE — rebuilding a
210
- * path at signing time is how a quote on one path becomes a signature on
211
- * another.
212
- *
213
- * The recorded path is checked against the hop's own edge before it is
214
- * returned: it must start at the edge's input pool token, end at its output
215
- * pool token, hold only addresses, and name no token twice. A path that fails
216
- * any check is refused (null) rather than signed.
217
- *
218
- * @param hop - the planned hop to sign
219
- * @returns the path to sign, or null when the hop is not a PulseX hop, records no path, or records one that does not fit its edge
220
- */
221
100
  const pulseXSwapPathForHop = (hop) => {
222
101
  if (hop.edge.carrier !== 'PulseX')
223
102
  return null;
@@ -236,42 +115,11 @@ const pulseXSwapPathForHop = (hop) => {
236
115
  return path;
237
116
  };
238
117
  exports.pulseXSwapPathForHop = pulseXSwapPathForHop;
239
- /**
240
- * PulseX's own constant-product swap fee in basis points (29 of 10000, a
241
- * numerator of 9971) — the default for {@link amountOutAlongPath}; a
242
- * venue carries its own `feeBasisPoints`. The SAME `getAmountOut` formula every
243
- * Uniswap-V2-style router uses: `amountOut = floor(amountIn * feeNumerator *
244
- * reserveOut / (reserveIn * feeDenominator + amountIn * feeNumerator))`.
245
- *
246
- * MEASURED, NOT ASSUMED — see `uniV2Factories`'s own doc in `config.ts` for
247
- * the full account. Reading live reserves from both PulseX router
248
- * deployments (V1 `0x98bf93…`, V2 `0x165C34…`) and back-solving this ratio
249
- * against each router's own `getAmountsOut`, on a one-hop and a two-hop
250
- * path, gave the IDENTICAL ratio — `9971/10000` — for both, to the base
251
- * unit. One constant serves both, and `pulsex-carrier.fork.test.ts` pins
252
- * this claim against live chain data rather than trusting the arithmetic
253
- * alone.
254
- */
255
118
  const PULSEX_FEE_BASIS_POINTS = 29;
256
- /** Basis points in a whole: the denominator of every venue's fee. */
257
119
  const BASIS_POINTS_DENOMINATOR = 10000n;
258
- /** The minimal read-only factory ABI `prepare` needs — just enough to resolve a pool's address from its two tokens. */
259
120
  const univ2FactoryAbi = (0, viem_1.parseAbi)([
260
121
  'function getPair(address tokenA, address tokenB) external view returns (address pair)',
261
122
  ]);
262
- /**
263
- * The Uniswap-V2-style constant-product `getAmountOut`, applied with
264
- * the venue's fee. Reproduces the router's own on-chain formula
265
- * exactly — see `pulsex-carrier.fork.test.ts` — so PRICE IMPACT IS ALREADY
266
- * IN HERE: this is the same nonlinear function the router evaluates, not a
267
- * flat-fee approximation with an impact correction bolted on afterward.
268
- *
269
- * @param amountIn - the input amount, in `reserveIn`'s token's base units
270
- * @param reserveIn - the pool's reserve of the input token
271
- * @param reserveOut - the pool's reserve of the output token
272
- * @param feeBasisPoints - the venue's swap fee, in basis points
273
- * @returns the output amount, in `reserveOut`'s token's base units; 0 when either reserve is 0
274
- */
275
123
  const constantProductAmountOut = (amountIn, reserveIn, reserveOut, feeBasisPoints) => {
276
124
  if (reserveIn <= 0n || reserveOut <= 0n || amountIn <= 0n)
277
125
  return 0n;
@@ -280,38 +128,12 @@ const constantProductAmountOut = (amountIn, reserveIn, reserveOut, feeBasisPoint
280
128
  const denominator = reserveIn * BASIS_POINTS_DENOMINATOR + amountInWithFee;
281
129
  return numerator / denominator;
282
130
  };
283
- /**
284
- * What `amountIn` delivers after crossing every pool of a path in order,
285
- * chaining {@link constantProductAmountOut}.
286
- *
287
- * @param amountIn - the amount entering the first pool
288
- * @param steps - the pools in order of travel
289
- * @param feeBasisPoints - the venue's swap fee in basis points; PulseX's 29 when omitted
290
- * @returns the amount leaving the last pool; 0 when any pool cannot pay out
291
- */
292
131
  const amountOutAlongPath = (amountIn, steps, feeBasisPoints = PULSEX_FEE_BASIS_POINTS) => steps.reduce((runningAmount, step) => constantProductAmountOut(runningAmount, step.reserveIn, step.reserveOut, feeBasisPoints), amountIn);
293
132
  exports.amountOutAlongPath = amountOutAlongPath;
294
- /**
295
- * How far a trade's executed rate falls below the marginal (near-spot) rate,
296
- * in percent. The marginal rate is a tiny reference trade through the same
297
- * pools (`amountIn / PULSEX_MARGINAL_REFERENCE_DIVISOR`). The swap fee rides
298
- * in both rates, so it cancels and the gap is price impact alone. A path
299
- * through several pools composes by itself: the reference trade crosses the
300
- * same pools in the same order.
301
- *
302
- * impact = (1 - (amountOut / amountIn) / (referenceAmountOut / referenceAmountIn)) * 100
303
- *
304
- * @param options.amountIn - the full trade's input
305
- * @param options.amountOut - what the full trade delivers
306
- * @param options.referenceAmountIn - the reference trade's input
307
- * @param options.referenceAmountOut - what the reference trade delivers
308
- * @returns the impact in percent, never below 0; null when a reference figure is zero (impact cannot be measured)
309
- */
310
133
  const priceImpactAgainstMarginal = (options) => {
311
134
  const { amountIn, amountOut, referenceAmountIn, referenceAmountOut } = options;
312
135
  if (amountIn <= 0n || referenceAmountIn <= 0n || referenceAmountOut <= 0n)
313
136
  return null;
314
- // The output the trade would deliver at the marginal rate with no impact.
315
137
  const idealAmountOut = (referenceAmountOut * amountIn) / referenceAmountIn;
316
138
  if (idealAmountOut <= 0n)
317
139
  return null;
@@ -321,26 +143,12 @@ const priceImpactAgainstMarginal = (options) => {
321
143
  return Number(((idealAmountOut - amountOut) * MILLIONTHS * 100n) / idealAmountOut) / Number(MILLIONTHS);
322
144
  };
323
145
  exports.priceImpactAgainstMarginal = priceImpactAgainstMarginal;
324
- /** The stable key one pool is stored and looked up under — the two token addresses, lower-cased and sorted, so either query order finds the same entry. */
325
146
  const poolKey = (tokenA, tokenB) => {
326
147
  const [lower, higher] = [tokenA.toLowerCase(), tokenB.toLowerCase()].sort();
327
148
  return `${lower}|${higher}`;
328
149
  };
329
- /** The key one venue's pool is stored under: the same two tokens on V1 and on V2 are two different pools with two different reserves. */
330
150
  const venuePoolKey = (venue, tokenA, tokenB) => `${venue.chainId}|${venue.factory.toLowerCase()}|${poolKey(tokenA, tokenB)}`;
331
- /** Every consecutive (tokenIn, tokenOut) hop a PulseX swap path implies. */
332
151
  const hopsAlongPath = (path) => path.slice(0, -1).map((tokenIn, index) => [tokenIn, path[index + 1]]);
333
- /**
334
- * The pools a path crosses on one venue, each read in the direction of
335
- * travel, or null when any of them was never read — `getPair` found no pool,
336
- * or the read failed. A path with a missing pool cannot be priced, exactly as
337
- * the router's own `getAmountsOut` would revert on it.
338
- *
339
- * @param options.venue - the deployment whose pools the path trades against
340
- * @param options.path - the tokens, in order
341
- * @param options.reservesByPoolKey - every pool `prepare` read, keyed by {@link venuePoolKey}
342
- * @returns the steps in order, or null when a pool is missing
343
- */
344
152
  const stepsAlongPath = (options) => {
345
153
  const { venue, path, reservesByPoolKey } = options;
346
154
  const steps = [];
@@ -356,35 +164,6 @@ const stepsAlongPath = (options) => {
356
164
  }
357
165
  return steps;
358
166
  };
359
- /**
360
- * Batch-reads reserves for every pool `nodes`' candidate PulseX paths touch,
361
- * on EVERY venue in `venues`, once, so {@link createPulseXCarrier}'s `exact`
362
- * can answer every hop SYNCHRONOUSLY afterward.
363
- *
364
- * EACH VENUE IS READ THROUGH ITS OWN FACTORY. The same two tokens usually
365
- * have a pool on V1 and another on V2, with different reserves; the two are
366
- * stored apart ({@link venuePoolKey}) and never blended. `PULSEX_FEE_NUMERATOR`
367
- * is proven identical for both deployments (see its own doc), so the formula
368
- * is shared and only the reserves differ.
369
- *
370
- * TWO MULTICALLS ON PULSECHAIN, WHATEVER THE VENUE COUNT: `getPair` must
371
- * resolve each pool's address before `getReserves` can be read from it, so
372
- * the second read cannot join the first — but every venue's `getPair` rides
373
- * the first multicall and every pool's `getReserves` rides the second.
374
- *
375
- * THE HUB SEARCH WIDENS EACH READ, NOT THEIR NUMBER. Every pool any candidate
376
- * path crosses is read here ({@link pulseXCandidatePaths}), so `exact` can
377
- * price every path with no further call. Measured 2026-10-06: an 8-token
378
- * PulseChain universe asked 84 entries across the two reads (the fixed
379
- * Wrapped-PLS path asked about 16). The shared multicall helper splits a read
380
- * past its request-size budget into sequential posts; `getPair` crosses that
381
- * line at about 11 pool tokens (198 entries over two venues).
382
- *
383
- * @param nodes - every node the search might branch through
384
- * @param venues - the venues this carrier offers
385
- * @param clientForChain - see {@link CreatePulseXCarrierOptions}'s own field
386
- * @returns every pool this universe touches, keyed by {@link venuePoolKey} — pools with no `getPair` result are simply absent, not an error
387
- */
388
167
  const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
389
168
  const reservesByPoolKey = new Map();
390
169
  const chainIds = Object.keys(venues)
@@ -397,12 +176,6 @@ const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
397
176
  }
398
177
  return reservesByPoolKey;
399
178
  };
400
- /**
401
- * One chain's share of {@link preparePulseXReserves}: two multicalls, the
402
- * `getPair` reads and then the `getReserves` reads, over every venue of the
403
- * chain. A chain with no client, or with no chain description to multicall
404
- * against, reads nothing.
405
- */
406
179
  const preparePulseXReservesOnChain = async ({ nodes, chainId, venues, clientForChain, }) => {
407
180
  const reservesByPoolKey = new Map();
408
181
  const client = clientForChain(chainId);
@@ -447,10 +220,6 @@ const preparePulseXReservesOnChain = async ({ nodes, chainId, venues, clientForC
447
220
  const pairAddressByPoolKey = new Map();
448
221
  poolQueries.forEach((query, index) => {
449
222
  const pairAddress = factoryResults[index];
450
- // A decoded address is exactly 42 characters; a failed `allowFailure`
451
- // call's undecoded revert bytes are almost never that length — see
452
- // `omnibridge.ts`'s identical guard for why `typeof` alone cannot tell
453
- // success from failure for an address-returning call.
454
223
  if (typeof pairAddress === 'string' && pairAddress.length === 42 && pairAddress !== viem_1.zeroAddress) {
455
224
  pairAddressByPoolKey.set(query.key, (0, viem_1.getAddress)(pairAddress));
456
225
  }
@@ -475,23 +244,10 @@ const preparePulseXReservesOnChain = async ({ nodes, chainId, venues, clientForC
475
244
  const [reserve0, reserve1] = result;
476
245
  if (typeof reserve0 !== 'bigint' || typeof reserve1 !== 'bigint')
477
246
  return;
478
- // Standard Uniswap-V2-style factories sort a pair's tokens before
479
- // creating it, so `token0` is always the LEXICOGRAPHICALLY LOWER
480
- // address — confirmed against live PulseX pools 2026-09-24 (both V1 and
481
- // V2), never re-derived with an extra `token0()` read.
482
247
  reservesByPoolKey.set(key, { reserveOfLowerAddress: reserve0, reserveOfHigherAddress: reserve1 });
483
248
  });
484
249
  return reservesByPoolKey;
485
250
  };
486
- /**
487
- * The venues a carrier offers: every venue in `venues`, or only the routers
488
- * the host names. A named router that is in no venue is a configuration
489
- * error, thrown at construction rather than offered silently.
490
- *
491
- * @param venues - the resolved venues
492
- * @param routers - the routers the host can sign on, or undefined for all
493
- * @returns the venues, in their original order
494
- */
495
251
  const selectPulseXVenues = (venues, routers) => {
496
252
  if (routers === undefined)
497
253
  return venues;
@@ -503,18 +259,9 @@ const selectPulseXVenues = (venues, routers) => {
503
259
  }
504
260
  return Object.freeze(Object.fromEntries(Object.entries(venues).map(([chainId, list]) => [chainId, list.filter(named)])));
505
261
  };
506
- /**
507
- * Builds the PulseX conversion swap as a plug-in carrier.
508
- *
509
- * @param options - see {@link CreatePulseXCarrierOptions}
510
- * @returns the carrier — `id: 'PulseX'`
511
- */
512
262
  const createPulseXCarrier = (options) => {
513
263
  const { readAmountOut, clientForChain } = options;
514
264
  const venues = selectPulseXVenues((0, swap_venues_js_1.resolveSwapVenues)(options.venues), options.routers);
515
- // ONE EDGE PER VENUE. Each names its router as its `venue`, so the search
516
- // quotes and ranks V1 and V2 as two separate hops and the better one wins
517
- // on its own figure — and the winner is signed on the router it names.
518
265
  const edgesFrom = (from, to) => {
519
266
  if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
520
267
  return [];
@@ -538,17 +285,10 @@ const createPulseXCarrier = (options) => {
538
285
  return {
539
286
  ok: true,
540
287
  deliveredAmount: amountOut,
541
- // getAmountsOut is exact for the CURRENT block; no slippage tolerance is
542
- // modelled here, so the guaranteed figure is left equal to the expected
543
- // one, matching the omnibridge carrier's own convention.
544
288
  minimumDeliveredAmount: amountOut,
545
289
  claimedDurationSeconds: 0,
546
290
  };
547
291
  };
548
- // Populated by `prepare`, read by `exact` — see `omnibridge.ts`'s identical
549
- // field for why a mutable closure here is safe (client-side,
550
- // one-search-at-a-time; PulseX is likewise "never built into" the shared
551
- // display-quote service).
552
292
  let reservesByPoolKey = new Map();
553
293
  const prepare = async (nodes) => {
554
294
  if (clientForChain === undefined)
@@ -558,31 +298,19 @@ const createPulseXCarrier = (options) => {
558
298
  const exact = (edge, amountIn) => {
559
299
  if (!pulseXCarries(venues, edge.from, edge.to))
560
300
  return null;
561
- // Quoted on the edge's OWN venue — the router it will be signed on — and
562
- // only on a venue this carrier offers.
563
301
  const venue = (0, exports.venueForEdge)(venues, edge);
564
302
  if (venue === null)
565
303
  return null;
566
- // EVERY CANDIDATE PATH IS PRICED, AND THE BEST ONE WINS. A path with a
567
- // pool `prepare` never found is left out — the same as a router call
568
- // that would revert — and a path that pays out nothing is never picked.
569
304
  const quotes = pulseXCandidatePaths(edge.from, edge.to).flatMap((path) => {
570
305
  const steps = stepsAlongPath({ venue, path, reservesByPoolKey });
571
306
  return steps === null ? [] : [{ path, steps, deliveredAmount: (0, exports.amountOutAlongPath)(amountIn, steps, venue.feeBasisPoints) }];
572
307
  });
573
- // No path had every pool read — a structural non-answer, not a refusal
574
- // (`exact`'s own doc: `null` means "nothing to say"). NOTE THIS DOES NOT
575
- // FALL BACK TO AN ESTIMATE: a carrier with `exact` defined never yields
576
- // an `'estimate'` outcome (see `enumerate.ts`'s `resolveHopOutcome`,
577
- // branch 1) — `null` here PRUNES the hop entirely, the same as a refusal.
578
308
  if (quotes.length === 0)
579
309
  return null;
580
310
  const best = (0, pulsex_paths_js_1.bestPulseXPathQuote)(quotes);
581
311
  if (best === null) {
582
312
  return { outcome: 'refused', refusal: { kind: 'no-quote', reason: 'no liquidity on this PulseX path', minimumAmount: null } };
583
313
  }
584
- // Price impact is measured on the CHOSEN path's own pools, with the same
585
- // reference trade as before.
586
314
  const referenceAmountIn = amountIn / producers_js_1.PULSEX_MARGINAL_REFERENCE_DIVISOR;
587
315
  const priceImpactPercent = (0, exports.priceImpactAgainstMarginal)({
588
316
  amountIn,
@@ -590,10 +318,6 @@ const createPulseXCarrier = (options) => {
590
318
  referenceAmountIn,
591
319
  referenceAmountOut: (0, exports.amountOutAlongPath)(referenceAmountIn, best.steps, venue.feeBasisPoints),
592
320
  });
593
- // The path rides on the answer, so the signer signs exactly what was
594
- // priced (`pulseXSwapPathForHop`). Impact is left off (not zero) when the
595
- // reference trade rounds away: an unknown impact must stay unknown so the
596
- // quality gate withholds the plan.
597
321
  const delivered = { outcome: 'delivered', deliveredAmount: best.deliveredAmount, swapPath: best.path };
598
322
  return priceImpactPercent === null ? delivered : { ...delivered, priceImpactPercent };
599
323
  };
@@ -604,11 +328,8 @@ const createPulseXCarrier = (options) => {
604
328
  prepare,
605
329
  exact,
606
330
  isRateLimited: false,
607
- // A same-chain swap requires the reader's own wallet signature — never
608
- // a by-hand deposit.
609
331
  depositMode: 'wallet-signed',
610
332
  sender: 'swap',
611
- // A same-chain swap settles with the transaction itself.
612
333
  settlementSeconds: 0,
613
334
  };
614
335
  };
@@ -4,48 +4,6 @@ exports.createWrapCarrier = void 0;
4
4
  const viem_1 = require("viem");
5
5
  const config_js_1 = require("../../config.js");
6
6
  const graph_js_1 = require("../search/graph.js");
7
- /**
8
- * Native-coin wrap/unwrap (PLS <-> WPLS on PulseChain, and the same pair on
9
- * every other Ethereum-Virtual-Machine chain the search graph touches — ETH
10
- * <-> WETH, BNB <-> WBNB) as a plug-in `Carrier` for `@gibs/quotes`'s
11
- * route-search engine. GIBS-SPECIFIC in the sense `omnibridge.ts`/`pulsex.ts`
12
- * are (see `omnibridge.ts`'s head doc for why this returns a plain object
13
- * shaped like `Carrier` rather than importing that type) — though the
14
- * carrier itself names no chain: it derives every wrap pair from
15
- * `config.ts`'s own `nativeAssetOut` map, so a chain added there gains a
16
- * wrap edge with no change here.
17
- *
18
- * WHY THIS IS A SEPARATE CARRIER FROM PulseX. `pulsex.ts`'s own
19
- * `readPulseXAmountOut` (via `pulseXSwapPathForEdge` in
20
- * `packages/ui/src/lib/state/useRouteSearch.ts`) quotes native Pulse AS
21
- * Wrapped Pulse — both map to the same `quotePathAddress` — so a PLS<->WPLS
22
- * pair collapses to "no swap to quote" and PulseX has never carried it.
23
- * There is no PulseX POOL for native-versus-wrapped; it is a 1:1 contract
24
- * call (`deposit()`/`withdraw(uint256)`), not a trade, so it needed its own
25
- * carrier rather than a PulseX special case.
26
- *
27
- * SENDER: `'swap'`, THE SAME NAME `pulsex.ts` USES — the reader's own
28
- * connected wallet signs a `deposit()`/`withdraw(uint256)` call on the
29
- * wrapped token's own contract (WETH9-style; confirmed present on WPLS by
30
- * bytecode selector 2026-09-24), exactly the wallet a same-chain PulseX swap
31
- * already uses. WHETHER `settings.ts`'s EXISTING swap-transaction builder
32
- * (`pulseXSwapTransactionInputs`) CAN BUILD THIS CALL: NO, not without a
33
- * change there — that builder only ever targets a PulseX ROUTER address and
34
- * encodes `swapExact*` calldata; a wrap needs `to` = the wrapped token
35
- * itself and `data` = plain `deposit()`/`withdraw(amount)` calldata, which
36
- * is a different (and simpler) shape. Sharing the `'swap'` sender name says
37
- * "the same wallet, the same signing step in the flow" — it does not claim
38
- * the existing builder already emits this calldata. Building that call is
39
- * `settings.ts`'s job, out of this carrier's scope; this module answers only
40
- * the route-search question ("what does this edge deliver, exactly").
41
- */
42
- /**
43
- * Whether this carrier can wrap/unwrap directly between `from` and `to`:
44
- * same Ethereum-Virtual-Machine chain, and the two addresses are EXACTLY the
45
- * zero-address/wrapped-native pair `config.ts`'s `nativeAssetOut` names for
46
- * that chain — nothing else, so a plain token pair that merely happens to
47
- * share a chain is never mistaken for a wrap.
48
- */
49
7
  const wrapCarries = (from, to) => {
50
8
  if (from.ecosystem !== 'evm' || to.ecosystem !== 'evm')
51
9
  return false;
@@ -65,22 +23,11 @@ const wrapCarries = (from, to) => {
65
23
  return fromAddress === wrapped;
66
24
  return false;
67
25
  };
68
- /** Every wrap/unwrap edge is worth exactly `amountIn`, in `edge.to`'s base units — native and wrapped share the same decimals on every chain this carrier has ever seen (18, both sides, everywhere `nativeAssetOut` names). */
69
26
  const wrapExact = (edge, amountIn) => {
70
27
  if (!wrapCarries(edge.from, edge.to))
71
28
  return null;
72
29
  return { outcome: 'delivered', deliveredAmount: amountIn };
73
30
  };
74
- /**
75
- * Builds the native wrap/unwrap carrier as a plug-in.
76
- *
77
- * NO OPTIONS, NO `prepare`. There is no local read to batch — a wrap has no
78
- * minimum, no maximum, no fee rate and no reserve to be wrong about; it is a
79
- * pure 1:1 identity over every chain `config.ts` names a wrapped-native
80
- * token for. `exact` alone is total over every edge this carrier offers.
81
- *
82
- * @returns the carrier — `id: 'wrap'`
83
- */
84
31
  const createWrapCarrier = () => {
85
32
  const edgesFrom = (from, to) => {
86
33
  if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
@@ -97,7 +44,6 @@ const createWrapCarrier = () => {
97
44
  return {
98
45
  ok: true,
99
46
  deliveredAmount: result.deliveredAmount,
100
- // 1:1, always — there is no slippage to guarantee against.
101
47
  minimumDeliveredAmount: result.deliveredAmount,
102
48
  claimedDurationSeconds: 0,
103
49
  };
@@ -108,11 +54,8 @@ const createWrapCarrier = () => {
108
54
  ask,
109
55
  exact: wrapExact,
110
56
  isRateLimited: false,
111
- // A `deposit()`/`withdraw()` call requires the reader's own wallet
112
- // signature — never a by-hand deposit.
113
57
  depositMode: 'wallet-signed',
114
58
  sender: 'swap',
115
- // A same-chain call settles with the transaction itself.
116
59
  settlementSeconds: 0,
117
60
  };
118
61
  };
@@ -1,92 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.composedRouteMayBeOffered = exports.assessComposedRoute = exports.COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = exports.COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = exports.COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = void 0;
4
- /**
5
- * Whether a composed multi-signature route is good enough to offer at all.
6
- *
7
- * WHY A ROUTE NEEDS A GATE THAT A SINGLE CROSSING DOES NOT. A one-signature
8
- * route that turns out badly costs the reader one transaction and they still
9
- * hold something they chose. A composed route — an aggregator leg, then an
10
- * omnibridge crossing, then a swap — strands them between signatures: after
11
- * the first they hold an anchor token on a chain they did not ask for, and the
12
- * only way out is to pay again. So a cheap first leg followed by a bad swap is
13
- * worse than the route it replaced, and the difference has to be found BEFORE
14
- * the route is listed rather than after the reader has signed.
15
- *
16
- * NOTHING HERE COSTS A REQUEST. Both figures the verdict reads are already in
17
- * hand when a composer runs: the swap's price impact comes from the on-chain
18
- * `getAmountsOut` pair the estimate already fetched, and the two dollar values
19
- * come from the route's own basis. The gate is free, which is why it can run
20
- * on every route rather than only on suspicious ones.
21
- */
22
- /**
23
- * The price impact above which a composed route is withdrawn, in percent.
24
- *
25
- * MEASURED RATHER THAN CHOSEN. Across the anchor and size combinations swept
26
- * on 2026-09-21 — bridged wrapped ether, USD Coin, DAI and USDT into wrapped
27
- * PulseChain coin, at roughly forty and one hundred and forty United States
28
- * dollars — the impact ran from 0.008 percent to 0.037 percent, with bridged
29
- * USDT the one outlier at 0.34 to 0.37 percent. Three percent is two orders of
30
- * magnitude above the normal case and an order above the worst observed, so it
31
- * cannot fire on an ordinary route; and it is still far below the point where
32
- * a reader is quietly losing real money on a leg they cannot undo.
33
- *
34
- * See `docs/near-intents-conversion-composition.md` for the full sweep.
35
- */
36
4
  exports.COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = 3;
37
- /**
38
- * The end-to-end value loss above which a composed route is withdrawn, in
39
- * percent of what the reader pays.
40
- *
41
- * A SECOND TEST BECAUSE THE FIRST CAN READ FINE ON A BROKEN POOL. Price impact
42
- * is measured against a marginal reference quote taken from the same pool, so
43
- * a pool that is uniformly mispriced — a stale pair, a pair holding almost
44
- * nothing — reports a small impact while paying out far less than the tokens
45
- * going in are worth. Only comparing the two ends of the journey catches that.
46
- *
47
- * Measured whole-journey loss on the same sweep ran from 0.81 to 1.36 percent,
48
- * of which the swap fee alone is 0.29. Five percent leaves every observed
49
- * route untouched and still stops a route that is quietly eating a twentieth
50
- * of the reader's money.
51
- */
52
5
  exports.COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = 5;
53
- /**
54
- * The end-to-end value GAIN above which a composed route is withdrawn, in
55
- * percent of what the reader pays.
56
- *
57
- * A ROUTE CANNOT MAKE MONEY. Every leg charges a fee, so a route that
58
- * delivers more value than it takes says one of its figures is wrong. Only a
59
- * disagreement between the two price feeds can make a gain look real, and the
60
- * value-loss ceiling already trusts those feeds to within the same five
61
- * percent. So the bound is symmetric with
62
- * {@link COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT}.
63
- *
64
- * THE DEFECT THIS STOPS. On 2026-10-03 the engine served 0.0002 BNB to HEX at
65
- * 495,256 HEX, about seven thousand times the rate of the next tier. The
66
- * value-loss test read that as a loss of minus seven hundred thousand percent
67
- * and passed it. A PulseX hop rescaled from an upstream live answer keeps the
68
- * price impact measured at its original, smaller input
69
- * (`recomposePath` in `@gibs/quotes`'s `search/waves.ts`), so the impact test
70
- * cannot see an inflated amount either. Only the two ends of the journey can.
71
- */
72
6
  exports.COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = 5;
73
- /**
74
- * Judges a composed route against its three ceilings.
75
- *
76
- * Checks price impact first, then value loss, then value gain. Impact names
77
- * the specific leg at fault, while the two value tests only say the journey as
78
- * a whole went wrong, so impact is the more useful answer when several fail.
79
- * In the measured percent of a `'value-gain'` rejection, the gain is a
80
- * positive number.
81
- *
82
- * @param options.priceImpactPercent - the swap leg's price impact, from the
83
- * estimate's marginal reference quote; null when not yet quoted
84
- * @param options.paidUsdValue - what the reader spends at the origin, in
85
- * United States dollars; null when unpriced
86
- * @param options.deliveredUsdValue - what the route delivers, valued on the
87
- * SAME basis as `paidUsdValue`; null when unpriced
88
- * @returns the verdict
89
- */
90
7
  const assessComposedRoute = ({ priceImpactPercent, paidUsdValue, deliveredUsdValue, }) => {
91
8
  if (typeof priceImpactPercent !== 'number' || !Number.isFinite(priceImpactPercent)) {
92
9
  return { state: 'unknown', missing: 'price-impact' };
@@ -128,17 +45,5 @@ const assessComposedRoute = ({ priceImpactPercent, paidUsdValue, deliveredUsdVal
128
45
  return { state: 'acceptable' };
129
46
  };
130
47
  exports.assessComposedRoute = assessComposedRoute;
131
- /**
132
- * Whether a composed route may be listed on the strip.
133
- *
134
- * ONLY AN EXPLICIT PASS LISTS THE ROUTE. An unknown verdict withholds it, the
135
- * same as a rejection, because a three-signature route nobody has checked is
136
- * the case this gate exists for. The two remain distinct in
137
- * {@link ComposedRouteVerdict} so a surface can tell the reader "still
138
- * pricing" rather than "no route", which a boolean alone could not.
139
- *
140
- * @param verdict - the verdict from {@link assessComposedRoute}
141
- * @returns true only when the route passed every ceiling
142
- */
143
48
  const composedRouteMayBeOffered = (verdict) => verdict.state === 'acceptable';
144
49
  exports.composedRouteMayBeOffered = composedRouteMayBeOffered;