@gibs/bridge-sdk 1.17.0 → 1.18.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 +52 -1
- package/dist/cjs/config.d.ts +18 -3
- package/dist/cjs/config.js +49 -17
- package/dist/cjs/execution.d.ts +17 -4
- package/dist/cjs/execution.js +40 -13
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/prices.d.ts +10 -0
- package/dist/cjs/prices.js +54 -2
- package/dist/cjs/routing/carrier-provider-key.d.ts +31 -0
- package/dist/cjs/routing/carrier-provider-key.js +42 -0
- package/dist/cjs/routing/carriers/pulsex.d.ts +50 -25
- package/dist/cjs/routing/carriers/pulsex.js +135 -85
- package/dist/cjs/routing/index.d.ts +6 -2
- package/dist/cjs/routing/index.js +9 -1
- package/dist/cjs/routing/route-data.d.ts +47 -5
- package/dist/cjs/routing/search/plan-route-data.d.ts +17 -1
- package/dist/cjs/routing/search/plan-route-data.js +30 -7
- package/dist/cjs/routing/swap-transactions.d.ts +7 -3
- package/dist/cjs/routing/swap-transactions.js +6 -4
- package/dist/cjs/swap-venues.d.ts +43 -0
- package/dist/cjs/swap-venues.js +29 -0
- package/dist/config.d.ts +18 -3
- package/dist/config.js +48 -16
- package/dist/execution.d.ts +17 -4
- package/dist/execution.js +41 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/prices.d.ts +10 -0
- package/dist/prices.js +52 -1
- package/dist/routing/carrier-provider-key.d.ts +31 -0
- package/dist/routing/carrier-provider-key.js +37 -0
- package/dist/routing/carriers/pulsex.d.ts +50 -25
- package/dist/routing/carriers/pulsex.js +133 -84
- package/dist/routing/index.d.ts +6 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/route-data.d.ts +47 -5
- package/dist/routing/search/plan-route-data.d.ts +17 -1
- package/dist/routing/search/plan-route-data.js +30 -7
- package/dist/routing/swap-transactions.d.ts +7 -3
- package/dist/routing/swap-transactions.js +7 -5
- package/dist/swap-venues.d.ts +43 -0
- package/dist/swap-venues.js +24 -0
- package/package.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Hex } from 'viem';
|
|
2
2
|
import type { QuoteFee } from '../quote-fees.js';
|
|
3
3
|
import type { ProviderKey } from '../providers.js';
|
|
4
|
+
import type { CarrierStepKind } from './carrier-provider-key.js';
|
|
4
5
|
/**
|
|
5
6
|
* The executing engine family a route runs through.
|
|
6
7
|
*
|
|
@@ -91,7 +92,12 @@ export type RouteHopData = {
|
|
|
91
92
|
/** Display name for the hop's provider, e.g. "LI.FI", "PulseChain Bridge". */
|
|
92
93
|
providerName: string;
|
|
93
94
|
/** Raw provider key for logo resolution when no `providerLogoUri` is supplied. */
|
|
94
|
-
providerKey?:
|
|
95
|
+
providerKey?: ProviderKey | null;
|
|
96
|
+
/**
|
|
97
|
+
* What the hop does when no provider performs it (a native wrap). Such a hop
|
|
98
|
+
* has a null `providerKey`; the adapter draws its mark from this kind.
|
|
99
|
+
*/
|
|
100
|
+
stepKind?: CarrierStepKind | null;
|
|
95
101
|
/** Quote-supplied provider logo url (aggregator legs); preferred when present. */
|
|
96
102
|
providerLogoUri?: string | null;
|
|
97
103
|
/** Human-readable hop title, e.g. "Bridge to PulseChain". */
|
|
@@ -110,11 +116,27 @@ export type RouteHopData = {
|
|
|
110
116
|
* completes. Null/undefined when the provider named no output token.
|
|
111
117
|
*/
|
|
112
118
|
toToken?: RouteTokenRef | null;
|
|
113
|
-
/** Per-hop fee in United States dollars as a NUMBER, or null when unknown. */
|
|
119
|
+
/** Per-hop fee in United States dollars as a NUMBER, or null when unknown. A search route's figure INCLUDES the signature's network gas (carried on the section's first hop). */
|
|
114
120
|
feeUsdValue?: number | null;
|
|
121
|
+
/** The part of {@link feeUsdValue} that is not network gas: what the hop costs in value. Absent when the producer states no gas split. */
|
|
122
|
+
feeExcludingGasUsdValue?: number | null;
|
|
123
|
+
/** The network gas of the signature this hop opens, in dollars; null or absent on every hop that opens none, and when gas is unknown. */
|
|
124
|
+
gasUsdValue?: number | null;
|
|
115
125
|
/** True when this hop runs automatically inside the section's single signature. */
|
|
116
126
|
automatic?: boolean;
|
|
117
127
|
};
|
|
128
|
+
/**
|
|
129
|
+
* The network gas a route needs on one signing chain, summed across the
|
|
130
|
+
* signatures it signs there.
|
|
131
|
+
*/
|
|
132
|
+
export type RouteGasByChain = {
|
|
133
|
+
/** The chain the signatures broadcast on. */
|
|
134
|
+
chainId: number;
|
|
135
|
+
/** The native coin they burn, in base units (wei), or null when the chain's gas price is unknown. */
|
|
136
|
+
amountWei: bigint | null;
|
|
137
|
+
/** The same gas in dollars, or null when the amount or the native price is unknown. */
|
|
138
|
+
usdValue: number | null;
|
|
139
|
+
};
|
|
118
140
|
/**
|
|
119
141
|
* What a signature section's funds LAND IN when that step completes — the stop
|
|
120
142
|
* the money actually comes to rest at, named in tokens rather than only drawn as
|
|
@@ -181,7 +203,9 @@ export type RouteSectionData = {
|
|
|
181
203
|
/** Short label naming what this signature does, e.g. "Bridge to PulseChain". */
|
|
182
204
|
label: string;
|
|
183
205
|
/** Raw provider key for the service executing this signature, or null. */
|
|
184
|
-
providerKey?:
|
|
206
|
+
providerKey?: ProviderKey | null;
|
|
207
|
+
/** What the signature does when no provider performs it (a native wrap); see {@link RouteHopData.stepKind}. */
|
|
208
|
+
stepKind?: CarrierStepKind | null;
|
|
185
209
|
/**
|
|
186
210
|
* The token-icon flow this signature walks, in raw references: the source, then
|
|
187
211
|
* every INTERMEDIATE stop the funds pass through, then the destination. A
|
|
@@ -202,8 +226,12 @@ export type RouteSectionData = {
|
|
|
202
226
|
* a cost.
|
|
203
227
|
*/
|
|
204
228
|
unspent?: RouteUnspentData | null;
|
|
205
|
-
/** Section total fee in United States dollars as a NUMBER, or null. */
|
|
229
|
+
/** Section total fee in United States dollars as a NUMBER, or null. A search route's figure INCLUDES this signature's network gas. */
|
|
206
230
|
feeUsdValue?: number | null;
|
|
231
|
+
/** The part of {@link feeUsdValue} that is not network gas. Absent when the producer states no gas split. */
|
|
232
|
+
feeExcludingGasUsdValue?: number | null;
|
|
233
|
+
/** This signature's network gas in dollars, or null/absent when unknown. */
|
|
234
|
+
gasUsdValue?: number | null;
|
|
207
235
|
/** Numeric inputs for an optional fee breakdown line (conversion mode). */
|
|
208
236
|
feePercent?: number | null;
|
|
209
237
|
/** Price-impact percentage for an optional fee breakdown line (conversion mode). */
|
|
@@ -316,8 +344,22 @@ export type RouteData = {
|
|
|
316
344
|
sections: RouteSectionData[];
|
|
317
345
|
/** What the route delivers — used for ranking and the output row. */
|
|
318
346
|
output: RouteOutputData;
|
|
319
|
-
/**
|
|
347
|
+
/**
|
|
348
|
+
* Aggregate ALL-IN cost in United States dollars as a NUMBER, or null:
|
|
349
|
+
* value lost plus network gas. The ranking reads this figure. For the
|
|
350
|
+
* figure without gas, read {@link costExcludingGasUsdValue}.
|
|
351
|
+
*/
|
|
320
352
|
totalCostUsdValue?: number | null;
|
|
353
|
+
/** {@link totalCostUsdValue} without network gas: the value the reader gives up. Absent when the producer states no gas split. */
|
|
354
|
+
costExcludingGasUsdValue?: number | null;
|
|
355
|
+
/**
|
|
356
|
+
* The network gas the reader pays across every signature, in dollars, or
|
|
357
|
+
* null/absent when no signing chain has a gas reading. Never zero for
|
|
358
|
+
* "unknown".
|
|
359
|
+
*/
|
|
360
|
+
gasCostUsdValue?: number | null;
|
|
361
|
+
/** The gas by signing chain, in signing order: what to hold, and in which coin. */
|
|
362
|
+
gasByChain?: RouteGasByChain[];
|
|
321
363
|
/** The user's net United States dollar outcome; the figure routes are ranked on. */
|
|
322
364
|
netValueUsd?: number | null;
|
|
323
365
|
/** Estimated end-to-end duration in minutes, or null when unknown. */
|
|
@@ -3,7 +3,7 @@ import type { Provider } from '../../config.js';
|
|
|
3
3
|
import type { QuoteFee } from '../../quote-fees.js';
|
|
4
4
|
import type { Token } from '../../types.js';
|
|
5
5
|
import type { DeferredCrossing, RouteCandidate } from '../candidate.js';
|
|
6
|
-
import type { RouteData, RouteKind } from '../route-data.js';
|
|
6
|
+
import type { RouteData, RouteGasByChain, RouteKind } from '../route-data.js';
|
|
7
7
|
import type { RouteScoreInputs } from './score.js';
|
|
8
8
|
import { type AssetNode, type RoutePlan } from './graph.js';
|
|
9
9
|
/**
|
|
@@ -141,6 +141,16 @@ export type PlanSectionPricing = {
|
|
|
141
141
|
* caller passes shares that add up to `feeUsdValue`, or nothing.
|
|
142
142
|
*/
|
|
143
143
|
hopFeeUsdValues?: readonly (number | null)[];
|
|
144
|
+
/**
|
|
145
|
+
* The gas split of `feeUsdValue`, from a producer that prices network gas
|
|
146
|
+
* apart: `feeExcludingGasUsdValue` is the cost without gas, `gasUsdValue` the
|
|
147
|
+
* signature's gas (null when unknown). `hopFeeExcludingGasUsdValues` splits
|
|
148
|
+
* the first the way `hopFeeUsdValues` splits the all-in figure. The gas sits
|
|
149
|
+
* on the section's first hop.
|
|
150
|
+
*/
|
|
151
|
+
feeExcludingGasUsdValue?: number | null;
|
|
152
|
+
gasUsdValue?: number | null;
|
|
153
|
+
hopFeeExcludingGasUsdValues?: readonly (number | null)[];
|
|
144
154
|
/** The itemized charges behind `feeUsdValue`, passed straight through to `RouteSectionData.fees`. */
|
|
145
155
|
fees?: readonly QuoteFee[];
|
|
146
156
|
/** Estimated duration for this signature in minutes, or null/undefined to omit. */
|
|
@@ -175,6 +185,12 @@ export type PlanRouteDataInputs = {
|
|
|
175
185
|
sections: readonly PlanSectionPricing[];
|
|
176
186
|
/** The route's aggregate all-in cost in United States dollars, or null/undefined. */
|
|
177
187
|
totalCostUsdValue?: number | null;
|
|
188
|
+
/** The all-in cost without network gas, or null/undefined. */
|
|
189
|
+
costExcludingGasUsdValue?: number | null;
|
|
190
|
+
/** The route's network gas in dollars, or null/undefined when unknown. */
|
|
191
|
+
gasCostUsdValue?: number | null;
|
|
192
|
+
/** The gas by signing chain, in signing order. */
|
|
193
|
+
gasByChain?: readonly RouteGasByChain[];
|
|
178
194
|
/** The route's net United States dollar outcome — the figure routes are ranked on. */
|
|
179
195
|
netValueUsd?: number | null;
|
|
180
196
|
/** Estimated end-to-end duration in minutes, or null/undefined. */
|
|
@@ -37,6 +37,7 @@ exports.buildRouteScoreInputsFromPlan = exports.buildRouteCandidateFromPlan = ex
|
|
|
37
37
|
const imageLinks = __importStar(require("../../image-links.js"));
|
|
38
38
|
const chain_names_js_1 = require("../../chain-names.js");
|
|
39
39
|
const providers_js_1 = require("../../providers.js");
|
|
40
|
+
const carrier_provider_key_js_1 = require("../carrier-provider-key.js");
|
|
40
41
|
const near_intents_route_js_1 = require("../../near-intents-route.js");
|
|
41
42
|
const producers_js_1 = require("../producers.js");
|
|
42
43
|
const graph_js_1 = require("./graph.js");
|
|
@@ -265,6 +266,8 @@ const hopProviderName = (carrier) => {
|
|
|
265
266
|
return (0, producers_js_1.providerDisplayName)(carrier);
|
|
266
267
|
if (carrier === 'PulseX')
|
|
267
268
|
return 'PulseX';
|
|
269
|
+
if ((0, carrier_provider_key_js_1.carrierStepKind)(carrier) === 'wrap')
|
|
270
|
+
return 'Wrap';
|
|
268
271
|
// TOTAL OVER EVERY CARRIER. The engine plans hops for carriers the provider
|
|
269
272
|
// registry does not hold (a local wrap, a host's own plug-in). Casting such
|
|
270
273
|
// an id to a provider id and asking the throwing lookup crashed the whole
|
|
@@ -386,11 +389,13 @@ const hopIsAutomatic = (hopIndex, plan) => {
|
|
|
386
389
|
* @param plan - the plan the hop belongs to
|
|
387
390
|
* @param resolveToken - resolves a graph node to its full display token
|
|
388
391
|
* @param feeUsdValue - the hop's own share of its section's cost, or null
|
|
392
|
+
* @param gas - the hop's gas split, or null when the producer states none
|
|
389
393
|
* @returns the raw hop data
|
|
390
394
|
*/
|
|
391
|
-
const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue) => ({
|
|
395
|
+
const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue, gas) => ({
|
|
392
396
|
providerName: hopProviderName(hop.edge.carrier),
|
|
393
|
-
providerKey: hop.edge.carrier,
|
|
397
|
+
providerKey: (0, carrier_provider_key_js_1.carrierProviderKey)(hop.edge.carrier),
|
|
398
|
+
stepKind: (0, carrier_provider_key_js_1.carrierStepKind)(hop.edge.carrier),
|
|
394
399
|
title: hopTitle(hop, hopIndex, plan, resolveToken),
|
|
395
400
|
conversion: hopConversion(hop, hopIndex, plan, resolveToken),
|
|
396
401
|
fromToken: toRouteTokenRef(hop.edge.from, resolveToken),
|
|
@@ -398,6 +403,7 @@ const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue) => ({
|
|
|
398
403
|
// The per-hop dollar figure is a caller-supplied input (see this module's
|
|
399
404
|
// head doc); the caller passes it in, never derived here.
|
|
400
405
|
feeUsdValue,
|
|
406
|
+
...(gas === null ? {} : gas),
|
|
401
407
|
automatic: hopIsAutomatic(hopIndex, plan),
|
|
402
408
|
});
|
|
403
409
|
/**
|
|
@@ -542,8 +548,6 @@ exports.derivePlanKind = derivePlanKind;
|
|
|
542
548
|
* @param carrier - the plan's headline carrier
|
|
543
549
|
* @returns the same value, typed as `ProviderKey`
|
|
544
550
|
*/
|
|
545
|
-
/** The carrier id of the native-coin wrap step (`routing/carriers/wrap.ts`). */
|
|
546
|
-
const WRAP_CARRIER = 'wrap';
|
|
547
551
|
const toHeadlineProviderKey = (carrier) => {
|
|
548
552
|
if (isBridgeCarrier(carrier))
|
|
549
553
|
return carrier;
|
|
@@ -576,7 +580,7 @@ exports.deriveHeadlineProviderKeyFromPlan = deriveHeadlineProviderKeyFromPlan;
|
|
|
576
580
|
* @param plan - the plan to read
|
|
577
581
|
* @returns the carrier to headline
|
|
578
582
|
*/
|
|
579
|
-
const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !==
|
|
583
|
+
const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !== carrier_provider_key_js_1.WRAP_CARRIER_ID)?.edge.carrier ?? 'PulseX';
|
|
580
584
|
/**
|
|
581
585
|
* The anchor a two-signature plan leaves the reader holding between its
|
|
582
586
|
* first and second groups — `RouteCandidate.plan`'s own TSDoc: "the anchor
|
|
@@ -696,10 +700,13 @@ const buildRouteDataFromPlan = (inputs) => {
|
|
|
696
700
|
const landing = resolveNodeWithAssertedDecimals(lastHop.edge.to, resolveToken);
|
|
697
701
|
const landingIsUnpriced = hopOutcomeIsUnpriced(lastHop);
|
|
698
702
|
const hopFeeUsdValues = groupHopFeeUsdValues(pricing, group, groupIndex);
|
|
703
|
+
const statesGasSplit = pricing.feeExcludingGasUsdValue !== undefined;
|
|
704
|
+
const hopFeeExcludingGasUsdValues = pricing.hopFeeExcludingGasUsdValues;
|
|
699
705
|
return {
|
|
700
706
|
id: `${routeId}-signature-${groupIndex + 1}`,
|
|
701
707
|
label: sectionLabel(group, groupIndex, plan),
|
|
702
|
-
providerKey: hops[0].edge.carrier,
|
|
708
|
+
providerKey: (0, carrier_provider_key_js_1.carrierProviderKey)(hops[0].edge.carrier),
|
|
709
|
+
stepKind: (0, carrier_provider_key_js_1.carrierStepKind)(hops[0].edge.carrier),
|
|
703
710
|
tokenFlow: buildGroupTokenFlow(hops, resolveToken),
|
|
704
711
|
landing: {
|
|
705
712
|
amount: sectionLandingAmount(pricing, lastHop),
|
|
@@ -709,10 +716,19 @@ const buildRouteDataFromPlan = (inputs) => {
|
|
|
709
716
|
estimateBasis: hopEstimateBasis(lastHop),
|
|
710
717
|
},
|
|
711
718
|
feeUsdValue: pricing.feeUsdValue,
|
|
719
|
+
...(statesGasSplit
|
|
720
|
+
? { feeExcludingGasUsdValue: pricing.feeExcludingGasUsdValue, gasUsdValue: pricing.gasUsdValue ?? null }
|
|
721
|
+
: {}),
|
|
712
722
|
fees: pricing.fees ? [...pricing.fees] : undefined,
|
|
713
723
|
etaMinutes: pricing.etaMinutes,
|
|
714
724
|
etaSeconds: pricing.etaSeconds,
|
|
715
|
-
hops: group.map((hopIndex, positionInGroup) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken, hopFeeUsdValues[positionInGroup] ?? null
|
|
725
|
+
hops: group.map((hopIndex, positionInGroup) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken, hopFeeUsdValues[positionInGroup] ?? null, statesGasSplit
|
|
726
|
+
? {
|
|
727
|
+
feeExcludingGasUsdValue: hopFeeExcludingGasUsdValues?.[positionInGroup] ??
|
|
728
|
+
(group.length === 1 ? (pricing.feeExcludingGasUsdValue ?? null) : null),
|
|
729
|
+
gasUsdValue: positionInGroup === 0 ? (pricing.gasUsdValue ?? null) : null,
|
|
730
|
+
}
|
|
731
|
+
: null)),
|
|
716
732
|
deferred: groupIndex > 0 ? true : undefined,
|
|
717
733
|
deferredNote: pricing.deferredNote,
|
|
718
734
|
};
|
|
@@ -731,6 +747,13 @@ const buildRouteDataFromPlan = (inputs) => {
|
|
|
731
747
|
estimateBasis: lastSection.landing?.estimateBasis ?? null,
|
|
732
748
|
},
|
|
733
749
|
totalCostUsdValue,
|
|
750
|
+
...(inputs.costExcludingGasUsdValue === undefined
|
|
751
|
+
? {}
|
|
752
|
+
: {
|
|
753
|
+
costExcludingGasUsdValue: inputs.costExcludingGasUsdValue,
|
|
754
|
+
gasCostUsdValue: inputs.gasCostUsdValue ?? null,
|
|
755
|
+
gasByChain: [...(inputs.gasByChain ?? [])],
|
|
756
|
+
}),
|
|
734
757
|
netValueUsd,
|
|
735
758
|
etaMinutes,
|
|
736
759
|
// The single legacy `viaChainId` a not-yet-migrated consumer may still
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Hex } from 'viem';
|
|
2
2
|
import { type SignedLeg } from '../settings.js';
|
|
3
|
+
import type { SwapVenues } from '../swap-venues.js';
|
|
3
4
|
import type { HopEdge, PlannedHop } from './search/graph.js';
|
|
4
5
|
/**
|
|
5
6
|
* The same-chain, one-signature transaction shape every sender in this file
|
|
@@ -27,7 +28,7 @@ export type SwapCallInputs = {
|
|
|
27
28
|
* looking at) and the native-in/native-out/token-token branching.
|
|
28
29
|
*
|
|
29
30
|
* SIGNS ON THE EDGE'S OWN ROUTER. A PulseX edge names the deployment it was
|
|
30
|
-
* quoted on (`
|
|
31
|
+
* quoted on (`venueForEdge`: V1 or V2, each with its own pools). The
|
|
31
32
|
* caller's `routerAddress` must be that same router — it is also the router
|
|
32
33
|
* the caller approved to spend the input token — or nothing is built. A V2
|
|
33
34
|
* quote signed on V1 would trade against a different pool than the one the
|
|
@@ -35,8 +36,9 @@ export type SwapCallInputs = {
|
|
|
35
36
|
*
|
|
36
37
|
* @param options - the hop to swap, the amount and slippage-adjusted
|
|
37
38
|
* minimum, who receives the output, the signing deadline, what the caller
|
|
38
|
-
* believes this signature does (`signedLeg`), the chain it executes on,
|
|
39
|
-
* the
|
|
39
|
+
* believes this signature does (`signedLeg`), the chain it executes on, the
|
|
40
|
+
* resolved router address, and the swap venues the router must belong to
|
|
41
|
+
* (the defaults when omitted)
|
|
40
42
|
* @returns the swap transaction inputs, or null when the hop records no
|
|
41
43
|
* PulseX swap path, names no known router, `routerAddress` is not the
|
|
42
44
|
* edge's own router, the leg on screen disagrees, or any of
|
|
@@ -51,6 +53,8 @@ export declare const buildPulseXSwapTransaction: (options: {
|
|
|
51
53
|
readonly signedLeg: SignedLeg | null;
|
|
52
54
|
readonly chainId: number | null;
|
|
53
55
|
readonly routerAddress: Hex | null;
|
|
56
|
+
/** The venues the edge's router must belong to. Omitted means the defaults. */
|
|
57
|
+
readonly venues?: SwapVenues;
|
|
54
58
|
}) => SwapCallInputs | null;
|
|
55
59
|
/**
|
|
56
60
|
* Builds a native-coin wrap/unwrap transaction for one route-search graph
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.swapMinimumOut = exports.buildWrapTransaction = exports.buildPulseXSwapTransaction = void 0;
|
|
4
4
|
const viem_1 = require("viem");
|
|
5
5
|
const settings_js_1 = require("../settings.js");
|
|
6
|
+
const config_js_1 = require("../config.js");
|
|
6
7
|
const pulsex_js_1 = require("./carriers/pulsex.js");
|
|
7
8
|
/**
|
|
8
9
|
* The minimal WETH9-style wrap/unwrap interface: `deposit()` mints the
|
|
@@ -32,7 +33,7 @@ const wrapAbi = (0, viem_1.parseAbi)(['function deposit() payable', 'function wi
|
|
|
32
33
|
* looking at) and the native-in/native-out/token-token branching.
|
|
33
34
|
*
|
|
34
35
|
* SIGNS ON THE EDGE'S OWN ROUTER. A PulseX edge names the deployment it was
|
|
35
|
-
* quoted on (`
|
|
36
|
+
* quoted on (`venueForEdge`: V1 or V2, each with its own pools). The
|
|
36
37
|
* caller's `routerAddress` must be that same router — it is also the router
|
|
37
38
|
* the caller approved to spend the input token — or nothing is built. A V2
|
|
38
39
|
* quote signed on V1 would trade against a different pool than the one the
|
|
@@ -40,8 +41,9 @@ const wrapAbi = (0, viem_1.parseAbi)(['function deposit() payable', 'function wi
|
|
|
40
41
|
*
|
|
41
42
|
* @param options - the hop to swap, the amount and slippage-adjusted
|
|
42
43
|
* minimum, who receives the output, the signing deadline, what the caller
|
|
43
|
-
* believes this signature does (`signedLeg`), the chain it executes on,
|
|
44
|
-
* the
|
|
44
|
+
* believes this signature does (`signedLeg`), the chain it executes on, the
|
|
45
|
+
* resolved router address, and the swap venues the router must belong to
|
|
46
|
+
* (the defaults when omitted)
|
|
45
47
|
* @returns the swap transaction inputs, or null when the hop records no
|
|
46
48
|
* PulseX swap path, names no known router, `routerAddress` is not the
|
|
47
49
|
* edge's own router, the leg on screen disagrees, or any of
|
|
@@ -54,7 +56,7 @@ const buildPulseXSwapTransaction = (options) => {
|
|
|
54
56
|
if (swapPath === null) {
|
|
55
57
|
return null;
|
|
56
58
|
}
|
|
57
|
-
const quotedRouter = (0, pulsex_js_1.
|
|
59
|
+
const quotedRouter = (0, pulsex_js_1.venueForEdge)(options.venues ?? config_js_1.defaultSwapVenues, edge)?.router ?? null;
|
|
58
60
|
if (quotedRouter === null || routerAddress === null || !(0, viem_1.isAddressEqual)(quotedRouter, routerAddress)) {
|
|
59
61
|
return null;
|
|
60
62
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Hex } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* A place a same-chain token swap can execute. Today only Uniswap-V2-shaped
|
|
4
|
+
* routers exist: `getAmountsOut`, `swapExactTokensForTokens`,
|
|
5
|
+
* `swapExactETHForTokens`, `swapExactTokensForETH`, a factory with
|
|
6
|
+
* `getPair`, and pairs with `getReserves`. The `kind` field leaves room for
|
|
7
|
+
* other shapes (for example `'uniswap-v3'`) without breaking callers.
|
|
8
|
+
*/
|
|
9
|
+
export type SwapVenue = {
|
|
10
|
+
/** The router shape. Only `'uniswap-v2'` is supported. */
|
|
11
|
+
readonly kind: 'uniswap-v2';
|
|
12
|
+
/** The numeric chain id the router lives on. */
|
|
13
|
+
readonly chainId: number;
|
|
14
|
+
/** The router a swap is signed on. It is also the token approval spender. */
|
|
15
|
+
readonly router: Hex;
|
|
16
|
+
/** The factory whose pools the router trades against. */
|
|
17
|
+
readonly factory: Hex;
|
|
18
|
+
/** The swap fee in basis points: 29 for PulseX, 30 for Uniswap V2 forks. */
|
|
19
|
+
readonly feeBasisPoints: number;
|
|
20
|
+
/** A name for logs and displays. */
|
|
21
|
+
readonly label?: string;
|
|
22
|
+
};
|
|
23
|
+
/** The venues on each chain, keyed by numeric chain id. The first venue of a chain is its default. */
|
|
24
|
+
export type SwapVenues = Readonly<Record<number, readonly SwapVenue[]>>;
|
|
25
|
+
/**
|
|
26
|
+
* Merges caller venues over {@link defaultSwapVenues}. A chain present in
|
|
27
|
+
* `overrides` replaces the default list for that chain. A chain absent from
|
|
28
|
+
* `overrides` keeps its default list. An empty list switches swaps off on
|
|
29
|
+
* that chain. Neither input is changed; the result is frozen.
|
|
30
|
+
*
|
|
31
|
+
* @param overrides - the caller's venues, or undefined for the defaults
|
|
32
|
+
* @returns the venues to use
|
|
33
|
+
*/
|
|
34
|
+
export declare const resolveSwapVenues: (overrides?: SwapVenues) => SwapVenues;
|
|
35
|
+
/**
|
|
36
|
+
* Finds the venue on a chain that has the given router.
|
|
37
|
+
*
|
|
38
|
+
* @param venues - the venues to search
|
|
39
|
+
* @param chainId - the numeric chain id
|
|
40
|
+
* @param router - the router address, in any letter case
|
|
41
|
+
* @returns the venue, or null when the chain has no venue with that router
|
|
42
|
+
*/
|
|
43
|
+
export declare const findSwapVenue: (venues: SwapVenues, chainId: number, router: Hex) => SwapVenue | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findSwapVenue = exports.resolveSwapVenues = void 0;
|
|
4
|
+
const config_js_1 = require("./config.js");
|
|
5
|
+
/**
|
|
6
|
+
* Merges caller venues over {@link defaultSwapVenues}. A chain present in
|
|
7
|
+
* `overrides` replaces the default list for that chain. A chain absent from
|
|
8
|
+
* `overrides` keeps its default list. An empty list switches swaps off on
|
|
9
|
+
* that chain. Neither input is changed; the result is frozen.
|
|
10
|
+
*
|
|
11
|
+
* @param overrides - the caller's venues, or undefined for the defaults
|
|
12
|
+
* @returns the venues to use
|
|
13
|
+
*/
|
|
14
|
+
const resolveSwapVenues = (overrides) => {
|
|
15
|
+
if (overrides === undefined)
|
|
16
|
+
return config_js_1.defaultSwapVenues;
|
|
17
|
+
return Object.freeze({ ...config_js_1.defaultSwapVenues, ...overrides });
|
|
18
|
+
};
|
|
19
|
+
exports.resolveSwapVenues = resolveSwapVenues;
|
|
20
|
+
/**
|
|
21
|
+
* Finds the venue on a chain that has the given router.
|
|
22
|
+
*
|
|
23
|
+
* @param venues - the venues to search
|
|
24
|
+
* @param chainId - the numeric chain id
|
|
25
|
+
* @param router - the router address, in any letter case
|
|
26
|
+
* @returns the venue, or null when the chain has no venue with that router
|
|
27
|
+
*/
|
|
28
|
+
const findSwapVenue = (venues, chainId, router) => venues[chainId]?.find((venue) => venue.router.toLowerCase() === router.toLowerCase()) ?? null;
|
|
29
|
+
exports.findSwapVenue = findSwapVenue;
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Hex } from 'viem';
|
|
2
|
+
import type { SwapVenues } from './swap-venues.js';
|
|
2
3
|
import type { BridgeKey, Token, TokenOut } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
|
|
@@ -59,8 +60,22 @@ export declare const pulsechainTokenAddresses: Readonly<{
|
|
|
59
60
|
}>;
|
|
60
61
|
/** A map of chains to deprecated native asset out addresses */
|
|
61
62
|
export declare const deprecatedNativeAssetOut: Partial<Record<Chains, Set<Hex>>>;
|
|
62
|
-
/**
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The swap venues the package ships with: PulseX V1 then V2 on PulseChain.
|
|
65
|
+
* Frozen. A caller who wants other routers passes its own
|
|
66
|
+
* {@link SwapVenues} (see `resolveSwapVenues`); this table is never mutated.
|
|
67
|
+
*
|
|
68
|
+
* Each factory is the router's own `router.factory()` read on chain — see
|
|
69
|
+
* {@link uniV2Factories}'s doc. The fee is 29 basis points for both
|
|
70
|
+
* deployments (measured, same doc).
|
|
71
|
+
*/
|
|
72
|
+
export declare const defaultSwapVenues: SwapVenues;
|
|
73
|
+
/**
|
|
74
|
+
* A map of chains to uni v2 router addresses. Frozen. PulseChain's entries
|
|
75
|
+
* derive from {@link defaultSwapVenues}, so the two cannot drift. Kept for
|
|
76
|
+
* compatibility; to swap on other routers pass `swapVenues` instead.
|
|
77
|
+
*/
|
|
78
|
+
export declare const uniV2Routers: Readonly<Record<Chains, readonly Hex[]>>;
|
|
64
79
|
/**
|
|
65
80
|
* Each {@link uniV2Routers} entry's OWN factory address, same index, same
|
|
66
81
|
* chain — `router.factory()` read live from `one.valve.city` on 2026-09-24.
|
|
@@ -85,7 +100,7 @@ export declare const uniV2Routers: Record<Chains, Hex[]>;
|
|
|
85
100
|
* salt collision) and worth this note so a future reader does not assume a
|
|
86
101
|
* transcription error and "fix" it back to something wrong.
|
|
87
102
|
*/
|
|
88
|
-
export declare const uniV2Factories: Record<Chains, Hex[]
|
|
103
|
+
export declare const uniV2Factories: Readonly<Record<Chains, readonly Hex[]>>;
|
|
89
104
|
/** A map of chains to native token symbols */
|
|
90
105
|
export declare const nativeTokenSymbol: Record<Chains, string>;
|
|
91
106
|
/** A map of chains to native token names */
|
package/dist/config.js
CHANGED
|
@@ -71,12 +71,13 @@ export const pulsechainTokenAddresses = Object.freeze({
|
|
|
71
71
|
export const deprecatedNativeAssetOut = {
|
|
72
72
|
[Chains.PLS]: new Set(['0x97Ac4a2439A47c07ad535bb1188c989dae755341']),
|
|
73
73
|
};
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Routers on chains where this package knows the router but has not verified
|
|
76
|
+
* a factory, so no {@link SwapVenue} can be built for them. They stay in
|
|
77
|
+
* {@link uniV2Routers} for the native-price reads (`chain-info.ts`,
|
|
78
|
+
* `readAmountOut`) that need only a router.
|
|
79
|
+
*/
|
|
80
|
+
const routersWithoutVenue = {
|
|
80
81
|
[Chains.ETH]: ['0x7a250d5630b4cf539739df2c5dacb4c659f2488d'],
|
|
81
82
|
[Chains.BNB]: ['0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'],
|
|
82
83
|
[Chains.SEP]: ['0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3'],
|
|
@@ -85,6 +86,43 @@ export const uniV2Routers = {
|
|
|
85
86
|
'0x636f6407B90661b73b1C0F7e24F4C79f624d0738',
|
|
86
87
|
],
|
|
87
88
|
};
|
|
89
|
+
/**
|
|
90
|
+
* The swap venues the package ships with: PulseX V1 then V2 on PulseChain.
|
|
91
|
+
* Frozen. A caller who wants other routers passes its own
|
|
92
|
+
* {@link SwapVenues} (see `resolveSwapVenues`); this table is never mutated.
|
|
93
|
+
*
|
|
94
|
+
* Each factory is the router's own `router.factory()` read on chain — see
|
|
95
|
+
* {@link uniV2Factories}'s doc. The fee is 29 basis points for both
|
|
96
|
+
* deployments (measured, same doc).
|
|
97
|
+
*/
|
|
98
|
+
export const defaultSwapVenues = Object.freeze({
|
|
99
|
+
[Number(Chains.PLS)]: Object.freeze([
|
|
100
|
+
Object.freeze({
|
|
101
|
+
kind: 'uniswap-v2',
|
|
102
|
+
chainId: Number(Chains.PLS),
|
|
103
|
+
router: '0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02',
|
|
104
|
+
factory: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
105
|
+
feeBasisPoints: 29,
|
|
106
|
+
label: 'PulseX V1',
|
|
107
|
+
}),
|
|
108
|
+
Object.freeze({
|
|
109
|
+
kind: 'uniswap-v2',
|
|
110
|
+
chainId: Number(Chains.PLS),
|
|
111
|
+
router: '0x165C3410fC91EF562C50559f7d2289fEbed552d9',
|
|
112
|
+
factory: '0x29eA7545DEf87022BAdc76323F373EA1e707C523',
|
|
113
|
+
feeBasisPoints: 29,
|
|
114
|
+
label: 'PulseX V2',
|
|
115
|
+
}),
|
|
116
|
+
]),
|
|
117
|
+
});
|
|
118
|
+
/** One chain's routers: its default venues' routers, else the router-only entries. */
|
|
119
|
+
const defaultRoutersFor = (chain) => defaultSwapVenues[Number(chain)]?.map((venue) => venue.router) ?? routersWithoutVenue[chain] ?? [];
|
|
120
|
+
/**
|
|
121
|
+
* A map of chains to uni v2 router addresses. Frozen. PulseChain's entries
|
|
122
|
+
* derive from {@link defaultSwapVenues}, so the two cannot drift. Kept for
|
|
123
|
+
* compatibility; to swap on other routers pass `swapVenues` instead.
|
|
124
|
+
*/
|
|
125
|
+
export const uniV2Routers = Object.freeze(Object.fromEntries(Object.values(Chains).map((chain) => [chain, Object.freeze([...defaultRoutersFor(chain)])])));
|
|
88
126
|
/**
|
|
89
127
|
* Each {@link uniV2Routers} entry's OWN factory address, same index, same
|
|
90
128
|
* chain — `router.factory()` read live from `one.valve.city` on 2026-09-24.
|
|
@@ -109,16 +147,10 @@ export const uniV2Routers = {
|
|
|
109
147
|
* salt collision) and worth this note so a future reader does not assume a
|
|
110
148
|
* transcription error and "fix" it back to something wrong.
|
|
111
149
|
*/
|
|
112
|
-
export const uniV2Factories =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
],
|
|
117
|
-
[Chains.ETH]: [],
|
|
118
|
-
[Chains.BNB]: [],
|
|
119
|
-
[Chains.SEP]: [],
|
|
120
|
-
[Chains.V4PLS]: [],
|
|
121
|
-
};
|
|
150
|
+
export const uniV2Factories = Object.freeze(Object.fromEntries(Object.values(Chains).map((chain) => [
|
|
151
|
+
chain,
|
|
152
|
+
Object.freeze((defaultSwapVenues[Number(chain)] ?? []).map((venue) => venue.factory)),
|
|
153
|
+
])));
|
|
122
154
|
/** A map of chains to native token symbols */
|
|
123
155
|
export const nativeTokenSymbol = {
|
|
124
156
|
[Chains.PLS]: 'PLS',
|
package/dist/execution.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Hex, type PublicClient } from 'viem';
|
|
2
2
|
import type { TokenBridgeInfo } from './chain-info.js';
|
|
3
|
+
import { type SwapVenues } from './swap-venues.js';
|
|
3
4
|
import { type AggregatorOriginRefusal, type ConsolidatedQuote, type ConsolidatedQuoteRequest } from './consolidated-quote.js';
|
|
4
5
|
import type { FeeType } from './fee-type.js';
|
|
5
6
|
import type { RouteCandidate } from './routing/candidate.js';
|
|
@@ -20,7 +21,7 @@ import type { BridgeKey, Token } from './types.js';
|
|
|
20
21
|
* EVERY SPENDER IS ONE THIS CODE ALREADY KNOWS. Approvals are for the exact
|
|
21
22
|
* amount spent, never unlimited, and go only to:
|
|
22
23
|
* - the omnibridge pathway's `from` mediator, read from `config.ts`;
|
|
23
|
-
* - the
|
|
24
|
+
* - the swap router the edge was quoted on, found in the swap venues (the defaults in `config.ts` unless the caller passes its own);
|
|
24
25
|
* - an aggregator spender that `aggregatorOriginCalls` checked against a
|
|
25
26
|
* fixed table for its provider and chain (LI.FI's Diamond, Relay's
|
|
26
27
|
* depository). A quote cannot name its own spender.
|
|
@@ -212,12 +213,15 @@ export type ExecutionPlan = {
|
|
|
212
213
|
* @param options.account - the account that signs every transaction
|
|
213
214
|
* @param options.legs - the legs, in the order the wallet signs them
|
|
214
215
|
* @param options.readAllowance - reads an allowance on chain; see {@link AllowanceReader}
|
|
216
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults; a swap whose router is in none of them throws
|
|
215
217
|
* @returns the ordered transactions, or the first leg that cannot be planned
|
|
218
|
+
* @throws when a swap leg's router is not in the swap venues
|
|
216
219
|
*/
|
|
217
|
-
export declare const planExecution: ({ account, legs, readAllowance, }: {
|
|
220
|
+
export declare const planExecution: ({ account, legs, readAllowance, swapVenues, }: {
|
|
218
221
|
readonly account: Hex;
|
|
219
222
|
readonly legs: readonly ExecutionLeg[];
|
|
220
223
|
readonly readAllowance: AllowanceReader;
|
|
224
|
+
readonly swapVenues?: SwapVenues;
|
|
221
225
|
}) => Promise<ExecutionPlan>;
|
|
222
226
|
/**
|
|
223
227
|
* What the caller supplies for one signature section of a candidate. Each
|
|
@@ -270,6 +274,12 @@ export type CandidateExecutionInputs = {
|
|
|
270
274
|
readonly readAllowance: AllowanceReader;
|
|
271
275
|
/** The resolver the candidate was built with; turns a plan's graph node into its token. */
|
|
272
276
|
readonly resolveToken: ResolveNodeToken;
|
|
277
|
+
/**
|
|
278
|
+
* The routers swaps may sign on, merged over the defaults: a chain named
|
|
279
|
+
* here replaces that chain's default list. Pass the SAME venues the route
|
|
280
|
+
* search used. A swap whose router is in none of them throws.
|
|
281
|
+
*/
|
|
282
|
+
readonly swapVenues?: SwapVenues;
|
|
273
283
|
/**
|
|
274
284
|
* The index of the section that `sections[0]` plans. It defaults to 0.
|
|
275
285
|
* Pass it to plan a later section after the earlier ones landed, with the
|
|
@@ -337,10 +347,13 @@ export declare const planCandidateExecution: (inputs: CandidateExecutionInputs)
|
|
|
337
347
|
* @param options.hop - the swap hop; its recorded path and edge name what to read
|
|
338
348
|
* @param options.amountIn - the amount the swap spends, in base units
|
|
339
349
|
* @param options.client - a client that can call `readContract`
|
|
340
|
-
* @
|
|
350
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults
|
|
351
|
+
* @returns the amount the last token of the path delivers, or null when the hop records no path, the amount is not positive, or the read fails or returns nothing
|
|
352
|
+
* @throws when the hop records a path but its router is not in the swap venues
|
|
341
353
|
*/
|
|
342
|
-
export declare const readPulseXSectionQuote: ({ hop, amountIn, client, }: {
|
|
354
|
+
export declare const readPulseXSectionQuote: ({ hop, amountIn, client, swapVenues, }: {
|
|
343
355
|
readonly hop: Pick<PlannedHop, "edge" | "outcome">;
|
|
344
356
|
readonly amountIn: bigint;
|
|
345
357
|
readonly client: Pick<PublicClient, "readContract">;
|
|
358
|
+
readonly swapVenues?: SwapVenues;
|
|
346
359
|
}) => Promise<bigint | null>;
|