@gibs/bridge-sdk 1.17.0 → 1.19.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 (56) hide show
  1. package/README.md +85 -1
  2. package/dist/cjs/config.d.ts +18 -3
  3. package/dist/cjs/config.js +49 -17
  4. package/dist/cjs/consolidated-quote.js +3 -0
  5. package/dist/cjs/execution.d.ts +17 -4
  6. package/dist/cjs/execution.js +40 -13
  7. package/dist/cjs/index.d.ts +1 -0
  8. package/dist/cjs/index.js +1 -0
  9. package/dist/cjs/near-intents-route.d.ts +13 -0
  10. package/dist/cjs/near-intents-route.js +19 -3
  11. package/dist/cjs/prices.d.ts +10 -0
  12. package/dist/cjs/prices.js +54 -2
  13. package/dist/cjs/routing/candidate-route.d.ts +46 -1
  14. package/dist/cjs/routing/candidate-route.js +47 -2
  15. package/dist/cjs/routing/carrier-provider-key.d.ts +31 -0
  16. package/dist/cjs/routing/carrier-provider-key.js +42 -0
  17. package/dist/cjs/routing/carriers/pulsex.d.ts +50 -25
  18. package/dist/cjs/routing/carriers/pulsex.js +135 -85
  19. package/dist/cjs/routing/index.d.ts +7 -3
  20. package/dist/cjs/routing/index.js +11 -2
  21. package/dist/cjs/routing/route-data.d.ts +47 -5
  22. package/dist/cjs/routing/search/plan-route-data.d.ts +17 -1
  23. package/dist/cjs/routing/search/plan-route-data.js +39 -13
  24. package/dist/cjs/routing/swap-transactions.d.ts +7 -3
  25. package/dist/cjs/routing/swap-transactions.js +6 -4
  26. package/dist/cjs/swap-venues.d.ts +43 -0
  27. package/dist/cjs/swap-venues.js +29 -0
  28. package/dist/cjs/types.d.ts +8 -0
  29. package/dist/config.d.ts +18 -3
  30. package/dist/config.js +48 -16
  31. package/dist/consolidated-quote.js +3 -0
  32. package/dist/execution.d.ts +17 -4
  33. package/dist/execution.js +41 -14
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +1 -0
  36. package/dist/near-intents-route.d.ts +13 -0
  37. package/dist/near-intents-route.js +17 -2
  38. package/dist/prices.d.ts +10 -0
  39. package/dist/prices.js +52 -1
  40. package/dist/routing/candidate-route.d.ts +46 -1
  41. package/dist/routing/candidate-route.js +45 -1
  42. package/dist/routing/carrier-provider-key.d.ts +31 -0
  43. package/dist/routing/carrier-provider-key.js +37 -0
  44. package/dist/routing/carriers/pulsex.d.ts +50 -25
  45. package/dist/routing/carriers/pulsex.js +133 -84
  46. package/dist/routing/index.d.ts +7 -3
  47. package/dist/routing/index.js +4 -2
  48. package/dist/routing/route-data.d.ts +47 -5
  49. package/dist/routing/search/plan-route-data.d.ts +17 -1
  50. package/dist/routing/search/plan-route-data.js +40 -14
  51. package/dist/routing/swap-transactions.d.ts +7 -3
  52. package/dist/routing/swap-transactions.js +7 -5
  53. package/dist/swap-venues.d.ts +43 -0
  54. package/dist/swap-venues.js +24 -0
  55. package/dist/types.d.ts +8 -0
  56. package/package.json +3 -3
@@ -33,13 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.createPulseXCarrier = exports.priceImpactAgainstMarginal = exports.amountOutAlongPath = exports.pulseXSwapPathForHop = exports.pulseXWrappedNativePath = exports.pulseXRouterForEdge = exports.pulseXVenues = void 0;
36
+ exports.createPulseXCarrier = exports.priceImpactAgainstMarginal = exports.amountOutAlongPath = exports.pulseXSwapPathForHop = exports.pulseXWrappedNativePath = exports.pulseXRouterForEdge = exports.venueForEdge = exports.pulseXVenues = void 0;
37
37
  const multicall_1 = require("@gibs/common/multicall");
38
38
  const viem_1 = require("viem");
39
39
  const abis = __importStar(require("../../abis.js"));
40
40
  const chains_js_1 = require("../../chains.js");
41
41
  const config_js_1 = require("../../config.js");
42
42
  const ecosystems_js_1 = require("../../ecosystems.js");
43
+ const swap_venues_js_1 = require("../../swap-venues.js");
43
44
  const providers_js_1 = require("../providers.js");
44
45
  const producers_js_1 = require("../producers.js");
45
46
  const graph_js_1 = require("../search/graph.js");
@@ -54,58 +55,75 @@ const pulsex_paths_js_1 = require("./pulsex-paths.js");
54
55
  * See `omnibridge.ts`'s head doc for why this returns a plain object shaped
55
56
  * like `Carrier` rather than importing that type.
56
57
  */
57
- /** WPLS, the wrapped-native intermediary {@link pulseXConversionSwapPath} routes conversions through. */
58
- const wplsAddress = config_js_1.nativeAssetOut[config_js_1.Chains.PLS];
59
- /** The hub addresses every candidate path is built from — see {@link pulseXHubTokens}. */
58
+ /** The hub addresses every candidate path on PulseChain is built from — see {@link pulseXHubTokens}. */
60
59
  const hubAddresses = pulsex_paths_js_1.pulseXHubTokens.map((hub) => hub.address);
61
60
  /**
62
- * Every PulseX deployment on PulseChain, V1 first: `uniV2Routers` paired with
63
- * `uniV2Factories` by index. Each factory is the router's own
64
- * `router.factory()` read on chain — see `uniV2Factories`'s own doc in
65
- * `config.ts`. A router with no recorded factory is left out, because its
66
- * pools cannot be read.
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
67
66
  */
68
- exports.pulseXVenues = (config_js_1.uniV2Routers[config_js_1.Chains.PLS] ?? []).flatMap((router, index) => {
69
- const factory = config_js_1.uniV2Factories[config_js_1.Chains.PLS]?.[index];
70
- return factory === undefined ? [] : [{ router: (0, viem_1.getAddress)(router), factory: (0, viem_1.getAddress)(factory) }];
71
- });
67
+ const wrappedNativeFor = (chainId) => {
68
+ const wrapped = config_js_1.nativeAssetOut[(0, config_js_1.toChain)(chainId)];
69
+ return wrapped === undefined ? undefined : (0, viem_1.getAddress)(wrapped);
70
+ };
72
71
  /**
73
- * The venue an edge with no {@link HopEdge.venue} runs on: V1, index 0. Every
74
- * PulseX edge built before venues existed was quoted and signed on V1 — the
75
- * same-chain swap sender's own hand-built edge still is — so this keeps that
76
- * meaning rather than guessing a new one.
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.
77
75
  */
