@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.
Files changed (44) hide show
  1. package/README.md +52 -1
  2. package/dist/cjs/config.d.ts +18 -3
  3. package/dist/cjs/config.js +49 -17
  4. package/dist/cjs/execution.d.ts +17 -4
  5. package/dist/cjs/execution.js +40 -13
  6. package/dist/cjs/index.d.ts +1 -0
  7. package/dist/cjs/index.js +1 -0
  8. package/dist/cjs/prices.d.ts +10 -0
  9. package/dist/cjs/prices.js +54 -2
  10. package/dist/cjs/routing/carrier-provider-key.d.ts +31 -0
  11. package/dist/cjs/routing/carrier-provider-key.js +42 -0
  12. package/dist/cjs/routing/carriers/pulsex.d.ts +50 -25
  13. package/dist/cjs/routing/carriers/pulsex.js +135 -85
  14. package/dist/cjs/routing/index.d.ts +6 -2
  15. package/dist/cjs/routing/index.js +9 -1
  16. package/dist/cjs/routing/route-data.d.ts +47 -5
  17. package/dist/cjs/routing/search/plan-route-data.d.ts +17 -1
  18. package/dist/cjs/routing/search/plan-route-data.js +30 -7
  19. package/dist/cjs/routing/swap-transactions.d.ts +7 -3
  20. package/dist/cjs/routing/swap-transactions.js +6 -4
  21. package/dist/cjs/swap-venues.d.ts +43 -0
  22. package/dist/cjs/swap-venues.js +29 -0
  23. package/dist/config.d.ts +18 -3
  24. package/dist/config.js +48 -16
  25. package/dist/execution.d.ts +17 -4
  26. package/dist/execution.js +41 -14
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1 -0
  29. package/dist/prices.d.ts +10 -0
  30. package/dist/prices.js +52 -1
  31. package/dist/routing/carrier-provider-key.d.ts +31 -0
  32. package/dist/routing/carrier-provider-key.js +37 -0
  33. package/dist/routing/carriers/pulsex.d.ts +50 -25
  34. package/dist/routing/carriers/pulsex.js +133 -84
  35. package/dist/routing/index.d.ts +6 -2
  36. package/dist/routing/index.js +3 -1
  37. package/dist/routing/route-data.d.ts +47 -5
  38. package/dist/routing/search/plan-route-data.d.ts +17 -1
  39. package/dist/routing/search/plan-route-data.js +30 -7
  40. package/dist/routing/swap-transactions.d.ts +7 -3
  41. package/dist/routing/swap-transactions.js +7 -5
  42. package/dist/swap-venues.d.ts +43 -0
  43. package/dist/swap-venues.js +24 -0
  44. package/package.json +3 -3
@@ -1,6 +1,7 @@
1
1
  import * as imageLinks from '../../image-links.js';
2
2
  import { chainDisplayName } from '../../chain-names.js';
3
3
  import { findProviderById } from '../../providers.js';
4
+ import { WRAP_CARRIER_ID, carrierProviderKey, carrierStepKind } from '../carrier-provider-key.js';
4
5
  import { describeDeferredCrossing } from '../../near-intents-route.js';
5
6
  import { candidateDeliveredUsd, candidateOutsideFeesUsd, candidatePaidUsd, pulsechainChainId, providerDisplayName, } from '../producers.js';
6
7
  import { assetNodeKey, planComposedBasis, } from './graph.js';
