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