@gibs/bridge-sdk 1.13.0 → 1.17.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 +244 -18
- package/abis/package.json +6 -0
- package/bridge-limits/package.json +6 -0
- package/chain-info/package.json +6 -0
- package/chain-names/package.json +6 -0
- package/chains/package.json +6 -0
- package/claims/package.json +6 -0
- package/config/package.json +6 -0
- package/consolidated-quote/package.json +6 -0
- package/destination-calldata/package.json +6 -0
- package/dist/chain-info.d.ts +1 -1
- package/dist/chain-info.js +11 -12
- package/dist/chains.d.ts +25 -0
- package/dist/chains.js +55 -20
- package/dist/cjs/abis.d.ts +1 -0
- package/dist/cjs/abis.js +19 -0
- package/dist/cjs/bridge-limits.d.ts +103 -0
- package/dist/cjs/bridge-limits.js +89 -0
- package/dist/cjs/chain-info.d.ts +169 -0
- package/dist/cjs/chain-info.js +452 -0
- package/dist/cjs/chain-names.d.ts +160 -0
- package/dist/cjs/chain-names.js +207 -0
- package/dist/cjs/chains.d.ts +90 -0
- package/dist/cjs/chains.js +357 -0
- package/dist/cjs/claims.d.ts +198 -0
- package/dist/cjs/claims.js +182 -0
- package/dist/cjs/config.d.ts +240 -0
- package/dist/cjs/config.js +537 -0
- package/dist/cjs/consolidated-quote.d.ts +648 -0
- package/dist/cjs/consolidated-quote.js +1048 -0
- package/dist/cjs/destination-calldata.d.ts +118 -0
- package/dist/cjs/destination-calldata.js +156 -0
- package/dist/cjs/ecosystems.d.ts +217 -0
- package/dist/cjs/ecosystems.js +298 -0
- package/dist/cjs/execution.d.ts +346 -0
- package/dist/cjs/execution.js +468 -0
- package/dist/cjs/fee-type.d.ts +10 -0
- package/dist/cjs/fee-type.js +10 -0
- package/dist/cjs/hyperliquid-execution.d.ts +152 -0
- package/dist/cjs/hyperliquid-execution.js +175 -0
- package/dist/cjs/image-links.d.ts +117 -0
- package/dist/cjs/image-links.js +153 -0
- package/dist/cjs/index.d.ts +23 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/lifi-execution-guard.d.ts +210 -0
- package/dist/cjs/lifi-execution-guard.js +409 -0
- package/dist/cjs/near-intents-conformance.d.ts +85 -0
- package/dist/cjs/near-intents-conformance.js +337 -0
- package/dist/cjs/near-intents-route.d.ts +47 -0
- package/dist/cjs/near-intents-route.js +303 -0
- package/dist/cjs/near-intents.d.ts +524 -0
- package/dist/cjs/near-intents.js +1071 -0
- package/dist/cjs/origin-deposit.d.ts +326 -0
- package/dist/cjs/origin-deposit.js +82 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pre-quote.d.ts +198 -0
- package/dist/cjs/pre-quote.js +180 -0
- package/dist/cjs/prices.d.ts +107 -0
- package/dist/cjs/prices.js +172 -0
- package/dist/cjs/providers.d.ts +238 -0
- package/dist/cjs/providers.js +171 -0
- package/dist/cjs/quote-fees.d.ts +102 -0
- package/dist/cjs/quote-fees.js +201 -0
- package/dist/cjs/referral.d.ts +85 -0
- package/dist/cjs/referral.js +85 -0
- package/dist/cjs/relay-currencies.d.ts +47 -0
- package/dist/cjs/relay-currencies.js +95 -0
- package/dist/cjs/relay-depository.d.ts +2 -0
- package/dist/cjs/relay-depository.js +85 -0
- package/dist/cjs/relay-quote.d.ts +301 -0
- package/dist/cjs/relay-quote.js +996 -0
- package/dist/cjs/route-steps.d.ts +138 -0
- package/dist/cjs/route-steps.js +310 -0
- package/dist/cjs/routing/amounts.d.ts +80 -0
- package/dist/cjs/routing/amounts.js +126 -0
- package/dist/cjs/routing/candidate-route.d.ts +43 -0
- package/dist/cjs/routing/candidate-route.js +50 -0
- package/dist/cjs/routing/candidate.d.ts +237 -0
- package/dist/cjs/routing/candidate.js +100 -0
- package/dist/cjs/routing/carriers/ask-answer.d.ts +77 -0
- package/dist/cjs/routing/carriers/ask-answer.js +2 -0
- package/dist/cjs/routing/carriers/omnibridge.d.ts +137 -0
- package/dist/cjs/routing/carriers/omnibridge.js +459 -0
- package/dist/cjs/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/cjs/routing/carriers/pulsex-paths.js +97 -0
- package/dist/cjs/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/cjs/routing/carriers/pulsex-split.js +193 -0
- package/dist/cjs/routing/carriers/pulsex.d.ts +159 -0
- package/dist/cjs/routing/carriers/pulsex.js +565 -0
- package/dist/cjs/routing/carriers/wrap.d.ts +25 -0
- package/dist/cjs/routing/carriers/wrap.js +119 -0
- package/dist/cjs/routing/composed-route-quality.d.ts +125 -0
- package/dist/cjs/routing/composed-route-quality.js +144 -0
- package/dist/cjs/routing/entry-chain-anchor.d.ts +88 -0
- package/dist/cjs/routing/entry-chain-anchor.js +59 -0
- package/dist/cjs/routing/format.d.ts +31 -0
- package/dist/cjs/routing/format.js +64 -0
- package/dist/cjs/routing/index.d.ts +38 -0
- package/dist/cjs/routing/index.js +134 -0
- package/dist/cjs/routing/producers.d.ts +770 -0
- package/dist/cjs/routing/producers.js +1617 -0
- package/dist/cjs/routing/providers.d.ts +31 -0
- package/dist/cjs/routing/providers.js +48 -0
- package/dist/cjs/routing/route-data.d.ts +355 -0
- package/dist/cjs/routing/route-data.js +2 -0
- package/dist/cjs/routing/search/compose.d.ts +168 -0
- package/dist/cjs/routing/search/compose.js +104 -0
- package/dist/cjs/routing/search/graph.d.ts +668 -0
- package/dist/cjs/routing/search/graph.js +461 -0
- package/dist/cjs/routing/search/plan-route-data.d.ts +269 -0
- package/dist/cjs/routing/search/plan-route-data.js +848 -0
- package/dist/cjs/routing/search/score.d.ts +356 -0
- package/dist/cjs/routing/search/score.js +445 -0
- package/dist/cjs/routing/swap-transactions.d.ts +91 -0
- package/dist/cjs/routing/swap-transactions.js +131 -0
- package/dist/cjs/routing/usd-value.d.ts +17 -0
- package/dist/cjs/routing/usd-value.js +29 -0
- package/dist/cjs/settings.d.ts +785 -0
- package/dist/cjs/settings.js +1074 -0
- package/dist/cjs/ton-connect.d.ts +98 -0
- package/dist/cjs/ton-connect.js +206 -0
- package/dist/cjs/ton-execution.d.ts +7 -0
- package/dist/cjs/ton-execution.js +23 -0
- package/dist/cjs/ton.d.ts +37 -0
- package/dist/cjs/ton.js +83 -0
- package/dist/cjs/tron-execution.d.ts +146 -0
- package/dist/cjs/tron-execution.js +248 -0
- package/dist/cjs/types.d.ts +51 -0
- package/dist/cjs/types.js +2 -0
- package/dist/claims.d.ts +198 -0
- package/dist/claims.js +140 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +39 -5
- package/dist/consolidated-quote.d.ts +109 -15
- package/dist/consolidated-quote.js +310 -52
- package/dist/execution.d.ts +346 -0
- package/dist/execution.js +428 -0
- package/dist/hyperliquid-execution.js +2 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/lifi-execution-guard.d.ts +13 -2
- package/dist/lifi-execution-guard.js +4 -0
- package/dist/near-intents.js +2 -3
- package/dist/pre-quote.d.ts +6 -6
- package/dist/pre-quote.js +10 -14
- package/dist/providers.d.ts +17 -0
- package/dist/providers.js +6 -0
- package/dist/referral.d.ts +12 -4
- package/dist/referral.js +12 -4
- package/dist/relay-depository.d.ts +2 -0
- package/dist/relay-depository.js +82 -0
- package/dist/relay-quote.js +2 -3
- package/dist/routing/candidate.d.ts +3 -3
- package/dist/routing/candidate.js +3 -3
- package/dist/routing/carriers/ask-answer.d.ts +19 -0
- package/dist/routing/carriers/omnibridge.js +23 -21
- package/dist/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/routing/carriers/pulsex-paths.js +92 -0
- package/dist/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/routing/carriers/pulsex-split.js +185 -0
- package/dist/routing/carriers/pulsex.d.ts +69 -21
- package/dist/routing/carriers/pulsex.js +184 -65
- package/dist/routing/composed-route-quality.d.ts +28 -6
- package/dist/routing/composed-route-quality.js +36 -5
- package/dist/routing/entry-chain-anchor.d.ts +1 -1
- package/dist/routing/entry-chain-anchor.js +1 -1
- package/dist/routing/index.d.ts +7 -3
- package/dist/routing/index.js +7 -3
- package/dist/routing/producers.js +1 -1
- package/dist/routing/providers.d.ts +5 -0
- package/dist/routing/providers.js +5 -0
- package/dist/routing/search/graph.d.ts +29 -0
- package/dist/routing/search/plan-route-data.d.ts +25 -0
- package/dist/routing/search/plan-route-data.js +43 -7
- package/dist/routing/search/score.d.ts +33 -1
- package/dist/routing/search/score.js +41 -6
- package/dist/routing/swap-transactions.d.ts +91 -0
- package/dist/routing/swap-transactions.js +125 -0
- package/dist/settings.d.ts +94 -1
- package/dist/settings.js +95 -3
- package/dist/ton-connect.d.ts +98 -0
- package/dist/ton-connect.js +200 -0
- package/dist/ton-execution.d.ts +4 -114
- package/dist/ton-execution.js +4 -244
- package/dist/ton.d.ts +37 -0
- package/dist/ton.js +65 -0
- package/docs/examples/example-a-ether-to-pulse.ts +300 -0
- package/docs/examples/example-b-token-pulsechain-to-ethereum.ts +270 -0
- package/docs/examples/shared.ts +261 -0
- package/docs/examples/tsconfig.json +18 -0
- package/docs/integration-guide.md +617 -0
- package/ecosystems/package.json +6 -0
- package/execution/package.json +6 -0
- package/fee-type/package.json +6 -0
- package/hyperliquid-execution/package.json +6 -0
- package/image-links/package.json +6 -0
- package/lifi-execution-guard/package.json +6 -0
- package/llms.txt +234 -0
- package/near-intents/package.json +6 -0
- package/near-intents-route/package.json +6 -0
- package/origin-deposit/package.json +6 -0
- package/package.json +818 -10
- package/pre-quote/package.json +6 -0
- package/prices/package.json +6 -0
- package/providers/package.json +6 -0
- package/quote-fees/package.json +6 -0
- package/referral/package.json +6 -0
- package/relay-currencies/package.json +6 -0
- package/relay-quote/package.json +6 -0
- package/route-steps/package.json +6 -0
- package/routing/package.json +6 -0
- package/settings/package.json +6 -0
- package/ton/package.json +6 -0
- package/ton-connect/package.json +6 -0
- package/ton-execution/package.json +6 -0
- package/tron-execution/package.json +6 -0
- package/types/package.json +6 -0
|
@@ -47,6 +47,26 @@ export declare const COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = 3;
|
|
|
47
47
|
* of the reader's money.
|
|
48
48
|
*/
|
|
49
49
|
export declare const COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = 5;
|
|
50
|
+
/**
|
|
51
|
+
* The end-to-end value GAIN above which a composed route is withdrawn, in
|
|
52
|
+
* percent of what the reader pays.
|
|
53
|
+
*
|
|
54
|
+
* A ROUTE CANNOT MAKE MONEY. Every leg charges a fee, so a route that
|
|
55
|
+
* delivers more value than it takes says one of its figures is wrong. Only a
|
|
56
|
+
* disagreement between the two price feeds can make a gain look real, and the
|
|
57
|
+
* value-loss ceiling already trusts those feeds to within the same five
|
|
58
|
+
* percent. So the bound is symmetric with
|
|
59
|
+
* {@link COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT}.
|
|
60
|
+
*
|
|
61
|
+
* THE DEFECT THIS STOPS. On 2026-10-03 the engine served 0.0002 BNB to HEX at
|
|
62
|
+
* 495,256 HEX, about seven thousand times the rate of the next tier. The
|
|
63
|
+
* value-loss test read that as a loss of minus seven hundred thousand percent
|
|
64
|
+
* and passed it. A PulseX hop rescaled from an upstream live answer keeps the
|
|
65
|
+
* price impact measured at its original, smaller input
|
|
66
|
+
* (`recomposePath` in `@gibs/quotes`'s `search/waves.ts`), so the impact test
|
|
67
|
+
* cannot see an inflated amount either. Only the two ends of the journey can.
|
|
68
|
+
*/
|
|
69
|
+
export declare const COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = 5;
|
|
50
70
|
/**
|
|
51
71
|
* The verdict on one composed route.
|
|
52
72
|
*
|
|
@@ -62,18 +82,20 @@ export type ComposedRouteVerdict = {
|
|
|
62
82
|
readonly missing: 'price-impact' | 'route-value';
|
|
63
83
|
} | {
|
|
64
84
|
readonly state: 'rejected';
|
|
65
|
-
readonly reason: 'price-impact' | 'value-loss';
|
|
85
|
+
readonly reason: 'price-impact' | 'value-loss' | 'value-gain';
|
|
66
86
|
/** The measured figure, in percent, that failed its ceiling. */
|
|
67
87
|
readonly measuredPercent: number;
|
|
68
88
|
/** The ceiling it failed. */
|
|
69
89
|
readonly ceilingPercent: number;
|
|
70
90
|
};
|
|
71
91
|
/**
|
|
72
|
-
* Judges a composed route against
|
|
92
|
+
* Judges a composed route against its three ceilings.
|
|
73
93
|
*
|
|
74
|
-
* Checks price impact first
|
|
75
|
-
* specific leg at fault while value
|
|
76
|
-
* wrong
|
|
94
|
+
* Checks price impact first, then value loss, then value gain. Impact names
|
|
95
|
+
* the specific leg at fault, while the two value tests only say the journey as
|
|
96
|
+
* a whole went wrong, so impact is the more useful answer when several fail.
|
|
97
|
+
* In the measured percent of a `'value-gain'` rejection, the gain is a
|
|
98
|
+
* positive number.
|
|
77
99
|
*
|
|
78
100
|
* @param options.priceImpactPercent - the swap leg's price impact, from the
|
|
79
101
|
* estimate's marginal reference quote; null when not yet quoted
|
|
@@ -98,6 +120,6 @@ export declare const assessComposedRoute: ({ priceImpactPercent, paidUsdValue, d
|
|
|
98
120
|
* pricing" rather than "no route", which a boolean alone could not.
|
|
99
121
|
*
|
|
100
122
|
* @param verdict - the verdict from {@link assessComposedRoute}
|
|
101
|
-
* @returns true only when the route passed
|
|
123
|
+
* @returns true only when the route passed every ceiling
|
|
102
124
|
*/
|
|
103
125
|
export declare const composedRouteMayBeOffered: (verdict: ComposedRouteVerdict) => boolean;
|
|
@@ -48,11 +48,33 @@ export const COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT = 3;
|
|
|
48
48
|
*/
|
|
49
49
|
export const COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT = 5;
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* The end-to-end value GAIN above which a composed route is withdrawn, in
|
|
52
|
+
* percent of what the reader pays.
|
|
53
|
+
*
|
|
54
|
+
* A ROUTE CANNOT MAKE MONEY. Every leg charges a fee, so a route that
|
|
55
|
+
* delivers more value than it takes says one of its figures is wrong. Only a
|
|
56
|
+
* disagreement between the two price feeds can make a gain look real, and the
|
|
57
|
+
* value-loss ceiling already trusts those feeds to within the same five
|
|
58
|
+
* percent. So the bound is symmetric with
|
|
59
|
+
* {@link COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT}.
|
|
60
|
+
*
|
|
61
|
+
* THE DEFECT THIS STOPS. On 2026-10-03 the engine served 0.0002 BNB to HEX at
|
|
62
|
+
* 495,256 HEX, about seven thousand times the rate of the next tier. The
|
|
63
|
+
* value-loss test read that as a loss of minus seven hundred thousand percent
|
|
64
|
+
* and passed it. A PulseX hop rescaled from an upstream live answer keeps the
|
|
65
|
+
* price impact measured at its original, smaller input
|
|
66
|
+
* (`recomposePath` in `@gibs/quotes`'s `search/waves.ts`), so the impact test
|
|
67
|
+
* cannot see an inflated amount either. Only the two ends of the journey can.
|
|
68
|
+
*/
|
|
69
|
+
export const COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT = 5;
|
|
70
|
+
/**
|
|
71
|
+
* Judges a composed route against its three ceilings.
|
|
52
72
|
*
|
|
53
|
-
* Checks price impact first
|
|
54
|
-
* specific leg at fault while value
|
|
55
|
-
* wrong
|
|
73
|
+
* Checks price impact first, then value loss, then value gain. Impact names
|
|
74
|
+
* the specific leg at fault, while the two value tests only say the journey as
|
|
75
|
+
* a whole went wrong, so impact is the more useful answer when several fail.
|
|
76
|
+
* In the measured percent of a `'value-gain'` rejection, the gain is a
|
|
77
|
+
* positive number.
|
|
56
78
|
*
|
|
57
79
|
* @param options.priceImpactPercent - the swap leg's price impact, from the
|
|
58
80
|
* estimate's marginal reference quote; null when not yet quoted
|
|
@@ -91,6 +113,15 @@ export const assessComposedRoute = ({ priceImpactPercent, paidUsdValue, delivere
|
|
|
91
113
|
ceilingPercent: COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT,
|
|
92
114
|
};
|
|
93
115
|
}
|
|
116
|
+
const gainPercent = -lossPercent;
|
|
117
|
+
if (gainPercent > COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT) {
|
|
118
|
+
return {
|
|
119
|
+
state: 'rejected',
|
|
120
|
+
reason: 'value-gain',
|
|
121
|
+
measuredPercent: gainPercent,
|
|
122
|
+
ceilingPercent: COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
94
125
|
return { state: 'acceptable' };
|
|
95
126
|
};
|
|
96
127
|
/**
|
|
@@ -103,6 +134,6 @@ export const assessComposedRoute = ({ priceImpactPercent, paidUsdValue, delivere
|
|
|
103
134
|
* pricing" rather than "no route", which a boolean alone could not.
|
|
104
135
|
*
|
|
105
136
|
* @param verdict - the verdict from {@link assessComposedRoute}
|
|
106
|
-
* @returns true only when the route passed
|
|
137
|
+
* @returns true only when the route passed every ceiling
|
|
107
138
|
*/
|
|
108
139
|
export const composedRouteMayBeOffered = (verdict) => verdict.state === 'acceptable';
|
|
@@ -74,7 +74,7 @@ export type EntryChainAnchor = {
|
|
|
74
74
|
* PulseChain token. A USD Coin pair does not gain an ether anchor here —
|
|
75
75
|
* ether would land as bridged ether, a different token, needing a different
|
|
76
76
|
* swap path. That is a different DESTINATION PAIR, and choosing between pairs
|
|
77
|
-
*
|
|
77
|
+
* is made by the route engine, which holds every destination pair.
|
|
78
78
|
*
|
|
79
79
|
* @param options.entryChainToken - the destination pair's entry-chain side
|
|
80
80
|
* @param options.pulsechainToken - the destination pair's PulseChain side
|
|
@@ -15,7 +15,7 @@ import { sameAddress } from '../ecosystems.js';
|
|
|
15
15
|
* PulseChain token. A USD Coin pair does not gain an ether anchor here —
|
|
16
16
|
* ether would land as bridged ether, a different token, needing a different
|
|
17
17
|
* swap path. That is a different DESTINATION PAIR, and choosing between pairs
|
|
18
|
-
*
|
|
18
|
+
* is made by the route engine, which holds every destination pair.
|
|
19
19
|
*
|
|
20
20
|
* @param options.entryChainToken - the destination pair's entry-chain side
|
|
21
21
|
* @param options.pulsechainToken - the destination pair's PulseChain side
|
package/dist/routing/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { omnibridgeProviderForEntryChain, pulseXConversionSwapPath } from './pro
|
|
|
9
9
|
export { buildDirectCrossingCandidate } from './candidate.js';
|
|
10
10
|
export type { RouteCandidate, DeferredCrossing, DirectCrossingCandidateInputs } from './candidate.js';
|
|
11
11
|
export { buildRouteDataForCandidate, routeSignatureCount } from './candidate-route.js';
|
|
12
|
-
export { COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT, COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT, assessComposedRoute, composedRouteMayBeOffered, } from './composed-route-quality.js';
|
|
12
|
+
export { COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT, COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT, COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT, assessComposedRoute, composedRouteMayBeOffered, } from './composed-route-quality.js';
|
|
13
13
|
export type { ComposedRouteVerdict } from './composed-route-quality.js';
|
|
14
14
|
export { entryChainAnchorsForPair } from './entry-chain-anchor.js';
|
|
15
15
|
export type { EntryChainAnchor, EntryChainCrossingCall } from './entry-chain-anchor.js';
|
|
@@ -22,13 +22,17 @@ export { RANKABLE_ESTIMATE_BASES, assetNodeKey, combineComposedPathBasis, compar
|
|
|
22
22
|
export type { AssetNode, CarrierId, ComposedPathBasis, HopCarrier, HopEdge, HopEdgeOrigin, HopFloor, HopOutcome, HopRequest, PlannedHop, RoutePlan, } from './search/graph.js';
|
|
23
23
|
export { composableHopBasis, composeRouteDelivery, hopOutputAmount, } from './search/compose.js';
|
|
24
24
|
export type { ComposableHop, ComposableHopFigure, ComposeRefusalReason, ComposedRouteDelivery, ExchangeRate, } from './search/compose.js';
|
|
25
|
-
export { GAS_TOPUP_PENALTY_FLOOR_USD, GAS_TOPUP_PENALTY_PPM, SIGNATURE_PENALTY_FLOOR_USD, SIGNATURE_PENALTY_PPM, compareRoutePlansByGasAdjustedDeliveredAmount, compareRoutePlansByScoreAdjustedDeliveredAmount, gasAdjustedDeliveredAmount, scoreRoutePlan, waitPenaltyPpm, zeroGasCostFor, } from './search/score.js';
|
|
25
|
+
export { GAS_TOPUP_PENALTY_FLOOR_USD, GAS_TOPUP_PENALTY_PPM, SIGNATURE_PENALTY_FLOOR_USD, SIGNATURE_PENALTY_PPM, compareRoutePlansByGasAdjustedDeliveredAmount, compareRoutePlansByScoreAdjustedDeliveredAmount, gasAdjustedDeliveredAmount, scoreRoutePlan, sectionIsFundedByLanding, waitPenaltyPpm, zeroGasCostFor, } from './search/score.js';
|
|
26
26
|
export type { GasCostFor, GasCostQuote, RouteScoreInputs } from './search/score.js';
|
|
27
27
|
export { buildRouteCandidateFromPlan, buildRouteDataFromPlan, buildRouteScoreInputsFromPlan, deriveDeferredCrossingFromPlan, deriveEntryChainIdFromPlan, deriveOmnibridgeProviderFromPlan, deriveViaChainIdsFromPlan, derivePlanKind, } from './search/plan-route-data.js';
|
|
28
28
|
export type { PlanCandidateInputs, PlanRouteDataInputs, PlanSectionPricing, PlanValuation, ResolveNodeToken, } from './search/plan-route-data.js';
|
|
29
29
|
export { createOmnibridgeCarrier, withOmnibridgeEntryFusing, } from './carriers/omnibridge.js';
|
|
30
30
|
export type { BridgeablePair, CreateOmnibridgeCarrierOptions, } from './carriers/omnibridge.js';
|
|
31
|
-
export { createPulseXCarrier,
|
|
31
|
+
export { createPulseXCarrier, pulseXRouterForEdge, pulseXSwapPathForHop, pulseXVenues, pulseXWrappedNativePath, } from './carriers/pulsex.js';
|
|
32
32
|
export type { CreatePulseXCarrierOptions, PulseXVenue } from './carriers/pulsex.js';
|
|
33
|
+
export { bestPulseXPathQuote, candidatePulseXPaths, pulseXHubTokens } from './carriers/pulsex-paths.js';
|
|
34
|
+
export type { PulseXHubToken, PulseXPathQuote } from './carriers/pulsex-paths.js';
|
|
33
35
|
export { createWrapCarrier } from './carriers/wrap.js';
|
|
34
36
|
export type { CarrierAskAnswer, CarrierExactResult } from './carriers/ask-answer.js';
|
|
37
|
+
export { buildPulseXSwapTransaction, buildWrapTransaction, swapMinimumOut } from './swap-transactions.js';
|
|
38
|
+
export type { SwapCallInputs } from './swap-transactions.js';
|
package/dist/routing/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { netDeliveredAmount, restateInDecimals, valueEquivalentAmount } from './
|
|
|
8
8
|
export { omnibridgeProviderForEntryChain, pulseXConversionSwapPath } from './providers.js';
|
|
9
9
|
export { buildDirectCrossingCandidate } from './candidate.js';
|
|
10
10
|
export { buildRouteDataForCandidate, routeSignatureCount } from './candidate-route.js';
|
|
11
|
-
export { COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT, COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT, assessComposedRoute, composedRouteMayBeOffered, } from './composed-route-quality.js';
|
|
11
|
+
export { COMPOSED_ROUTE_MAX_PRICE_IMPACT_PERCENT, COMPOSED_ROUTE_MAX_VALUE_GAIN_PERCENT, COMPOSED_ROUTE_MAX_VALUE_LOSS_PERCENT, assessComposedRoute, composedRouteMayBeOffered, } from './composed-route-quality.js';
|
|
12
12
|
export { entryChainAnchorsForPair } from './entry-chain-anchor.js';
|
|
13
13
|
export { usdValueOfAmount } from './usd-value.js';
|
|
14
14
|
export { formatUsd, formatUsdNullable } from './format.js';
|
|
@@ -25,7 +25,7 @@ export { BRIDGE_SETTLEMENT_ETA_MINUTES, PULSEX_MARGINAL_REFERENCE_DIVISOR, pulse
|
|
|
25
25
|
// under its `Carrier*` names.
|
|
26
26
|
export { RANKABLE_ESTIMATE_BASES, assetNodeKey, combineComposedPathBasis, compareRoutePlansByDeliveredAmount, groupHopsBySignature, hopComposedBasis, hopEdgeKey, isConfirmedBasis, isRankableBasis, isRateLimitedCarrier, planComposedBasis, planIsConfirmed, planIsRankable, routePlanPathKey, } from './search/graph.js';
|
|
27
27
|
export { composableHopBasis, composeRouteDelivery, hopOutputAmount, } from './search/compose.js';
|
|
28
|
-
export { GAS_TOPUP_PENALTY_FLOOR_USD, GAS_TOPUP_PENALTY_PPM, SIGNATURE_PENALTY_FLOOR_USD, SIGNATURE_PENALTY_PPM, compareRoutePlansByGasAdjustedDeliveredAmount, compareRoutePlansByScoreAdjustedDeliveredAmount, gasAdjustedDeliveredAmount, scoreRoutePlan, waitPenaltyPpm, zeroGasCostFor, } from './search/score.js';
|
|
28
|
+
export { GAS_TOPUP_PENALTY_FLOOR_USD, GAS_TOPUP_PENALTY_PPM, SIGNATURE_PENALTY_FLOOR_USD, SIGNATURE_PENALTY_PPM, compareRoutePlansByGasAdjustedDeliveredAmount, compareRoutePlansByScoreAdjustedDeliveredAmount, gasAdjustedDeliveredAmount, scoreRoutePlan, sectionIsFundedByLanding, waitPenaltyPpm, zeroGasCostFor, } from './search/score.js';
|
|
29
29
|
// General route search — Phase 1: plan-route-data.ts. Turns a priced
|
|
30
30
|
// RoutePlan into the same RouteData/RouteCandidate/RouteScoreInputs shapes
|
|
31
31
|
// today's four hand-written producers emit — see that module's head doc.
|
|
@@ -38,5 +38,9 @@ export { buildRouteCandidateFromPlan, buildRouteDataFromPlan, buildRouteScoreInp
|
|
|
38
38
|
// `searchRouteWaves` and their types from `@gibs/quotes/search`. Gibs's own
|
|
39
39
|
// carriers (the omnibridge crossing, PulseX) live in `./carriers/`, below.
|
|
40
40
|
export { createOmnibridgeCarrier, withOmnibridgeEntryFusing, } from './carriers/omnibridge.js';
|
|
41
|
-
export { createPulseXCarrier,
|
|
41
|
+
export { createPulseXCarrier, pulseXRouterForEdge, pulseXSwapPathForHop, pulseXVenues, pulseXWrappedNativePath, } from './carriers/pulsex.js';
|
|
42
|
+
export { bestPulseXPathQuote, candidatePulseXPaths, pulseXHubTokens } from './carriers/pulsex-paths.js';
|
|
42
43
|
export { createWrapCarrier } from './carriers/wrap.js';
|
|
44
|
+
// The same-chain swap and wrap builders. The interface's section signing and
|
|
45
|
+
// the SDK's execution planner (`../execution.ts`) share them.
|
|
46
|
+
export { buildPulseXSwapTransaction, buildWrapTransaction, swapMinimumOut } from './swap-transactions.js';
|
|
@@ -1182,7 +1182,7 @@ export const buildOutboundRouteData = (roots) => {
|
|
|
1182
1182
|
...(onward === null ? { etaSeconds: PULSEX_SWAP_ETA_SECONDS } : {}),
|
|
1183
1183
|
deferred: true,
|
|
1184
1184
|
deferredNote: onward === null
|
|
1185
|
-
? `
|
|
1185
|
+
? `The fee shown is an estimate. You get a new quote at ${entryChainName} when you come back.`
|
|
1186
1186
|
: `Quoted by ${onward.providerName} at ${entryChainName} when you return.`,
|
|
1187
1187
|
hops: [deferredHop],
|
|
1188
1188
|
};
|
|
@@ -8,6 +8,11 @@ import { type Provider } from '../config.js';
|
|
|
8
8
|
* IS WPLS (no point hopping through WPLS to reach WPLS). The router prices the path
|
|
9
9
|
* with `getAmountsOut`, so a path with no liquidity simply yields no quote.
|
|
10
10
|
*
|
|
11
|
+
* A FIXED PATH, NOT THE BEST ONE. The route search's PulseX carrier prices
|
|
12
|
+
* this path alongside others through its hub tokens and signs whichever
|
|
13
|
+
* delivers most (`carriers/pulsex-paths.ts`). This one remains the
|
|
14
|
+
* carrier's asynchronous `ask` path (`pulseXWrappedNativePath`).
|
|
15
|
+
*
|
|
11
16
|
* @param anchorAddress - the bridged anchor token (the swap input)
|
|
12
17
|
* @param wantedAddress - the token the user wants (the swap output)
|
|
13
18
|
* @param wplsAddress - the wrapped-native intermediary (WPLS)
|
|
@@ -8,6 +8,11 @@ import { sameAddress } from '../ecosystems.js';
|
|
|
8
8
|
* IS WPLS (no point hopping through WPLS to reach WPLS). The router prices the path
|
|
9
9
|
* with `getAmountsOut`, so a path with no liquidity simply yields no quote.
|
|
10
10
|
*
|
|
11
|
+
* A FIXED PATH, NOT THE BEST ONE. The route search's PulseX carrier prices
|
|
12
|
+
* this path alongside others through its hub tokens and signs whichever
|
|
13
|
+
* delivers most (`carriers/pulsex-paths.ts`). This one remains the
|
|
14
|
+
* carrier's asynchronous `ask` path (`pulseXWrappedNativePath`).
|
|
15
|
+
*
|
|
11
16
|
* @param anchorAddress - the bridged anchor token (the swap input)
|
|
12
17
|
* @param wantedAddress - the token the user wants (the swap output)
|
|
13
18
|
* @param wplsAddress - the wrapped-native intermediary (WPLS)
|
|
@@ -318,6 +318,20 @@ export type HopOutcome = {
|
|
|
318
318
|
* build exactly the request the route was priced for.
|
|
319
319
|
*/
|
|
320
320
|
readonly minimumDeliveredAmount?: bigint;
|
|
321
|
+
/**
|
|
322
|
+
* How far the hop's executed rate falls below its marginal rate, in
|
|
323
|
+
* percent — present only when the carrier measured it from its own
|
|
324
|
+
* pool reserves (a PulseX swap). Absent means "not measured", never zero.
|
|
325
|
+
*/
|
|
326
|
+
readonly priceImpactPercent?: number;
|
|
327
|
+
/**
|
|
328
|
+
* The token addresses the carrier's execution passes through, in order,
|
|
329
|
+
* when the carrier chose among several (a PulseX swap path). The figure
|
|
330
|
+
* was priced along exactly this path, and the signer must sign exactly
|
|
331
|
+
* this path — never rebuild one. Opaque to the search, like
|
|
332
|
+
* {@link HopEdge.venue}. Absent when the carrier makes no such choice.
|
|
333
|
+
*/
|
|
334
|
+
readonly swapPath?: readonly string[];
|
|
321
335
|
} | {
|
|
322
336
|
readonly kind: 'estimate';
|
|
323
337
|
/** The composed/probed figure, in the hop's `to` node's base units. */
|
|
@@ -353,6 +367,21 @@ export type HopOutcome = {
|
|
|
353
367
|
* a plan on any more.
|
|
354
368
|
*/
|
|
355
369
|
readonly refusalReason?: string | null;
|
|
370
|
+
/**
|
|
371
|
+
* The price impact measured when the carrier answered this hop exactly,
|
|
372
|
+
* carried onto the rescaled estimate when an upstream amount moved and
|
|
373
|
+
* the exact figure no longer applies verbatim. Impact grows roughly in
|
|
374
|
+
* proportion to the trade, so the carried figure stays close for the
|
|
375
|
+
* small moves a live upstream quote makes. Absent means "not measured".
|
|
376
|
+
*/
|
|
377
|
+
readonly priceImpactPercent?: number;
|
|
378
|
+
/**
|
|
379
|
+
* The swap path the hop was priced along when the carrier answered it
|
|
380
|
+
* exactly, carried onto the rescaled estimate like
|
|
381
|
+
* {@link priceImpactPercent}. The rescale changes the amount, not the
|
|
382
|
+
* path, so this is still the path to sign.
|
|
383
|
+
*/
|
|
384
|
+
readonly swapPath?: readonly string[];
|
|
356
385
|
};
|
|
357
386
|
/**
|
|
358
387
|
* Whether every hop of `hops` carries a figure a ranking may trust: a live
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProviderKey } from '../../providers.js';
|
|
1
2
|
import type { Provider } from '../../config.js';
|
|
2
3
|
import type { QuoteFee } from '../../quote-fees.js';
|
|
3
4
|
import type { Token } from '../../types.js';
|
|
@@ -83,6 +84,18 @@ export declare const deriveViaChainIdsFromPlan: (plan: RoutePlan) => number[] |
|
|
|
83
84
|
* @returns the route's kind
|
|
84
85
|
*/
|
|
85
86
|
export declare const derivePlanKind: (plan: RoutePlan) => RouteKind;
|
|
87
|
+
/**
|
|
88
|
+
* The plan's headline provider — the omnibridge crossing it out when it
|
|
89
|
+
* crosses one, or whichever carrier performs signature 1 when it does not.
|
|
90
|
+
* Matches every existing builder's own headline choice: the omnibridge when
|
|
91
|
+
* present (`buildCandidateRouteData`, `buildNearIntentsTwoStepRouteData`,
|
|
92
|
+
* `buildOutboundRouteData`), the aggregator's own key as the fallback
|
|
93
|
+
* (`buildCandidateRouteData`'s no-omnibridge branch).
|
|
94
|
+
*
|
|
95
|
+
* @param plan - the plan to derive from
|
|
96
|
+
* @returns the headline provider key
|
|
97
|
+
*/
|
|
98
|
+
export declare const deriveHeadlineProviderKeyFromPlan: (plan: RoutePlan) => ProviderKey;
|
|
86
99
|
/**
|
|
87
100
|
* The anchor a two-signature plan leaves the reader holding between its
|
|
88
101
|
* first and second groups — `RouteCandidate.plan`'s own TSDoc: "the anchor
|
|
@@ -116,6 +129,18 @@ export type PlanSectionPricing = {
|
|
|
116
129
|
landingUsdValue: number | null;
|
|
117
130
|
/** This section's own cost, in United States dollars, or null when unpriced. */
|
|
118
131
|
feeUsdValue: number | null;
|
|
132
|
+
/**
|
|
133
|
+
* Each hop's own share of `feeUsdValue`, one entry per hop of the group in
|
|
134
|
+
* order, or omitted when the caller cannot price every hop.
|
|
135
|
+
*
|
|
136
|
+
* WHY A FUSED SECTION NEEDS THIS. One signature can carry two legs: an
|
|
137
|
+
* aggregator lands on the bridge's entry chain and makes the entry call in
|
|
138
|
+
* the same transaction. With no per-hop figure, a display that splits the
|
|
139
|
+
* section into legs can only put the whole cost on the first leg, so the
|
|
140
|
+
* aggregator wore the bridge's fee and the bridge leg read as free. The
|
|
141
|
+
* caller passes shares that add up to `feeUsdValue`, or nothing.
|
|
142
|
+
*/
|
|
143
|
+
hopFeeUsdValues?: readonly (number | null)[];
|
|
119
144
|
/** The itemized charges behind `feeUsdValue`, passed straight through to `RouteSectionData.fees`. */
|
|
120
145
|
fees?: readonly QuoteFee[];
|
|
121
146
|
/** Estimated duration for this signature in minutes, or null/undefined to omit. */
|
|
@@ -349,20 +349,41 @@ const hopIsAutomatic = (hopIndex, plan) => {
|
|
|
349
349
|
* @param hopIndex - the hop's index into `plan.hops`
|
|
350
350
|
* @param plan - the plan the hop belongs to
|
|
351
351
|
* @param resolveToken - resolves a graph node to its full display token
|
|
352
|
+
* @param feeUsdValue - the hop's own share of its section's cost, or null
|
|
352
353
|
* @returns the raw hop data
|
|
353
354
|
*/
|
|
354
|
-
const toRouteHopData = (hop, hopIndex, plan, resolveToken) => ({
|
|
355
|
+
const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue) => ({
|
|
355
356
|
providerName: hopProviderName(hop.edge.carrier),
|
|
356
357
|
providerKey: hop.edge.carrier,
|
|
357
358
|
title: hopTitle(hop, hopIndex, plan, resolveToken),
|
|
358
359
|
conversion: hopConversion(hop, hopIndex, plan, resolveToken),
|
|
359
360
|
fromToken: toRouteTokenRef(hop.edge.from, resolveToken),
|
|
360
361
|
toToken: toRouteTokenRef(hop.edge.to, resolveToken),
|
|
361
|
-
// The per-hop dollar figure is a
|
|
362
|
-
//
|
|
363
|
-
feeUsdValue
|
|
362
|
+
// The per-hop dollar figure is a caller-supplied input (see this module's
|
|
363
|
+
// head doc); the caller passes it in, never derived here.
|
|
364
|
+
feeUsdValue,
|
|
364
365
|
automatic: hopIsAutomatic(hopIndex, plan),
|
|
365
366
|
});
|
|
367
|
+
/**
|
|
368
|
+
* The per-hop shares a section's pricing names, checked against the group
|
|
369
|
+
* they describe.
|
|
370
|
+
*
|
|
371
|
+
* @param pricing - the section's caller-supplied pricing
|
|
372
|
+
* @param group - the signature group's hop indices
|
|
373
|
+
* @param groupIndex - the group's index, for the error message
|
|
374
|
+
* @returns one share per hop, null where none was given
|
|
375
|
+
* @throws when the caller passed a share list of the wrong length — a share
|
|
376
|
+
* matched to the wrong hop is a figure on the wrong leg
|
|
377
|
+
*/
|
|
378
|
+
const groupHopFeeUsdValues = (pricing, group, groupIndex) => {
|
|
379
|
+
const shares = pricing.hopFeeUsdValues;
|
|
380
|
+
if (shares === undefined)
|
|
381
|
+
return group.map(() => null);
|
|
382
|
+
if (shares.length !== group.length) {
|
|
383
|
+
throw new Error(`buildRouteDataFromPlan: section ${groupIndex + 1} has ${group.length} hop(s) but ${shares.length} hop fee(s)`);
|
|
384
|
+
}
|
|
385
|
+
return shares;
|
|
386
|
+
};
|
|
366
387
|
// ---------------------------------------------------------------------------
|
|
367
388
|
// Route-level derivations — candidate.ts's promised derivation, made real
|
|
368
389
|
// ---------------------------------------------------------------------------
|
|
@@ -472,7 +493,8 @@ export const derivePlanKind = (plan) => {
|
|
|
472
493
|
*
|
|
473
494
|
* A GIBS PLAN NEVER CARRIES ANYTHING ELSE. Every carrier a `RoutePlan` this
|
|
474
495
|
* module renders can name is one gibs itself registered — the three built-in
|
|
475
|
-
* aggregators, the omnibridge, or
|
|
496
|
+
* aggregators, the omnibridge, PulseX, or a wrap step, which
|
|
497
|
+
* {@link deriveHeadlineProviderKeyFromPlan} skips (`routing/carriers/`,
|
|
476
498
|
* `packages/ui/src/lib/state/useRouteSearch.ts`) — so this throws rather than
|
|
477
499
|
* guessing when it sees anything else: a plan naming an unrecognized carrier
|
|
478
500
|
* is a bug in whatever built it, not a display gap to paper over silently.
|
|
@@ -480,6 +502,8 @@ export const derivePlanKind = (plan) => {
|
|
|
480
502
|
* @param carrier - the plan's headline carrier
|
|
481
503
|
* @returns the same value, typed as `ProviderKey`
|
|
482
504
|
*/
|
|
505
|
+
/** The carrier id of the native-coin wrap step (`routing/carriers/wrap.ts`). */
|
|
506
|
+
const WRAP_CARRIER = 'wrap';
|
|
483
507
|
const toHeadlineProviderKey = (carrier) => {
|
|
484
508
|
if (isBridgeCarrier(carrier))
|
|
485
509
|
return carrier;
|
|
@@ -500,7 +524,18 @@ const toHeadlineProviderKey = (carrier) => {
|
|
|
500
524
|
* @param plan - the plan to derive from
|
|
501
525
|
* @returns the headline provider key
|
|
502
526
|
*/
|
|
503
|
-
const deriveHeadlineProviderKeyFromPlan = (plan) => toHeadlineProviderKey(deriveOmnibridgeProviderFromPlan(plan) ?? plan
|
|
527
|
+
export const deriveHeadlineProviderKeyFromPlan = (plan) => toHeadlineProviderKey(deriveOmnibridgeProviderFromPlan(plan) ?? firstHeadlineCarrier(plan));
|
|
528
|
+
/**
|
|
529
|
+
* The carrier of the first hop that can headline a route. A `wrap` hop
|
|
530
|
+
* (native coin to its wrapped token, or back) is a step inside a route, never
|
|
531
|
+
* the route itself, so it is skipped. A plan made only of wraps headlines as
|
|
532
|
+
* PulseX, the same key the same-chain swap builder gives that route
|
|
533
|
+
* (`buildSameChainSwapRouteData`).
|
|
534
|
+
*
|
|
535
|
+
* @param plan - the plan to read
|
|
536
|
+
* @returns the carrier to headline
|
|
537
|
+
*/
|
|
538
|
+
const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !== WRAP_CARRIER)?.edge.carrier ?? 'PulseX';
|
|
504
539
|
/**
|
|
505
540
|
* The anchor a two-signature plan leaves the reader holding between its
|
|
506
541
|
* first and second groups — `RouteCandidate.plan`'s own TSDoc: "the anchor
|
|
@@ -618,6 +653,7 @@ export const buildRouteDataFromPlan = (inputs) => {
|
|
|
618
653
|
const lastHop = hops[hops.length - 1];
|
|
619
654
|
const landing = resolveNodeWithAssertedDecimals(lastHop.edge.to, resolveToken);
|
|
620
655
|
const landingIsUnpriced = hopOutcomeIsUnpriced(lastHop);
|
|
656
|
+
const hopFeeUsdValues = groupHopFeeUsdValues(pricing, group, groupIndex);
|
|
621
657
|
return {
|
|
622
658
|
id: `${routeId}-signature-${groupIndex + 1}`,
|
|
623
659
|
label: sectionLabel(group, groupIndex, plan),
|
|
@@ -634,7 +670,7 @@ export const buildRouteDataFromPlan = (inputs) => {
|
|
|
634
670
|
fees: pricing.fees ? [...pricing.fees] : undefined,
|
|
635
671
|
etaMinutes: pricing.etaMinutes,
|
|
636
672
|
etaSeconds: pricing.etaSeconds,
|
|
637
|
-
hops: group.map((hopIndex) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken)),
|
|
673
|
+
hops: group.map((hopIndex, positionInGroup) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken, hopFeeUsdValues[positionInGroup] ?? null)),
|
|
638
674
|
deferred: groupIndex > 0 ? true : undefined,
|
|
639
675
|
deferredNote: pricing.deferredNote,
|
|
640
676
|
};
|
|
@@ -244,8 +244,20 @@ export type GasCostQuote = {
|
|
|
244
244
|
* the user needs gas, then they should get it" — a gasless intermediate
|
|
245
245
|
* chain is modelled as ONE MORE CHARGE against the route, exactly like the
|
|
246
246
|
* gas itself, never as a refusal or an exclusion from the ranking.
|
|
247
|
+
*
|
|
248
|
+
* A SECTION THE PREVIOUS SECTION FUNDS IS NEVER CHARGED THIS. When the
|
|
249
|
+
* hop landing on this chain delivers native coin, the reader arrives
|
|
250
|
+
* holding gas — {@link gasAdjustedDeliveredAmount} drops this figure for
|
|
251
|
+
* that section (see {@link sectionIsFundedByLanding}).
|
|
247
252
|
*/
|
|
248
253
|
readonly refuelAmount: bigint;
|
|
254
|
+
/**
|
|
255
|
+
* The native coin this signature burns, in the signing chain's own native
|
|
256
|
+
* base units (wei on an Ethereum-Virtual-Machine chain) — the amount a gas
|
|
257
|
+
* top-up has to land at minimum. Optional: a caller with no gas-price read
|
|
258
|
+
* omits it, and a served top-up then reports its native amount as unknown.
|
|
259
|
+
*/
|
|
260
|
+
readonly nativeGasAmount?: bigint;
|
|
249
261
|
};
|
|
250
262
|
/**
|
|
251
263
|
* Supplies the REAL cost of signing one step, for the chain it broadcasts on
|
|
@@ -285,12 +297,29 @@ export type GasCostFor = (chainId: number, sender: HopCarrier) => GasCostQuote;
|
|
|
285
297
|
* has something total to call when nobody has.
|
|
286
298
|
*/
|
|
287
299
|
export declare const zeroGasCostFor: GasCostFor;
|
|
300
|
+
/**
|
|
301
|
+
* Whether the section at `sectionIndex` starts with native coin already in
|
|
302
|
+
* the reader's hand: the hop that ends the PREVIOUS section delivers the
|
|
303
|
+
* chain's native coin, so the reader arrives holding gas for this section's
|
|
304
|
+
* own signature and needs no top-up there, whatever their balance was.
|
|
305
|
+
*
|
|
306
|
+
* Always false for the first section — nothing lands before it, so its gas
|
|
307
|
+
* can only come from the reader's own balance.
|
|
308
|
+
*
|
|
309
|
+
* @param plan - the plan the section belongs to
|
|
310
|
+
* @param sectionIndex - 0-based index into `plan.signatures`
|
|
311
|
+
* @returns true when the previous section lands native coin on this
|
|
312
|
+
* section's chain
|
|
313
|
+
*/
|
|
314
|
+
export declare const sectionIsFundedByLanding: (plan: RoutePlan, sectionIndex: number) => boolean;
|
|
288
315
|
/**
|
|
289
316
|
* `plan`'s last-hop `deliveredAmount`, minus the REAL cost of every
|
|
290
317
|
* signature it needs — `gasCostFor` charged ONCE per signature group
|
|
291
318
|
* (`plan.signatures`, built by `groupHopsBySignature`), on the chain and
|
|
292
319
|
* carrier of that group's FIRST hop, which is the hop the reader actually
|
|
293
|
-
* signs (or deposits by hand) for that group.
|
|
320
|
+
* signs (or deposits by hand) for that group. A group's refuel cost is
|
|
321
|
+
* dropped when the previous group lands native coin
|
|
322
|
+
* ({@link sectionIsFundedByLanding}). Floored at zero: a plan whose
|
|
294
323
|
* gas costs exceed its own delivered amount is worth nothing, never negative.
|
|
295
324
|
*
|
|
296
325
|
* @param plan - the plan to charge
|
|
@@ -318,6 +347,9 @@ export declare const gasAdjustedDeliveredAmount: (plan: RoutePlan, gasCostFor: G
|
|
|
318
347
|
* own real charges.
|
|
319
348
|
*
|
|
320
349
|
* @param gasCostFor - supplies each signature's real cost; see {@link GasCostFor}
|
|
350
|
+
* TIE-BREAK: when the gas-adjusted amounts are equal, the plan with fewer
|
|
351
|
+
* signatures ranks first.
|
|
352
|
+
*
|
|
321
353
|
* @returns a comparator: negative when `a` ranks ahead of `b`, positive when
|
|
322
354
|
* `b` ranks ahead of `a`, zero when they tie
|
|
323
355
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNativeAsset } from '../../ecosystems.js';
|
|
1
2
|
import { compareRoutePlansByDeliveredAmount, planIsRankable, isRankableBasis, } from './graph.js';
|
|
2
3
|
/**
|
|
3
4
|
* PHASE 0 — FOUNDATION ONLY. Today this module holds exactly one function,
|
|
@@ -337,12 +338,37 @@ export const scoreRoutePlan = (inputs) => {
|
|
|
337
338
|
* has something total to call when nobody has.
|
|
338
339
|
*/
|
|
339
340
|
export const zeroGasCostFor = () => ({ gasAmount: 0n, refuelAmount: 0n });
|
|
341
|
+
/**
|
|
342
|
+
* Whether the section at `sectionIndex` starts with native coin already in
|
|
343
|
+
* the reader's hand: the hop that ends the PREVIOUS section delivers the
|
|
344
|
+
* chain's native coin, so the reader arrives holding gas for this section's
|
|
345
|
+
* own signature and needs no top-up there, whatever their balance was.
|
|
346
|
+
*
|
|
347
|
+
* Always false for the first section — nothing lands before it, so its gas
|
|
348
|
+
* can only come from the reader's own balance.
|
|
349
|
+
*
|
|
350
|
+
* @param plan - the plan the section belongs to
|
|
351
|
+
* @param sectionIndex - 0-based index into `plan.signatures`
|
|
352
|
+
* @returns true when the previous section lands native coin on this
|
|
353
|
+
* section's chain
|
|
354
|
+
*/
|
|
355
|
+
export const sectionIsFundedByLanding = (plan, sectionIndex) => {
|
|
356
|
+
if (sectionIndex < 1)
|
|
357
|
+
return false;
|
|
358
|
+
const previousGroup = plan.signatures[sectionIndex - 1];
|
|
359
|
+
const landingHop = plan.hops[previousGroup?.[previousGroup.length - 1] ?? -1];
|
|
360
|
+
if (landingHop === undefined)
|
|
361
|
+
return false;
|
|
362
|
+
return isNativeAsset(landingHop.edge.to.address, landingHop.edge.to.ecosystem);
|
|
363
|
+
};
|
|
340
364
|
/**
|
|
341
365
|
* `plan`'s last-hop `deliveredAmount`, minus the REAL cost of every
|
|
342
366
|
* signature it needs — `gasCostFor` charged ONCE per signature group
|
|
343
367
|
* (`plan.signatures`, built by `groupHopsBySignature`), on the chain and
|
|
344
368
|
* carrier of that group's FIRST hop, which is the hop the reader actually
|
|
345
|
-
* signs (or deposits by hand) for that group.
|
|
369
|
+
* signs (or deposits by hand) for that group. A group's refuel cost is
|
|
370
|
+
* dropped when the previous group lands native coin
|
|
371
|
+
* ({@link sectionIsFundedByLanding}). Floored at zero: a plan whose
|
|
346
372
|
* gas costs exceed its own delivered amount is worth nothing, never negative.
|
|
347
373
|
*
|
|
348
374
|
* @param plan - the plan to charge
|
|
@@ -351,13 +377,14 @@ export const zeroGasCostFor = () => ({ gasAmount: 0n, refuelAmount: 0n });
|
|
|
351
377
|
*/
|
|
352
378
|
export const gasAdjustedDeliveredAmount = (plan, gasCostFor) => {
|
|
353
379
|
const deliveredAmount = plan.hops[plan.hops.length - 1]?.outcome.deliveredAmount ?? 0n;
|
|
354
|
-
const totalGasCost = plan.signatures.reduce((total, group) => {
|
|
380
|
+
const totalGasCost = plan.signatures.reduce((total, group, sectionIndex) => {
|
|
355
381
|
const firstHopIndex = group[0];
|
|
356
382
|
const firstHop = firstHopIndex === undefined ? undefined : plan.hops[firstHopIndex];
|
|
357
383
|
if (firstHop === undefined)
|
|
358
384
|
return total;
|
|
359
385
|
const quote = gasCostFor(firstHop.edge.from.chainId, firstHop.edge.carrier);
|
|
360
|
-
|
|
386
|
+
const refuelAmount = sectionIsFundedByLanding(plan, sectionIndex) ? 0n : quote.refuelAmount;
|
|
387
|
+
return total + quote.gasAmount + refuelAmount;
|
|
361
388
|
}, 0n);
|
|
362
389
|
const netAmount = deliveredAmount - totalGasCost;
|
|
363
390
|
return netAmount < 0n ? 0n : netAmount;
|
|
@@ -382,6 +409,9 @@ export const gasAdjustedDeliveredAmount = (plan, gasCostFor) => {
|
|
|
382
409
|
* own real charges.
|
|
383
410
|
*
|
|
384
411
|
* @param gasCostFor - supplies each signature's real cost; see {@link GasCostFor}
|
|
412
|
+
* TIE-BREAK: when the gas-adjusted amounts are equal, the plan with fewer
|
|
413
|
+
* signatures ranks first.
|
|
414
|
+
*
|
|
385
415
|
* @returns a comparator: negative when `a` ranks ahead of `b`, positive when
|
|
386
416
|
* `b` ranks ahead of `a`, zero when they tie
|
|
387
417
|
*/
|
|
@@ -394,7 +424,12 @@ export const compareRoutePlansByGasAdjustedDeliveredAmount = (gasCostFor) => (a,
|
|
|
394
424
|
return compareRoutePlansByDeliveredAmount(a, b);
|
|
395
425
|
const adjustedA = gasAdjustedDeliveredAmount(a, gasCostFor);
|
|
396
426
|
const adjustedB = gasAdjustedDeliveredAmount(b, gasCostFor);
|
|
397
|
-
if (adjustedA
|
|
398
|
-
return
|
|
399
|
-
|
|
427
|
+
if (adjustedA !== adjustedB)
|
|
428
|
+
return adjustedA > adjustedB ? -1 : 1;
|
|
429
|
+
// EQUAL FIGURES: FEWER SIGNATURES RANKS FIRST. Two plans that deliver the
|
|
430
|
+
// same amount cost the reader different effort — each signature is one more
|
|
431
|
+
// wallet prompt. Without this the stable sort kept the depth-first walk
|
|
432
|
+
// order, so a three-signature plan could be selected over a two-signature
|
|
433
|
+
// plan showing the same figure.
|
|
434
|
+
return a.signatures.length - b.signatures.length;
|
|
400
435
|
};
|