78
- const defaultPulseXVenue = exports.pulseXVenues[0];
76
+ const hubsFor = (chainId) => {
77
+ if (chainId === producers_js_1.pulsechainChainId)
78
+ return hubAddresses;
79
+ const wrapped = wrappedNativeFor(chainId);
80
+ return wrapped === undefined ? [] : [wrapped];
81
+ };
79
82
  /**
80
- * The router a PulseX edge must be signed on — the one its figure was quoted
81
- * against. Reads the edge's own {@link HopEdge.venue}; an edge with none is a
82
- * V1 edge (see {@link defaultPulseXVenue}).
83
- *
84
- * @param edge - the edge to sign
85
- * @returns the router, or null when the edge is not a PulseX edge or names a venue that is not a known PulseX router
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
86
  */
87
- const pulseXRouterForEdge = (edge) => pulseXVenueForEdge(edge)?.router ?? null;
88
- exports.pulseXRouterForEdge = pulseXRouterForEdge;
87
+ exports.pulseXVenues = config_js_1.defaultSwapVenues[producers_js_1.pulsechainChainId] ?? [];
89
88
  /**
90
- * The {@link PulseXVenue} a PulseX edge runs on — see {@link pulseXRouterForEdge}.
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.
91
97
  *
92
- * @param edge - the edge
93
- * @returns the venue, or null when there is none
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`
94
101
  */