@@ -229,6 +230,8 @@ const hopProviderName = (carrier) => {
229
230
  return providerDisplayName(carrier);
230
231
  if (carrier === 'PulseX')
231
232
  return 'PulseX';
233
+ if (carrierStepKind(carrier) === 'wrap')
234
+ return 'Wrap';
232
235
  // TOTAL OVER EVERY CARRIER. The engine plans hops for carriers the provider
233
236
  // registry does not hold (a local wrap, a host's own plug-in). Casting such
234
237
  // an id to a provider id and asking the throwing lookup crashed the whole
@@ -350,11 +353,13 @@ const hopIsAutomatic = (hopIndex, plan) => {
350
353
  * @param plan - the plan the hop belongs to
351
354
  * @param resolveToken - resolves a graph node to its full display token
352
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
353
357
  * @returns the raw hop data
354
358
  */
355
- const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue) => ({
359
+ const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue, gas) => ({
356
360
  providerName: hopProviderName(hop.edge.carrier),
357
- providerKey: hop.edge.carrier,
361
+ providerKey: carrierProviderKey(hop.edge.carrier),
362
+ stepKind: carrierStepKind(hop.edge.carrier),
358
363
  title: hopTitle(hop, hopIndex, plan, resolveToken),
359
364
  conversion: hopConversion(hop, hopIndex, plan, resolveToken),
360
365
  fromToken: toRouteTokenRef(hop.edge.from, resolveToken),
@@ -362,6 +367,7 @@ const toRouteHopData = (hop, hopIndex, plan, resolveToken, feeUsdValue) => ({
362
367
  // The per-hop dollar figure is a caller-supplied input (see this module's
363
368
  // head doc); the caller passes it in, never derived here.
364
369
  feeUsdValue,
370
+ ...(gas === null ? {} : gas),
365
371
  automatic: hopIsAutomatic(hopIndex, plan),
366
372
  });
367
373
  /**
@@ -502,8 +508,6 @@ export const derivePlanKind = (plan) => {
502
508
  * @param carrier - the plan's headline carrier
503
509
  * @returns the same value, typed as `ProviderKey`
504
510
  */
505
- /** The carrier id of the native-coin wrap step (`routing/carriers/wrap.ts`). */
506
- const WRAP_CARRIER = 'wrap';
507
511
  const toHeadlineProviderKey = (carrier) => {
508
512
  if (isBridgeCarrier(carrier))
509
513
  return carrier;
@@ -535,7 +539,7 @@ export const deriveHeadlineProviderKeyFromPlan = (plan) => toHeadlineProviderKey
535
539
  * @param plan - the plan to read
536
540
  * @returns the carrier to headline
537
541
  */
538
- const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !== WRAP_CARRIER)?.edge.carrier ?? 'PulseX';
542
+ const firstHeadlineCarrier = (plan) => plan.hops.find((hop) => hop.edge.carrier !== WRAP_CARRIER_ID)?.edge.carrier ?? 'PulseX';
539
543
  /**
540
544
  * The anchor a two-signature plan leaves the reader holding between its
541
545
  * first and second groups — `RouteCandidate.plan`'s own TSDoc: "the anchor
@@ -654,10 +658,13 @@ export const buildRouteDataFromPlan = (inputs) => {
654
658
  const landing = resolveNodeWithAssertedDecimals(lastHop.edge.to, resolveToken);
655
659
  const landingIsUnpriced = hopOutcomeIsUnpriced(lastHop);
656
660
  const hopFeeUsdValues = groupHopFeeUsdValues(pricing, group, groupIndex);
661
+ const statesGasSplit = pricing.feeExcludingGasUsdValue !== undefined;
662
+ const hopFeeExcludingGasUsdValues = pricing.hopFeeExcludingGasUsdValues;
657
663
  return {
658
664
  id: `${routeId}-signature-${groupIndex + 1}`,
659
665
  label: sectionLabel(group, groupIndex, plan),
660
- providerKey: hops[0].edge.carrier,
666
+ providerKey: carrierProviderKey(hops[0].edge.carrier),
667
+ stepKind: carrierStepKind(hops[0].edge.carrier),
661
668
  tokenFlow: buildGroupTokenFlow(hops, resolveToken),
662
669
  landing: {
663
670
  amount: sectionLandingAmount(pricing, lastHop),
@@ -667,10 +674,19 @@ export const buildRouteDataFromPlan = (inputs) => {
667
674
  estimateBasis: hopEstimateBasis(lastHop),
668
675
  },
669
676
  feeUsdValue: pricing.feeUsdValue,
677
+ ...(statesGasSplit
678
+ ? { feeExcludingGasUsdValue: pricing.feeExcludingGasUsdValue, gasUsdValue: pricing.gasUsdValue ?? null }
679
+ : {}),
670
680
  fees: pricing.fees ? [...pricing.fees] : undefined,
671
681
  etaMinutes: pricing.etaMinutes,
672
682
  etaSeconds: pricing.etaSeconds,
673
- hops: group.map((hopIndex, positionInGroup) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken, hopFeeUsdValues[positionInGroup] ?? null)),
683
+ hops: group.map((hopIndex, positionInGroup) => toRouteHopData(plan.hops[hopIndex], hopIndex, plan, resolveToken, hopFeeUsdValues[positionInGroup] ?? null, statesGasSplit
684
+ ? {
685
+ feeExcludingGasUsdValue: hopFeeExcludingGasUsdValues?.[positionInGroup] ??
686
+ (group.length === 1 ? (pricing.feeExcludingGasUsdValue ?? null) : null),
687
+ gasUsdValue: positionInGroup === 0 ? (pricing.gasUsdValue ?? null) : null,
688
+ }
689
+ : null)),
674
690
  deferred: groupIndex > 0 ? true : undefined,
675
691
  deferredNote: pricing.deferredNote,
676
692
  };
@@ -689,6 +705,13 @@ export const buildRouteDataFromPlan = (inputs) => {
689
705
  estimateBasis: lastSection.landing?.estimateBasis ?? null,
690
706
  },
691
707
  totalCostUsdValue,
708
+ ...(inputs.costExcludingGasUsdValue === undefined
709
+ ? {}
710
+ : {
711
+ costExcludingGasUsdValue: inputs.costExcludingGasUsdValue,
712
+ gasCostUsdValue: inputs.gasCostUsdValue ?? null,
713
+ gasByChain: [...(inputs.gasByChain ?? [])],
714
+ }),
692
715
  netValueUsd,
693
716
  etaMinutes,
694
717
  // 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 (`pulseXRouterForEdge`: V1 or V2, each with its own pools). The
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, and
39
- * the resolved router address
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
@@ -1,6 +1,7 @@
1
1
  import { encodeFunctionData, isAddressEqual, parseAbi, zeroAddress } from 'viem';
2
2
  import { pulseXSwapTransactionInputs } from '../settings.js';
3
- import { pulseXRouterForEdge, pulseXSwapPathForHop } from './carriers/pulsex.js';
3
+ import { defaultSwapVenues } from '../config.js';
4
+ import { pulseXSwapPathForHop, venueForEdge } from './carriers/pulsex.js';
4
5
  /**
5
6
  * The minimal WETH9-style wrap/unwrap interface: `deposit()` mints the
6
7
  * wrapped token for the native coin sent with the call, `withdraw(uint256)`
@@ -29,7 +30,7 @@ const wrapAbi = parseAbi(['function deposit() payable', 'function withdraw(uint2
29
30
  * looking at) and the native-in/native-out/token-token branching.
30
31
  *
31
32
  * SIGNS ON THE EDGE'S OWN ROUTER. A PulseX edge names the deployment it was
32
- * quoted on (`pulseXRouterForEdge`: V1 or V2, each with its own pools). The
33
+ * quoted on (`venueForEdge`: V1 or V2, each with its own pools). The
33
34
  * caller's `routerAddress` must be that same router — it is also the router
34
35
  * the caller approved to spend the input token — or nothing is built. A V2
35
36
  * quote signed on V1 would trade against a different pool than the one the
@@ -37,8 +38,9 @@ const wrapAbi = parseAbi(['function deposit() payable', 'function withdraw(uint2
37
38
  *
38
39
  * @param options - the hop to swap, the amount and slippage-adjusted
39
40
  * minimum, who receives the output, the signing deadline, what the caller
40
- * believes this signature does (`signedLeg`), the chain it executes on, and
41
- * the resolved router address
41
+ * believes this signature does (`signedLeg`), the chain it executes on, the
42
+ * resolved router address, and the swap venues the router must belong to
43
+ * (the defaults when omitted)
42
44
  * @returns the swap transaction inputs, or null when the hop records no
43
45
  * PulseX swap path, names no known router, `routerAddress` is not the
44
46
  * edge's own router, the leg on screen disagrees, or any of
@@ -51,7 +53,7 @@ export const buildPulseXSwapTransaction = (options) => {
51
53
  if (swapPath === null) {
52
54
  return null;
53
55
  }
54
- const quotedRouter = pulseXRouterForEdge(edge);
56
+ const quotedRouter = venueForEdge(options.venues ?? defaultSwapVenues, edge)?.router ?? null;
55
57
  if (quotedRouter === null || routerAddress === null || !isAddressEqual(quotedRouter, routerAddress)) {
56
58
  return null;
57
59
  }
@@ -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,24 @@
1
+ import { defaultSwapVenues } from './config.js';
2
+ /**
3
+ * Merges caller venues over {@link defaultSwapVenues}. A chain present in
4
+ * `overrides` replaces the default list for that chain. A chain absent from
5
+ * `overrides` keeps its default list. An empty list switches swaps off on
6
+ * that chain. Neither input is changed; the result is frozen.
7
+ *
8
+ * @param overrides - the caller's venues, or undefined for the defaults
9
+ * @returns the venues to use
10
+ */
11
+ export const resolveSwapVenues = (overrides) => {
12
+ if (overrides === undefined)
13
+ return defaultSwapVenues;
14
+ return Object.freeze({ ...defaultSwapVenues, ...overrides });
15
+ };
16
+ /**
17
+ * Finds the venue on a chain that has the given router.
18
+ *
19
+ * @param venues - the venues to search
20
+ * @param chainId - the numeric chain id
21
+ * @param router - the router address, in any letter case
22
+ * @returns the venue, or null when the chain has no venue with that router
23
+ */
24
+ export const findSwapVenue = (venues, chainId, router) => venues[chainId]?.find((venue) => venue.router.toLowerCase() === router.toLowerCase()) ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gibs/bridge-sdk",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Build and price cross-chain transfers between PulseChain, Ethereum, and Binance Smart Chain.",
5
5
  "keywords": [
6
6
  "ethereum",
@@ -1007,8 +1007,8 @@
1007
1007
  "test:live:near-intents": "../../scripts/near-intents-live.sh"
1008
1008
  },
1009
1009
  "dependencies": {
1010
- "@gibs/abis": "^1.17.0",
1011
- "@gibs/common": "^1.17.0",
1010
+ "@gibs/abis": "^1.18.0",
1011
+ "@gibs/common": "^1.18.0",
1012
1012
  "@ton/core": "^0.63.1",
1013
1013
  "@ton/crypto": "^3.3.0"
1014
1014
  },