@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
package/dist/execution.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { zeroAddress } from 'viem';
2
2
  import * as abis from './abis.js';
3
3
  import { Providers, toChain } from './config.js';
4
+ import { resolveSwapVenues } from './swap-venues.js';
4
5
  import { aggregatorOriginCalls, } from './consolidated-quote.js';
5
- import { pulseXRouterForEdge, pulseXSwapPathForHop } from './routing/carriers/pulsex.js';
6
+ import { pulseXSwapPathForHop, venueForEdge } from './routing/carriers/pulsex.js';
6
7
  import { buildPulseXSwapTransaction, buildWrapTransaction, swapMinimumOut } from './routing/swap-transactions.js';
7
8
  import { approvalAmounts, approveCall, bridgePathway, destinationDataParam, feeDirectorStructEncoded, feeTypeSettings, omnibridgeApproval, transactionInputs, } from './settings.js';
8
9
  /**
@@ -94,10 +95,30 @@ const omnibridgeCalls = (leg, account) => {
94
95
  delivery: deliveryTermsOf(leg.delivery),
95
96
  };
96
97
  };
97
- const pulseXSwapCalls = (leg) => {
98
+ /**
99
+ * The venue a swap edge was priced on, from the venues execution was given.
100
+ * A router that is not in those venues THROWS: signing on another router, on
101
+ * PulseX most of all, would trade against pools the reader was never shown.
102
+ *
103
+ * @param venues - the venues passed to execution
104
+ * @param edge - the swap edge
105
+ * @returns the venue
106
+ * @throws when the edge's router is not in `venues`, or its chain has no venue
107
+ */
108
+ const requireSwapVenue = (venues, edge) => {
109
+ const venue = venueForEdge(venues, edge);
110
+ if (venue !== null)
111
+ return venue;
112
+ const router = edge.venue ?? 'the default router';
113
+ throw new Error(`The swap was priced on ${router} on chain ${edge.from.chainId}, but that router is not in the swap venues passed to execution. ` +
114
+ 'Pass the same swapVenues the route search used.');
115
+ };
116
+ const pulseXSwapCalls = (leg, venues) => {
98
117
  const { edge } = leg.hop;
99
118
  const chainId = edge.from.chainId;
100
- const routerAddress = pulseXRouterForEdge(edge);
119
+ if (edge.carrier !== 'PulseX')
120
+ return refusal({ reason: 'swap-refused' });
121
+ const routerAddress = requireSwapVenue(venues, edge).router;
101
122
  // The builder refuses a hop that records no priced path, a router other
102
123
  // than the edge's own, and a leg on screen that disagrees.
103
124
  const call = buildPulseXSwapTransaction({
@@ -109,8 +130,9 @@ const pulseXSwapCalls = (leg) => {
109
130
  signedLeg: { kind: 'swap', chainId },
110
131
  chainId,
111
132
  routerAddress,
133
+ venues,
112
134
  });
113
- if (call === null || routerAddress === null)
135
+ if (call === null)
114
136
  return refusal({ reason: 'swap-refused' });
115
137
  const spendsNative = edge.from.address.toLowerCase() === zeroAddress;
116
138
  return {
@@ -154,11 +176,11 @@ const aggregatorCalls = (leg) => {
154
176
  delivery: null,
155
177
  };
156
178
  };
157
- const legCalls = (leg, account) => {
179
+ const legCalls = (leg, account, venues) => {
158
180
  if (leg.kind === 'omnibridge')
159
181
  return omnibridgeCalls(leg, account);
160
182
  if (leg.kind === 'pulsex-swap')
161
- return pulseXSwapCalls(leg);
183
+ return pulseXSwapCalls(leg, venues);
162
184
  if (leg.kind === 'wrap')
163
185
  return wrapCalls(leg);
164
186
  return aggregatorCalls(leg);
@@ -183,10 +205,13 @@ const approvalTransactions = async ({ approval, account, readAllowance, }) => {
183
205
  * @param options.account - the account that signs every transaction
184
206
  * @param options.legs - the legs, in the order the wallet signs them
185
207
  * @param options.readAllowance - reads an allowance on chain; see {@link AllowanceReader}
208
+ * @param options.swapVenues - the routers swaps may sign on, merged over the defaults; a swap whose router is in none of them throws
186
209
  * @returns the ordered transactions, or the first leg that cannot be planned
210
+ * @throws when a swap leg's router is not in the swap venues
187
211
  */
188
- export const planExecution = async ({ account, legs, readAllowance, }) => {
189
- const calls = legs.map((leg) => legCalls(leg, account));
212
+ export const planExecution = async ({ account, legs, readAllowance, swapVenues, }) => {
213
+ const venues = resolveSwapVenues(swapVenues);
214
+ const calls = legs.map((leg) => legCalls(leg, account, venues));
190
215
  const refusedIndex = calls.findIndex((entry) => entry.kind === 'refused');
191
216
  const refused = calls[refusedIndex];
192
217
  if (refused !== undefined && refused.kind === 'refused') {
@@ -363,7 +388,7 @@ const sectionWindowProblem = ({ firstSectionIndex, plannedCount, sectionCount, }
363
388
  * @returns the ordered transactions with the deliveries they buy, or the first section that cannot be planned
364
389
  */
365
390
  export const planCandidateExecution = async (inputs) => {
366
- const { candidate, account, readAllowance, resolveToken, sections } = inputs;
391
+ const { candidate, account, readAllowance, resolveToken, sections, swapVenues } = inputs;
367
392
  const firstSectionIndex = inputs.firstSectionIndex ?? 0;
368
393
  const plan = candidate.plan;
369
394
  const allSections = plan === undefined ? null : planSections(plan);
@@ -386,7 +411,7 @@ export const planCandidateExecution = async (inputs) => {
386
411
  return { kind: 'refused', sectionIndex: firstSectionIndex + refusedIndex, refusal: refused.refusal };
387
412
  }
388
413
  const legs = sectionLegs.flatMap((entry) => (entry.kind === 'leg' ? [entry.leg] : []));
389
- const planned = await planExecution({ account, legs, readAllowance });
414
+ const planned = await planExecution({ account, legs, readAllowance, ...(swapVenues === undefined ? {} : { swapVenues }) });
390
415
  if (planned.kind === 'refused') {
391
416
  return { kind: 'refused', sectionIndex: firstSectionIndex + planned.legIndex, refusal: planned.refusal };
392
417
  }
@@ -406,13 +431,15 @@ export const planCandidateExecution = async (inputs) => {
406
431
  * @param options.hop - the swap hop; its recorded path and edge name what to read
407
432
  * @param options.amountIn - the amount the swap spends, in base units
408
433
  * @param options.client - a client that can call `readContract`
409
- * @returns the amount the last token of the path delivers, or null when the hop records no path, names no known router, the amount is not positive, or the read fails or returns nothing
434
+ * @param options.swapVenues - the routers swaps may sign on, merged over the defaults
435
+ * @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
436
+ * @throws when the hop records a path but its router is not in the swap venues
410
437
  */
411
- export const readPulseXSectionQuote = async ({ hop, amountIn, client, }) => {
438
+ export const readPulseXSectionQuote = async ({ hop, amountIn, client, swapVenues, }) => {
412
439
  const path = pulseXSwapPathForHop(hop);
413
- const router = pulseXRouterForEdge(hop.edge);
414
- if (path === null || router === null || amountIn <= 0n)
440
+ if (path === null || amountIn <= 0n)
415
441
  return null;
442
+ const router = requireSwapVenue(resolveSwapVenues(swapVenues), hop.edge).router;
416
443
  try {
417
444
  const amounts = await client.readContract({
418
445
  address: router,
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export * from './referral.js';
17
17
  export * from './route-steps.js';
18
18
  export * from './routing/index.js';
19
19
  export * from './settings.js';
20
+ export * from './swap-venues.js';
20
21
  export * from './types.js';
21
22
  export * from './claims.js';
22
23
  export * from './execution.js';
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ export * from './referral.js';
17
17
  export * from './route-steps.js';
18
18
  export * from './routing/index.js';
19
19
  export * from './settings.js';
20
+ export * from './swap-venues.js';
20
21
  export * from './types.js';
21
22
  export * from './claims.js';
22
23
  export * from './execution.js';
package/dist/prices.d.ts CHANGED
@@ -105,3 +105,13 @@ export declare const resolveTokenPriceUsd: (token: Token, { pulsexPrices, lifiPr
105
105
  pulsexPrices: Map<string, number>;
106
106
  lifiPrices: Map<string, number>;
107
107
  }) => number | null;
108
+ /**
109
+ * The chain's canonical wrapped native address when `token` is the native
110
+ * coin (zero address) or that wrapper itself, and null for every other token.
111
+ * Only the SAME chain's pair is returned: a bridged copy of a wrapper on
112
+ * another chain is a different token and keeps its own price.
113
+ *
114
+ * @param token - the token to test
115
+ * @returns the wrapped native address on the token's own chain, or null
116
+ */
117
+ export declare const sameChainWrappedNative: (token: Token) => Hex | null;
package/dist/prices.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { formatUnits } from 'viem';
2
- import { Chains, nativeAssetOut } from './config.js';
2
+ import { Chains, nativeAssetOut, toChain } from './config.js';
3
3
  /**
4
4
  * PulseChain mainnet numeric chain id (`Chains.PLS` = `0x171`). Tokens on this
5
5
  * chain (and its testnet sibling below) are priced on-chain via PulseX rather
@@ -138,6 +138,57 @@ export const computeUsdPriceFromAmountsOut = ({ results, stableDecimals = PULSEC
138
138
  * @returns the USD price as a number, or null when no source can price it
139
139
  */
140
140
  export const resolveTokenPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
141
+ const wrappedNative = sameChainWrappedNative(token);
142
+ if (wrappedNative !== null) {
143
+ return resolveNativeFamilyPriceUsd(token, wrappedNative, { pulsexPrices, lifiPrices });
144
+ }
145
+ return resolveOwnPriceUsd(token, { pulsexPrices, lifiPrices });
146
+ };
147
+ /**
148
+ * The chain's canonical wrapped native address when `token` is the native
149
+ * coin (zero address) or that wrapper itself, and null for every other token.
150
+ * Only the SAME chain's pair is returned: a bridged copy of a wrapper on
151
+ * another chain is a different token and keeps its own price.
152
+ *
153
+ * @param token - the token to test
154
+ * @returns the wrapped native address on the token's own chain, or null
155
+ */
156
+ export const sameChainWrappedNative = (token) => {
157
+ const wrapped = nativeAssetOut[toChain(token.chainId)];
158
+ if (wrapped === undefined) {
159
+ return null;
160
+ }
161
+ const isNativeCoin = sameAddress(token.address, NATIVE_TOKEN_ADDRESS);
162
+ return isNativeCoin || sameAddress(token.address, wrapped) ? wrapped : null;
163
+ };
164
+ /**
165
+ * One price for a native coin and its canonical wrapper. The contract wraps
166
+ * and unwraps 1:1, so two prices would show a value loss on a wrap step that
167
+ * loses nothing.
168
+ *
169
+ * Off PulseChain the native coin's market feed (LI.FI) wins. The wrapper's
170
+ * own price there is the PulseX price of its bridged copy (it carries a
171
+ * PulseChain origin link), which is a derivative market with its own
172
+ * premium or discount. The coin's feed prices the underlying asset. When the
173
+ * feed has no native price, the wrapper's feed price, then its own resolved
174
+ * price, stand in.
175
+ *
176
+ * On PulseChain both are priced by PulseX through the WPLS path, so the
177
+ * wrapper's entry is used and the coin's entry stands in when the wrapper was
178
+ * not in the priced set.
179
+ */
180
+ const resolveNativeFamilyPriceUsd = (token, wrappedNative, maps) => {
181
+ const wrapped = { ...token, address: wrappedNative };
182
+ if (isPulsechain(token.chainId)) {
183
+ return (resolveOwnPriceUsd(wrapped, maps) ??
184
+ resolveOwnPriceUsd({ ...token, address: NATIVE_TOKEN_ADDRESS }, maps));
185
+ }
186
+ return (maps.lifiPrices.get(lifiPriceKey(token.chainId, NATIVE_TOKEN_ADDRESS)) ??
187
+ maps.lifiPrices.get(lifiPriceKey(token.chainId, wrappedNative)) ??
188
+ resolveOwnPriceUsd(wrapped, maps));
189
+ };
190
+ /** The token's price by its own address, with no native-wrapper pairing. */
191
+ const resolveOwnPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
141
192
  if (isPulsechain(token.chainId)) {
142
193
  if (sameAddress(token.address, PULSECHAIN_USD_ANCHOR_ADDRESS)) {
143
194
  return 1;
@@ -0,0 +1,31 @@
1
+ import { type ProviderKey } from '../providers.js';
2
+ /**
3
+ * The carrier id of the native-coin wrap and unwrap step
4
+ * (`routing/carriers/wrap.ts`).
5
+ */
6
+ export declare const WRAP_CARRIER_ID = "wrap";
7
+ /**
8
+ * The steps a route can take that no third-party provider performs. A step of
9
+ * this kind is drawn from its own mark, never from a provider's.
10
+ */
11
+ export type CarrierStepKind = 'wrap';
12
+ /**
13
+ * The one place an engine carrier id becomes a {@link ProviderKey}.
14
+ *
15
+ * AN ENGINE CARRIER ID IS NOT A PROVIDER KEY. The engine is a plug-in host: a
16
+ * carrier id is any string, and one of them, `'wrap'`, names a step that no
17
+ * provider performs. Handing a raw id to the logo, glyph, or name tables drew a
18
+ * blank mark and logged an error. Route data resolves the key here, once, and
19
+ * every display site reads the resolved key.
20
+ *
21
+ * @param carrierId - the `carrier` of a planned hop's edge
22
+ * @returns the provider key, or null when no registered provider carries that id
23
+ */
24
+ export declare const carrierProviderKey: (carrierId: string) => ProviderKey | null;
25
+ /**
26
+ * The kind of provider-less step a carrier id performs.
27
+ *
28
+ * @param carrierId - the `carrier` of a planned hop's edge
29
+ * @returns the step kind, or null when the carrier is a provider or unknown
30
+ */
31
+ export declare const carrierStepKind: (carrierId: string) => CarrierStepKind | null;
@@ -0,0 +1,37 @@
1
+ import { Providers } from '../config.js';
2
+ import { providerRegistry } from '../providers.js';
3
+ /**
4
+ * The carrier id of the native-coin wrap and unwrap step
5
+ * (`routing/carriers/wrap.ts`).
6
+ */
7
+ export const WRAP_CARRIER_ID = 'wrap';
8
+ /**
9
+ * Every engine carrier id that IS a provider, mapped to its {@link ProviderKey}.
10
+ * The omnibridge crossings carry the provider value itself; the aggregators
11
+ * carry their registry id; the PulseChain exchange carries its own literal.
12
+ */
13
+ const providerKeyByCarrierId = new Map([
14
+ ...Object.values(Providers).map((provider) => [provider, provider]),
15
+ ...providerRegistry.map((descriptor) => [descriptor.id, descriptor.id]),
16
+ ['PulseX', 'PulseX'],
17
+ ]);
18
+ /**
19
+ * The one place an engine carrier id becomes a {@link ProviderKey}.
20
+ *
21
+ * AN ENGINE CARRIER ID IS NOT A PROVIDER KEY. The engine is a plug-in host: a
22
+ * carrier id is any string, and one of them, `'wrap'`, names a step that no
23
+ * provider performs. Handing a raw id to the logo, glyph, or name tables drew a
24
+ * blank mark and logged an error. Route data resolves the key here, once, and
25
+ * every display site reads the resolved key.
26
+ *
27
+ * @param carrierId - the `carrier` of a planned hop's edge
28
+ * @returns the provider key, or null when no registered provider carries that id
29
+ */
30
+ export const carrierProviderKey = (carrierId) => providerKeyByCarrierId.get(carrierId) ?? null;
31
+ /**
32
+ * The kind of provider-less step a carrier id performs.
33
+ *
34
+ * @param carrierId - the `carrier` of a planned hop's edge
35
+ * @returns the step kind, or null when the carrier is a provider or unknown
36
+ */
37
+ export const carrierStepKind = (carrierId) => carrierId === WRAP_CARRIER_ID ? 'wrap' : null;
@@ -1,34 +1,50 @@
1
1
  import { type Hex } from 'viem';
2
2
  import type { PublicClient } from 'viem';
3
+ import { type SwapVenue, type SwapVenues } from '../../swap-venues.js';
3
4
  import { type AssetNode, type HopEdge, type PlannedHop } from '../search/graph.js';
4
5
  import type { CarrierAskAnswer, CarrierExactResult } from './ask-answer.js';
5
6
  /**
6
- * One PulseX deployment a swap can execute on: the router the reader signs
7
- * and the factory whose pools that router trades against. A Uniswap-V2-style
7
+ * One swap deployment a swap can execute on: the router the reader signs and
8
+ * the factory whose pools that router trades against. A Uniswap-V2-style
8
9
  * router resolves every pool through its OWN factory, so one router call can
9
10
  * only ever touch that one deployment's pools — a path can never mix them.
11
+ *
12
+ * Same type as {@link SwapVenue}; the old name stays for existing callers.
10
13
  */
11
- export type PulseXVenue = {
12
- readonly router: Hex;
13
- readonly factory: Hex;
14
- };
14
+ export type PulseXVenue = SwapVenue;
15
15
  /**
16
- * Every PulseX deployment on PulseChain, V1 first: `uniV2Routers` paired with
17
- * `uniV2Factories` by index. Each factory is the router's own
18
- * `router.factory()` read on chain — see `uniV2Factories`'s own doc in
19
- * `config.ts`. A router with no recorded factory is left out, because its
20
- * pools cannot be read.
16
+ * Every PulseX deployment on PulseChain, V1 first: the PulseChain entry of
17
+ * {@link defaultSwapVenues}. Frozen. It lists the DEFAULTS only — a carrier
18
+ * built with its own `venues` offers those instead.
21
19
  */
22
20
  export declare const pulseXVenues: readonly PulseXVenue[];
23
21
  /**
24
- * The router a PulseX edge must be signed on — the one its figure was quoted
25
- * against. Reads the edge's own {@link HopEdge.venue}; an edge with none is a
26
- * V1 edge (see {@link defaultPulseXVenue}).
22
+ * The {@link SwapVenue} a swap edge runs on: the one its figure was quoted
23
+ * against. Reads the edge's own {@link HopEdge.venue}; an edge with none runs
24
+ * on the FIRST venue of its chain (V1 for PulseX) — every swap edge built
25
+ * before venues existed was quoted and signed there, and the same-chain swap
26
+ * sender's own hand-built edge still is.
27
+ *
28
+ * NEVER FALLS BACK TO ANOTHER CHAIN OR ANOTHER ROUTER. An edge that names a
29
+ * router `venues` does not list gets null, so the caller can refuse it.
30
+ *
31
+ * @param venues - the venues the caller allows
32
+ * @param edge - the edge to sign; `from` names the chain, and defaults to PulseChain when absent
33
+ * @returns the venue, or null when the edge is not a swap edge or names a router that is not in `venues`
34
+ */
35
+ export declare const venueForEdge: (venues: SwapVenues, edge: Pick<HopEdge, "carrier" | "venue"> & {
36
+ readonly from?: Pick<AssetNode, "chainId">;
37
+ }) => SwapVenue | null;
38
+ /**
39
+ * The router a PulseX edge must be signed on, judged against the DEFAULT
40
+ * venues only. Prefer {@link venueForEdge} with the venues your host passes.
27
41
  *
28
42
  * @param edge - the edge to sign
29
- * @returns the router, or null when the edge is not a PulseX edge or names a venue that is not a known PulseX router
43
+ * @returns the router, or null when the edge is not a swap edge or names a router that is not a default
30
44
  */
31
- export declare const pulseXRouterForEdge: (edge: Pick<HopEdge, "carrier" | "venue">) => Hex | null;
45
+ export declare const pulseXRouterForEdge: (edge: Pick<HopEdge, "carrier" | "venue"> & {
46
+ readonly from?: Pick<AssetNode, "chainId">;
47
+ }) => Hex | null;
32
48
  /**
33
49
  * The fixed Wrapped-PLS path from `from` to `to` — `[from, WPLS, to]`, or
34
50
  * `[from, to]` when one side is WPLS — or null when both ends resolve to the
@@ -78,9 +94,10 @@ export type PulseXPathStep = {
78
94
  *
79
95
  * @param amountIn - the amount entering the first pool
80
96
  * @param steps - the pools in order of travel
97
+ * @param feeBasisPoints - the venue's swap fee in basis points; PulseX's 29 when omitted
81
98
  * @returns the amount leaving the last pool; 0 when any pool cannot pay out
82
99
  */
83
- export declare const amountOutAlongPath: (amountIn: bigint, steps: readonly PulseXPathStep[]) => bigint;
100
+ export declare const amountOutAlongPath: (amountIn: bigint, steps: readonly PulseXPathStep[], feeBasisPoints?: number) => bigint;
84
101
  /**
85
102
  * How far a trade's executed rate falls below the marginal (near-spot) rate,
86
103
  * in percent. The marginal rate is a tiny reference trade through the same
@@ -110,12 +127,12 @@ export type CreatePulseXCarrierOptions = {
110
127
  * in the edge's `to` token's base units. Returns null when the path has no
111
128
  * liquidity.
112
129
  *
113
- * MUST READ THE EDGE'S OWN ROUTER ({@link pulseXRouterForEdge}). A V2 edge
130
+ * MUST READ THE EDGE'S OWN ROUTER ({@link venueForEdge}). A V2 edge
114
131
  * answered from V1 would carry V1's figure into a V2 signature.
115
132
  */
116
133
  readonly readAmountOut: (edge: HopEdge, inputAmount: bigint) => Promise<bigint | null>;
117
134
  /**
118
- * Returns a PulseChain client, for {@link Carrier.prepare}'s own batched
135
+ * Returns a client for a chain, for {@link Carrier.prepare}'s own batched
119
136
  * reserve reads — never called by `ask`, which keeps using
120
137
  * {@link readAmountOut} unchanged.
121
138
  *
@@ -123,17 +140,25 @@ export type CreatePulseXCarrierOptions = {
123
140
  * field for why: a host that has not wired this yet gets the carrier's
124
141
  * original behavior, `prepare` a no-op and `exact` always `null`.
125
142
  *
126
- * @param chainId - always {@link pulsechainChainId} — PulseX runs nowhere else
143
+ * @param chainId - a chain that has swap venues
127
144
  * @returns a client, or null when this host has none
128
145
  */
129
146
  readonly clientForChain?: (chainId: number) => PublicClient | null;
130
147
  /**
131
- * The PulseX routers this host can SIGN on. The carrier offers an edge on
132
- * each, and only on these: an edge is always signed on the router it was
133
- * quoted against ({@link pulseXRouterForEdge}), so a router the host cannot
134
- * sign on must never be quoted either.
148
+ * The swap venues to offer. Merged over {@link defaultSwapVenues} by
149
+ * {@link resolveSwapVenues}: a chain named here REPLACES that chain's
150
+ * default list, a chain left out keeps its defaults, and an empty list
151
+ * switches swaps off on that chain. A venue on ANY chain builds swap edges
152
+ * there.
153
+ */
154
+ readonly venues?: SwapVenues;
155
+ /**
156
+ * Narrows the offered venues to these routers. An edge is always signed on
157
+ * the router it was quoted against ({@link venueForEdge}), so a router the
158
+ * host cannot sign on must never be quoted either. A router that is in no
159
+ * offered venue is a configuration error and throws at construction.
135
160
  *
136
- * Defaults to every known venue ({@link pulseXVenues}: V1 and V2).
161
+ * Defaults to every offered venue.
137
162
  */
138
163
  readonly routers?: readonly Hex[];
139
164
  };