@gibs/bridge-sdk 1.12.0 → 1.13.1

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 (118) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +234 -432
  3. package/dist/abis.d.ts +1 -0
  4. package/dist/bridge-limits.d.ts +103 -0
  5. package/dist/bridge-limits.js +84 -0
  6. package/dist/chain-info.d.ts +169 -0
  7. package/dist/chain-info.js +410 -0
  8. package/dist/chain-names.d.ts +160 -0
  9. package/dist/chain-names.js +200 -0
  10. package/dist/chains.d.ts +65 -0
  11. package/dist/chains.js +284 -0
  12. package/dist/config.d.ts +216 -0
  13. package/dist/config.js +493 -0
  14. package/dist/consolidated-quote.d.ts +554 -0
  15. package/dist/consolidated-quote.js +773 -0
  16. package/dist/destination-calldata.d.ts +118 -0
  17. package/dist/destination-calldata.js +152 -0
  18. package/dist/ecosystems.d.ts +217 -0
  19. package/dist/ecosystems.js +288 -0
  20. package/dist/fee-type.d.ts +10 -0
  21. package/dist/fee-type.js +9 -0
  22. package/dist/hyperliquid-execution.d.ts +152 -0
  23. package/dist/hyperliquid-execution.js +168 -0
  24. package/dist/image-links.d.ts +117 -0
  25. package/dist/image-links.js +142 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.js +20 -0
  28. package/dist/lifi-execution-guard.d.ts +199 -0
  29. package/dist/lifi-execution-guard.js +400 -0
  30. package/dist/near-intents-conformance.d.ts +85 -0
  31. package/dist/near-intents-conformance.js +329 -0
  32. package/dist/near-intents-route.d.ts +47 -0
  33. package/dist/near-intents-route.js +265 -0
  34. package/dist/near-intents.d.ts +524 -0
  35. package/dist/near-intents.js +1055 -0
  36. package/dist/origin-deposit.d.ts +326 -0
  37. package/dist/origin-deposit.js +68 -0
  38. package/dist/pre-quote.d.ts +198 -0
  39. package/dist/pre-quote.js +175 -0
  40. package/dist/prices.d.ts +107 -0
  41. package/dist/prices.js +162 -0
  42. package/dist/providers.d.ts +221 -0
  43. package/dist/providers.js +157 -0
  44. package/dist/quote-fees.d.ts +102 -0
  45. package/dist/quote-fees.js +195 -0
  46. package/dist/referral.d.ts +77 -0
  47. package/dist/referral.js +72 -0
  48. package/dist/relay-currencies.d.ts +47 -0
  49. package/dist/relay-currencies.js +89 -0
  50. package/dist/relay-quote.d.ts +301 -0
  51. package/dist/relay-quote.js +983 -0
  52. package/dist/route-steps.d.ts +138 -0
  53. package/dist/route-steps.js +305 -0
  54. package/dist/routing/amounts.d.ts +80 -0
  55. package/dist/routing/amounts.js +120 -0
  56. package/dist/routing/candidate-route.d.ts +43 -0
  57. package/dist/routing/candidate-route.js +45 -0
  58. package/dist/routing/candidate.d.ts +237 -0
  59. package/dist/routing/candidate.js +96 -0
  60. package/dist/routing/carriers/ask-answer.d.ts +60 -0
  61. package/dist/routing/carriers/ask-answer.js +1 -0
  62. package/dist/routing/carriers/omnibridge.d.ts +137 -0
  63. package/dist/routing/carriers/omnibridge.js +419 -0
  64. package/dist/routing/carriers/pulsex.d.ts +149 -0
  65. package/dist/routing/carriers/pulsex.js +453 -0
  66. package/dist/routing/carriers/wrap.d.ts +25 -0
  67. package/dist/routing/carriers/wrap.js +115 -0
  68. package/dist/routing/composed-route-quality.d.ts +103 -0
  69. package/dist/routing/composed-route-quality.js +108 -0
  70. package/dist/routing/entry-chain-anchor.d.ts +88 -0
  71. package/dist/routing/entry-chain-anchor.js +55 -0
  72. package/dist/routing/format.d.ts +31 -0
  73. package/dist/routing/format.js +58 -0
  74. package/dist/routing/index.d.ts +34 -0
  75. package/dist/routing/index.js +42 -0
  76. package/dist/routing/producers.d.ts +770 -0
  77. package/dist/routing/producers.js +1564 -0
  78. package/dist/routing/providers.d.ts +26 -0
  79. package/dist/routing/providers.js +38 -0
  80. package/dist/routing/route-data.d.ts +355 -0
  81. package/dist/routing/route-data.js +1 -0
  82. package/dist/routing/search/compose.d.ts +168 -0
  83. package/dist/routing/search/compose.js +98 -0
  84. package/dist/routing/search/graph.d.ts +653 -0
  85. package/dist/routing/search/graph.js +445 -0
  86. package/dist/routing/search/plan-route-data.d.ts +244 -0
  87. package/dist/routing/search/plan-route-data.js +767 -0
  88. package/dist/routing/search/score.d.ts +353 -0
  89. package/dist/routing/search/score.js +427 -0
  90. package/dist/routing/usd-value.d.ts +17 -0
  91. package/dist/routing/usd-value.js +25 -0
  92. package/dist/settings.d.ts +692 -0
  93. package/dist/settings.js +909 -0
  94. package/dist/ton-execution.d.ts +117 -0
  95. package/dist/ton-execution.js +247 -0
  96. package/dist/tron-execution.d.ts +146 -0
  97. package/dist/tron-execution.js +239 -0
  98. package/dist/types.d.ts +51 -0
  99. package/dist/types.js +1 -0
  100. package/llms.txt +169 -0
  101. package/package.json +204 -23
  102. package/src/chain-info.test.ts +0 -968
  103. package/src/chain-info.ts +0 -498
  104. package/src/chains.test.ts +0 -123
  105. package/src/chains.ts +0 -88
  106. package/src/config.test.ts +0 -679
  107. package/src/config.ts +0 -500
  108. package/src/fee-type.test.ts +0 -45
  109. package/src/fee-type.ts +0 -14
  110. package/src/image-links.test.ts +0 -160
  111. package/src/image-links.ts +0 -65
  112. package/src/index.ts +0 -8
  113. package/src/settings.test.ts +0 -2754
  114. package/src/settings.ts +0 -875
  115. package/src/types.ts +0 -48
  116. package/tsconfig.json +0 -17
  117. package/vitest.config.ts +0 -13
  118. /package/{src/abis.ts → dist/abis.js} +0 -0
