@gibs/bridge-sdk 1.18.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 (120) hide show
  1. package/README.md +33 -0
  2. package/dist/abis.js +0 -2
  3. package/dist/bridge-limits.js +0 -38
  4. package/dist/chain-info.js +0 -116
  5. package/dist/chain-names.js +0 -151
  6. package/dist/chains.js +0 -198
  7. package/dist/cjs/abis.js +0 -2
  8. package/dist/cjs/bridge-limits.js +0 -38
  9. package/dist/cjs/chain-info.js +0 -116
  10. package/dist/cjs/chain-names.js +0 -151
  11. package/dist/cjs/chains.js +0 -198
  12. package/dist/cjs/claims.js +0 -60
  13. package/dist/cjs/config.js +1 -208
  14. package/dist/cjs/consolidated-quote.js +2 -464
  15. package/dist/cjs/destination-calldata.js +0 -71
  16. package/dist/cjs/ecosystems.js +0 -183
  17. package/dist/cjs/execution.js +0 -92
  18. package/dist/cjs/fee-type.js +0 -1
  19. package/dist/cjs/hyperliquid-execution.js +0 -98
  20. package/dist/cjs/image-links.js +0 -108
  21. package/dist/cjs/lifi-execution-guard.js +65 -172
  22. package/dist/cjs/near-intents-conformance.js +0 -110
  23. package/dist/cjs/near-intents-route.d.ts +13 -0
  24. package/dist/cjs/near-intents-route.js +7 -140
  25. package/dist/cjs/near-intents.js +0 -562
  26. package/dist/cjs/origin-deposit.js +0 -56
  27. package/dist/cjs/pre-quote.js +0 -80
  28. package/dist/cjs/prices.js +0 -113
  29. package/dist/cjs/providers.js +0 -83
  30. package/dist/cjs/quote-fees.js +0 -80
  31. package/dist/cjs/referral.js +0 -75
  32. package/dist/cjs/relay-currencies.js +0 -31
  33. package/dist/cjs/relay-depository.js +52 -77
  34. package/dist/cjs/relay-quote.js +0 -394
  35. package/dist/cjs/route-steps.js +0 -100
  36. package/dist/cjs/routing/amounts.js +0 -83
  37. package/dist/cjs/routing/candidate-route.d.ts +46 -1
  38. package/dist/cjs/routing/candidate-route.js +12 -40
  39. package/dist/cjs/routing/candidate.js +0 -46
  40. package/dist/cjs/routing/carrier-provider-key.js +0 -27
  41. package/dist/cjs/routing/carriers/omnibridge.js +0 -120
  42. package/dist/cjs/routing/carriers/pulsex-paths.js +0 -57
  43. package/dist/cjs/routing/carriers/pulsex-split.js +0 -92
  44. package/dist/cjs/routing/carriers/pulsex.js +0 -279
  45. package/dist/cjs/routing/carriers/wrap.js +0 -57
  46. package/dist/cjs/routing/composed-route-quality.js +0 -95
  47. package/dist/cjs/routing/entry-chain-anchor.js +0 -27
  48. package/dist/cjs/routing/format.js +0 -50
  49. package/dist/cjs/routing/index.d.ts +1 -1
  50. package/dist/cjs/routing/index.js +3 -30
  51. package/dist/cjs/routing/producers.js +0 -854
  52. package/dist/cjs/routing/providers.js +0 -27
  53. package/dist/cjs/routing/search/compose.js +0 -56
  54. package/dist/cjs/routing/search/graph.js +0 -364
  55. package/dist/cjs/routing/search/plan-route-data.js +5 -510
  56. package/dist/cjs/routing/search/score.js +0 -344
  57. package/dist/cjs/routing/swap-transactions.js +0 -74
  58. package/dist/cjs/routing/usd-value.js +0 -12
  59. package/dist/cjs/settings.js +1 -456
  60. package/dist/cjs/swap-venues.js +0 -17
  61. package/dist/cjs/ton-connect.js +0 -82
  62. package/dist/cjs/ton-execution.js +0 -6
  63. package/dist/cjs/ton.js +0 -42
  64. package/dist/cjs/tron-execution.js +0 -145
  65. package/dist/cjs/types.d.ts +8 -0
  66. package/dist/claims.js +0 -60
  67. package/dist/config.js +1 -211
  68. package/dist/consolidated-quote.js +2 -464
  69. package/dist/destination-calldata.js +0 -71
  70. package/dist/ecosystems.js +0 -183
  71. package/dist/execution.js +0 -92
  72. package/dist/fee-type.js +0 -2
  73. package/dist/hyperliquid-execution.js +0 -98
  74. package/dist/image-links.js +0 -108
  75. package/dist/lifi-execution-guard.js +65 -172
  76. package/dist/near-intents-conformance.js +0 -110
  77. package/dist/near-intents-route.d.ts +13 -0
  78. package/dist/near-intents-route.js +5 -139
  79. package/dist/near-intents.js +0 -562
  80. package/dist/origin-deposit.js +0 -56
  81. package/dist/pre-quote.js +0 -80
  82. package/dist/prices.js +0 -113
  83. package/dist/providers.js +0 -83
  84. package/dist/quote-fees.js +0 -80
  85. package/dist/referral.js +0 -75
  86. package/dist/relay-currencies.js +0 -31
  87. package/dist/relay-depository.js +52 -77
  88. package/dist/relay-quote.js +0 -394
  89. package/dist/route-steps.js +0 -100
  90. package/dist/routing/amounts.js +0 -83
  91. package/dist/routing/candidate-route.d.ts +46 -1
  92. package/dist/routing/candidate-route.js +10 -39
  93. package/dist/routing/candidate.js +0 -46
  94. package/dist/routing/carrier-provider-key.js +0 -27
  95. package/dist/routing/carriers/omnibridge.js +0 -120
  96. package/dist/routing/carriers/pulsex-paths.js +0 -57
  97. package/dist/routing/carriers/pulsex-split.js +0 -92
  98. package/dist/routing/carriers/pulsex.js +0 -279
  99. package/dist/routing/carriers/wrap.js +0 -57
  100. package/dist/routing/composed-route-quality.js +0 -95
  101. package/dist/routing/entry-chain-anchor.js +0 -27
  102. package/dist/routing/format.js +0 -50
  103. package/dist/routing/index.d.ts +1 -1
  104. package/dist/routing/index.js +1 -29
  105. package/dist/routing/producers.js +0 -854
  106. package/dist/routing/providers.js +0 -27
  107. package/dist/routing/search/compose.js +0 -56
  108. package/dist/routing/search/graph.js +0 -364
  109. package/dist/routing/search/plan-route-data.js +6 -511
  110. package/dist/routing/search/score.js +0 -344
  111. package/dist/routing/swap-transactions.js +0 -74
  112. package/dist/routing/usd-value.js +0 -12
  113. package/dist/settings.js +1 -456
  114. package/dist/swap-venues.js +0 -17
  115. package/dist/ton-connect.js +0 -82
  116. package/dist/ton-execution.js +0 -6
  117. package/dist/ton.js +0 -42
  118. package/dist/tron-execution.js +0 -145
  119. package/dist/types.d.ts +8 -0
  120. package/package.json +3 -3