95
- const pulseXVenueForEdge = (edge) => {
102
+ const venueForEdge = (venues, edge) => {
96
103
  if (edge.carrier !== 'PulseX')
97
104
  return null;
105
+ const chainId = edge.from?.chainId ?? producers_js_1.pulsechainChainId;
98
106
  if (edge.venue === undefined)
99
- return defaultPulseXVenue ?? null;
100
- const venue = edge.venue;
101
- return exports.pulseXVenues.find((candidate) => (0, ecosystems_js_1.sameAddress)(candidate.router, venue, 'evm')) ?? null;
107
+ return venues[chainId]?.[0] ?? null;
108
+ return (0, swap_venues_js_1.findSwapVenue)(venues, chainId, edge.venue);
102
109
  };
110
+ exports.venueForEdge = venueForEdge;
103
111
  /**
104
- * A node's own address for a PulseX pool lookup: native PLS (the zero
105
- * address) is not a token the router or its factory ever holds a pool for —
106
- * only Wrapped PLS is — so a native node resolves to {@link wplsAddress}
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
+ const pulseXRouterForEdge = (edge) => (0, exports.venueForEdge)(config_js_1.defaultSwapVenues, edge)?.router ?? null;
119
+ 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
107
124
  * before it ever reaches {@link pulseXConversionSwapPath}. Every other node
108
- * is unchanged.
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.
109
127
  *
110
128
  * MIRRORS `useRouteSearch.ts`'s `quotePathAddress` (the UI's async `ask`
111
129
  * already resolves native this way through that function) — this is the
@@ -113,9 +131,9 @@ const pulseXVenueForEdge = (edge) => {
113
131
  * UI's `readAmountOut` at all; see {@link pulseXCandidatePaths}'s own doc.
114
132
  *
115
133
  * @param node - the node to resolve
116
- * @returns the node's own address, or {@link wplsAddress} for native PLS
134
+ * @returns the node's own address, or its chain's wrapped-native address for a native node
117
135
  */
118
- const poolTokenAddress = (node) => ((0, ecosystems_js_1.sameAddress)(node.address, viem_1.zeroAddress, 'evm') ? wplsAddress : node.address);
136
+ const poolTokenAddress = (node) => (0, ecosystems_js_1.sameAddress)(node.address, viem_1.zeroAddress, 'evm') ? (wrappedNativeFor(node.chainId) ?? viem_1.zeroAddress) : node.address;
119
137
  /**
120
138
  * The fixed Wrapped-PLS path from `from` to `to` — `[from, WPLS, to]`, or
121
139
  * `[from, to]` when one side is WPLS — or null when both ends resolve to the
@@ -140,7 +158,8 @@ const pulseXWrappedNativePath = (from, to) => {
140
158
  const toAddress = poolTokenAddress(to);
141
159
  if ((0, ecosystems_js_1.sameAddress)(fromAddress, toAddress, 'evm'))
142
160
  return null;
143
- return (0, providers_js_1.pulseXConversionSwapPath)(fromAddress, toAddress, wplsAddress);
161
+ const wrappedNative = wrappedNativeFor(from.chainId);
162
+ return wrappedNative === undefined ? [fromAddress, toAddress] : (0, providers_js_1.pulseXConversionSwapPath)(fromAddress, toAddress, wrappedNative);
144
163
  };
145
164
  exports.pulseXWrappedNativePath = pulseXWrappedNativePath;
146
165
  /**
@@ -160,21 +179,27 @@ const pulseXCandidatePaths = (from, to) => {
160
179
  const toAddress = poolTokenAddress(to);
161
180
  if ((0, ecosystems_js_1.sameAddress)(fromAddress, toAddress, 'evm'))
162
181
  return [];
163
- return (0, pulsex_paths_js_1.candidatePulseXPaths)({ from: fromAddress, to: toAddress, wrappedNative: wplsAddress, hubs: hubAddresses });
182
+ const wrappedNative = wrappedNativeFor(from.chainId);
183
+ // No known wrapped-native token: the direct pool is the only path.
184
+ if (wrappedNative === undefined)
185
+ return [[fromAddress, toAddress]];
186
+ return (0, pulsex_paths_js_1.candidatePulseXPaths)({ from: fromAddress, to: toAddress, wrappedNative, hubs: hubsFor(from.chainId) });
164
187
  };
165
188
  /**
166
- * Whether PulseX can carry funds directly from `from` to `to`: both must be
167
- * Ethereum-Virtual-Machine tokens on PulseChain itself — PulseX runs only on
168
- * PulseChain, never on an entry chain — that resolve to two different pool
169
- * tokens, so there is at least one path to price.
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.
170
193
  *
171
194
  * NO TOKEN ALLOWLIST IS CONSULTED, on purpose: whether a pool exists is the
172
195
  * reserve read's question, answered in `prepare`, not this one's.
173
196
  */
174
- const pulseXCarries = (from, to) => {
197
+ const pulseXCarries = (venues, from, to) => {
175
198
  if (from.ecosystem !== 'evm' || to.ecosystem !== 'evm')
176
199
  return false;
177
- if (from.chainId !== producers_js_1.pulsechainChainId || to.chainId !== producers_js_1.pulsechainChainId)
200
+ if (from.chainId !== to.chainId)
201
+ return false;
202
+ if ((venues[from.chainId] ?? []).length === 0)
178
203
  return false;
179
204
  return pulseXCandidatePaths(from, to).length > 0;
180
205
  };
@@ -212,8 +237,9 @@ const pulseXSwapPathForHop = (hop) => {
212
237
  };
213
238
  exports.pulseXSwapPathForHop = pulseXSwapPathForHop;
214
239
  /**
215
- * PulseX's own constant-product swap fee, as a numerator over
216
- * {@link PULSEX_FEE_DENOMINATOR} — the SAME `getAmountOut` formula every
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
217
243
  * Uniswap-V2-style router uses: `amountOut = floor(amountIn * feeNumerator *
218
244
  * reserveOut / (reserveIn * feeDenominator + amountIn * feeNumerator))`.
219
245
  *
@@ -226,16 +252,16 @@ exports.pulseXSwapPathForHop = pulseXSwapPathForHop;
226
252
  * this claim against live chain data rather than trusting the arithmetic
227
253
  * alone.
228
254
  */
229
- const PULSEX_FEE_NUMERATOR = 9971n;
230
- /** The denominator {@link PULSEX_FEE_NUMERATOR} is a fraction of. */
231
- const PULSEX_FEE_DENOMINATOR = 10000n;
255
+ const PULSEX_FEE_BASIS_POINTS = 29;
256
+ /** Basis points in a whole: the denominator of every venue's fee. */
257
+ const BASIS_POINTS_DENOMINATOR = 10000n;
232
258
  /** The minimal read-only factory ABI `prepare` needs — just enough to resolve a pool's address from its two tokens. */
233
259
  const univ2FactoryAbi = (0, viem_1.parseAbi)([
234
260
  'function getPair(address tokenA, address tokenB) external view returns (address pair)',
235
261
  ]);
236
262
  /**
237
263
  * The Uniswap-V2-style constant-product `getAmountOut`, applied with
238
- * {@link PULSEX_FEE_NUMERATOR}. Reproduces the router's own on-chain formula
264
+ * the venue's fee. Reproduces the router's own on-chain formula
239
265
  * exactly — see `pulsex-carrier.fork.test.ts` — so PRICE IMPACT IS ALREADY
240
266
  * IN HERE: this is the same nonlinear function the router evaluates, not a
241
267
  * flat-fee approximation with an impact correction bolted on afterward.
@@ -243,14 +269,15 @@ const univ2FactoryAbi = (0, viem_1.parseAbi)([
243
269
  * @param amountIn - the input amount, in `reserveIn`'s token's base units
244
270
  * @param reserveIn - the pool's reserve of the input token
245
271
  * @param reserveOut - the pool's reserve of the output token
272
+ * @param feeBasisPoints - the venue's swap fee, in basis points
246
273
  * @returns the output amount, in `reserveOut`'s token's base units; 0 when either reserve is 0
247
274
  */
248
- const constantProductAmountOut = (amountIn, reserveIn, reserveOut) => {
275
+ const constantProductAmountOut = (amountIn, reserveIn, reserveOut, feeBasisPoints) => {
249
276
  if (reserveIn <= 0n || reserveOut <= 0n || amountIn <= 0n)
250
277
  return 0n;
251
- const amountInWithFee = amountIn * PULSEX_FEE_NUMERATOR;
278
+ const amountInWithFee = amountIn * (BASIS_POINTS_DENOMINATOR - BigInt(feeBasisPoints));
252
279
  const numerator = amountInWithFee * reserveOut;
253
- const denominator = reserveIn * PULSEX_FEE_DENOMINATOR + amountInWithFee;
280
+ const denominator = reserveIn * BASIS_POINTS_DENOMINATOR + amountInWithFee;
254
281
  return numerator / denominator;
255
282
  };
256
283
  /**
@@ -259,9 +286,10 @@ const constantProductAmountOut = (amountIn, reserveIn, reserveOut) => {
259
286
  *
260
287
  * @param amountIn - the amount entering the first pool
261
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
262
290
  * @returns the amount leaving the last pool; 0 when any pool cannot pay out
263
291
  */
264
- const amountOutAlongPath = (amountIn, steps) => steps.reduce((runningAmount, step) => constantProductAmountOut(runningAmount, step.reserveIn, step.reserveOut), amountIn);
292
+ const amountOutAlongPath = (amountIn, steps, feeBasisPoints = PULSEX_FEE_BASIS_POINTS) => steps.reduce((runningAmount, step) => constantProductAmountOut(runningAmount, step.reserveIn, step.reserveOut, feeBasisPoints), amountIn);
265
293
  exports.amountOutAlongPath = amountOutAlongPath;
266
294
  /**
267
295
  * How far a trade's executed rate falls below the marginal (near-spot) rate,
@@ -299,7 +327,7 @@ const poolKey = (tokenA, tokenB) => {
299
327
  return `${lower}|${higher}`;
300
328
  };
301
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. */
302
- const venuePoolKey = (venue, tokenA, tokenB) => `${venue.factory.toLowerCase()}|${poolKey(tokenA, tokenB)}`;
330
+ const venuePoolKey = (venue, tokenA, tokenB) => `${venue.chainId}|${venue.factory.toLowerCase()}|${poolKey(tokenA, tokenB)}`;
303
331
  /** Every consecutive (tokenIn, tokenOut) hop a PulseX swap path implies. */
304
332
  const hopsAlongPath = (path) => path.slice(0, -1).map((tokenIn, index) => [tokenIn, path[index + 1]]);
305
333
  /**
@@ -359,16 +387,36 @@ const stepsAlongPath = (options) => {
359
387
  */
360
388
  const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
361
389
  const reservesByPoolKey = new Map();
362
- const client = clientForChain(producers_js_1.pulsechainChainId);
363
- if (client === null || venues.length === 0)
390
+ const chainIds = Object.keys(venues)
391
+ .map(Number)
392
+ .filter((chainId) => (venues[chainId] ?? []).length > 0);
393
+ for (const chainId of chainIds) {
394
+ const chainReserves = await preparePulseXReservesOnChain({ nodes, chainId, venues, clientForChain });
395
+ for (const [key, reserves] of chainReserves)
396
+ reservesByPoolKey.set(key, reserves);
397
+ }
398
+ return reservesByPoolKey;
399
+ };
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
+ const preparePulseXReservesOnChain = async ({ nodes, chainId, venues, clientForChain, }) => {
407
+ const reservesByPoolKey = new Map();
408
+ const client = clientForChain(chainId);
409
+ const knownChain = chains_js_1.chainsMetadata[(0, config_js_1.toChain)(chainId)];
410
+ const chain = knownChain ?? client?.chain;
411
+ if (client === null || chain === undefined)
364
412
  return reservesByPoolKey;
365
- const pulsechainNodes = nodes.filter((node) => node.ecosystem === 'evm' && node.chainId === producers_js_1.pulsechainChainId);
413
+ const chainNodes = nodes.filter((node) => node.ecosystem === 'evm' && node.chainId === chainId);
366
414
  const poolTokenPairs = new Map();
367
- for (const from of pulsechainNodes) {
368
- for (const to of pulsechainNodes) {
415
+ for (const from of chainNodes) {
416
+ for (const to of chainNodes) {
369
417
  if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
370
418
  continue;
371
- if (!pulseXCarries(from, to))
419
+ if (!pulseXCarries(venues, from, to))
372
420
  continue;
373
421
  for (const path of pulseXCandidatePaths(from, to)) {
374
422
  for (const [tokenIn, tokenOut] of hopsAlongPath(path)) {
@@ -379,14 +427,14 @@ const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
379
427
  }
380
428
  if (poolTokenPairs.size === 0)
381
429
  return reservesByPoolKey;
382
- const poolQueries = venues.flatMap((venue) => Array.from(poolTokenPairs.values()).map(([tokenIn, tokenOut]) => ({
430
+ const poolQueries = (venues[chainId] ?? []).flatMap((venue) => Array.from(poolTokenPairs.values()).map(([tokenIn, tokenOut]) => ({
383
431
  key: venuePoolKey(venue, tokenIn, tokenOut),
384
432
  factory: venue.factory,
385
433
  tokenIn,
386
434
  tokenOut,
387
435
  })));
388
436
  const factoryResults = await (0, multicall_1.multicallRead)({
389
- chain: chains_js_1.chainsMetadata[(0, config_js_1.toChain)(producers_js_1.pulsechainChainId)],
437
+ chain,
390
438
  client,
391
439
  abi: univ2FactoryAbi,
392
440
  calls: poolQueries.map((query) => ({
@@ -411,7 +459,7 @@ const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
411
459
  return reservesByPoolKey;
412
460
  const reservePoolKeys = Array.from(pairAddressByPoolKey.keys());
413
461
  const reserveResults = await (0, multicall_1.multicallRead)({
414
- chain: chains_js_1.chainsMetadata[(0, config_js_1.toChain)(producers_js_1.pulsechainChainId)],
462
+ chain,
415
463
  client,
416
464
  abi: abis.pair,
417
465
  calls: reservePoolKeys.map((key) => ({
@@ -436,22 +484,24 @@ const preparePulseXReserves = async ({ nodes, venues, clientForChain, }) => {
436
484
  return reservesByPoolKey;
437
485
  };
438
486
  /**
439
- * The venues a carrier offers: every known venue, or only the routers the
440
- * host names. A named router that is not a known PulseX router is a
441
- * configuration error, thrown at construction rather than offered silently.
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.
442
490
  *
491
+ * @param venues - the resolved venues
443
492
  * @param routers - the routers the host can sign on, or undefined for all
444
- * @returns the venues, in {@link pulseXVenues} order
493
+ * @returns the venues, in their original order
445
494
  */
446
- const selectPulseXVenues = (routers) => {
495
+ const selectPulseXVenues = (venues, routers) => {
447
496
  if (routers === undefined)
448
- return exports.pulseXVenues;
497
+ return venues;
498
+ const named = (venue) => routers.some((router) => (0, ecosystems_js_1.sameAddress)(venue.router, router, 'evm'));
449
499
  for (const router of routers) {
450
- if (!exports.pulseXVenues.some((venue) => (0, ecosystems_js_1.sameAddress)(venue.router, router, 'evm'))) {
451
- throw new Error(`createPulseXCarrier: ${router} is not a known PulseX router on PulseChain`);
452
- }
500
+ const offered = Object.values(venues).some((list) => list.some((venue) => (0, ecosystems_js_1.sameAddress)(venue.router, router, 'evm')));
501
+ if (!offered)
502
+ throw new Error(`createPulseXCarrier: ${router} is not a known PulseX router in the offered swap venues`);
453
503
  }
454
- return exports.pulseXVenues.filter((venue) => routers.some((router) => (0, ecosystems_js_1.sameAddress)(venue.router, router, 'evm')));
504
+ return Object.freeze(Object.fromEntries(Object.entries(venues).map(([chainId, list]) => [chainId, list.filter(named)])));
455
505
  };
456
506
  /**
457
507
  * Builds the PulseX conversion swap as a plug-in carrier.
@@ -461,16 +511,16 @@ const selectPulseXVenues = (routers) => {
461
511
  */
462
512
  const createPulseXCarrier = (options) => {
463
513
  const { readAmountOut, clientForChain } = options;
464
- const venues = selectPulseXVenues(options.routers);
514
+ const venues = selectPulseXVenues((0, swap_venues_js_1.resolveSwapVenues)(options.venues), options.routers);
465
515
  // ONE EDGE PER VENUE. Each names its router as its `venue`, so the search
466
516
  // quotes and ranks V1 and V2 as two separate hops and the better one wins
467
517
  // on its own figure — and the winner is signed on the router it names.
468
518
  const edgesFrom = (from, to) => {
469
519
  if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
470
520
  return [];
471
- if (!pulseXCarries(from, to))
521
+ if (!pulseXCarries(venues, from, to))
472
522
  return [];
473
- return venues.map((venue) => ({
523
+ return (venues[from.chainId] ?? []).map((venue) => ({
474
524
  carrier: 'PulseX',
475
525
  from,
476
526
  to,
@@ -506,19 +556,19 @@ const createPulseXCarrier = (options) => {
506
556
  reservesByPoolKey = await preparePulseXReserves({ nodes, venues, clientForChain });
507
557
  };
508
558
  const exact = (edge, amountIn) => {
509
- if (!pulseXCarries(edge.from, edge.to))
559
+ if (!pulseXCarries(venues, edge.from, edge.to))
510
560
  return null;
511
561
  // Quoted on the edge's OWN venue — the router it will be signed on — and
512
562
  // only on a venue this carrier offers.
513
- const venue = pulseXVenueForEdge(edge);
514
- if (venue === null || !venues.includes(venue))
563
+ const venue = (0, exports.venueForEdge)(venues, edge);
564
+ if (venue === null)
515
565
  return null;
516
566
  // EVERY CANDIDATE PATH IS PRICED, AND THE BEST ONE WINS. A path with a
517
567
  // pool `prepare` never found is left out — the same as a router call
518
568
  // that would revert — and a path that pays out nothing is never picked.
519
569
  const quotes = pulseXCandidatePaths(edge.from, edge.to).flatMap((path) => {
520
570
  const steps = stepsAlongPath({ venue, path, reservesByPoolKey });
521
- return steps === null ? [] : [{ path, steps, deliveredAmount: (0, exports.amountOutAlongPath)(amountIn, steps) }];
571
+ return steps === null ? [] : [{ path, steps, deliveredAmount: (0, exports.amountOutAlongPath)(amountIn, steps, venue.feeBasisPoints) }];
522
572
  });
523
573
  // No path had every pool read — a structural non-answer, not a refusal
524
574
  // (`exact`'s own doc: `null` means "nothing to say"). NOTE THIS DOES NOT
@@ -538,7 +588,7 @@ const createPulseXCarrier = (options) => {
538
588
  amountIn,
539
589
  amountOut: best.deliveredAmount,
540
590
  referenceAmountIn,
541
- referenceAmountOut: (0, exports.amountOutAlongPath)(referenceAmountIn, best.steps),
591
+ referenceAmountOut: (0, exports.amountOutAlongPath)(referenceAmountIn, best.steps, venue.feeBasisPoints),
542
592
  });
543
593
  // The path rides on the answer, so the signer signs exactly what was
544
594
  // priced (`pulseXSwapPathForHop`). Impact is left off (not zero) when the
@@ -8,12 +8,12 @@ export { netDeliveredAmount, restateInDecimals, valueEquivalentAmount } from './
8
8
  export { omnibridgeProviderForEntryChain, pulseXConversionSwapPath } from './providers.js';
9
9
  export { buildDirectCrossingCandidate } from './candidate.js';
10
10
  export type { RouteCandidate, DeferredCrossing, DirectCrossingCandidateInputs } from './candidate.js';
11
- export { buildRouteDataForCandidate, routeSignatureCount } from './candidate-route.js';
11
+ export { buildRouteDataForCandidate, routeSignatureCount, routeSignatureCountForAccount, type RouteSignatureCount, } from './candidate-route.js';
12
12
  export { COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT, COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT, COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT, assessComposedRoute, composedRouteMayBeOffered, } from './composed-route-quality.js';
13
13
  export type { ComposedRouteVerdict } from './composed-route-quality.js';
14
14
  export { entryChainAnchorsForPair } from './entry-chain-anchor.js';
15
15
  export type { EntryChainAnchor, EntryChainCrossingCall } from './entry-chain-anchor.js';
16
- export type { EstimateBasis, RouteData, RouteHopData, RouteKind, RouteOutputData, RouteSectionData, RouteTokenRef, } from './route-data.js';
16
+ export type { EstimateBasis, RouteData, RouteGasByChain, RouteHopData, RouteKind, RouteOutputData, RouteSectionData, RouteTokenRef, } from './route-data.js';
17
17
  export { usdValueOfAmount } from './usd-value.js';
18
18
  export { formatUsd, formatUsdNullable } from './format.js';
19
19
  export { BRIDGE_SETTLEMENT_ETA_MINUTES, PULSEX_MARGINAL_REFERENCE_DIVISOR, pulsechainChainId, addUsdValues, providerDisplayName, resolveEntryChainName, buildCandidateRouteData, buildConversionRouteData, buildDirectRouteData, buildPulseXSwapEstimateData, buildPulseXSwapSectionData, buildOutboundRouteData, buildSameChainSwapRouteData, candidateDeliveredUsd, candidateNetValueUsd, candidateOutsideFeesUsd, candidatePaidUsd, } from './producers.js';
@@ -24,14 +24,18 @@ export { composableHopBasis, composeRouteDelivery, hopOutputAmount, } from './se
24
24
  export type { ComposableHop, ComposableHopFigure, ComposeRefusalReason, ComposedRouteDelivery, ExchangeRate, } from './search/compose.js';
25
25
  export { GAS_TOPUP_PENALTY_FLOOR_USD, GAS_TOPUP_PENALTY_PPM, SIGNATURE_PENALTY_FLOOR_USD, SIGNATURE_PENALTY_PPM, compareRoutePlansByGasAdjustedDeliveredAmount, compareRoutePlansByScoreAdjustedDeliveredAmount, gasAdjustedDeliveredAmount, scoreRoutePlan, sectionIsFundedByLanding, waitPenaltyPpm, zeroGasCostFor, } from './search/score.js';
26
26
  export type { GasCostFor, GasCostQuote, RouteScoreInputs } from './search/score.js';
27
+ export { WRAP_CARRIER_ID, carrierProviderKey, carrierStepKind } from './carrier-provider-key.js';
28
+ export type { CarrierStepKind } from './carrier-provider-key.js';
27
29
  export { buildRouteCandidateFromPlan, buildRouteDataFromPlan, buildRouteScoreInputsFromPlan, deriveDeferredCrossingFromPlan, deriveEntryChainIdFromPlan, deriveOmnibridgeProviderFromPlan, deriveViaChainIdsFromPlan, derivePlanKind, } from './search/plan-route-data.js';
28
30
  export type { PlanCandidateInputs, PlanRouteDataInputs, PlanSectionPricing, PlanValuation, ResolveNodeToken, } from './search/plan-route-data.js';
29
31
  export { createOmnibridgeCarrier, withOmnibridgeEntryFusing, } from './carriers/omnibridge.js';
30
32
  export type { BridgeablePair, CreateOmnibridgeCarrierOptions, } from './carriers/omnibridge.js';
31
- export { createPulseXCarrier, pulseXRouterForEdge, pulseXSwapPathForHop, pulseXVenues, pulseXWrappedNativePath, } from './carriers/pulsex.js';
33
+ export { createPulseXCarrier, pulseXRouterForEdge, venueForEdge, pulseXSwapPathForHop, pulseXVenues, pulseXWrappedNativePath, } from './carriers/pulsex.js';
32
34
  export type { CreatePulseXCarrierOptions, PulseXVenue } from './carriers/pulsex.js';
33
35
  export { bestPulseXPathQuote, candidatePulseXPaths, pulseXHubTokens } from './carriers/pulsex-paths.js';
34
36
  export type { PulseXHubToken, PulseXPathQuote } from './carriers/pulsex-paths.js';
37
+ export { findSwapVenue, resolveSwapVenues } from '../swap-venues.js';
38
+ export type { SwapVenue, SwapVenues } from '../swap-venues.js';
35
39
  export { createWrapCarrier } from './carriers/wrap.js';
36
40
  export type { CarrierAskAnswer, CarrierExactResult } from './carriers/ask-answer.js';
37
41
  export { buildPulseXSwapTransaction, buildWrapTransaction, swapMinimumOut } from './swap-transactions.js';
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.composeRouteDelivery = exports.composableHopBasis = exports.routePlanPathKey = exports.planIsRankable = exports.planIsConfirmed = exports.planComposedBasis = exports.isRateLimitedCarrier = exports.isRankableBasis = exports.isConfirmedBasis = exports.hopEdgeKey = exports.hopComposedBasis = exports.groupHopsBySignature = exports.compareRoutePlansByDeliveredAmount = exports.combineComposedPathBasis = exports.assetNodeKey = exports.RANKABLE_ESTIMATE_BASES = exports.candidatePaidUsd = exports.candidateOutsideFeesUsd = exports.candidateNetValueUsd = exports.candidateDeliveredUsd = exports.buildSameChainSwapRouteData = exports.buildOutboundRouteData = exports.buildPulseXSwapSectionData = exports.buildPulseXSwapEstimateData = exports.buildDirectRouteData = exports.buildConversionRouteData = exports.buildCandidateRouteData = exports.resolveEntryChainName = exports.providerDisplayName = exports.addUsdValues = exports.pulsechainChainId = exports.PULSEX_MARGINAL_REFERENCE_DIVISOR = exports.BRIDGE_SETTLEMENT_ETA_MINUTES = exports.formatUsdNullable = exports.formatUsd = exports.usdValueOfAmount = exports.entryChainAnchorsForPair = exports.composedRouteMayBeOffered = exports.assessComposedRoute = exports.COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = exports.COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = exports.COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = exports.routeSignatureCount = exports.buildRouteDataForCandidate = exports.buildDirectCrossingCandidate = exports.pulseXConversionSwapPath = exports.omnibridgeProviderForEntryChain = exports.valueEquivalentAmount = exports.restateInDecimals = exports.netDeliveredAmount = void 0;
4
- exports.swapMinimumOut = exports.buildWrapTransaction = exports.buildPulseXSwapTransaction = exports.createWrapCarrier = exports.pulseXHubTokens = exports.candidatePulseXPaths = exports.bestPulseXPathQuote = exports.pulseXWrappedNativePath = exports.pulseXVenues = exports.pulseXSwapPathForHop = exports.pulseXRouterForEdge = exports.createPulseXCarrier = exports.withOmnibridgeEntryFusing = exports.createOmnibridgeCarrier = exports.derivePlanKind = exports.deriveViaChainIdsFromPlan = exports.deriveOmnibridgeProviderFromPlan = exports.deriveEntryChainIdFromPlan = exports.deriveDeferredCrossingFromPlan = exports.buildRouteScoreInputsFromPlan = exports.buildRouteDataFromPlan = exports.buildRouteCandidateFromPlan = exports.zeroGasCostFor = exports.waitPenaltyPpm = exports.sectionIsFundedByLanding = exports.scoreRoutePlan = exports.gasAdjustedDeliveredAmount = exports.compareRoutePlansByScoreAdjustedDeliveredAmount = exports.compareRoutePlansByGasAdjustedDeliveredAmount = exports.SIGNATURE_PENALTY_PPM = exports.SIGNATURE_PENALTY_FLOOR_USD = exports.GAS_TOPUP_PENALTY_PPM = exports.GAS_TOPUP_PENALTY_FLOOR_USD = exports.hopOutputAmount = void 0;
3
+ exports.composableHopBasis = exports.routePlanPathKey = exports.planIsRankable = exports.planIsConfirmed = exports.planComposedBasis = exports.isRateLimitedCarrier = exports.isRankableBasis = exports.isConfirmedBasis = exports.hopEdgeKey = exports.hopComposedBasis = exports.groupHopsBySignature = exports.compareRoutePlansByDeliveredAmount = exports.combineComposedPathBasis = exports.assetNodeKey = exports.RANKABLE_ESTIMATE_BASES = exports.candidatePaidUsd = exports.candidateOutsideFeesUsd = exports.candidateNetValueUsd = exports.candidateDeliveredUsd = exports.buildSameChainSwapRouteData = exports.buildOutboundRouteData = exports.buildPulseXSwapSectionData = exports.buildPulseXSwapEstimateData = exports.buildDirectRouteData = exports.buildConversionRouteData = exports.buildCandidateRouteData = exports.resolveEntryChainName = exports.providerDisplayName = exports.addUsdValues = exports.pulsechainChainId = exports.PULSEX_MARGINAL_REFERENCE_DIVISOR = exports.BRIDGE_SETTLEMENT_ETA_MINUTES = exports.formatUsdNullable = exports.formatUsd = exports.usdValueOfAmount = exports.entryChainAnchorsForPair = exports.composedRouteMayBeOffered = exports.assessComposedRoute = exports.COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = exports.COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = exports.COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = exports.routeSignatureCountForAccount = exports.routeSignatureCount = exports.buildRouteDataForCandidate = exports.buildDirectCrossingCandidate = exports.pulseXConversionSwapPath = exports.omnibridgeProviderForEntryChain = exports.valueEquivalentAmount = exports.restateInDecimals = exports.netDeliveredAmount = void 0;
4
+ exports.swapMinimumOut = exports.buildWrapTransaction = exports.buildPulseXSwapTransaction = exports.createWrapCarrier = exports.resolveSwapVenues = exports.findSwapVenue = exports.pulseXHubTokens = exports.candidatePulseXPaths = exports.bestPulseXPathQuote = exports.pulseXWrappedNativePath = exports.pulseXVenues = exports.pulseXSwapPathForHop = exports.venueForEdge = exports.pulseXRouterForEdge = exports.createPulseXCarrier = exports.withOmnibridgeEntryFusing = exports.createOmnibridgeCarrier = exports.derivePlanKind = exports.deriveViaChainIdsFromPlan = exports.deriveOmnibridgeProviderFromPlan = exports.deriveEntryChainIdFromPlan = exports.deriveDeferredCrossingFromPlan = exports.buildRouteScoreInputsFromPlan = exports.buildRouteDataFromPlan = exports.buildRouteCandidateFromPlan = exports.carrierStepKind = exports.carrierProviderKey = exports.WRAP_CARRIER_ID = exports.zeroGasCostFor = exports.waitPenaltyPpm = exports.sectionIsFundedByLanding = exports.scoreRoutePlan = exports.gasAdjustedDeliveredAmount = exports.compareRoutePlansByScoreAdjustedDeliveredAmount = exports.compareRoutePlansByGasAdjustedDeliveredAmount = exports.SIGNATURE_PENALTY_PPM = exports.SIGNATURE_PENALTY_FLOOR_USD = exports.GAS_TOPUP_PENALTY_PPM = exports.GAS_TOPUP_PENALTY_FLOOR_USD = exports.hopOutputAmount = exports.composeRouteDelivery = void 0;
5
5
  /**
6
6
  * Framework-agnostic routing core for the Gibs bridge. Holds the raw numeric
7
7
  * primitives, the candidate shape, and candidate construction — none of which
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "buildDirectCrossingCandidate", { enumerable: tru
20
20
  var candidate_route_js_1 = require("./candidate-route.js");
21
21
  Object.defineProperty(exports, "buildRouteDataForCandidate", { enumerable: true, get: function () { return candidate_route_js_1.buildRouteDataForCandidate; } });
22
22
  Object.defineProperty(exports, "routeSignatureCount", { enumerable: true, get: function () { return candidate_route_js_1.routeSignatureCount; } });
23
+ Object.defineProperty(exports, "routeSignatureCountForAccount", { enumerable: true, get: function () { return candidate_route_js_1.routeSignatureCountForAccount; } });
23
24
  var composed_route_quality_js_1 = require("./composed-route-quality.js");
24
25
  Object.defineProperty(exports, "COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT", { enumerable: true, get: function () { return composed_route_quality_js_1.COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT; } });
25
26
  Object.defineProperty(exports, "COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT", { enumerable: true, get: function () { return composed_route_quality_js_1.COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT; } });
@@ -92,6 +93,10 @@ Object.defineProperty(exports, "scoreRoutePlan", { enumerable: true, get: functi
92
93
  Object.defineProperty(exports, "sectionIsFundedByLanding", { enumerable: true, get: function () { return score_js_1.sectionIsFundedByLanding; } });
93
94
  Object.defineProperty(exports, "waitPenaltyPpm", { enumerable: true, get: function () { return score_js_1.waitPenaltyPpm; } });
94
95
  Object.defineProperty(exports, "zeroGasCostFor", { enumerable: true, get: function () { return score_js_1.zeroGasCostFor; } });
96
+ var carrier_provider_key_js_1 = require("./carrier-provider-key.js");
97
+ Object.defineProperty(exports, "WRAP_CARRIER_ID", { enumerable: true, get: function () { return carrier_provider_key_js_1.WRAP_CARRIER_ID; } });
98
+ Object.defineProperty(exports, "carrierProviderKey", { enumerable: true, get: function () { return carrier_provider_key_js_1.carrierProviderKey; } });
99
+ Object.defineProperty(exports, "carrierStepKind", { enumerable: true, get: function () { return carrier_provider_key_js_1.carrierStepKind; } });
95
100
  // General route search — Phase 1: plan-route-data.ts. Turns a priced
96
101
  // RoutePlan into the same RouteData/RouteCandidate/RouteScoreInputs shapes
97
102
  // today's four hand-written producers emit — see that module's head doc.
@@ -117,6 +122,7 @@ Object.defineProperty(exports, "withOmnibridgeEntryFusing", { enumerable: true,
117
122
  var pulsex_js_1 = require("./carriers/pulsex.js");
118
123
  Object.defineProperty(exports, "createPulseXCarrier", { enumerable: true, get: function () { return pulsex_js_1.createPulseXCarrier; } });
119
124
  Object.defineProperty(exports, "pulseXRouterForEdge", { enumerable: true, get: function () { return pulsex_js_1.pulseXRouterForEdge; } });
125
+ Object.defineProperty(exports, "venueForEdge", { enumerable: true, get: function () { return pulsex_js_1.venueForEdge; } });
120
126
  Object.defineProperty(exports, "pulseXSwapPathForHop", { enumerable: true, get: function () { return pulsex_js_1.pulseXSwapPathForHop; } });
121
127
  Object.defineProperty(exports, "pulseXVenues", { enumerable: true, get: function () { return pulsex_js_1.pulseXVenues; } });
122
128
  Object.defineProperty(exports, "pulseXWrappedNativePath", { enumerable: true, get: function () { return pulsex_js_1.pulseXWrappedNativePath; } });
@@ -124,6 +130,9 @@ var pulsex_paths_js_1 = require("./carriers/pulsex-paths.js");
124
130
  Object.defineProperty(exports, "bestPulseXPathQuote", { enumerable: true, get: function () { return pulsex_paths_js_1.bestPulseXPathQuote; } });
125
131
  Object.defineProperty(exports, "candidatePulseXPaths", { enumerable: true, get: function () { return pulsex_paths_js_1.candidatePulseXPaths; } });
126
132
  Object.defineProperty(exports, "pulseXHubTokens", { enumerable: true, get: function () { return pulsex_paths_js_1.pulseXHubTokens; } });
133
+ var swap_venues_js_1 = require("../swap-venues.js");
134
+ Object.defineProperty(exports, "findSwapVenue", { enumerable: true, get: function () { return swap_venues_js_1.findSwapVenue; } });
135
+ Object.defineProperty(exports, "resolveSwapVenues", { enumerable: true, get: function () { return swap_venues_js_1.resolveSwapVenues; } });
127
136
  var wrap_js_1 = require("./carriers/wrap.js");
128
137
  Object.defineProperty(exports, "createWrapCarrier", { enumerable: true, get: function () { return wrap_js_1.createWrapCarrier; } });
129
138
  // The same-chain swap and wrap builders. The interface's section signing and
@@ -1,6 +1,7 @@
1
1
  import type { Hex } from 'viem';
2
2
  import type { QuoteFee } from '../quote-fees.js';
3
3
  import type { ProviderKey } from '../providers.js';
4
+ import type { CarrierStepKind } from './carrier-provider-key.js';
4
5
  /**
5
6
  * The executing engine family a route runs through.
6
7
  *
@@ -91,7 +92,12 @@ export type RouteHopData = {
91
92
  /** Display name for the hop's provider, e.g. "LI.FI", "PulseChain Bridge". */
92
93
  providerName: string;
93
94
  /** Raw provider key for logo resolution when no `providerLogoUri` is supplied. */
94
- providerKey?: string | null;
95
+ providerKey?: ProviderKey | null;
96
+ /**
97
+ * What the hop does when no provider performs it (a native wrap). Such a hop
98
+ * has a null `providerKey`; the adapter draws its mark from this kind.
99
+ */
100
+ stepKind?: CarrierStepKind | null;
95
101
  /** Quote-supplied provider logo url (aggregator legs); preferred when present. */
96
102
  providerLogoUri?: string | null;
97
103
  /** Human-readable hop title, e.g. "Bridge to PulseChain". */
@@ -110,11 +116,27 @@ export type RouteHopData = {
110
116
  * completes. Null/undefined when the provider named no output token.
111
117
  */
112
118
  toToken?: RouteTokenRef | null;
113
- /** Per-hop fee in United States dollars as a NUMBER, or null when unknown. */
119
+ /** Per-hop fee in United States dollars as a NUMBER, or null when unknown. A search route's figure INCLUDES the signature's network gas (carried on the section's first hop). */
114
120
  feeUsdValue?: number | null;
121
+ /** The part of {@link feeUsdValue} that is not network gas: what the hop costs in value. Absent when the producer states no gas split. */
122
+ feeExcludingGasUsdValue?: number | null;
123
+ /** The network gas of the signature this hop opens, in dollars; null or absent on every hop that opens none, and when gas is unknown. */
124
+ gasUsdValue?: number | null;
115
125
  /** True when this hop runs automatically inside the section's single signature. */
116
126
  automatic?: boolean;
117
127
  };
128
+ /**
129
+ * The network gas a route needs on one signing chain, summed across the
130
+ * signatures it signs there.
131
+ */
132
+ export type RouteGasByChain = {
133
+ /** The chain the signatures broadcast on. */
134
+ chainId: number;
135
+ /** The native coin they burn, in base units (wei), or null when the chain's gas price is unknown. */
136
+ amountWei: bigint | null;
137
+ /** The same gas in dollars, or null when the amount or the native price is unknown. */
138
+ usdValue: number | null;
139
+ };
118
140
  /**
119
141
  * What a signature section's funds LAND IN when that step completes — the stop
120
142
  * the money actually comes to rest at, named in tokens rather than only drawn as
@@ -181,7 +203,9 @@ export type RouteSectionData = {
181
203
  /** Short label naming what this signature does, e.g. "Bridge to PulseChain". */
182
204
  label: string;
183
205
  /** Raw provider key for the service executing this signature, or null. */
184
- providerKey?: string | null;
206
+ providerKey?: ProviderKey | null;
207
+ /** What the signature does when no provider performs it (a native wrap); see {@link RouteHopData.stepKind}. */
208
+ stepKind?: CarrierStepKind | null;
185
209
  /**
186
210
  * The token-icon flow this signature walks, in raw references: the source, then
187
211
  * every INTERMEDIATE stop the funds pass through, then the destination. A
@@ -202,8 +226,12 @@ export type RouteSectionData = {
202
226
  * a cost.
203
227
  */
204
228
  unspent?: RouteUnspentData | null;
205
- /** Section total fee in United States dollars as a NUMBER, or null. */
229
+ /** Section total fee in United States dollars as a NUMBER, or null. A search route's figure INCLUDES this signature's network gas. */
206
230
  feeUsdValue?: number | null;
231
+ /** The part of {@link feeUsdValue} that is not network gas. Absent when the producer states no gas split. */
232
+ feeExcludingGasUsdValue?: number | null;
233
+ /** This signature's network gas in dollars, or null/absent when unknown. */
234
+ gasUsdValue?: number | null;
207
235
  /** Numeric inputs for an optional fee breakdown line (conversion mode). */
208
236
  feePercent?: number | null;
209
237
  /** Price-impact percentage for an optional fee breakdown line (conversion mode). */
@@ -316,8 +344,22 @@ export type RouteData = {
316
344
  sections: RouteSectionData[];
317
345
  /** What the route delivers — used for ranking and the output row. */
318
346
  output: RouteOutputData;
319
- /** Aggregate all-in cost in United States dollars as a NUMBER, or null. */
347
+ /**
348
+ * Aggregate ALL-IN cost in United States dollars as a NUMBER, or null:
349
+ * value lost plus network gas. The ranking reads this figure. For the
350
+ * figure without gas, read {@link costExcludingGasUsdValue}.
351
+ */
320
352
  totalCostUsdValue?: number | null;
353
+ /** {@link totalCostUsdValue} without network gas: the value the reader gives up. Absent when the producer states no gas split. */
354
+ costExcludingGasUsdValue?: number | null;
355
+ /**
356
+ * The network gas the reader pays across every signature, in dollars, or
357
+ * null/absent when no signing chain has a gas reading. Never zero for
358
+ * "unknown".
359
+ */
360
+ gasCostUsdValue?: number | null;
361
+ /** The gas by signing chain, in signing order: what to hold, and in which coin. */
362
+ gasByChain?: RouteGasByChain[];
321
363
  /** The user's net United States dollar outcome; the figure routes are ranked on. */
322
364
  netValueUsd?: number | null;
323
365
  /** Estimated end-to-end duration in minutes, or null when unknown. */