@@ -0,0 +1,157 @@
1
+ import { buildLifiContractCallsRequest, parseLifiContractCallsQuote, } from './consolidated-quote.js';
2
+ import { Chains } from './config.js';
3
+ import { buildLifiPreQuoteRequest, buildRelayPreQuoteRequest, parseLifiPreQuote, parseRelayPreQuote, } from './pre-quote.js';
4
+ import { buildNearIntentsPreQuoteRequest, buildNearIntentsQuoteRequest, parseNearIntentsPreQuote, parseNearIntentsQuote, } from './near-intents.js';
5
+ import { buildRelayQuoteRequest, parseRelayQuote } from './relay-quote.js';
6
+ const lifiDescriptor = {
7
+ id: 'lifi',
8
+ label: 'LI.FI',
9
+ supportedSourceEcosystems: ['evm'],
10
+ // Its contract-calls endpoint exists to deliver and then call something.
11
+ executesDestinationCalls: true,
12
+ destinationCallOriginChainIds: [Number(Chains.ETH)],
13
+ destinationCallCarriesNativeValue: false,
14
+ preQuote: {
15
+ method: 'GET',
16
+ build: buildLifiPreQuoteRequest,
17
+ parse: parseLifiPreQuote,
18
+ },
19
+ executionQuote: {
20
+ method: 'POST',
21
+ build: buildLifiContractCallsRequest,
22
+ parse: parseLifiContractCallsQuote,
23
+ },
24
+ };
25
+ const relayDescriptor = {
26
+ id: 'relay',
27
+ label: 'Relay',
28
+ // Relay carries every origin family the registry models. Listed explicitly
29
+ // rather than derived from the registry so that registering a future
30
+ // ecosystem does not silently claim Relay support the integration lacks.
31
+ supportedSourceEcosystems: ['evm', 'svm', 'bvm', 'tvm', 'tonvm', 'hypevm'],
32
+ // Its solver performs the `txs` attached to a quote on arrival.
33
+ executesDestinationCalls: true,
34
+ destinationCallOriginChainIds: 'any',
35
+ destinationCallCarriesNativeValue: true,
36
+ preQuote: {
37
+ method: 'POST',
38
+ build: buildRelayPreQuoteRequest,
39
+ parse: parseRelayPreQuote,
40
+ },
41
+ executionQuote: {
42
+ method: 'POST',
43
+ build: buildRelayQuoteRequest,
44
+ parse: parseRelayQuote,
45
+ },
46
+ };
47
+ const nearIntentsDescriptor = {
48
+ id: 'near-intents',
49
+ label: 'NEAR Intents',
50
+ // ETHEREUM ORIGINS, AND THE CHAINS A READER PAYS BY HAND. The service
51
+ // carries Solana, Bitcoin, Tron, The Open Network and more, but it returns a
52
+ // deposit ADDRESS and no transaction. For a wallet-connected origin the
53
+ // transfer that funds a route has to be composed on this side, and only the
54
+ // Ethereum one is composed today (`nearIntentsOriginDeposit`); claiming a
55
+ // family whose transfer nothing here can build would offer a route that
56
+ // dead-ends at the signing step. A pay-by-hand chain needs nothing composed:
57
+ // the address and the amount ARE the deposit (`DepositAddressDeposit`).
58
+ supportedSourceEcosystems: ['evm', 'zcash'],
59
+ // The measured answer, and the reason this provider's routes take two
60
+ // signatures. See the head of `near-intents.ts`.
61
+ executesDestinationCalls: false,
62
+ destinationCallOriginChainIds: [],
63
+ destinationCallCarriesNativeValue: false,
64
+ preQuote: {
65
+ method: 'POST',
66
+ build: buildNearIntentsPreQuoteRequest,
67
+ parse: parseNearIntentsPreQuote,
68
+ },
69
+ executionQuote: {
70
+ method: 'POST',
71
+ build: buildNearIntentsQuoteRequest,
72
+ parse: parseNearIntentsQuote,
73
+ },
74
+ };
75
+ /**
76
+ * Every integrated provider. LI.FI is first: the interface preserves this order
77
+ * in its active lists, and LI.FI-first is the existing tie-break that prefers
78
+ * the verified provider on an equal-cost quote. NEAR Intents is last, so it
79
+ * wins a tie against neither — appropriate for the one provider whose route
80
+ * costs the reader a second signature.
81
+ */
82
+ export const providerRegistry = [
83
+ lifiDescriptor,
84
+ relayDescriptor,
85
+ nearIntentsDescriptor,
86
+ ];
87
+ const byId = new Map(providerRegistry.map((descriptor) => [descriptor.id, descriptor]));
88
+ /**
89
+ * Looks up a provider descriptor for ANY carrier id, answering null for an id
90
+ * the registry does not hold. The route-search engine carries hops for
91
+ * carriers that are not providers at all (a local wrap, a pool swap, a
92
+ * host's own plug-in), so a caller holding an open carrier id must ask this,
93
+ * never cast the id to {@link ProviderId} and call {@link providerById}, which
94
+ * throws for such an id.
95
+ *
96
+ * @param id - any carrier id
97
+ * @returns the descriptor, or null when no provider is registered under `id`
98
+ */
99
+ export const findProviderById = (id) => byId.get(id) ?? null;
100
+ /**
101
+ * Looks up a provider descriptor. Total over the `ProviderId` union; throws only
102
+ * if the registry is left incomplete.
103
+ *
104
+ * @param id - the provider key
105
+ * @returns the descriptor whose `id` equals `id`
106
+ */
107
+ export const providerById = (id) => {
108
+ const descriptor = byId.get(id);
109
+ if (descriptor === undefined) {
110
+ throw new Error(`no provider descriptor registered for id "${id}"`);
111
+ }
112
+ return descriptor;
113
+ };
114
+ /**
115
+ * The providers that can originate a route from `ecosystem`, in registry order.
116
+ * This is the data that used to be the hand-written capability conditional.
117
+ *
118
+ * @param ecosystem - the source ecosystem
119
+ * @returns the descriptors whose `supportedSourceEcosystems` include `ecosystem`
120
+ */
121
+ export const providersForOrigin = (ecosystem) => providerRegistry.filter((descriptor) => descriptor.supportedSourceEcosystems.includes(ecosystem));
122
+ /**
123
+ * The providers that may bid in the entry-anchor pre-quote for an origin.
124
+ *
125
+ * A STRICTER LIST THAN {@link providersForOrigin}, AND THE DIFFERENCE IS NOT
126
+ * COSMETIC. The pre-quote exists for one purpose: to learn, for the reader's
127
+ * fixed input, how much anchor an entry really delivers, so that figure can
128
+ * become the exact-output target of the execution quote. The winner is whoever
129
+ * delivers MOST.
130
+ *
131
+ * A provider that cannot execute the entry call can still win that comparison —
132
+ * it has one less thing to pay for. Its number then becomes a target handed to
133
+ * the providers that must actually fill it, and they cannot, so the whole entry
134
+ * produces no route. The failure is silent and looks like an absent quote.
135
+ *
136
+ * So the pre-quote is restricted to providers that execute. This is not a
137
+ * judgement about quality; it is that a pre-quote and an execution quote have
138
+ * to be answerable by the same provider for the handoff between them to mean
139
+ * anything.
140
+ *
141
+ * @param ecosystem - the source ecosystem
142
+ * @returns the descriptors that may pre-quote this origin, in registry order
143
+ */
144
+ export const preQuoteProvidersForOrigin = (ecosystem) => providersForOrigin(ecosystem).filter((descriptor) => descriptor.executesDestinationCalls);
145
+ // A request with no origin family is the existing-caller shape and is treated as
146
+ // Ethereum, preserving the original single-ecosystem default.
147
+ const ecosystemOf = (request) => request.originVmType ?? 'evm';
148
+ /**
149
+ * Whether a provider can quote a request's origin ecosystem, derived from the
150
+ * registry. Equivalent to `providersForOrigin(...).some(...)`, kept as a named
151
+ * predicate for the call sites that ask about one provider at a time.
152
+ *
153
+ * @param provider - the aggregator under consideration
154
+ * @param request - the request whose origin is being checked
155
+ * @returns true when the provider supports the request's origin ecosystem
156
+ */
157
+ export const providerSupportsOrigin = (provider, request) => providerById(provider).supportedSourceEcosystems.includes(ecosystemOf(request));
@@ -0,0 +1,102 @@
1
+ /**
2
+ * What a consolidated route actually charges, normalized across the aggregators.
3
+ *
4
+ * WHY THIS EXISTS. A quote used to carry `fromAmount` and nothing else about
5
+ * cost, so the interface could show the reader only the omnibridge crossing fee
6
+ * — a figure that is real, and on a twenty-five dollar transfer is a rounding
7
+ * error beside what the aggregator takes. Measured on one live route on
8
+ * 2026-09-01: Relay charged 1.466338 USD Coin, or 5.54%, and none of it
9
+ * appeared anywhere on screen.
10
+ *
11
+ * THE FLAG THAT MATTERS IS `includedInFromAmount`. The two providers express
12
+ * cost differently, and the difference is not cosmetic:
13
+ *
14
+ * Relay folds everything into `fromAmount`. You send 26.466338 to receive
15
+ * 25.000000, and the 1.466338 difference IS the fee.
16
+ *
17
+ * LI.FI does not. On the same route it quoted `fromAmount` 25.317074 and a
18
+ * SEPARATE LayerZero fee of 0.000561734871157119 ether — about 1.37 dollars —
19
+ * marked `included: false`, which the sender pays on top in native currency.
20
+ *
21
+ * So comparing the two on `fromAmount` alone reads LI.FI as more than a dollar
22
+ * cheaper when it is in fact slightly dearer. Anything that ranks or displays
23
+ * cost has to add the charges that sit outside `fromAmount`, and that is what
24
+ * this flag is for.
25
+ */
26
+ /** The token a charge is denominated in, enough to format it. */
27
+ export type QuoteFeeToken = {
28
+ symbol: string;
29
+ decimals: number;
30
+ chainId: number;
31
+ };
32
+ /**
33
+ * What a charge IS, independent of the name its provider gives it.
34
+ *
35
+ * Deliberately coarse. The provider's own wording travels in `label`, so this
36
+ * only has to carry the distinction a reader acts on: is this the network's
37
+ * cost, somebody's margin, or our own referral.
38
+ */
39
+ export type QuoteFeeKind = 'gas' | 'service' | 'integrator';
40
+ /** One charge on a route. */
41
+ export type QuoteFee = {
42
+ kind: QuoteFeeKind;
43
+ /** The provider's own name for it, so a reader can reconcile against them. */
44
+ label: string;
45
+ /** The charge, in the token's base units. */
46
+ amount: bigint;
47
+ token: QuoteFeeToken;
48
+ /** United States dollar value, exactly as the provider reported it. */
49
+ amountUsd: string | null;
50
+ /**
51
+ * FALSE means the sender pays this ON TOP of `fromAmount`.
52
+ *
53
+ * Getting this backwards double-counts a fee or hides one entirely, and in
54
+ * both directions the number on screen is wrong in the direction that
55
+ * flatters the route.
56
+ */
57
+ includedInFromAmount: boolean;
58
+ };
59
+ /**
60
+ * The charges Relay reports, in the order a reader meets them.
61
+ *
62
+ * `fees.relayer` IS DELIBERATELY SKIPPED. It is the sum of `relayerGas` and
63
+ * `relayerService`, so listing it beside them would count the same money twice
64
+ * and make the breakdown disagree with the amount the sender pays.
65
+ *
66
+ * `fees.gas` is the sender's OWN origin-chain transaction cost. It sits outside
67
+ * `fromAmount` because the wallet pays it directly, which is exactly the case
68
+ * `includedInFromAmount` exists to mark.
69
+ * @param payload - the raw body from Relay's quote endpoint
70
+ * @returns the charges, in display order
71
+ */
72
+ export declare const relayQuoteFees: (payload: unknown) => QuoteFee[];
73
+ /**
74
+ * The charges LI.FI reports.
75
+ *
76
+ * `feeCosts` CARRIES ITS OWN `included` FLAG and it is the whole point of
77
+ * reading this: a fee marked `included: false` is paid on top of `fromAmount`,
78
+ * in that fee's own token, and on a live route it was worth more than a dollar.
79
+ * `gasCosts` is the sender's own origin transaction and is never included.
80
+ * @param payload - the raw body from LI.FI's quote endpoint
81
+ * @returns the charges, in display order
82
+ */
83
+ export declare const lifiQuoteFees: (payload: unknown) => QuoteFee[];
84
+ /**
85
+ * What the route costs the sender all in, in United States dollars.
86
+ *
87
+ * `fromAmount` already contains every charge marked included, so only the ones
88
+ * marked otherwise are added. This is the figure two providers must be compared
89
+ * on: ranking them on `fromAmount` alone picked LI.FI over Relay by more than a
90
+ * dollar on a route where LI.FI was in fact the dearer of the two, because its
91
+ * LayerZero fee is charged outside the amount.
92
+ *
93
+ * Returns null when the input's own dollar value is unknown — a partial total
94
+ * would rank routes against each other on different bases.
95
+ * @param quote.fromAmountUsd - the provider's dollar value for the input amount
96
+ * @param quote.fees - the route's charges
97
+ * @returns the all-in dollar cost, or null when it cannot be totalled
98
+ */
99
+ export declare const totalQuoteCostUsd: ({ fromAmountUsd, fees, }: {
100
+ fromAmountUsd: string | null;
101
+ fees: QuoteFee[];
102
+ }) => number | null;
@@ -0,0 +1,195 @@
1
+ /**
2
+ * What a consolidated route actually charges, normalized across the aggregators.
3
+ *
4
+ * WHY THIS EXISTS. A quote used to carry `fromAmount` and nothing else about
5
+ * cost, so the interface could show the reader only the omnibridge crossing fee
6
+ * — a figure that is real, and on a twenty-five dollar transfer is a rounding
7
+ * error beside what the aggregator takes. Measured on one live route on
8
+ * 2026-09-01: Relay charged 1.466338 USD Coin, or 5.54%, and none of it
9
+ * appeared anywhere on screen.
10
+ *
11
+ * THE FLAG THAT MATTERS IS `includedInFromAmount`. The two providers express
12
+ * cost differently, and the difference is not cosmetic:
13
+ *
14
+ * Relay folds everything into `fromAmount`. You send 26.466338 to receive
15
+ * 25.000000, and the 1.466338 difference IS the fee.
16
+ *
17
+ * LI.FI does not. On the same route it quoted `fromAmount` 25.317074 and a
18
+ * SEPARATE LayerZero fee of 0.000561734871157119 ether — about 1.37 dollars —
19
+ * marked `included: false`, which the sender pays on top in native currency.
20
+ *
21
+ * So comparing the two on `fromAmount` alone reads LI.FI as more than a dollar
22
+ * cheaper when it is in fact slightly dearer. Anything that ranks or displays
23
+ * cost has to add the charges that sit outside `fromAmount`, and that is what
24
+ * this flag is for.
25
+ */
26
+ const asRecord = (value) => typeof value === 'object' && value !== null ? value : null;
27
+ const asNumber = (value) => typeof value === 'number' && Number.isFinite(value) ? value : null;
28
+ /**
29
+ * Reads a provider's currency block into a token descriptor.
30
+ *
31
+ * Returns null rather than filling in defaults: a charge whose token is unknown
32
+ * cannot be formatted, and inventing eighteen decimals for it would print a
33
+ * number wrong by many orders of magnitude.
34
+ */
35
+ const feeToken = (value) => {
36
+ const currency = asRecord(value);
37
+ const decimals = asNumber(currency?.decimals);
38
+ const chainId = asNumber(currency?.chainId);
39
+ if (typeof currency?.symbol !== 'string' || decimals === null || chainId === null) {
40
+ return null;
41
+ }
42
+ return { symbol: currency.symbol, decimals, chainId };
43
+ };
44
+ /** Parses a provider's amount string, dropping a charge that is zero or unreadable. */
45
+ const feeAmount = (value) => {
46
+ if (typeof value !== 'string' && typeof value !== 'number') {
47
+ return null;
48
+ }
49
+ try {
50
+ // Relay reports "0.0" for an unused fee slot, which BigInt rejects; a
51
+ // fractional string here is always zero in practice, and a charge we cannot
52
+ // read exactly is one we must not display.
53
+ const amount = BigInt(String(value).split('.')[0] ?? '');
54
+ return amount > 0n ? amount : null;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ };
60
+ const usdOf = (value) => typeof value === 'string' || typeof value === 'number' ? String(value) : null;
61
+ /**
62
+ * The charges Relay reports, in the order a reader meets them.
63
+ *
64
+ * `fees.relayer` IS DELIBERATELY SKIPPED. It is the sum of `relayerGas` and
65
+ * `relayerService`, so listing it beside them would count the same money twice
66
+ * and make the breakdown disagree with the amount the sender pays.
67
+ *
68
+ * `fees.gas` is the sender's OWN origin-chain transaction cost. It sits outside
69
+ * `fromAmount` because the wallet pays it directly, which is exactly the case
70
+ * `includedInFromAmount` exists to mark.
71
+ * @param payload - the raw body from Relay's quote endpoint
72
+ * @returns the charges, in display order
73
+ */
74
+ export const relayQuoteFees = (payload) => {
75
+ const fees = asRecord(asRecord(payload)?.fees);
76
+ if (!fees) {
77
+ return [];
78
+ }
79
+ const rows = [
80
+ { key: 'relayerGas', label: 'Destination network cost', kind: 'gas', included: true },
81
+ { key: 'relayerService', label: 'Relay service fee', kind: 'service', included: true },
82
+ { key: 'app', label: 'Referral fee', kind: 'integrator', included: true },
83
+ { key: 'gas', label: 'Origin network cost', kind: 'gas', included: false },
84
+ ];
85
+ return rows.flatMap(({ key, label, kind, included }) => {
86
+ const node = asRecord(fees[key]);
87
+ const amount = feeAmount(node?.amount);
88
+ const token = feeToken(node?.currency);
89
+ if (amount === null || token === null) {
90
+ return [];
91
+ }
92
+ return [
93
+ {
94
+ kind,
95
+ label,
96
+ amount,
97
+ token,
98
+ amountUsd: usdOf(node?.amountUsd),
99
+ includedInFromAmount: included,
100
+ },
101
+ ];
102
+ });
103
+ };
104
+ /**
105
+ * The charges LI.FI reports.
106
+ *
107
+ * `feeCosts` CARRIES ITS OWN `included` FLAG and it is the whole point of
108
+ * reading this: a fee marked `included: false` is paid on top of `fromAmount`,
109
+ * in that fee's own token, and on a live route it was worth more than a dollar.
110
+ * `gasCosts` is the sender's own origin transaction and is never included.
111
+ * @param payload - the raw body from LI.FI's quote endpoint
112
+ * @returns the charges, in display order
113
+ */
114
+ export const lifiQuoteFees = (payload) => {
115
+ const estimate = asRecord(asRecord(payload)?.estimate);
116
+ if (!estimate) {
117
+ return [];
118
+ }
119
+ const feeCosts = Array.isArray(estimate.feeCosts) ? estimate.feeCosts : [];
120
+ const gasCosts = Array.isArray(estimate.gasCosts) ? estimate.gasCosts : [];
121
+ const fromFeeCosts = feeCosts.flatMap((entry) => {
122
+ const fee = asRecord(entry);
123
+ const amount = feeAmount(fee?.amount);
124
+ const token = feeToken(fee?.token);
125
+ if (amount === null || token === null) {
126
+ return [];
127
+ }
128
+ return [
129
+ {
130
+ kind: 'service',
131
+ label: typeof fee?.name === 'string' ? fee.name : 'Provider fee',
132
+ amount,
133
+ token,
134
+ amountUsd: usdOf(fee?.amountUSD),
135
+ // Absent is treated as NOT included. A fee we wrongly call included
136
+ // disappears from the total and understates the route; one we wrongly
137
+ // call excluded overstates it. Overstating is the safe direction.
138
+ includedInFromAmount: fee?.included === true,
139
+ },
140
+ ];
141
+ });
142
+ const fromGasCosts = gasCosts.flatMap((entry) => {
143
+ const gas = asRecord(entry);
144
+ const amount = feeAmount(gas?.amount);
145
+ const token = feeToken(gas?.token);
146
+ if (amount === null || token === null) {
147
+ return [];
148
+ }
149
+ return [
150
+ {
151
+ kind: 'gas',
152
+ label: 'Origin network cost',
153
+ amount,
154
+ token,
155
+ amountUsd: usdOf(gas?.amountUSD),
156
+ includedInFromAmount: false,
157
+ },
158
+ ];
159
+ });
160
+ return [...fromFeeCosts, ...fromGasCosts];
161
+ };
162
+ /**
163
+ * What the route costs the sender all in, in United States dollars.
164
+ *
165
+ * `fromAmount` already contains every charge marked included, so only the ones
166
+ * marked otherwise are added. This is the figure two providers must be compared
167
+ * on: ranking them on `fromAmount` alone picked LI.FI over Relay by more than a
168
+ * dollar on a route where LI.FI was in fact the dearer of the two, because its
169
+ * LayerZero fee is charged outside the amount.
170
+ *
171
+ * Returns null when the input's own dollar value is unknown — a partial total
172
+ * would rank routes against each other on different bases.
173
+ * @param quote.fromAmountUsd - the provider's dollar value for the input amount
174
+ * @param quote.fees - the route's charges
175
+ * @returns the all-in dollar cost, or null when it cannot be totalled
176
+ */
177
+ export const totalQuoteCostUsd = ({ fromAmountUsd, fees, }) => {
178
+ if (fromAmountUsd === null) {
179
+ return null;
180
+ }
181
+ const base = Number(fromAmountUsd);
182
+ if (!Number.isFinite(base)) {
183
+ return null;
184
+ }
185
+ return fees.reduce((total, fee) => {
186
+ if (fee.includedInFromAmount) {
187
+ return total;
188
+ }
189
+ const value = Number(fee.amountUsd);
190
+ // A charge outside the amount whose dollar value we cannot read is left out
191
+ // rather than guessed. It is named in the breakdown either way, so the
192
+ // reader still sees it even when the total cannot count it.
193
+ return Number.isFinite(value) ? total + value : total;
194
+ }, base);
195
+ };
@@ -0,0 +1,77 @@
1
+ import type { Hex } from 'viem';
2
+ /**
3
+ * Referral configuration — the one place to set who gets paid.
4
+ *
5
+ * This module exists so the payout address is a single named edit rather than a
6
+ * constant buried among API urls. It deliberately imports nothing but a type, so
7
+ * the interface can read the address without pulling in a quote builder.
8
+ */
9
+ /**
10
+ * Gibs Finance's own integrator identifier with LI.FI and Relay. LI.FI pays its
11
+ * integrator share against this string — the payout account is configured on
12
+ * LI.FI's side, which is why LI.FI needs no address here and Relay does.
13
+ *
14
+ * This is Gibs Finance's identity, not a setting for other consumers. The
15
+ * Relay API key that used to live beside it moved into the Gibs interface's
16
+ * own configuration (`packages/ui/src/lib/config.ts`): this package ships no
17
+ * key. Pass your own identifiers on each request — see the README.
18
+ */
19
+ export declare const referralIntegrator = "gibs.finance";
20
+ /**
21
+ * Resolves one attribution field (a LI.FI `integrator`, a Relay `referrer`)
22
+ * from a caller's override and this package's default for it.
23
+ *
24
+ * Three states, not two:
25
+ * - `undefined` (left out): the package default — which may itself be
26
+ * `undefined`, meaning the field is not sent.
27
+ * - `null`: the field is OMITTED from the request. Never sent as `''`: Relay
28
+ * reads any `referrer`, an empty one included, as "a referrer was supplied".
29
+ * - a string: sent as-is.
30
+ *
31
+ * @param override - the caller's value for the field
32
+ * @param packageDefault - the value used when the caller leaves the field out
33
+ * @returns the value to send, or undefined to omit the field
34
+ */
35
+ export declare const resolveAttribution: (override: string | null | undefined, packageDefault: string | undefined) => string | undefined;
36
+ /**
37
+ * Payout address for Relay application fees.
38
+ *
39
+ * Set to the owner's receiving wallet. Setting it back to `null` is a working
40
+ * state rather than a broken one, and is the way to stop collecting: Relay
41
+ * rejects a fee entry that names no recipient, so the fee is omitted from the
42
+ * request entirely and nothing is charged. Every consumer already reads it this
43
+ * way — see {@link referralIsCollectable}.
44
+ *
45
+ * The literal is stored CHECKSUMMED (mixed case). That is not decoration: the
46
+ * mixed case IS the checksum, so a typo in the address fails `getAddress`
47
+ * instead of silently paying a wrong-but-valid account. Verified against viem's
48
+ * `getAddress` before being written here.
49
+ *
50
+ * One address serves every chain: Relay pays application fees on the DESTINATION
51
+ * chain, and every destination this app bridges to is Ethereum-style, so an
52
+ * ordinary Ethereum address receives from all of them.
53
+ */
54
+ export declare const relayReferralRecipient: Hex | null;
55
+ /**
56
+ * Default application fee in basis points. Ten basis points is one tenth of one
57
+ * percent. The interface can override it per provider in stored settings; this
58
+ * is the value a reader who has never touched the setting gets.
59
+ */
60
+ export declare const defaultReferralFeeBasisPoints = 10n;
61
+ /**
62
+ * Whether a referral fee will ACTUALLY be collected, as opposed to merely being
63
+ * switched on.
64
+ *
65
+ * The distinction is the point of this helper. Relay's fee needs three things to
66
+ * be true at once, and the interface used to show a percentage whenever the
67
+ * first was true — so the fee row read "0.10%" while the missing recipient meant
68
+ * nothing was charged at all. Ask this rather than reading `enabled` alone.
69
+ *
70
+ * @param options - the switch state, the fee size, and the payout address
71
+ * @returns true only when a fee will reach a recipient
72
+ */
73
+ export declare const referralIsCollectable: ({ enabled, basisPoints, recipient, }: {
74
+ enabled: boolean;
75
+ basisPoints: bigint;
76
+ recipient: Hex | null;
77
+ }) => boolean;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Referral configuration — the one place to set who gets paid.
3
+ *
4
+ * This module exists so the payout address is a single named edit rather than a
5
+ * constant buried among API urls. It deliberately imports nothing but a type, so
6
+ * the interface can read the address without pulling in a quote builder.
7
+ */
8
+ /**
9
+ * Gibs Finance's own integrator identifier with LI.FI and Relay. LI.FI pays its
10
+ * integrator share against this string — the payout account is configured on
11
+ * LI.FI's side, which is why LI.FI needs no address here and Relay does.
12
+ *
13
+ * This is Gibs Finance's identity, not a setting for other consumers. The
14
+ * Relay API key that used to live beside it moved into the Gibs interface's
15
+ * own configuration (`packages/ui/src/lib/config.ts`): this package ships no
16
+ * key. Pass your own identifiers on each request — see the README.
17
+ */
18
+ export const referralIntegrator = 'gibs.finance';
19
+ /**
20
+ * Resolves one attribution field (a LI.FI `integrator`, a Relay `referrer`)
21
+ * from a caller's override and this package's default for it.
22
+ *
23
+ * Three states, not two:
24
+ * - `undefined` (left out): the package default — which may itself be
25
+ * `undefined`, meaning the field is not sent.
26
+ * - `null`: the field is OMITTED from the request. Never sent as `''`: Relay
27
+ * reads any `referrer`, an empty one included, as "a referrer was supplied".
28
+ * - a string: sent as-is.
29
+ *
30
+ * @param override - the caller's value for the field
31
+ * @param packageDefault - the value used when the caller leaves the field out
32
+ * @returns the value to send, or undefined to omit the field
33
+ */
34
+ export const resolveAttribution = (override, packageDefault) => (override === null ? undefined : (override ?? packageDefault));
35
+ /**
36
+ * Payout address for Relay application fees.
37
+ *
38
+ * Set to the owner's receiving wallet. Setting it back to `null` is a working
39
+ * state rather than a broken one, and is the way to stop collecting: Relay
40
+ * rejects a fee entry that names no recipient, so the fee is omitted from the
41
+ * request entirely and nothing is charged. Every consumer already reads it this
42
+ * way — see {@link referralIsCollectable}.
43
+ *
44
+ * The literal is stored CHECKSUMMED (mixed case). That is not decoration: the
45
+ * mixed case IS the checksum, so a typo in the address fails `getAddress`
46
+ * instead of silently paying a wrong-but-valid account. Verified against viem's
47
+ * `getAddress` before being written here.
48
+ *
49
+ * One address serves every chain: Relay pays application fees on the DESTINATION
50
+ * chain, and every destination this app bridges to is Ethereum-style, so an
51
+ * ordinary Ethereum address receives from all of them.
52
+ */
53
+ export const relayReferralRecipient = '0xAF2ce0189f46f5663715b0b9ED2a10eA924AB9B0';
54
+ /**
55
+ * Default application fee in basis points. Ten basis points is one tenth of one
56
+ * percent. The interface can override it per provider in stored settings; this
57
+ * is the value a reader who has never touched the setting gets.
58
+ */
59
+ export const defaultReferralFeeBasisPoints = 10n;
60
+ /**
61
+ * Whether a referral fee will ACTUALLY be collected, as opposed to merely being
62
+ * switched on.
63
+ *
64
+ * The distinction is the point of this helper. Relay's fee needs three things to
65
+ * be true at once, and the interface used to show a percentage whenever the
66
+ * first was true — so the fee row read "0.10%" while the missing recipient meant
67
+ * nothing was charged at all. Ask this rather than reading `enabled` alone.
68
+ *
69
+ * @param options - the switch state, the fee size, and the payout address
70
+ * @returns true only when a fee will reach a recipient
71
+ */
72
+ export const referralIsCollectable = ({ enabled, basisPoints, recipient, }) => enabled && basisPoints > 0n && recipient !== null;
@@ -0,0 +1,47 @@
1
+ import type { Token } from './types.js';
2
+ /**
3
+ * Relay currencies endpoint. Anonymous access works at the same 50-requests-per-
4
+ * minute tier as the quote endpoint; used to discover the source tokens a
5
+ * non-Ethereum origin chain (Solana, Bitcoin) can bridge from.
6
+ */
7
+ export declare const relayCurrenciesUrl = "https://api.relay.link/currencies/v2";
8
+ /** The JSON body for `POST /currencies/v2`. */
9
+ export type RelayCurrenciesRequestBody = {
10
+ /** Relay chain identifiers to list currencies for. */
11
+ chainIds: number[];
12
+ /** Optional cap on how many currencies Relay returns per chain. */
13
+ limit?: number;
14
+ };
15
+ /**
16
+ * Builds the Relay currencies request body.
17
+ *
18
+ * @returns the request body, or null when no chain ids are supplied
19
+ */
20
+ export declare const buildRelayCurrenciesRequest: ({ chainIds, limit, }: {
21
+ chainIds: number[];
22
+ limit?: number;
23
+ }) => RelayCurrenciesRequestBody | null;
24
+ /**
25
+ * Relay's own chain registry — the provider's word on which chains it
26
+ * supports AT ALL, distinct from `/currencies/v2` (which chains currently
27
+ * have priced currencies). A chain absent here can never be asked about, on
28
+ * `/currencies/v2` or `/quote` alike; see `parseRelayChains`.
29
+ */
30
+ export declare const relayChainsUrl = "https://api.relay.link/chains";
31
+ /**
32
+ * Normalizes a `GET /chains` response body to the chain ids Relay lists.
33
+ * Mirrors `parseLifiChains` (`consolidated-quote.ts`) for LI.FI's own chain
34
+ * list: read the provider's own registry, never a hand-written exclusion.
35
+ *
36
+ * @returns the chain ids Relay lists, or null when the payload shape is unexpected
37
+ */
38
+ export declare const parseRelayChains: (payload: unknown) => number[] | null;
39
+ /**
40
+ * Maps Relay's flat `/currencies/v2` array onto the shared `Token` shape. A
41
+ * Relay currency's `address` is already a string (a Solana mint or a Bitcoin
42
+ * native address), so no widening is needed. Malformed entries are skipped
43
+ * rather than failing the whole list.
44
+ *
45
+ * @returns the tokens, or an empty list when the payload is not an array
46
+ */
47
+ export declare const parseRelayCurrencies: (payload: unknown) => Token[];