@@ -41,78 +41,10 @@ const carrier_provider_key_js_1 = require("../carrier-provider-key.js");
41
41
  const near_intents_route_js_1 = require("../../near-intents-route.js");
42
42
  const producers_js_1 = require("../producers.js");
43
43
  const graph_js_1 = require("./graph.js");
44
- /**
45
- * PHASE 1 — turns a priced {@link RoutePlan} into the SAME `RouteData` /
46
- * `RouteCandidate` / `RouteScoreInputs` shapes today's four hand-written
47
- * producers emit: {@link buildNearIntentsTwoStepRouteData} (`../../near-intents-route.js`),
48
- * {@link buildConversionRouteData}, {@link buildOutboundRouteData} and
49
- * {@link buildDirectCrossingCandidate} (paired with `buildCandidateRouteData`)
50
- * — all in `../producers.js` and `../candidate.js`. See the route-search
51
- * design's section 6, "what it absorbs," and this module's own tests
52
- * (`plan-route-data.test.ts`) for the parity checks against each of the four.
53
- *
54
- * WHAT THIS MODULE DOES NOT DO: PRICE A PLAN. Every existing builder prices
55
- * a section from carrier-specific knowledge this module has no access to — a
56
- * bridge's own fee-manager rate (`crossingCost` in `../producers.js`), a
57
- * PulseX swap's estimate (`buildPulseXSwapEstimateData`), or an aggregator's
58
- * own paid/delivered spread (`candidatePaidUsd` / `candidateDeliveredUsd`,
59
- * both reused here rather than restated). A `RoutePlan` (`./graph.js`) names
60
- * the STRUCTURE of a path and a base-unit delivered amount per hop; it
61
- * carries no United States dollar figure at all. So every dollar figure this
62
- * module places on a `RouteData` — a section's fee, a section's landing
63
- * value, the route's total cost — is a CALLER-SUPPLIED input
64
- * ({@link PlanSectionPricing}), never a formula computed here. This mirrors
65
- * `RouteScoreInputs`' own doc in `score.ts`: "the caller is responsible for
66
- * turning a priced RoutePlan into these fields."
67
- *
68
- * WHAT THIS MODULE DOES DO: compose the STRUCTURE — which section a hop
69
- * belongs to, whether that section is deferred, what it is labelled, what
70
- * its hops are titled, and what its token flow is — directly from the
71
- * plan's graph shape, using exactly the same conventions today's four
72
- * builders use. Where those four builders DISAGREE with each other (and two
73
- * of them genuinely do — see `hopIsAutomatic` below), this module picks ONE
74
- * rule and documents which existing convention it follows and which it
75
- * knowingly departs from, per this project's "surface conflicts, don't
76
- * average them" rule. Every departure is named in this module's tests.
77
- */
78
- // ---------------------------------------------------------------------------
79
- // Carrier classification
80
- // ---------------------------------------------------------------------------
81
- /**
82
- * Whether `carrier` is an omnibridge crossing (`'pulsechain'` or
83
- * `'tokensex'`) — a genuine type predicate, not a plain boolean, so every
84
- * call site narrows `carrier` from the wide {@link HopCarrier} union down to
85
- * {@link Provider}. `providerDisplayName` (`../producers.js`) is total only
86
- * over `Provider`'s two keys and falls back to the generic string
87
- * "Omnibridge" for anything else; a plain-boolean guard left the call sites
88
- * below typed as `HopCarrier` and accepting a bare `string`, which is the
89
- * SAME shape of fault that let a NEAR Intents leg display as "Omnibridge"
90
- * (see `plan-route-data.test.ts`'s now-fixed "documented divergence" case).
91
- * Narrowing here makes that class of mistake fail to compile instead of
92
- * silently mis-naming a hop.
93
- */
94
44
  const isBridgeCarrier = (carrier) => carrier === 'pulsechain' || carrier === 'tokensex';
95
- /** Whether `carrier` is a consolidated-route aggregator (LI.FI, Relay, NEAR Intents). */
96
45
  const isAggregatorCarrier = (carrier) => carrier === 'lifi' || carrier === 'relay' || carrier === 'near-intents';
97
- /** Whether a hop lands on PulseChain — the signal every label/title rule below keys on. */
98
46
  const hopLandsOnPulsechain = (hop) => hop.edge.to.chainId === producers_js_1.pulsechainChainId;
99
- /** Whether `hopIndex` is the plan's LAST hop overall — a completing leg, not a starting one. */
100
47
  const isFinalHopOfPlan = (hopIndex, plan) => hopIndex === plan.hops.length - 1;
