@gibs/bridge-sdk 1.13.1 → 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 (228) hide show
  1. package/README.md +295 -18
  2. package/abis/package.json +6 -0
  3. package/bridge-limits/package.json +6 -0
  4. package/chain-info/package.json +6 -0
  5. package/chain-names/package.json +6 -0
  6. package/chains/package.json +6 -0
  7. package/claims/package.json +6 -0
  8. package/config/package.json +6 -0
  9. package/consolidated-quote/package.json +6 -0
  10. package/destination-calldata/package.json +6 -0
  11. package/dist/chain-info.d.ts +1 -1
  12. package/dist/chain-info.js +11 -12
  13. package/dist/chains.d.ts +25 -0
  14. package/dist/chains.js +55 -20
  15. package/dist/cjs/abis.d.ts +1 -0
  16. package/dist/cjs/abis.js +19 -0
  17. package/dist/cjs/bridge-limits.d.ts +103 -0
  18. package/dist/cjs/bridge-limits.js +89 -0
  19. package/dist/cjs/chain-info.d.ts +169 -0
  20. package/dist/cjs/chain-info.js +452 -0
  21. package/dist/cjs/chain-names.d.ts +160 -0
  22. package/dist/cjs/chain-names.js +207 -0
  23. package/dist/cjs/chains.d.ts +90 -0
  24. package/dist/cjs/chains.js +357 -0
  25. package/dist/cjs/claims.d.ts +198 -0
  26. package/dist/cjs/claims.js +182 -0
  27. package/dist/cjs/config.d.ts +255 -0
  28. package/dist/cjs/config.js +569 -0
  29. package/dist/cjs/consolidated-quote.d.ts +648 -0
  30. package/dist/cjs/consolidated-quote.js +1048 -0
  31. package/dist/cjs/destination-calldata.d.ts +118 -0
  32. package/dist/cjs/destination-calldata.js +156 -0
  33. package/dist/cjs/ecosystems.d.ts +217 -0
  34. package/dist/cjs/ecosystems.js +298 -0
  35. package/dist/cjs/execution.d.ts +359 -0
  36. package/dist/cjs/execution.js +495 -0
  37. package/dist/cjs/fee-type.d.ts +10 -0
  38. package/dist/cjs/fee-type.js +10 -0
  39. package/dist/cjs/hyperliquid-execution.d.ts +152 -0
  40. package/dist/cjs/hyperliquid-execution.js +175 -0
  41. package/dist/cjs/image-links.d.ts +117 -0
  42. package/dist/cjs/image-links.js +153 -0
  43. package/dist/cjs/index.d.ts +24 -0
  44. package/dist/cjs/index.js +40 -0
  45. package/dist/cjs/lifi-execution-guard.d.ts +210 -0
  46. package/dist/cjs/lifi-execution-guard.js +409 -0
  47. package/dist/cjs/near-intents-conformance.d.ts +85 -0
  48. package/dist/cjs/near-intents-conformance.js +337 -0
  49. package/dist/cjs/near-intents-route.d.ts +47 -0
  50. package/dist/cjs/near-intents-route.js +303 -0
  51. package/dist/cjs/near-intents.d.ts +524 -0
  52. package/dist/cjs/near-intents.js +1071 -0
  53. package/dist/cjs/origin-deposit.d.ts +326 -0
  54. package/dist/cjs/origin-deposit.js +82 -0
  55. package/dist/cjs/package.json +3 -0
  56. package/dist/cjs/pre-quote.d.ts +198 -0
  57. package/dist/cjs/pre-quote.js +180 -0
  58. package/dist/cjs/prices.d.ts +117 -0
  59. package/dist/cjs/prices.js +224 -0
  60. package/dist/cjs/providers.d.ts +238 -0
  61. package/dist/cjs/providers.js +171 -0
  62. package/dist/cjs/quote-fees.d.ts +102 -0
  63. package/dist/cjs/quote-fees.js +201 -0
  64. package/dist/cjs/referral.d.ts +85 -0
  65. package/dist/cjs/referral.js +85 -0
  66. package/dist/cjs/relay-currencies.d.ts +47 -0
  67. package/dist/cjs/relay-currencies.js +95 -0
  68. package/dist/cjs/relay-depository.d.ts +2 -0
  69. package/dist/cjs/relay-depository.js +85 -0
  70. package/dist/cjs/relay-quote.d.ts +301 -0
  71. package/dist/cjs/relay-quote.js +996 -0
  72. package/dist/cjs/route-steps.d.ts +138 -0
  73. package/dist/cjs/route-steps.js +310 -0
  74. package/dist/cjs/routing/amounts.d.ts +80 -0
  75. package/dist/cjs/routing/amounts.js +126 -0
  76. package/dist/cjs/routing/candidate-route.d.ts +43 -0
  77. package/dist/cjs/routing/candidate-route.js +50 -0
  78. package/dist/cjs/routing/candidate.d.ts +237 -0
  79. package/dist/cjs/routing/candidate.js +100 -0
  80. package/dist/cjs/routing/carrier-provider-key.d.ts +31 -0
  81. package/dist/cjs/routing/carrier-provider-key.js +42 -0
  82. package/dist/cjs/routing/carriers/ask-answer.d.ts +77 -0
  83. package/dist/cjs/routing/carriers/ask-answer.js +2 -0
  84. package/dist/cjs/routing/carriers/omnibridge.d.ts +137 -0
  85. package/dist/cjs/routing/carriers/omnibridge.js +459 -0
  86. package/dist/cjs/routing/carriers/pulsex-paths.d.ts +80 -0
  87. package/dist/cjs/routing/carriers/pulsex-paths.js +97 -0
  88. package/dist/cjs/routing/carriers/pulsex-split.d.ts +175 -0
  89. package/dist/cjs/routing/carriers/pulsex-split.js +193 -0
  90. package/dist/cjs/routing/carriers/pulsex.d.ts +184 -0
  91. package/dist/cjs/routing/carriers/pulsex.js +615 -0
  92. package/dist/cjs/routing/carriers/wrap.d.ts +25 -0
  93. package/dist/cjs/routing/carriers/wrap.js +119 -0
  94. package/dist/cjs/routing/composed-route-quality.d.ts +125 -0
  95. package/dist/cjs/routing/composed-route-quality.js +144 -0
  96. package/dist/cjs/routing/entry-chain-anchor.d.ts +88 -0
  97. package/dist/cjs/routing/entry-chain-anchor.js +59 -0
  98. package/dist/cjs/routing/format.d.ts +31 -0
  99. package/dist/cjs/routing/format.js +64 -0
  100. package/dist/cjs/routing/index.d.ts +42 -0
  101. package/dist/cjs/routing/index.js +142 -0
  102. package/dist/cjs/routing/producers.d.ts +770 -0
  103. package/dist/cjs/routing/producers.js +1617 -0
  104. package/dist/cjs/routing/providers.d.ts +31 -0
  105. package/dist/cjs/routing/providers.js +48 -0
  106. package/dist/cjs/routing/route-data.d.ts +397 -0
  107. package/dist/cjs/routing/route-data.js +2 -0
  108. package/dist/cjs/routing/search/compose.d.ts +168 -0
  109. package/dist/cjs/routing/search/compose.js +104 -0
  110. package/dist/cjs/routing/search/graph.d.ts +668 -0
  111. package/dist/cjs/routing/search/graph.js +461 -0
  112. package/dist/cjs/routing/search/plan-route-data.d.ts +285 -0
  113. package/dist/cjs/routing/search/plan-route-data.js +871 -0
  114. package/dist/cjs/routing/search/score.d.ts +356 -0
  115. package/dist/cjs/routing/search/score.js +445 -0
  116. package/dist/cjs/routing/swap-transactions.d.ts +95 -0
  117. package/dist/cjs/routing/swap-transactions.js +133 -0
  118. package/dist/cjs/routing/usd-value.d.ts +17 -0
  119. package/dist/cjs/routing/usd-value.js +29 -0
  120. package/dist/cjs/settings.d.ts +785 -0
  121. package/dist/cjs/settings.js +1074 -0
  122. package/dist/cjs/swap-venues.d.ts +43 -0
  123. package/dist/cjs/swap-venues.js +29 -0
  124. package/dist/cjs/ton-connect.d.ts +98 -0
  125. package/dist/cjs/ton-connect.js +206 -0
  126. package/dist/cjs/ton-execution.d.ts +7 -0
  127. package/dist/cjs/ton-execution.js +23 -0
  128. package/dist/cjs/ton.d.ts +37 -0
  129. package/dist/cjs/ton.js +83 -0
  130. package/dist/cjs/tron-execution.d.ts +146 -0
  131. package/dist/cjs/tron-execution.js +248 -0
  132. package/dist/cjs/types.d.ts +51 -0
  133. package/dist/cjs/types.js +2 -0
  134. package/dist/claims.d.ts +198 -0
  135. package/dist/claims.js +140 -0
  136. package/dist/config.d.ts +42 -3
  137. package/dist/config.js +87 -21
  138. package/dist/consolidated-quote.d.ts +109 -15
  139. package/dist/consolidated-quote.js +310 -52
  140. package/dist/execution.d.ts +359 -0
  141. package/dist/execution.js +455 -0
  142. package/dist/hyperliquid-execution.js +2 -3
  143. package/dist/index.d.ts +4 -0
  144. package/dist/index.js +4 -0
  145. package/dist/lifi-execution-guard.d.ts +13 -2
  146. package/dist/lifi-execution-guard.js +4 -0
  147. package/dist/near-intents.js +2 -3
  148. package/dist/pre-quote.d.ts +6 -6
  149. package/dist/pre-quote.js +10 -14
  150. package/dist/prices.d.ts +10 -0
  151. package/dist/prices.js +52 -1
  152. package/dist/providers.d.ts +17 -0
  153. package/dist/providers.js +6 -0
  154. package/dist/referral.d.ts +12 -4
  155. package/dist/referral.js +12 -4
  156. package/dist/relay-depository.d.ts +2 -0
  157. package/dist/relay-depository.js +82 -0
  158. package/dist/relay-quote.js +2 -3
  159. package/dist/routing/candidate.d.ts +3 -3
  160. package/dist/routing/candidate.js +3 -3
  161. package/dist/routing/carrier-provider-key.d.ts +31 -0
  162. package/dist/routing/carrier-provider-key.js +37 -0
  163. package/dist/routing/carriers/ask-answer.d.ts +17 -0
  164. package/dist/routing/carriers/omnibridge.js +23 -21
  165. package/dist/routing/carriers/pulsex-paths.d.ts +80 -0
  166. package/dist/routing/carriers/pulsex-paths.js +92 -0
  167. package/dist/routing/carriers/pulsex-split.d.ts +175 -0
  168. package/dist/routing/carriers/pulsex-split.js +185 -0
  169. package/dist/routing/carriers/pulsex.d.ts +81 -46
  170. package/dist/routing/carriers/pulsex.js +267 -148
  171. package/dist/routing/composed-route-quality.d.ts +28 -6
  172. package/dist/routing/composed-route-quality.js +36 -5
  173. package/dist/routing/entry-chain-anchor.d.ts +1 -1
  174. package/dist/routing/entry-chain-anchor.js +1 -1
  175. package/dist/routing/index.d.ts +11 -3
  176. package/dist/routing/index.js +8 -2
  177. package/dist/routing/producers.js +1 -1
  178. package/dist/routing/providers.d.ts +5 -0
  179. package/dist/routing/providers.js +5 -0
  180. package/dist/routing/route-data.d.ts +47 -5
  181. package/dist/routing/search/graph.d.ts +15 -0
  182. package/dist/routing/search/plan-route-data.d.ts +42 -1
  183. package/dist/routing/search/plan-route-data.js +68 -9
  184. package/dist/routing/search/score.d.ts +3 -0
  185. package/dist/routing/search/score.js +11 -3
  186. package/dist/routing/swap-transactions.d.ts +95 -0
  187. package/dist/routing/swap-transactions.js +127 -0
  188. package/dist/settings.d.ts +94 -1
  189. package/dist/settings.js +95 -3
  190. package/dist/swap-venues.d.ts +43 -0
  191. package/dist/swap-venues.js +24 -0
  192. package/dist/ton-connect.d.ts +98 -0
  193. package/dist/ton-connect.js +200 -0
  194. package/dist/ton-execution.d.ts +4 -114
  195. package/dist/ton-execution.js +4 -244
  196. package/dist/ton.d.ts +37 -0
  197. package/dist/ton.js +65 -0
  198. package/docs/examples/example-a-ether-to-pulse.ts +300 -0
  199. package/docs/examples/example-b-token-pulsechain-to-ethereum.ts +270 -0
  200. package/docs/examples/shared.ts +261 -0
  201. package/docs/examples/tsconfig.json +18 -0
  202. package/docs/integration-guide.md +617 -0
  203. package/ecosystems/package.json +6 -0
  204. package/execution/package.json +6 -0
  205. package/fee-type/package.json +6 -0
  206. package/hyperliquid-execution/package.json +6 -0
  207. package/image-links/package.json +6 -0
  208. package/lifi-execution-guard/package.json +6 -0
  209. package/llms.txt +75 -10
  210. package/near-intents/package.json +6 -0
  211. package/near-intents-route/package.json +6 -0
  212. package/origin-deposit/package.json +6 -0
  213. package/package.json +817 -10
  214. package/pre-quote/package.json +6 -0
  215. package/prices/package.json +6 -0
  216. package/providers/package.json +6 -0
  217. package/quote-fees/package.json +6 -0
  218. package/referral/package.json +6 -0
  219. package/relay-currencies/package.json +6 -0
  220. package/relay-quote/package.json +6 -0
  221. package/route-steps/package.json +6 -0
  222. package/routing/package.json +6 -0
  223. package/settings/package.json +6 -0
  224. package/ton/package.json +6 -0
  225. package/ton-connect/package.json +6 -0
  226. package/ton-execution/package.json +6 -0
  227. package/tron-execution/package.json +6 -0
  228. package/types/package.json +6 -0