101
- /**
102
- * Resolves a node's display token and asserts its `decimals` agree with the
103
- * NODE's own `decimals` — the field `graph.ts` added specifically so a hop
104
- * touching two independently-resolved mentions of the same token can be
105
- * checked against each other without a second lookup (see `AssetNode`'s own
106
- * doc). A name is a hypothesis, not evidence: `resolveToken` is a caller
107
- * hook this module does not control, and trusting its `decimals` blind would
108
- * silently misplace a decimal point in a real amount exactly the way
109
- * `compose.ts`'s `decimals-mismatch` refusal exists to prevent one hop
110
- * downstream. The NODE's decimals wins as the returned, asserted value.
111
- *
112
- * @param node - the graph node to resolve
113
- * @param resolveToken - resolves the node to its full display token
114
- * @returns the resolved token and the node's own, asserted decimals
115
- */
116
48
  const resolveNodeWithAssertedDecimals = (node, resolveToken) => {
117
49
  const token = resolveToken(node);
118
50
  if (token.decimals !== node.decimals) {
@@ -121,19 +53,6 @@ const resolveNodeWithAssertedDecimals = (node, resolveToken) => {
121
53
  }
122
54
  return { token, decimals: node.decimals };
123
55
  };
124
- /**
125
- * Builds the raw `RouteTokenRef` for one graph node, resolving its logo the
126
- * same way every existing builder's own token-node helper does
127
- * (`tokenNode` in `../../near-intents-route.js`, the inline node literals in
128
- * `buildCandidateRouteData`): the token's own image first, the chain badge
129
- * from `imageLinks.networkOrPseudo` second — which is what makes a
130
- * pay-by-hand origin's invented chain id resolve to a real badge instead of
131
- * a 404ing numeric lookup.
132
- *
133
- * @param node - the graph node to render
134
- * @param resolveToken - resolves the node to its full display token
135
- * @returns the raw token reference
136
- */
137
56
  const toRouteTokenRef = (node, resolveToken) => {
138
57
  const { token, decimals } = resolveNodeWithAssertedDecimals(node, resolveToken);
139
58
  return {
@@ -141,34 +60,10 @@ const toRouteTokenRef = (node, resolveToken) => {
141
60
  address: token.address,
142
61
  chainId: node.chainId,
143
62
  decimals,
144
- // NOT `image`, which checksums the address as an Ethereum one and throws
145
- // for anything else. A plan can start on a pay-by-hand chain (Zcash's
146
- // native sentinel is `t1Hsc…`), and that throw blanked the whole bridge
147
- // form the moment such a route was served. A token's own logo still wins;
148
- // an address no picture can be asked for draws its chain's mark.
149
63
  logoUri: token.logoURI ? token.logoURI : imageLinks.tokenImageOrChainFallback(token),
150
64
  chainLogoUri: imageLinks.networkOrPseudo(node.chainId),
151
65
  };
152
66
  };
153
- /**
154
- * The distinct stops a signature group's hops walk: the origin, then every
155
- * node a hop lands on, collapsing a hop that lands back on the node the
156
- * flow already holds — generalizing `deriveTokenFlow` in `../producers.js`
157
- * from RAW ADDRESS-STRING comparison (`isSameTokenRef`) to
158
- * {@link assetNodeKey}, which is already the collision-free, casing-normalized
159
- * identity every other part of the graph model uses.
160
- *
161
- * THIS IS WHERE THE RECENTLY-FIXED BUG STAYS FIXED. `deriveTokenFlow` used to
162
- * leave a route showing only its origin when a candidate itemized no hops at
163
- * all — see `../producers.js`'s own note on the defect. A `RoutePlan` always
164
- * carries at least one hop for a real crossing, and this walk always ends on
165
- * that hop's `to` node, so the flow can never stop short of the plan's own
166
- * declared destination for its group.
167
- *
168
- * @param hops - one signature group's hops, first to last
169
- * @param resolveToken - resolves a graph node to its full display token
170
- * @returns the group's token flow, origin first
171
- */
172
67
  const buildGroupTokenFlow = (hops, resolveToken) => {
173
68
  const originNode = hops[0].edge.from;
174
69
  const nodeFlow = hops.reduce((flow, hop) => {
@@ -180,49 +75,9 @@ const buildGroupTokenFlow = (hops, resolveToken) => {
180
75
  }, [originNode]);
181
76
  return nodeFlow.map((node) => toRouteTokenRef(node, resolveToken));
182
77
  };
183
- // ---------------------------------------------------------------------------
184
- // Section-level labelling
185
- // ---------------------------------------------------------------------------
186
- /**
187
- * A signature group's own hop-index list, resolved to its hops.
188
- *
189
- * @param group - one entry of `plan.signatures` — hop indices into `plan.hops`
190
- * @param plan - the plan those indices are drawn from
191
- * @returns the group's hops, first to last
192
- */
193
78
  const groupHops = (group, plan) => group.map((hopIndex) => plan.hops[hopIndex]);
194
- /**
195
- * Whether every hop of a group is carried by PulseX — the standalone-swap
196
- * shape `buildPulseXSwapSectionData` draws, labelled "Swap on PulseX"
197
- * regardless of the group's position in the plan.
198
- */
199
79
  const isPulseXOnlyGroup = (hops) => hops.every((hop) => hop.edge.carrier === 'PulseX');
200
- /** Whether a group's LAST hop is the omnibridge crossing — the fact that decides "Bridge to X". */
201
80
  const groupEndsWithBridgeHop = (hops) => isBridgeCarrier(hops[hops.length - 1].edge.carrier);
202
- /**
203
- * A signature section's label, generalizing the four distinct conventions
204
- * found across today's producers into one rule:
205
- *
206
- * - **"Swap on PulseX"** — every hop in the group is carried by PulseX.
207
- * Matches `buildPulseXSwapSectionData`'s hard-coded label.
208
- * - **"Bridge to {X}"** — the WHOLE plan is one signature (matches
209
- * `buildCandidateRouteData`, which always uses this label for its one
210
- * section, omnibridge leg or not), OR the group's last hop IS the
211
- * omnibridge crossing (matches `buildNearIntentsTwoStepRouteData`'s
212
- * second section AND `buildOutboundRouteData`'s first section).
213
- * - **"Complete at {X}"** — the group is the plan's LAST signature and does
214
- * NOT end on a bridge hop. Matches `buildOutboundRouteData`'s second
215
- * section, in BOTH its shapes (the entry-chain swap and the onward
216
- * aggregator hop) — the section label names where the journey finishes
217
- * regardless of which service carries the last leg.
218
- * - **"Send to {X}"** — every other case: a non-final, non-bridge-ending
219
- * group. Matches `buildNearIntentsTwoStepRouteData`'s first section.
220
- *
221
- * @param group - the group's hop indices
222
- * @param groupIndex - the group's position in `plan.signatures`
223
- * @param plan - the plan the group belongs to
224
- * @returns the section's label
225
- */
226
81
  const sectionLabel = (group, groupIndex, plan) => {
227
82
  const hops = groupHops(group, plan);
228
83
  if (isPulseXOnlyGroup(hops))
@@ -236,31 +91,8 @@ const sectionLabel = (group, groupIndex, plan) => {
236
91
  if (isFinalGroup) {
237
92
  return `Complete at ${(0, chain_names_js_1.chainDisplayName)(destinationChainId)}`;
238
93
  }
239
- return `Send to ${(0, chain_names_js_1.chainDisplayName)(destinationChainId)}`;
94
+ return (0, near_intents_route_js_1.sendOrSwapLabel)(hops[0].edge.from.chainId, destinationChainId);
240
95
  };
241
- // ---------------------------------------------------------------------------
242
- // Hop-level rendering
243
- // ---------------------------------------------------------------------------
244
- /**
245
- * A hop's display provider name.
246
- *
247
- * A bridge carrier resolves through {@link providerDisplayName} (the same
248
- * "PulseChain Bridge" / "Tokens Express Bridge" table every builder reads).
249
- * PulseX is named directly. An aggregator carrier resolves through the
250
- * PROVIDER REGISTRY's own label (`providerById(id).label`) — DELIBERATELY,
251
- * not through `providerDisplayName`, because that function's table only
252
- * ever held the two omnibridge providers. `buildNearIntentsTwoStepRouteData`
253
- * calls `providerDisplayName(candidate.quote.provider)` for its aggregator
254
- * hop today, and `'near-intents'` is not a key in that table, so it silently
255
- * falls back to the generic string `'Omnibridge'` — naming NEAR Intents
256
- * after the bridge it is not. That reads as an unintentional defect in the
257
- * existing code, not a convention worth reproducing; this function returns
258
- * `'NEAR Intents'` instead. See this module's test file and the report back
259
- * to the owner for the specific call-out.
260
- *
261
- * @param carrier - the hop's carrier
262
- * @returns the display name for that carrier
263
- */
264
96
  const hopProviderName = (carrier) => {
265
97
  if (isBridgeCarrier(carrier))
266
98
  return (0, producers_js_1.providerDisplayName)(carrier);
@@ -268,38 +100,8 @@ const hopProviderName = (carrier) => {
268
100
  return 'PulseX';
269
101
  if ((0, carrier_provider_key_js_1.carrierStepKind)(carrier) === 'wrap')
270
102
  return 'Wrap';
271
- // TOTAL OVER EVERY CARRIER. The engine plans hops for carriers the provider
272
- // registry does not hold (a local wrap, a host's own plug-in). Casting such
273
- // an id to a provider id and asking the throwing lookup crashed the whole
274
- // bridge form the first time a plan began with a wrap. A carrier with no
275
- // registered label is named by its own id.
276
103
  return (0, providers_js_1.findProviderById)(carrier)?.label ?? carrier;
277
104
  };
278
- /**
279
- * A hop's title, generalizing the title conventions of every existing
280
- * builder:
281
- *
282
- * - a bridge hop landing on PulseChain → `"Bridge to PulseChain"` (matches
283
- * `buildCandidateRouteData`'s omnibridge hop and
284
- * `buildNearIntentsTwoStepRouteData`'s crossing hop);
285
- * - a bridge hop landing elsewhere (leaving PulseChain) →
286
- * `providerDisplayName(carrier)`, e.g. `"PulseChain Bridge"` (matches
287
- * `buildOutboundRouteData`'s bridge hop exactly);
288
- * - a PulseX hop → `"Swap to {toSymbol}"` (matches `buildPulseXSwapSectionData`);
289
- * - an aggregator hop that is the plan's FINAL hop (nothing follows it,
290
- * so this signature is the one that COMPLETES the journey) →
291
- * `"Bridge to {toChainName}"` (matches `buildOutboundRouteData`'s onward
292
- * hop, which names the chain it finishes on even though an aggregator,
293
- * not the omnibridge, carries it);
294
- * - any other aggregator hop (something follows it) → `"Send to {toChainName}"`
295
- * (matches `buildNearIntentsTwoStepRouteData`'s aggregator hop).
296
- *
297
- * @param hop - the hop to title
298
- * @param hopIndex - the hop's index into `plan.hops`
299
- * @param plan - the plan the hop belongs to
300
- * @param resolveToken - resolves a graph node to its full display token
301
- * @returns the hop's title
302
- */
303
105
  const hopTitle = (hop, hopIndex, plan, resolveToken) => {
304
106
  const carrier = hop.edge.carrier;
305
107
  if (isBridgeCarrier(carrier)) {
@@ -310,33 +112,11 @@ const hopTitle = (hop, hopIndex, plan, resolveToken) => {
310
112
  if (carrier === 'PulseX') {
311
113
  return `Swap to ${resolveToken(hop.edge.to).symbol}`;
312
114
  }
313
- const destinationChainName = (0, chain_names_js_1.chainDisplayName)(hop.edge.to.chainId);
314
- return isFinalHopOfPlan(hopIndex, plan)
315
- ? `Bridge to ${destinationChainName}`
316
- : `Send to ${destinationChainName}`;
115
+ if (isFinalHopOfPlan(hopIndex, plan)) {
116
+ return `Bridge to ${(0, chain_names_js_1.chainDisplayName)(hop.edge.to.chainId)}`;
117
+ }
118
+ return (0, near_intents_route_js_1.sendOrSwapLabel)(hop.edge.from.chainId, hop.edge.to.chainId);
317
119
  };
318
- /**
319
- * A hop's conversion subtitle.
320
- *
321
- * The source data has two conventions for this string: the candidate-based
322
- * builders (`buildCandidateRouteData`'s omnibridge hop,
323
- * `buildNearIntentsTwoStepRouteData`'s both hops) write an ARROW-ONLY clause
324
- * (`"→ USDC"`), leaving the origin side implicit; the settings-based
325
- * builders (`buildOutboundRouteData`, `buildPulseXSwapSectionData`) write
326
- * the FULL two-sided clause (`"PLS → USDC"`). Both are correct prose for
327
- * their own hop; this function reproduces BOTH conventions rather than
328
- * picking one, keyed on the exact same signal {@link hopTitle} already uses
329
- * to distinguish a starting/intermediate leg from a completing one: arrow-only
330
- * for a bridge hop landing on PulseChain or an aggregator hop that is NOT the
331
- * plan's final hop (there is more journey to draw, so the origin side is
332
- * already on screen from the previous hop); full two-sided otherwise.
333
- *
334
- * @param hop - the hop to describe
335
- * @param hopIndex - the hop's index into `plan.hops`
336
- * @param plan - the plan the hop belongs to
337
- * @param resolveToken - resolves a graph node to its full display token
338
- * @returns the hop's conversion subtitle
339
- */
340
120
  const hopConversion = (hop, hopIndex, plan, resolveToken) => {
341
121
  const carrier = hop.edge.carrier;
342
122
  const toSymbol = resolveToken(hop.edge.to).symbol;
@@ -347,51 +127,10 @@ const hopConversion = (hop, hopIndex, plan, resolveToken) => {
347
127
  const fromSymbol = resolveToken(hop.edge.from).symbol;
348
128
  return `${fromSymbol} → ${toSymbol}`;
349
129
  };
350
- /**
351
- * Whether a hop runs AUTOMATICALLY inside a signature a PRECEDING hop
352
- * already signed — true exactly when the hop is not the first hop of its
353
- * own signature group, per `RouteHopData.automatic`'s own doc ("runs
354
- * automatically inside the SECTION's single signature").
355
- *
356
- * THIS MATCHES THREE OF THE FOUR TARGET SHAPES EXACTLY —
357
- * `buildCandidateRouteData` (an aggregator hop with index > 0 inside its
358
- * section, and the omnibridge hop only when an aggregator hop preceded it),
359
- * `buildNearIntentsTwoStepRouteData` (both of its hops are their own
360
- * section's first and only hop, so both are `false`), and
361
- * `buildPulseXSwapSectionData` (its one hop is always `false`, hard-coded
362
- * for the same reason).
363
- *
364
- * IT DOES NOT MATCH `buildOutboundRouteData`'s BRIDGE HOP, which hard-codes
365
- * `true` even though it is that section's own first and only hop — a SECOND,
366
- * DIFFERENT reading of "automatic" that describes the omnibridge's own
367
- * far-side auto-completion (no reader signature needed to FINISH the
368
- * crossing once signed) rather than whether the hop itself needed no
369
- * signature to START. `buildDirectRouteData` (a separate, non-plan family
370
- * this module does not otherwise touch) hard-codes the same reading. This
371
- * function follows the field's own documented meaning and the majority
372
- * convention (three of four builders) rather than that reading — see this
373
- * module's test file for the specific, deliberate divergence this produces
374
- * against `buildOutboundRouteData`'s bridge hop.
375
- *
376
- * @param hopIndex - the hop's index into `plan.hops`
377
- * @param plan - the plan the hop belongs to
378
- * @returns whether the hop is automatic
379
- */
380
130
  const hopIsAutomatic = (hopIndex, plan) => {
381
131
  const group = plan.signatures.find((candidateGroup) => candidateGroup.includes(hopIndex));
382
132
  return group !== undefined && group[0] !== hopIndex;
383
133
  };
384
- /**
385
- * Builds the raw `RouteHopData` for one planned hop.
386
- *
387
- * @param hop - the hop to render
388
- * @param hopIndex - the hop's index into `plan.hops`
389
- * @param plan - the plan the hop belongs to
390
- * @param resolveToken - resolves a graph node to its full display token
391
- * @param feeUsdValue - the hop's own share of its section's cost, or null
392
- * @param gas - the hop's gas split, or null when the producer states none
393
- * @returns the raw hop data
394
- */
395
134
  const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue, gas) => ({
396
135
  providerName: hopProviderName(hop.edge.carrier),
397
136
  providerKey: (0, carrier_provider_key_js_1.carrierProviderKey)(hop.edge.carrier),
@@ -400,23 +139,10 @@ const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue, gas) =>
400
139
  conversion: hopConversion(hop, hopIndex, plan, resolveToken),
401
140
  fromToken: toRouteTokenRef(hop.edge.from, resolveToken),
402
141
  toToken: toRouteTokenRef(hop.edge.to, resolveToken),
403
- // The per-hop dollar figure is a caller-supplied input (see this module's
404
- // head doc); the caller passes it in, never derived here.
405
142
  feeUsdValue,
406
143
  ...(gas === null ? {} : gas),
407
144
  automatic: hopIsAutomatic(hopIndex, plan),
408
145
  });
409
- /**
410
- * The per-hop shares a section's pricing names, checked against the group
411
- * they describe.
412
- *
413
- * @param pricing - the section's caller-supplied pricing
414
- * @param group - the signature group's hop indices
415
- * @param groupIndex - the group's index, for the error message
416
- * @returns one share per hop, null where none was given
417
- * @throws when the caller passed a share list of the wrong length — a share
418
- * matched to the wrong hop is a figure on the wrong leg
419
- */
420
146
  const groupHopFeeUsdValues = (pricing, group, groupIndex) => {
421
147
  const shares = pricing.hopFeeUsdValues;
422
148
  if (shares === undefined)
@@ -426,54 +152,14 @@ const groupHopFeeUsdValues = (pricing, group, groupIndex) => {
426
152
  }
427
153
  return shares;
428
154
  };
429
- // ---------------------------------------------------------------------------
430
- // Route-level derivations — candidate.ts's promised derivation, made real
431
- // ---------------------------------------------------------------------------
432
- /**
433
- * The plan's omnibridge provider, or null when the plan crosses no bridge
434
- * hop at all — exactly the derivation `RouteCandidate.plan`'s own TSDoc
435
- * (`../candidate.ts`) promises: "that same hop's `carrier`... the `to` node's
436
- * `chainId` of the plan's LAST hop whose carrier is `'pulsechain'` or
437
- * `'tokensex'`." Reading the LAST such hop, not the first, matters for a
438
- * plan with two bridge crossings (an outbound leg out of PulseChain followed
439
- * later by a separate inbound one) — not a shape any of today's four
440
- * builders draws, but one the general search can.
441
- *
442
- * @param plan - the plan to derive from
443
- * @returns the omnibridge provider, or null
444
- */
445
155
  const deriveOmnibridgeProviderFromPlan = (plan) => {
446
156
  const bridgeHop = [...plan.hops].reverse().find((hop) => isBridgeCarrier(hop.edge.carrier));
447
157
  if (bridgeHop === undefined)
448
158
  return null;
449
- // `Array.prototype.find`'s own predicate narrowing does not flow back out
450
- // of the callback into the found element's type — only `isBridgeCarrier`
451
- // called again, directly on the plain `carrier` binding, narrows it here.
452
- // Re-declaring the return type as the real `Provider | null` (rather than
453
- // the wide `string | null` this used to widen to) is what surfaces a
454
- // mismatch at the source the moment a caller narrows further downstream,
455
- // instead of a silent `string` riding all the way to `providerDisplayName`.
456
159
  const { carrier } = bridgeHop.edge;
457
160
  return isBridgeCarrier(carrier) ? carrier : null;
458
161
  };
459
162
  exports.deriveOmnibridgeProviderFromPlan = deriveOmnibridgeProviderFromPlan;
460
- /**
461
- * The plan's entry chain id — the chain the plan's FIRST hop lands on, when
462
- * that hop is carried by an aggregator (LI.FI, Relay or NEAR Intents); null
463
- * when the plan's first hop IS the omnibridge itself (a direct, same-chain
464
- * crossing — `buildDirectCrossingCandidate`'s shape) or is a PulseX swap.
465
- *
466
- * MATCHES `RouteCandidate.entryChainId`'S EXISTING MEANING throughout the
467
- * candidate-based builders — "the chain the omnibridge leg starts on" — for
468
- * every candidate that carries one, including a candidate whose aggregator
469
- * reaches its OWN destination with no further bridge hop at all (today's
470
- * `makeCandidate` test fixtures set `entryChainId` to the destination chain
471
- * in exactly that case, because it is still where the aggregator's own leg
472
- * lands).
473
- *
474
- * @param plan - the plan to derive from
475
- * @returns the entry chain id, or null for a direct crossing or a swap-only plan
476
- */
477
163
  const deriveEntryChainIdFromPlan = (plan) => {
478
164
  const firstHop = plan.hops[0];
479
165
  if (!firstHop)
@@ -481,45 +167,11 @@ const deriveEntryChainIdFromPlan = (plan) => {
481
167
  return isAggregatorCarrier(firstHop.edge.carrier) ? firstHop.edge.to.chainId : null;
482
168
  };
483
169
  exports.deriveEntryChainIdFromPlan = deriveEntryChainIdFromPlan;
484
- /**
485
- * The decimal chain ids every INTERMEDIATE hop of the plan passes through,
486
- * origin and destination excluded — `RouteData.viaChainIds`'s own contract.
487
- * Undefined (never an empty array) when the plan has nothing to report, per
488
- * that field's own "ABSENT... on any route with no intermediate chain" doc.
489
- *
490
- * @param plan - the plan to derive from
491
- * @returns the via chain ids in hop order, or undefined
492
- */
493
170
  const deriveViaChainIdsFromPlan = (plan) => {
494
171
  const intermediateChainIds = plan.hops.slice(0, -1).map((hop) => hop.edge.to.chainId);
495
172
  return intermediateChainIds.length > 0 ? intermediateChainIds : undefined;
496
173
  };
497
174
  exports.deriveViaChainIdsFromPlan = deriveViaChainIdsFromPlan;
498
- /**
499
- * The plan's `RouteKind`, per the route-search design's section 1: `'direct'`
500
- * when signature 1 IS the omnibridge crossing itself (no aggregator prefix —
501
- * `buildDirectCrossingCandidate`'s shape, and `buildOutboundRouteData`'s
502
- * shape), `'conversion'` when the LAST hop is a PulseX swap
503
- * (`buildConversionRouteData`'s shape), `'aggregated'` otherwise (an
504
- * aggregator carries signature 1, fused with a bridge hop or not —
505
- * `buildNearIntentsTwoStepRouteData`'s shape, and the general
506
- * aggregator-fused shape).
507
- *
508
- * DIVERGES FROM TODAY'S LITERAL OUTPUT FOR ONE CASE, DELIBERATELY: a
509
- * same-chain candidate built by `buildDirectCrossingCandidate` and rendered
510
- * through `buildCandidateRouteData` today always comes back `kind:
511
- * 'aggregated'`, because that builder hard-codes `'aggregated'`
512
- * unconditionally — an artifact of `RouteCandidate.entryChainId` being
513
- * non-nullable, so `buildCandidateRouteData` always assumes an
514
- * aggregator-entry shape even for a candidate with no aggregator leg at all.
515
- * This function instead answers `'direct'` for that exact plan, matching how
516
- * `buildDirectRouteData` (the OTHER, non-candidate 'direct' family) already
517
- * represents the identical real-world same-chain crossing. See this
518
- * module's test file for the specific case.
519
- *
520
- * @param plan - the plan to derive from
521
- * @returns the route's kind
522
- */
523
175
  const derivePlanKind = (plan) => {
524
176
  const lastHop = plan.hops[plan.hops.length - 1];
525
177
  if (lastHop && lastHop.edge.carrier === 'PulseX')
@@ -530,24 +182,6 @@ const derivePlanKind = (plan) => {
530
182
  return 'aggregated';
531
183
  };
532
184
  exports.derivePlanKind = derivePlanKind;
533
- /**
534
- * Narrows a plan's {@link HopCarrier} (a plain string, since `HopEdge.carrier`
535
- * moved off the closed `ProviderKey` union) down to `RouteData.headlineProviderKey`'s
536
- * own closed {@link ProviderKey} type, for the display/logo lookup tables that
537
- * type exists to protect (`resolveProviderLogoUri` / `resolveProviderDisplayName`
538
- * — see `ProviderKey`'s own doc in `../../providers.js`).
539
- *
540
- * A GIBS PLAN NEVER CARRIES ANYTHING ELSE. Every carrier a `RoutePlan` this
541
- * module renders can name is one gibs itself registered — the three built-in
542
- * aggregators, the omnibridge, PulseX, or a wrap step, which
543
- * {@link deriveHeadlineProviderKeyFromPlan} skips (`routing/carriers/`,
544
- * `packages/ui/src/lib/state/useRouteSearch.ts`) — so this throws rather than
545
- * guessing when it sees anything else: a plan naming an unrecognized carrier
546
- * is a bug in whatever built it, not a display gap to paper over silently.
547
- *
548
- * @param carrier - the plan's headline carrier
549
- * @returns the same value, typed as `ProviderKey`
550
- */
551
185
  const toHeadlineProviderKey = (carrier) => {
552
186
  if (isBridgeCarrier(carrier))
553
187
  return carrier;
@@ -557,49 +191,9 @@ const toHeadlineProviderKey = (carrier) => {
557
191
  return carrier;
558
192
  throw new Error(`toHeadlineProviderKey: unrecognized carrier "${carrier}"`);
559
193
  };
560
- /**
561
- * The plan's headline provider — the omnibridge crossing it out when it
562
- * crosses one, or whichever carrier performs signature 1 when it does not.
563
- * Matches every existing builder's own headline choice: the omnibridge when
564
- * present (`buildCandidateRouteData`, `buildNearIntentsTwoStepRouteData`,
565
- * `buildOutboundRouteData`), the aggregator's own key as the fallback
566
- * (`buildCandidateRouteData`'s no-omnibridge branch).
567
- *
568
- * @param plan - the plan to derive from
569
- * @returns the headline provider key
570
- */
571
194
  const deriveHeadlineProviderKeyFromPlan = (plan) => toHeadlineProviderKey((0, exports.deriveOmnibridgeProviderFromPlan)(plan) ?? firstHeadlineCarrier(plan));
572
195
  exports.deriveHeadlineProviderKeyFromPlan = deriveHeadlineProviderKeyFromPlan;
573
- /**
574
- * The carrier of the first hop that can headline a route. A `wrap` hop
575
- * (native coin to its wrapped token, or back) is a step inside a route, never
576
- * the route itself, so it is skipped. A plan made only of wraps headlines as
577
- * PulseX, the same key the same-chain swap builder gives that route
578
- * (`buildSameChainSwapRouteData`).
579
- *
580
- * @param plan - the plan to read
581
- * @returns the carrier to headline
582
- */
583
196
  const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !== carrier_provider_key_js_1.WRAP_CARRIER_ID)?.edge.carrier ?? 'PulseX';
584
- /**
585
- * The anchor a two-signature plan leaves the reader holding between its
586
- * first and second groups — `RouteCandidate.plan`'s own TSDoc: "the anchor
587
- * the reader is left holding between signatures... taken from the first hop
588
- * of the second group." Reuses `describeDeferredCrossing`
589
- * (`../../near-intents-route.js`) rather than re-validating the same
590
- * "positive amount, resolved token" rule a second time.
591
- *
592
- * Null when the plan has only one signature group (there is no deferred
593
- * crossing to describe) or the plan is malformed (a signature group naming
594
- * hop indices the plan does not have).
595
- *
596
- * @param plan - the plan to derive from
597
- * @param resolveToken - resolves a graph node to its full display token
598
- * @param anchorUsdValue - the anchor's own carrier's valuation of the
599
- * landing, exactly as `DeferredCrossing.anchorUsdValue` expects — supplied
600
- * by the caller, since a plan carries no United States dollar figures.
601
- * @returns the deferred crossing, or null
602
- */
603
197
  const deriveDeferredCrossingFromPlan = (plan, resolveToken, anchorUsdValue) => {
604
198
  const secondGroup = plan.signatures[1];
605
199
  const firstGroup = plan.signatures[0];
@@ -620,26 +214,7 @@ const deriveDeferredCrossingFromPlan = (plan, resolveToken, anchorUsdValue) => {
620
214
  });
621
215
  };
622
216
  exports.deriveDeferredCrossingFromPlan = deriveDeferredCrossingFromPlan;
623
- /**
624
- * Whether `hop`'s own figure carries no value at all — an `EstimateBasis`
625
- * (`graph.ts`) of `'unpriced'`: a bare decimal rescale with nothing behind
626
- * it (see that type's own doc). {@link sectionLandingAmount} is what a
627
- * `true` result means for the section's (and the route's) headline figure:
628
- * it is left OUT of `amount` entirely, never shown at all.
629
- */
630
217
  const hopOutcomeIsUnpriced = (hop) => hop.outcome.kind === 'estimate' && hop.outcome.basis === 'unpriced';
631
- /**
632
- * What `hop` reports for `RouteLandingData.estimateBasis` /
633
- * `RouteOutputData.estimateBasis` (`../route-data.ts`): the hop's own
634
- * `EstimateBasis` string when it is a composed/probed figure, `null` for a
635
- * bound quote — and `null` for `'unpriced'` too, per that field's own doc
636
- * ("'unpriced' never reaches here"), since {@link sectionLandingAmount}
637
- * already leaves an unpriced hop's `amount` out entirely rather than
638
- * labelling it.
639
- *
640
- * @param hop - the hop to read
641
- * @returns the estimate basis to report, or null
642
- */
643
218
  const hopEstimateBasis = (hop) => {
644
219
  if (hop.outcome.kind !== 'estimate')
645
220
  return null;
@@ -647,17 +222,6 @@ const hopEstimateBasis = (hop) => {
647
222
  return null;
648
223
  return hop.outcome.basis;
649
224
  };
650
- /**
651
- * A section's landing amount: the caller's explicit override when given
652
- * ({@link PlanSectionPricing.landingAmountOverride}'s own doc), else `null`
653
- * when the section's last hop is `'unpriced'` (see {@link hopOutcomeIsUnpriced}
654
- * — a figure with no value behind it is never shown, marked or not), else
655
- * the hop's own composed delivered amount.
656
- *
657
- * @param pricing - the section's caller-supplied pricing
658
- * @param lastHop - the section's last hop
659
- * @returns the amount to land the section on, or null
660
- */
661
225
  const sectionLandingAmount = (pricing, lastHop) => {
662
226
  if ('landingAmountOverride' in pricing)
663
227
  return pricing.landingAmountOverride ?? null;
@@ -665,26 +229,6 @@ const sectionLandingAmount = (pricing, lastHop) => {
665
229
  return null;
666
230
  return lastHop.outcome.deliveredAmount;
667
231
  };
668
- /**
669
- * Turns a priced {@link RoutePlan} into a raw {@link RouteData} — one section
670
- * per signature group, `deferred` on every group after the first, each
671
- * section's hops and token flow drawn structurally from the plan, and the
672
- * dollar figures overlaid from `inputs.sections`. See this module's head doc
673
- * for why the dollar figures are caller-supplied rather than derived here.
674
- *
675
- * NEVER SHOWS AN UNPRICED HEADLINE FIGURE, AND MARKS EVERY OTHER ESTIMATE AS
676
- * ONE — PHASE 3. A section (and the route's own `output`) whose last hop is
677
- * grounded on `EstimateBasis` `'unpriced'` lands on `amount: null`, whatever
678
- * the caller's own pricing says, per {@link sectionLandingAmount}'s own doc.
679
- * Every OTHER estimate (`'probe-rate'`, `'priced-conversion'`) still shows
680
- * its figure, with `estimateBasis` set to that basis so a reader is never
681
- * shown a composed/probed number as if it were a confirmed quote — see
682
- * `RouteOutputData.estimateBasis`'s own doc (`../route-data.ts`) for the
683
- * full rule this reads off {@link hopEstimateBasis}.
684
- *
685
- * @param inputs - the plan, a token resolver, an id, and per-section pricing
686
- * @returns the raw route data
687
- */
688
232
  const buildRouteDataFromPlan = (inputs) => {
689
233
  const { plan, resolveToken, routeId, sections, totalCostUsdValue, netValueUsd, etaMinutes } = inputs;
690
234
  if (plan.hops.length === 0) {
@@ -756,26 +300,12 @@ const buildRouteDataFromPlan = (inputs) => {
756
300
  }),
757
301
  netValueUsd,
758
302
  etaMinutes,
759
- // The single legacy `viaChainId` a not-yet-migrated consumer may still
760
- // read (`RouteOption`, `journeyPlan.ts`'s current `pathKeyFor` — see the
761
- // route-search design's section 1) — the first intermediate chain, when
762
- // there is one. `buildOutboundRouteData` sets this to the chain its one
763
- // bridge hop lands on, which is exactly this plan's first via chain.
764
303
  viaChainId: (0, exports.deriveViaChainIdsFromPlan)(plan)?.[0],
765
304
  pathKey: plan.pathKey,
766
305
  viaChainIds: (0, exports.deriveViaChainIdsFromPlan)(plan),
767
306
  };
768
307
  };
769
308
  exports.buildRouteDataFromPlan = buildRouteDataFromPlan;
770
- /**
771
- * Turns a priced {@link RoutePlan} into a {@link RouteCandidate}, filling
772
- * `plan` and deriving `deferredCrossing`, `entryChainId` and
773
- * `omnibridgeProvider` from it exactly as `RouteCandidate.plan`'s own TSDoc
774
- * (`../candidate.ts`) promises.
775
- *
776
- * @param inputs - the plan, a token resolver, an id, and the caller's pricing
777
- * @returns the route candidate
778
- */
779
309
  const buildRouteCandidateFromPlan = (inputs) => {
780
310
  const { plan, resolveToken, id, inputAmount, paidUsdValue, lastHopDeliveredUsdValue, destinationUsdPrice, omnibridgeFeeAmount = 0n, anchorUsdValue = null, requestedAmount, fees = [], executionDurationSeconds = 0, } = inputs;
781
311
  const firstHop = plan.hops[0];
@@ -803,23 +333,6 @@ const buildRouteCandidateFromPlan = (inputs) => {
803
333
  executionDurationSeconds,
804
334
  steps: [],
805
335
  fees: [...fees],
806
- // A PRICED-BUT-NOT-RESERVED deposit (`UnreservedDeposit`,
807
- // `../../origin-deposit.js`) — the variant that EXISTS for exactly this
808
- // case: "a route on display... priced with a dry quote, which carries
809
- // figures and no address" (that type's own doc). This candidate exists
810
- // for scoring and for `RouteData`/`RouteCandidate` shape assembly, never
811
- // for a real signature, so it has nothing to reserve.
812
- //
813
- // THIS IS NOT A STAND-IN FOR THE ORIGIN'S REAL ECOSYSTEM. An earlier
814
- // version of this line hard-coded `{ ecosystem: 'evm', ... }`
815
- // unconditionally, which is wrong for any non-Ethereum-Virtual-Machine
816
- // origin a general-search plan can now represent (a Zcash or Solana
817
- // first hop, for example) — a deposit's ecosystem must come from the
818
- // chain, never asserted past it. `UnreservedDeposit` sidesteps the
819
- // question honestly instead of answering it wrongly: it is
820
- // discriminated by `kind`, not by `ecosystem`, and carries no
821
- // ecosystem-specific fields at all, so there is no wrong shape to pick
822
- // regardless of which ecosystem `firstHop.edge.from` names.
823
336
  originDeposit: { kind: 'unreserved', chainId: firstHop.edge.from.chainId },
824
337
  };
825
338
  return {
@@ -838,20 +351,6 @@ const buildRouteCandidateFromPlan = (inputs) => {
838
351
  };
839
352
  };
840
353
  exports.buildRouteCandidateFromPlan = buildRouteCandidateFromPlan;
841
- /**
842
- * Turns a priced {@link RoutePlan} into {@link RouteScoreInputs} — reusing
843
- * `candidateDeliveredUsd`, `candidatePaidUsd` and `candidateOutsideFeesUsd`
844
- * (`../producers.js`) against the SAME candidate
845
- * {@link buildRouteCandidateFromPlan} builds, exactly as `RouteScoreInputs`'
846
- * own doc in `score.ts` asks: "generalized to the plan's LAST hop and FIRST
847
- * hop respectively... import and reuse them; do not write a second
848
- * valuation."
849
- *
850
- * @param valuation - the plan, a token resolver, an id, and the caller's pricing
851
- * @param gaslessIntermediateChainCount - intermediate chains the connected
852
- * account holds no gas on — supplied by the caller, never computed here
853
- * @returns the plan's score inputs
854
- */
855
354
  const buildRouteScoreInputsFromPlan = (valuation, gaslessIntermediateChainCount) => {
856
355
  const candidate = (0, exports.buildRouteCandidateFromPlan)(valuation);
857
356
  return {
@@ -861,10 +360,6 @@ const buildRouteScoreInputsFromPlan = (valuation, gaslessIntermediateChainCount)
861
360
  legClaimedDurationsSeconds: valuation.legClaimedDurationsSeconds,
862
361
  readerActionCount: valuation.plan.signatures.length,
863
362
  gaslessIntermediateChainCount,
864
- // PHASE 3 — read directly off the plan's own hops, never re-derived: see
865
- // `planComposedBasis`'s own doc (`graph.ts`) for why this always agrees
866
- // with `planIsRankable`, and `RouteScoreInputs.basis`'s own doc for what
867
- // `scoreRoutePlan` does with it.
868
363
  basis: (0, graph_js_1.planComposedBasis)(valuation.plan.hops),
869
364
  };
870
365
  };