@@ -0,0 +1,137 @@
1
+ import type { PublicClient } from 'viem';
2
+ import { type EntryLimits } from '../../bridge-limits.js';
3
+ import type { Token } from '../../types.js';
4
+ import { type AssetNode, type HopEdge } from '../search/graph.js';
5
+ import type { CarrierAskAnswer, CarrierExactResult } from './ask-answer.js';
6
+ /**
7
+ * The omnibridge crossing (`'pulsechain'` — the Ethereum entry — and
8
+ * `'tokensex'` — the Binance Smart Chain entry) as a plug-in `Carrier` for
9
+ * `@gibs/quotes`'s route-search engine. GIBS-SPECIFIC — see
10
+ * `docs/quote-service.md`'s "Carriers" section: "never built in ... the
11
+ * omnibridge crossing." Registered from `packages/ui/src/lib/state/useRouteSearch.ts`
12
+ * / `route-search-quotes.ts`.
13
+ *
14
+ * This module returns a plain object shaped like `@gibs/quotes`'s `Carrier`
15
+ * type WITHOUT importing it — `bridge-sdk` cannot depend on `@gibs/quotes`
16
+ * (see `@gibs/quotes/types`'s own doc on the circular-dependency reason).
17
+ * `HopEdge`/`AssetNode`/`AskEdgeAnswer` here are the SAME declarations that
18
+ * package's `CarrierHopEdge`/`CarrierAssetNode`/`CarrierAskAnswer` alias, so
19
+ * the object is structurally identical to a `Carrier` wherever a caller that
20
+ * imports both packages assigns it into one.
21
+ */
22
+ /**
23
+ * One token the omnibridge bridges between its entry chain (Ethereum or
24
+ * Binance Smart Chain today; a testnet entry chain under `testnetPathways`)
25
+ * and PulseChain.
26
+ *
27
+ * NAMED `entryChainToken`, NOT `ethereumToken`. The UI's own
28
+ * `ConsolidatedDestinationPair` (`packages/ui/src/lib/stores/consolidated-tokens.ts`)
29
+ * keeps the older name for its Ethereum-only history; this module reuses its
30
+ * shape, not its name, because a Binance Smart Chain pair routed through the
31
+ * same field would otherwise carry an Ethereum-shaped name for a token that
32
+ * never touches Ethereum.
33
+ */
34
+ export type BridgeablePair = {
35
+ /** The token on the omnibridge's entry chain. */
36
+ readonly entryChainToken: Token;
37
+ /** That token's PulseChain-side representation. */
38
+ readonly pulsechainToken: Token;
39
+ /** The entry chain's decimal chain id — the chain `entryChainToken` lives on. */
40
+ readonly entryChainId: number;
41
+ };
42
+ /** What {@link createOmnibridgeCarrier} needs. */
43
+ export type CreateOmnibridgeCarrierOptions = {
44
+ /** Every token pair the omnibridge bridges, entry chain by entry chain. */
45
+ readonly bridgeablePairs: readonly BridgeablePair[];
46
+ /** Whether this is a production build — see `pathway()`'s own `isProd`. */
47
+ readonly isProductionNetwork: boolean;
48
+ /** Reads the entry mediator's bounds for one bridge-crossing edge — a free, local read. */
49
+ readonly readEntryLimits: (edge: HopEdge) => Promise<EntryLimits | null>;
50
+ /** Reads the omnibridge's own fee rate (per ether) for one bridge-crossing edge, in whichever direction it runs. */
51
+ readonly readFeePerEther: (edge: HopEdge) => Promise<bigint | null>;
52
+ /**
53
+ * Returns a client for a chain id, for {@link Carrier.prepare}'s own
54
+ * batched multicall reads — never called by `ask`, which keeps using
55
+ * {@link readEntryLimits}/{@link readFeePerEther} unchanged.
56
+ *
57
+ * OPTIONAL, ON PURPOSE. A host that has not wired a client lookup yet gets
58
+ * the carrier's original behavior: `prepare` is a no-op and `exact` always
59
+ * returns `null`, so `enumerate.ts` falls through to `estimate`/the
60
+ * price-feed default exactly as it did before either method existed.
61
+ *
62
+ * @param chainId - the chain a batched read needs a client for
63
+ * @returns a client, or null when this host has none for that chain
64
+ */
65
+ readonly clientForChain?: (chainId: number) => PublicClient | null;
66
+ };
67
+ /**
68
+ * Builds the omnibridge crossing as a plug-in carrier.
69
+ *
70
+ * @param options - see {@link CreateOmnibridgeCarrierOptions}
71
+ * @returns the carrier — `id: 'omnibridge'`; every EDGE it offers carries the
72
+ * real `'pulsechain'`/`'tokensex'` provider id, since one registration can
73
+ * speak for either entry chain depending on the pair
74
+ */
75
+ export declare const createOmnibridgeCarrier: (options: CreateOmnibridgeCarrierOptions) => {
76
+ id: string;
77
+ edgesFrom: (from: AssetNode, to: AssetNode) => readonly HopEdge[];
78
+ ask: ({ edge, inputAmount }: {
79
+ edge: HopEdge;
80
+ inputAmount: bigint;
81
+ }) => Promise<CarrierAskAnswer>;
82
+ prepare: (nodes: readonly AssetNode[]) => Promise<void>;
83
+ exact: (edge: HopEdge, amountIn: bigint) => CarrierExactResult | null;
84
+ isRateLimited: boolean;
85
+ depositMode: "wallet-signed";
86
+ sender: "omnibridge";
87
+ settlementSeconds: number;
88
+ };
89
+ /** What {@link withOmnibridgeEntryFusing}'s `fusesWithNext` is asked — structurally `@gibs/quotes`'s `CarrierFusesWithNextOptions`. */
90
+ export type OmnibridgeFuseQuestion = {
91
+ /** What the acting (provider) hop spends. */
92
+ readonly hopFrom: AssetNode;
93
+ /** The carrier of the hop that follows. */
94
+ readonly nextHopCarrier: string;
95
+ /** What the following hop spends — where the provider hop lands. */
96
+ readonly nextHopFrom: AssetNode;
97
+ /** Where the following hop delivers. */
98
+ readonly nextHopDestinationChainId: number;
99
+ };
100
+ /**
101
+ * Wraps ANY carrier-shaped object (built-in or a host's own) with the
102
+ * omnibridge's OWN fuse rule: whether THIS carrier's execution, landing on
103
+ * arrival, also performs the omnibridge's entry call — so the two hops
104
+ * together cost the reader one signature instead of two.
105
+ *
106
+ * EVERY CONDITION IS A FACT THE PROVIDER'S OWN DESCRIPTOR STATES
107
+ * (`../../providers.ts`), never a name compared here:
108
+ *
109
+ * - it executes destination calls at all (`executesDestinationCalls` — true
110
+ * for LI.FI and Relay, false for NEAR Intents, which delivers to the
111
+ * reader's own wallet and stops);
112
+ * - it can do so FROM this hop's origin chain (`destinationCallOriginChainIds`
113
+ * — LI.FI's contract-calls endpoint is Ethereum only, so from Base or
114
+ * Arbitrum a LI.FI hop is an unfused delivery followed by a crossing the
115
+ * reader signs);
116
+ * - when the entry is the omnibridge's NATIVE one (the provider lands a
117
+ * native coin, and `wrapAndRelayTokens` is payable), it can attach value to
118
+ * the call (`destinationCallCarriesNativeValue`);
119
+ * - and the next hop really IS the omnibridge entry, landing on PulseChain
120
+ * (`relayTokens`/`wrapAndRelayTokens` both MINT there; any other
121
+ * destination is the omnibridge's OUTBOUND leg, which no arrival can make).
122
+ *
123
+ * THIS IS THE ONE PLACE `'pulsechain'`/`'tokensex'`/PulseChain'S CHAIN ID
124
+ * APPEAR IN A `fusesWithNext` CALLBACK — gibs's own registration
125
+ * (`packages/ui/src/lib/state/useRouteSearch.ts`) wraps `createLifiCarrier`/
126
+ * `createRelayCarrier` with this before registering them, so the generic
127
+ * search engine (`@gibs/quotes`'s `capabilities.ts`) never has to know what
128
+ * "the bridge" means.
129
+ *
130
+ * @param carrier - the carrier to wrap; its own shape is preserved, only `fusesWithNext` is added/replaced
131
+ * @returns the same carrier, with a `fusesWithNext` that recognizes the omnibridge entry call
132
+ */
133
+ export declare const withOmnibridgeEntryFusing: <T extends {
134
+ readonly id: string;
135
+ }>(carrier: T) => T & {
136
+ readonly fusesWithNext: (nextHop: OmnibridgeFuseQuestion) => boolean;
137
+ };
@@ -0,0 +1,459 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.withOmnibridgeEntryFusing = exports.createOmnibridgeCarrier = void 0;
37
+ const multicall_1 = require("@gibs/common/multicall");
38
+ const viem_1 = require("viem");
39
+ const abis = __importStar(require("../../abis.js"));
40
+ const bridge_limits_js_1 = require("../../bridge-limits.js");
41
+ const chains_js_1 = require("../../chains.js");
42
+ const config_js_1 = require("../../config.js");
43
+ const ecosystems_js_1 = require("../../ecosystems.js");
44
+ const providers_js_1 = require("../../providers.js");
45
+ const amounts_js_1 = require("../amounts.js");
46
+ const entry_chain_anchor_js_1 = require("../entry-chain-anchor.js");
47
+ const producers_js_1 = require("../producers.js");
48
+ const graph_js_1 = require("../search/graph.js");
49
+ /** Builds an {@link AssetNode} for an Ethereum-Virtual-Machine token. */
50
+ const evmNode = (token) => ({
51
+ chainId: token.chainId,
52
+ address: token.address,
53
+ decimals: token.decimals,
54
+ ecosystem: 'evm',
55
+ });
56
+ /**
57
+ * A directed pair of node keys, joined for use as a `Map` key. Never leaves
58
+ * this module — see `capabilities.ts`'s former `directedPairKey` doc for why
59
+ * a plain `|` join is enough here.
60
+ */
61
+ const directedPairKey = (from, to) => `${(0, graph_js_1.assetNodeKey)(from)}|${(0, graph_js_1.assetNodeKey)(to)}`;
62
+ /**
63
+ * Finds which omnibridge provider, if any, has a live pathway carrying funds
64
+ * from `fromChainId` to `toChainId`. Checks BOTH omnibridge providers rather
65
+ * than assuming one: `Providers.PULSECHAIN` carries the Ethereum pathway,
66
+ * `Providers.TOKENSEX` the Binance Smart Chain one.
67
+ */
68
+ const omnibridgeCarrierBetween = (fromChainId, toChainId, isProductionNetwork) => {
69
+ const fromChain = (0, config_js_1.toChain)(fromChainId);
70
+ const toChainKey = (0, config_js_1.toChain)(toChainId);
71
+ const omnibridgeProviders = [config_js_1.Providers.PULSECHAIN, config_js_1.Providers.TOKENSEX];
72
+ for (const provider of omnibridgeProviders) {
73
+ const group = (0, config_js_1.pathwayGroup)(provider, fromChain, isProductionNetwork);
74
+ if (group?.[toChainKey]) {
75
+ return provider;
76
+ }
77
+ }
78
+ return null;
79
+ };
80
+ /**
81
+ * Builds the omnibridge's directed edge map from the bridgeable pairs and the
82
+ * live pathway config, in both directions. INBOUND goes through
83
+ * {@link entryChainAnchorsForPair} so a native coin and its wrapped form both
84
+ * anchor the same PulseChain mint; OUTBOUND needs no aliasing.
85
+ */
86
+ const buildOmnibridgeEdgeMap = (bridgeablePairs, isProductionNetwork) => {
87
+ const edges = new Map();
88
+ for (const pair of bridgeablePairs) {
89
+ const inboundCarrier = omnibridgeCarrierBetween(pair.entryChainId, pair.pulsechainToken.chainId, isProductionNetwork);
90
+ if (inboundCarrier !== null) {
91
+ const anchors = (0, entry_chain_anchor_js_1.entryChainAnchorsForPair)({
92
+ entryChainToken: pair.entryChainToken,
93
+ pulsechainToken: pair.pulsechainToken,
94
+ entryChainId: pair.entryChainId,
95
+ });
96
+ for (const anchor of anchors) {
97
+ edges.set(directedPairKey(evmNode(anchor.token), evmNode(anchor.landsAs)), inboundCarrier);
98
+ }
99
+ }
100
+ const outboundCarrier = omnibridgeCarrierBetween(pair.pulsechainToken.chainId, pair.entryChainId, isProductionNetwork);
101
+ if (outboundCarrier !== null) {
102
+ edges.set(directedPairKey(evmNode(pair.pulsechainToken), evmNode(pair.entryChainToken)), outboundCarrier);
103
+ }
104
+ }
105
+ return edges;
106
+ };
107
+ /**
108
+ * Resolves one edge's crossing details from the live pathway config, or null
109
+ * when no pathway matches — the same "no live pathway" case `edgesFrom`
110
+ * already prunes, kept here as a guard rather than assumed away, because
111
+ * `prepare` walks every node pair itself rather than trusting the caller to
112
+ * hand it only edges this carrier already offered.
113
+ */
114
+ const resolveCrossing = (edge, isProductionNetwork) => {
115
+ const bridgeKey = [edge.carrier, (0, config_js_1.toChain)(edge.from.chainId), (0, config_js_1.toChain)(edge.to.chainId)];
116
+ const wrappedNativeAddress = config_js_1.nativeAssetOut[(0, config_js_1.toChain)(edge.from.chainId)];
117
+ const isNativeEntry = edge.from.address.toLowerCase() === viem_1.zeroAddress;
118
+ const entryTokenAddress = isNativeEntry && wrappedNativeAddress !== undefined ? (0, viem_1.getAddress)(wrappedNativeAddress) : (0, viem_1.getAddress)(edge.from.address);
119
+ const path = (0, config_js_1.pathway)(bridgeKey, isProductionNetwork, entryTokenAddress);
120
+ if (path === undefined)
121
+ return null;
122
+ const feeManagerHostChainId = path.feeManager === 'from' ? edge.from.chainId : edge.to.chainId;
123
+ const feeManagerHostAddress = path.feeManager === 'from' ? path.from : path.to;
124
+ return {
125
+ edge,
126
+ entryTokenAddress,
127
+ entryMediator: path.from,
128
+ limitsChainId: edge.from.chainId,
129
+ feeManagerHostAddress,
130
+ feeManagerHostChainId,
131
+ feeTypeKey: path.toHome ? config_js_1.FOREIGN_TO_HOME_FEE : config_js_1.HOME_TO_FOREIGN_FEE,
132
+ };
133
+ };
134
+ /** Groups values by a computed chain id, preserving the deduped-by-`keyOf` invariant callers rely on. */
135
+ const groupByChainId = (values, chainIdOf) => {
136
+ const groups = new Map();
137
+ for (const value of values) {
138
+ const chainId = chainIdOf(value);
139
+ const group = groups.get(chainId);
140
+ if (group === undefined) {
141
+ groups.set(chainId, [value]);
142
+ }
143
+ else {
144
+ group.push(value);
145
+ }
146
+ }
147
+ return groups;
148
+ };
149
+ /** Stage-one read: `minPerTx`, `maxAvailablePerTx` (an entry mediator's own bounds) and `feeManager()` (the fee contract's address) — every one a fixed-arity view call, so one multicall per chain covers all three kinds at once. */
150
+ const readStageOneBatch = async ({ resolvedCrossings, clientForChain, }) => {
151
+ const limitsByKey = new Map();
152
+ const feeManagerAddressByKey = new Map();
153
+ const limitsRequests = new Map();
154
+ for (const crossing of resolvedCrossings) {
155
+ limitsRequests.set(limitsKey(crossing), {
156
+ chainId: crossing.limitsChainId,
157
+ mediator: crossing.entryMediator,
158
+ token: crossing.entryTokenAddress,
159
+ });
160
+ }
161
+ const feeManagerAddressRequests = new Map();
162
+ for (const crossing of resolvedCrossings) {
163
+ feeManagerAddressRequests.set(feeManagerHostKey(crossing), {
164
+ chainId: crossing.feeManagerHostChainId,
165
+ mediatorHost: crossing.feeManagerHostAddress,
166
+ });
167
+ }
168
+ const requestsByChain = groupByChainId([
169
+ ...Array.from(limitsRequests.entries()).map(([key, request]) => ({ kind: 'limits', key, ...request })),
170
+ ...Array.from(feeManagerAddressRequests.entries()).map(([key, request]) => ({
171
+ kind: 'fee-manager-address',
172
+ key,
173
+ chainId: request.chainId,
174
+ mediatorHost: request.mediatorHost,
175
+ })),
176
+ ], (request) => request.chainId);
177
+ await Promise.all(Array.from(requestsByChain.entries()).map(async ([chainId, requests]) => {
178
+ const client = clientForChain(chainId);
179
+ if (client === null)
180
+ return;
181
+ const results = await (0, multicall_1.multicallRead)({
182
+ chain: chains_js_1.chainsMetadata[(0, config_js_1.toChain)(chainId)],
183
+ client,
184
+ abi: bridge_limits_js_1.entryLimitsAbi,
185
+ calls: requests.flatMap((request) => request.kind === 'limits'
186
+ ? [
187
+ { functionName: 'minPerTx', args: [request.token], target: request.mediator, allowFailure: true },
188
+ { functionName: 'maxAvailablePerTx', args: [request.token], target: request.mediator, allowFailure: true },
189
+ ]
190
+ : [{ functionName: 'feeManager', abi: abis.inputBridge, args: [], target: request.mediatorHost, allowFailure: true }]),
191
+ });
192
+ let cursor = 0;
193
+ for (const request of requests) {
194
+ if (request.kind === 'limits') {
195
+ const minimumPerTransaction = results[cursor];
196
+ const availablePerTransaction = results[cursor + 1];
197
+ cursor += 2;
198
+ if (typeof minimumPerTransaction === 'bigint' && typeof availablePerTransaction === 'bigint') {
199
+ limitsByKey.set(request.key, { minimumPerTransaction, availablePerTransaction });
200
+ }
201
+ continue;
202
+ }
203
+ const feeManagerAddress = results[cursor];
204
+ cursor += 1;
205
+ // A DECODED address is exactly 42 characters ("0x" + 40 hex digits).
206
+ // `multicallRead` returns the UNDECODED revert bytes on a failed
207
+ // `allowFailure` call — also a `string`, usually a different length
208
+ // (`'0x'` for a bare revert) — so `typeof` alone cannot tell success
209
+ // from failure here the way it does for the `bigint` reads above.
210
+ if (typeof feeManagerAddress === 'string' && feeManagerAddress.length === 42) {
211
+ feeManagerAddressByKey.set(request.key, (0, viem_1.getAddress)(feeManagerAddress));
212
+ }
213
+ }
214
+ }));
215
+ return { limitsByKey, feeManagerAddressByKey };
216
+ };
217
+ /** Stage-two read: `getFee(feeTypeKey, address(0))` on every fee manager contract stage one resolved — needs the address stage one just learned, so it cannot join that multicall. */
218
+ const readStageTwoBatch = async ({ resolvedCrossings, feeManagerAddressByKey, clientForChain, }) => {
219
+ const feeRateByKey = new Map();
220
+ const feeRateRequests = new Map();
221
+ for (const crossing of resolvedCrossings) {
222
+ const feeManagerAddress = feeManagerAddressByKey.get(feeManagerHostKey(crossing));
223
+ if (feeManagerAddress === undefined)
224
+ continue;
225
+ feeRateRequests.set(feeRateKey(crossing, feeManagerAddress), {
226
+ chainId: crossing.feeManagerHostChainId,
227
+ feeManagerAddress,
228
+ feeTypeKey: crossing.feeTypeKey,
229
+ });
230
+ }
231
+ const requestsByChain = groupByChainId(Array.from(feeRateRequests.entries()), ([, request]) => request.chainId);
232
+ await Promise.all(Array.from(requestsByChain.entries()).map(async ([chainId, entries]) => {
233
+ const client = clientForChain(chainId);
234
+ if (client === null)
235
+ return;
236
+ const results = await (0, multicall_1.multicallRead)({
237
+ chain: chains_js_1.chainsMetadata[(0, config_js_1.toChain)(chainId)],
238
+ client,
239
+ abi: abis.feeManager,
240
+ calls: entries.map(([, request]) => ({
241
+ functionName: 'getFee',
242
+ args: [request.feeTypeKey, viem_1.zeroAddress],
243
+ target: request.feeManagerAddress,
244
+ allowFailure: true,
245
+ })),
246
+ });
247
+ entries.forEach(([key], index) => {
248
+ const rate = results[index];
249
+ if (typeof rate === 'bigint') {
250
+ feeRateByKey.set(key, rate);
251
+ }
252
+ });
253
+ }));
254
+ return feeRateByKey;
255
+ };
256
+ const limitsKey = (crossing) => `${crossing.limitsChainId}|${crossing.entryMediator}|${crossing.entryTokenAddress}`;
257
+ const feeManagerHostKey = (crossing) => `${crossing.feeManagerHostChainId}|${crossing.feeManagerHostAddress}`;
258
+ const feeRateKey = (crossing, feeManagerAddress) => `${crossing.feeManagerHostChainId}|${feeManagerAddress}|${crossing.feeTypeKey}`;
259
+ const preparedEdgeKey = (edge) => `${edge.carrier}|${(0, graph_js_1.assetNodeKey)(edge.from)}|${(0, graph_js_1.assetNodeKey)(edge.to)}`;
260
+ /**
261
+ * Batch-reads every entry-mediator bound and fee rate `nodes` implies, once,
262
+ * so {@link createOmnibridgeCarrier}'s `exact` can answer every crossing
263
+ * SYNCHRONOUSLY afterward — the owner's own model, "each individual step and
264
+ * its associated fees can be known ahead of time."
265
+ *
266
+ * TWO MULTICALLS PER CHAIN, NOT ONE: `getFee` needs the fee manager's
267
+ * address, which stage one is what LEARNS — the same two-stage shape
268
+ * `chain-info.ts`'s `loadBridgeFees` already uses for one pathway at a time,
269
+ * generalized here to batch across every crossing the search's node universe
270
+ * touches. `HOME_TO_FOREIGN_FEE`/`FOREIGN_TO_HOME_FEE` are not re-read from
271
+ * the chain — they are fixed `keccak256` constants (`config.ts`), so reading
272
+ * them per pathway (as `loadBridgeFees` still does, defensively) would only
273
+ * spend a third multicall confirming a value that cannot change.
274
+ *
275
+ * WATCH PAYLOAD SIZE (request-payload-size-traps.md). `nodes` is the search
276
+ * graph's own hub-token-restricted universe, not a full token list — this
277
+ * function reads one `minPerTx`/`maxAvailablePerTx` pair per DISTINCT
278
+ * (mediator, token) crossing in that universe, never per token a chain
279
+ * lists. `multicallRead` (`@gibs/common`) chunks by measured encoded size on
280
+ * top of that, so a universe large enough to matter still cannot repeat the
281
+ * 8.5 MB trap that memory records.
282
+ *
283
+ * @param nodes - every node the search might branch through
284
+ * @param edgesFrom - this carrier's own edge enumerator, reused to find every candidate crossing among `nodes`
285
+ * @param isProductionNetwork - see `CreateOmnibridgeCarrierOptions`'s own field
286
+ * @param clientForChain - see `CreateOmnibridgeCarrierOptions`'s own field
287
+ * @returns every resolved crossing's edge key, mapped to what `exact` needs — never thrown; a chain this host has no client for is simply left unanswered
288
+ */
289
+ const prepareOmnibridgeReads = async ({ nodes, edgesFrom, isProductionNetwork, clientForChain, }) => {
290
+ const edges = [];
291
+ for (const from of nodes) {
292
+ for (const to of nodes) {
293
+ if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
294
+ continue;
295
+ edges.push(...edgesFrom(from, to));
296
+ }
297
+ }
298
+ const resolvedCrossings = edges
299
+ .map((edge) => resolveCrossing(edge, isProductionNetwork))
300
+ .filter((resolved) => resolved !== null);
301
+ const preparedByEdgeKey = new Map();
302
+ if (resolvedCrossings.length === 0)
303
+ return preparedByEdgeKey;
304
+ const { limitsByKey, feeManagerAddressByKey } = await readStageOneBatch({ resolvedCrossings, clientForChain });
305
+ const feeRateByKey = await readStageTwoBatch({ resolvedCrossings, feeManagerAddressByKey, clientForChain });
306
+ for (const crossing of resolvedCrossings) {
307
+ const feeManagerAddress = feeManagerAddressByKey.get(feeManagerHostKey(crossing));
308
+ const feePerEther = feeManagerAddress === undefined ? null : (feeRateByKey.get(feeRateKey(crossing, feeManagerAddress)) ?? null);
309
+ preparedByEdgeKey.set(preparedEdgeKey(crossing.edge), {
310
+ limits: limitsByKey.get(limitsKey(crossing)) ?? null,
311
+ feePerEther,
312
+ });
313
+ }
314
+ return preparedByEdgeKey;
315
+ };
316
+ /**
317
+ * The refusal an entry mediator's own bound produces, carrying the bound
318
+ * itself as a figure: `minimumAmount` below the floor, `availableAmount`
319
+ * above what is left today. A reader is told the number to type, so the
320
+ * number must survive here rather than live only inside the reason string.
321
+ *
322
+ * @param verdict - the crossed bound, in the entry token's base units
323
+ * @returns the refusal, in `edge.from`'s base units
324
+ */
325
+ const entryAmountRefusal = (verdict) => verdict.reason === 'below-minimum'
326
+ ? {
327
+ kind: 'below-minimum',
328
+ reason: `below the entry mediator's minimum of ${verdict.minimumPerTransaction} base units`,
329
+ minimumAmount: verdict.minimumPerTransaction,
330
+ }
331
+ : {
332
+ kind: 'above-available',
333
+ reason: `above what the entry mediator has left today (${verdict.availablePerTransaction} base units)`,
334
+ minimumAmount: null,
335
+ availableAmount: verdict.availablePerTransaction,
336
+ };
337
+ /**
338
+ * Builds the omnibridge crossing as a plug-in carrier.
339
+ *
340
+ * @param options - see {@link CreateOmnibridgeCarrierOptions}
341
+ * @returns the carrier — `id: 'omnibridge'`; every EDGE it offers carries the
342
+ * real `'pulsechain'`/`'tokensex'` provider id, since one registration can
343
+ * speak for either entry chain depending on the pair
344
+ */
345
+ const createOmnibridgeCarrier = (options) => {
346
+ const { bridgeablePairs, isProductionNetwork, readEntryLimits, readFeePerEther, clientForChain } = options;
347
+ const edgeMap = buildOmnibridgeEdgeMap(bridgeablePairs, isProductionNetwork);
348
+ const edgesFrom = (from, to) => {
349
+ if ((0, graph_js_1.assetNodeKey)(from) === (0, graph_js_1.assetNodeKey)(to))
350
+ return [];
351
+ const carrier = edgeMap.get(directedPairKey(from, to));
352
+ if (carrier === undefined)
353
+ return [];
354
+ return [{ carrier, from, to, fusesNext: false, origin: 'wallet', floors: [] }];
355
+ };
356
+ // Populated by `prepare`, read by `exact` — see that method's own doc for
357
+ // why a mutable closure here is safe: this carrier is a client-side,
358
+ // one-search-at-a-time object (quote-service.md: the omnibridge crossing
359
+ // is "never built into" the shared, multi-tenant display-quote service),
360
+ // so nothing else can be mutating this map while a search reads it.
361
+ let preparedByEdgeKey = new Map();
362
+ const prepare = async (nodes) => {
363
+ if (clientForChain === undefined)
364
+ return;
365
+ preparedByEdgeKey = await prepareOmnibridgeReads({ nodes, edgesFrom, isProductionNetwork, clientForChain });
366
+ };
367
+ const exact = (edge, amountIn) => {
368
+ const prepared = preparedByEdgeKey.get(preparedEdgeKey(edge));
369
+ if (prepared === undefined)
370
+ return null;
371
+ const verdict = (0, bridge_limits_js_1.checkEntryAmount)({ amount: amountIn, limits: prepared.limits });
372
+ if (!verdict.fits) {
373
+ return { outcome: 'refused', refusal: entryAmountRefusal(verdict) };
374
+ }
375
+ return { outcome: 'delivered', deliveredAmount: (0, amounts_js_1.netDeliveredAmount)(amountIn, prepared.feePerEther) };
376
+ };
377
+ const ask = async ({ edge, inputAmount }) => {
378
+ const limits = await readEntryLimits(edge);
379
+ const verdict = (0, bridge_limits_js_1.checkEntryAmount)({ amount: inputAmount, limits });
380
+ if (!verdict.fits) {
381
+ return { ok: false, refusal: entryAmountRefusal(verdict) };
382
+ }
383
+ const feePerEther = await readFeePerEther(edge);
384
+ const deliveredAmount = (0, amounts_js_1.netDeliveredAmount)(inputAmount, feePerEther);
385
+ return {
386
+ ok: true,
387
+ deliveredAmount,
388
+ // An exact local computation: the guaranteed figure IS the expected one.
389
+ minimumDeliveredAmount: deliveredAmount,
390
+ claimedDurationSeconds: producers_js_1.BRIDGE_SETTLEMENT_ETA_MINUTES * 60,
391
+ };
392
+ };
393
+ return {
394
+ id: 'omnibridge',
395
+ edgesFrom,
396
+ ask,
397
+ prepare,
398
+ exact,
399
+ isRateLimited: false,
400
+ // Crossing requires the reader's own wallet signature on the entry
401
+ // chain — never a by-hand deposit.
402
+ depositMode: 'wallet-signed',
403
+ sender: 'omnibridge',
404
+ settlementSeconds: producers_js_1.BRIDGE_SETTLEMENT_ETA_MINUTES * 60,
405
+ };
406
+ };
407
+ exports.createOmnibridgeCarrier = createOmnibridgeCarrier;
408
+ /**
409
+ * Wraps ANY carrier-shaped object (built-in or a host's own) with the
410
+ * omnibridge's OWN fuse rule: whether THIS carrier's execution, landing on
411
+ * arrival, also performs the omnibridge's entry call — so the two hops
412
+ * together cost the reader one signature instead of two.
413
+ *
414
+ * EVERY CONDITION IS A FACT THE PROVIDER'S OWN DESCRIPTOR STATES
415
+ * (`../../providers.ts`), never a name compared here:
416
+ *
417
+ * - it executes destination calls at all (`executesDestinationCalls` — true
418
+ * for LI.FI and Relay, false for NEAR Intents, which delivers to the
419
+ * reader's own wallet and stops);
420
+ * - it can do so FROM this hop's origin chain (`destinationCallOriginChainIds`
421
+ * — LI.FI's contract-calls endpoint is Ethereum only, so from Base or
422
+ * Arbitrum a LI.FI hop is an unfused delivery followed by a crossing the
423
+ * reader signs);
424
+ * - when the entry is the omnibridge's NATIVE one (the provider lands a
425
+ * native coin, and `wrapAndRelayTokens` is payable), it can attach value to
426
+ * the call (`destinationCallCarriesNativeValue`);
427
+ * - and the next hop really IS the omnibridge entry, landing on PulseChain
428
+ * (`relayTokens`/`wrapAndRelayTokens` both MINT there; any other
429
+ * destination is the omnibridge's OUTBOUND leg, which no arrival can make).
430
+ *
431
+ * THIS IS THE ONE PLACE `'pulsechain'`/`'tokensex'`/PulseChain'S CHAIN ID
432
+ * APPEAR IN A `fusesWithNext` CALLBACK — gibs's own registration
433
+ * (`packages/ui/src/lib/state/useRouteSearch.ts`) wraps `createLifiCarrier`/
434
+ * `createRelayCarrier` with this before registering them, so the generic
435
+ * search engine (`@gibs/quotes`'s `capabilities.ts`) never has to know what
436
+ * "the bridge" means.
437
+ *
438
+ * @param carrier - the carrier to wrap; its own shape is preserved, only `fusesWithNext` is added/replaced
439
+ * @returns the same carrier, with a `fusesWithNext` that recognizes the omnibridge entry call
440
+ */
441
+ const withOmnibridgeEntryFusing = (carrier) => {
442
+ const descriptor = (0, providers_js_1.findProviderById)(carrier.id);
443
+ return {
444
+ ...carrier,
445
+ fusesWithNext: (nextHop) => {
446
+ if (descriptor === null || !descriptor.executesDestinationCalls)
447
+ return false;
448
+ const nextHopIsOmnibridgeCrossing = nextHop.nextHopCarrier === config_js_1.Providers.PULSECHAIN || nextHop.nextHopCarrier === config_js_1.Providers.TOKENSEX;
449
+ if (!nextHopIsOmnibridgeCrossing || nextHop.nextHopDestinationChainId !== producers_js_1.pulsechainChainId)
450
+ return false;
451
+ const origins = descriptor.destinationCallOriginChainIds;
452
+ if (origins !== 'any' && !origins.includes(nextHop.hopFrom.chainId))
453
+ return false;
454
+ const entersNative = (0, ecosystems_js_1.isNativeAsset)(nextHop.nextHopFrom.address, nextHop.nextHopFrom.ecosystem);
455
+ return !entersNative || descriptor.destinationCallCarriesNativeValue;
456
+ },
457
+ };
458
+ };
459
+ exports.withOmnibridgeEntryFusing = withOmnibridgeEntryFusing;
@@ -0,0 +1,80 @@
1
+ import { type Hex } from 'viem';
2
+ /**
3
+ * Which swap path a PulseX hop takes. The hop no longer always goes through
4
+ * Wrapped PLS: the carrier prices every candidate path from the pool reserves
5
+ * it already read, and keeps the one that delivers the most. These are the
6
+ * pure parts of that choice — the hub list, the candidate paths, and the
7
+ * pick — kept apart from the carrier so each can be tested on its own.
8
+ */
9
+ /** One token a PulseX swap may pass through on its way from the input token to the output token. */
10
+ export type PulseXHubToken = {
11
+ /** The token's on-chain `symbol()`, for logs and tests only. Never used to match a token. */
12
+ readonly symbol: string;
13
+ /** The token's checksummed PulseChain address. Tokens are always matched by this. */
14
+ readonly address: Hex;
15
+ };
16
+ /**
17
+ * The tokens a PulseX swap may route through, Wrapped PLS first. Each address
18
+ * comes from a constant this package already holds; none is written here:
19
+ *
20
+ * - WPLS — `nativeAssetOut[Chains.PLS]` (`config.ts`).
21
+ * - PLSX, HEX, USDC, USDT — `pulsechainTokenAddresses` (`config.ts`).
22
+ * - DAI (from Ethereum) — `PULSECHAIN_USD_ANCHOR_ADDRESS` (`prices.ts`).
23
+ * - WETH (from Ethereum) — the Ethereum pathway's `bridgedNativeAssetOut` (`config.ts`).
24
+ *
25
+ * KEPT SMALL ON PURPOSE. Each hub adds pools to the one batched reserve read
26
+ * the carrier makes per search, and adds paths to every price it computes.
27
+ * Seven hubs give at most 20 candidate paths per hop (see
28
+ * {@link candidatePulseXPaths}).
29
+ */
30
+ export declare const pulseXHubTokens: readonly PulseXHubToken[];
31
+ /**
32
+ * Every swap path a PulseX hop from `from` to `to` may take:
33
+ *
34
+ * - direct: `[from, to]`;
35
+ * - one hub: `[from, hub, to]` for each hub, Wrapped PLS among them;
36
+ * - two hubs: `[from, wrappedNative, hub, to]` and `[from, hub, wrappedNative, to]`
37
+ * for each other hub — Wrapped PLS holds the deepest pools on PulseX, so it
38
+ * is the one middle token worth pairing with every other hub.
39
+ *
40
+ * A path that names a token twice is dropped. That removes every path whose
41
+ * hub IS `from` or `to`, and the same-token hop when `from` equals `to`.
42
+ *
43
+ * The fixed path this carrier used before — `[from, WPLS, to]`, or
44
+ * `[from, to]` when one side is WPLS — is always in the result, so the pick
45
+ * can only match or beat it.
46
+ *
47
+ * @param options.from - the pool token the swap spends
48
+ * @param options.to - the pool token the swap buys
49
+ * @param options.wrappedNative - Wrapped PLS
50
+ * @param options.hubs - the hub token addresses, Wrapped PLS included
51
+ * @returns the candidate paths, direct first, each at least two tokens long
52
+ */
53
+ export declare const candidatePulseXPaths: (options: {
54
+ readonly from: Hex;
55
+ readonly to: Hex;
56
+ readonly wrappedNative: Hex;
57
+ readonly hubs: readonly Hex[];
58
+ }) => readonly (readonly Hex[])[];
59
+ /** One candidate path, priced. */
60
+ export type PulseXPathQuote<Step> = {
61
+ /** The tokens the swap passes through, in order. */
62
+ readonly path: readonly Hex[];
63
+ /** What the path delivers for the trade, in the last token's base units. */
64
+ readonly deliveredAmount: bigint;
65
+ /** The pools the path crosses, in order — kept so the caller can measure price impact on the same pools. */
66
+ readonly steps: readonly Step[];
67
+ };
68
+ /**
69
+ * The priced path that delivers the most. A path that delivers nothing is
70
+ * never chosen. Among equal outputs the path with fewer pools wins, and among
71
+ * equal lengths the earlier candidate wins, so the pick is deterministic.
72
+ *
73
+ * Gas is not weighed beyond that tie-break: the search charges one flat gas
74
+ * figure per swap signature, whatever its path length (`route-search-gas.ts`
75
+ * in the interface), so there is no per-pool gas figure to subtract here.
76
+ *
77
+ * @param quotes - the priced candidates; paths with a missing pool are already left out
78
+ * @returns the best candidate, or null when none delivers anything
79
+ */
80
+ export declare const bestPulseXPathQuote: <Step>(quotes: readonly PulseXPathQuote<Step>[]) => PulseXPathQuote<Step> | null;