@gibs/bridge-sdk 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/abis.js +0 -2
- package/dist/bridge-limits.js +0 -38
- package/dist/chain-info.js +0 -116
- package/dist/chain-names.js +0 -151
- package/dist/chains.js +0 -198
- package/dist/cjs/abis.js +0 -2
- package/dist/cjs/bridge-limits.js +0 -38
- package/dist/cjs/chain-info.js +0 -116
- package/dist/cjs/chain-names.js +0 -151
- package/dist/cjs/chains.js +0 -198
- package/dist/cjs/claims.js +0 -60
- package/dist/cjs/config.js +1 -208
- package/dist/cjs/consolidated-quote.js +2 -464
- package/dist/cjs/destination-calldata.js +0 -71
- package/dist/cjs/ecosystems.js +0 -183
- package/dist/cjs/execution.js +0 -92
- package/dist/cjs/fee-type.js +0 -1
- package/dist/cjs/hyperliquid-execution.js +0 -98
- package/dist/cjs/image-links.js +0 -108
- package/dist/cjs/lifi-execution-guard.js +65 -172
- package/dist/cjs/near-intents-conformance.js +0 -110
- package/dist/cjs/near-intents-route.d.ts +13 -0
- package/dist/cjs/near-intents-route.js +7 -140
- package/dist/cjs/near-intents.js +0 -562
- package/dist/cjs/origin-deposit.js +0 -56
- package/dist/cjs/pre-quote.js +0 -80
- package/dist/cjs/prices.js +0 -113
- package/dist/cjs/providers.js +0 -83
- package/dist/cjs/quote-fees.js +0 -80
- package/dist/cjs/referral.js +0 -75
- package/dist/cjs/relay-currencies.js +0 -31
- package/dist/cjs/relay-depository.js +52 -77
- package/dist/cjs/relay-quote.js +0 -394
- package/dist/cjs/route-steps.js +0 -100
- package/dist/cjs/routing/amounts.js +0 -83
- package/dist/cjs/routing/candidate-route.d.ts +46 -1
- package/dist/cjs/routing/candidate-route.js +12 -40
- package/dist/cjs/routing/candidate.js +0 -46
- package/dist/cjs/routing/carrier-provider-key.js +0 -27
- package/dist/cjs/routing/carriers/omnibridge.js +0 -120
- package/dist/cjs/routing/carriers/pulsex-paths.js +0 -57
- package/dist/cjs/routing/carriers/pulsex-split.js +0 -92
- package/dist/cjs/routing/carriers/pulsex.js +0 -279
- package/dist/cjs/routing/carriers/wrap.js +0 -57
- package/dist/cjs/routing/composed-route-quality.js +0 -95
- package/dist/cjs/routing/entry-chain-anchor.js +0 -27
- package/dist/cjs/routing/format.js +0 -50
- package/dist/cjs/routing/index.d.ts +1 -1
- package/dist/cjs/routing/index.js +3 -30
- package/dist/cjs/routing/producers.js +0 -854
- package/dist/cjs/routing/providers.js +0 -27
- package/dist/cjs/routing/search/compose.js +0 -56
- package/dist/cjs/routing/search/graph.js +0 -364
- package/dist/cjs/routing/search/plan-route-data.js +5 -510
- package/dist/cjs/routing/search/score.js +0 -344
- package/dist/cjs/routing/swap-transactions.js +0 -74
- package/dist/cjs/routing/usd-value.js +0 -12
- package/dist/cjs/settings.js +1 -456
- package/dist/cjs/swap-venues.js +0 -17
- package/dist/cjs/ton-connect.js +0 -82
- package/dist/cjs/ton-execution.js +0 -6
- package/dist/cjs/ton.js +0 -42
- package/dist/cjs/tron-execution.js +0 -145
- package/dist/cjs/types.d.ts +8 -0
- package/dist/claims.js +0 -60
- package/dist/config.js +1 -211
- package/dist/consolidated-quote.js +2 -464
- package/dist/destination-calldata.js +0 -71
- package/dist/ecosystems.js +0 -183
- package/dist/execution.js +0 -92
- package/dist/fee-type.js +0 -2
- package/dist/hyperliquid-execution.js +0 -98
- package/dist/image-links.js +0 -108
- package/dist/lifi-execution-guard.js +65 -172
- package/dist/near-intents-conformance.js +0 -110
- package/dist/near-intents-route.d.ts +13 -0
- package/dist/near-intents-route.js +5 -139
- package/dist/near-intents.js +0 -562
- package/dist/origin-deposit.js +0 -56
- package/dist/pre-quote.js +0 -80
- package/dist/prices.js +0 -113
- package/dist/providers.js +0 -83
- package/dist/quote-fees.js +0 -80
- package/dist/referral.js +0 -75
- package/dist/relay-currencies.js +0 -31
- package/dist/relay-depository.js +52 -77
- package/dist/relay-quote.js +0 -394
- package/dist/route-steps.js +0 -100
- package/dist/routing/amounts.js +0 -83
- package/dist/routing/candidate-route.d.ts +46 -1
- package/dist/routing/candidate-route.js +10 -39
- package/dist/routing/candidate.js +0 -46
- package/dist/routing/carrier-provider-key.js +0 -27
- package/dist/routing/carriers/omnibridge.js +0 -120
- package/dist/routing/carriers/pulsex-paths.js +0 -57
- package/dist/routing/carriers/pulsex-split.js +0 -92
- package/dist/routing/carriers/pulsex.js +0 -279
- package/dist/routing/carriers/wrap.js +0 -57
- package/dist/routing/composed-route-quality.js +0 -95
- package/dist/routing/entry-chain-anchor.js +0 -27
- package/dist/routing/format.js +0 -50
- package/dist/routing/index.d.ts +1 -1
- package/dist/routing/index.js +1 -29
- package/dist/routing/producers.js +0 -854
- package/dist/routing/providers.js +0 -27
- package/dist/routing/search/compose.js +0 -56
- package/dist/routing/search/graph.js +0 -364
- package/dist/routing/search/plan-route-data.js +6 -511
- package/dist/routing/search/score.js +0 -344
- package/dist/routing/swap-transactions.js +0 -74
- package/dist/routing/usd-value.js +0 -12
- package/dist/settings.js +1 -456
- package/dist/swap-venues.js +0 -17
- package/dist/ton-connect.js +0 -82
- package/dist/ton-execution.js +0 -6
- package/dist/ton.js +0 -42
- package/dist/tron-execution.js +0 -145
- package/dist/types.d.ts +8 -0
- package/package.json +3 -3
package/dist/route-steps.js
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { chainDisplayName } from './chain-names.js';
|
|
2
2
|
const isHexString = (value) => typeof value === 'string' && value.startsWith('0x');
|
|
3
|
-
/**
|
|
4
|
-
* Sums the `amountUSD` field across a LI.FI cost array (`feeCosts` or
|
|
5
|
-
* `gasCosts`), tolerating missing/malformed entries by skipping them. LI.FI
|
|
6
|
-
* reports each cost's dollar value as a numeric string; entries without a finite
|
|
7
|
-
* `amountUSD` contribute nothing rather than poisoning the sum with `NaN`.
|
|
8
|
-
*
|
|
9
|
-
* @param costs - a raw `feeCosts`/`gasCosts` value from a step estimate
|
|
10
|
-
* @returns the total United States dollar cost; zero when none are present
|
|
11
|
-
*/
|
|
12
3
|
const sumUsdCosts = (costs) => {
|
|
13
4
|
if (!Array.isArray(costs)) {
|
|
14
5
|
return 0;
|
|
@@ -25,13 +16,6 @@ const asRecord = (value) => {
|
|
|
25
16
|
}
|
|
26
17
|
return value;
|
|
27
18
|
};
|
|
28
|
-
/**
|
|
29
|
-
* Maps a LI.FI step type string to the normalized `RouteStepKind`.
|
|
30
|
-
*
|
|
31
|
-
* LI.FI reports `"cross"` for bridge legs and `"swap"` for same-chain
|
|
32
|
-
* exchanges. `"wrap"` and `"unwrap"` map directly. Unknown types fall back to
|
|
33
|
-
* `"send"` rather than failing the whole normalization.
|
|
34
|
-
*/
|
|
35
19
|
const lifiTypeToKind = (type) => {
|
|
36
20
|
if (type === 'cross')
|
|
37
21
|
return 'bridge';
|
|
@@ -43,12 +27,6 @@ const lifiTypeToKind = (type) => {
|
|
|
43
27
|
return 'unwrap';
|
|
44
28
|
return 'send';
|
|
45
29
|
};
|
|
46
|
-
/**
|
|
47
|
-
* Builds a human-readable label for a LI.FI step.
|
|
48
|
-
*
|
|
49
|
-
* Labels are fully spelled out (no acronyms) so they can be displayed
|
|
50
|
-
* directly in the UI without any post-processing.
|
|
51
|
-
*/
|
|
52
30
|
const buildLifiLabel = (kind, toChainId, toolName) => {
|
|
53
31
|
if (kind === 'bridge')
|
|
54
32
|
return `Bridge to ${chainDisplayName(toChainId)}`;
|
|
@@ -60,15 +38,6 @@ const buildLifiLabel = (kind, toChainId, toolName) => {
|
|
|
60
38
|
return `Unwrap to native on ${chainDisplayName(toChainId)}`;
|
|
61
39
|
return `Transfer on ${chainDisplayName(toChainId)}`;
|
|
62
40
|
};
|
|
63
|
-
/**
|
|
64
|
-
* Normalizes a LI.FI `includedSteps` array into the provider-agnostic
|
|
65
|
-
* `RouteStep[]` shape. Steps with missing or non-hex token addresses are
|
|
66
|
-
* silently dropped rather than causing the whole normalization to fail.
|
|
67
|
-
*
|
|
68
|
-
* @param steps - the raw `includedSteps` value from a LI.FI quote response,
|
|
69
|
-
* or `undefined` when the field was absent (trimmed responses)
|
|
70
|
-
* @returns ordered list of normalized steps; empty when no usable legs are present
|
|
71
|
-
*/
|
|
72
41
|
export const normalizeLifiSteps = (steps) => {
|
|
73
42
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
74
43
|
return [];
|
|
@@ -89,12 +58,6 @@ export const normalizeLifiSteps = (steps) => {
|
|
|
89
58
|
if (fromChainId === null || toChainId === null) {
|
|
90
59
|
continue;
|
|
91
60
|
}
|
|
92
|
-
// A step that changes chains IS a bridge, whatever the provider called its
|
|
93
|
-
// type. LI.FI labels most cross-chain legs `"cross"`, but some (e.g. an Across
|
|
94
|
-
// leg, or a `"protocol"`/`"lifi"`-typed leg) would otherwise fall through to
|
|
95
|
-
// the generic `"send"` kind and read as "Transfer on Ethereum" — misleading,
|
|
96
|
-
// since the funds are actually being BRIDGED there. Deciding from the chains
|
|
97
|
-
// rather than the type string keeps the label honest.
|
|
98
61
|
const typeKind = lifiTypeToKind(type);
|
|
99
62
|
const kind = typeKind === 'send' && fromChainId !== toChainId ? 'bridge' : typeKind;
|
|
100
63
|
const fromTokenRecord = asRecord(action.fromToken);
|
|
@@ -114,10 +77,6 @@ export const normalizeLifiSteps = (steps) => {
|
|
|
114
77
|
: typeof step.tool === 'string'
|
|
115
78
|
? step.tool
|
|
116
79
|
: 'unknown';
|
|
117
|
-
// The per-step tool's logo, when the provider supplies one. This is the best
|
|
118
|
-
// source for a real service-provider image on the consuming interface (for
|
|
119
|
-
// example the Uniswap or Stargate logo for the leg the aggregator routes
|
|
120
|
-
// through). Null when absent, so the interface falls back to an initials tile.
|
|
121
80
|
const providerLogoUri = typeof toolDetails?.logoURI === 'string' ? toolDetails.logoURI : null;
|
|
122
81
|
const label = buildLifiLabel(kind, toChainId, toolName);
|
|
123
82
|
const estimate = asRecord(step.estimate);
|
|
@@ -135,15 +94,10 @@ export const normalizeLifiSteps = (steps) => {
|
|
|
135
94
|
const router = isHexString(integrationData?.callToAddress)
|
|
136
95
|
? integrationData.callToAddress
|
|
137
96
|
: null;
|
|
138
|
-
// Per-step cost = protocol fees + gas, both reported in United States dollars.
|
|
139
|
-
// Null when the provider reported neither, so the UI omits the hop's fee.
|
|
140
97
|
const protocolFeeUsd = sumUsdCosts(estimate?.feeCosts);
|
|
141
98
|
const gasCostUsd = sumUsdCosts(estimate?.gasCosts);
|
|
142
99
|
const stepCostUsd = protocolFeeUsd + gasCostUsd;
|
|
143
100
|
const feeUsd = stepCostUsd > 0 ? stepCostUsd : null;
|
|
144
|
-
// Gas alone, for route ranking (which charges gas on top of the paid-versus-
|
|
145
|
-
// delivered spread and must not double-count the protocol fees already baked
|
|
146
|
-
// into the quote's `fromAmount`). Null when no gas was reported.
|
|
147
101
|
const gasUsd = gasCostUsd > 0 ? gasCostUsd : null;
|
|
148
102
|
result.push({
|
|
149
103
|
kind,
|
|
@@ -165,12 +119,6 @@ export const normalizeLifiSteps = (steps) => {
|
|
|
165
119
|
}
|
|
166
120
|
return result;
|
|
167
121
|
};
|
|
168
|
-
/**
|
|
169
|
-
* Parses one `{ currency, amount }` entry from Relay's `details.route`.
|
|
170
|
-
*
|
|
171
|
-
* @param value - a raw `inputCurrency`/`outputCurrency` value
|
|
172
|
-
* @returns the endpoint, or null when the chain id or address is unusable
|
|
173
|
-
*/
|
|
174
122
|
const relayRouteEndpoint = (value) => {
|
|
175
123
|
const entry = asRecord(value);
|
|
176
124
|
const currency = asRecord(entry?.currency);
|
|
@@ -179,8 +127,6 @@ const relayRouteEndpoint = (value) => {
|
|
|
179
127
|
}
|
|
180
128
|
const chainId = typeof currency.chainId === 'number' ? currency.chainId : null;
|
|
181
129
|
const address = typeof currency.address === 'string' ? currency.address : null;
|
|
182
|
-
// Without a chain and an identifier there is no leg to describe. Both are
|
|
183
|
-
// always present in Relay's verified shape; bail rather than invent one.
|
|
184
130
|
if (chainId === null || address === null) {
|
|
185
131
|
return null;
|
|
186
132
|
}
|
|
@@ -202,46 +148,12 @@ const relayRouteEndpoint = (value) => {
|
|
|
202
148
|
amount,
|
|
203
149
|
};
|
|
204
150
|
};
|
|
205
|
-
/**
|
|
206
|
-
* Reads a `{ amountUsd }` entry from Relay's `fees` object as a number.
|
|
207
|
-
* Relay reports dollar values as numeric strings; anything non-finite
|
|
208
|
-
* contributes nothing rather than poisoning the sum with `NaN`.
|
|
209
|
-
*/
|
|
210
151
|
const relayFeeUsd = (fees, key) => {
|
|
211
152
|
const fee = asRecord(fees?.[key]);
|
|
212
153
|
const amountUsd = typeof fee?.amountUsd === 'string' ? Number(fee.amountUsd) : Number.NaN;
|
|
213
154
|
return Number.isFinite(amountUsd) ? amountUsd : 0;
|
|
214
155
|
};
|
|
215
|
-
/** True when two endpoints are the same currency on the same chain. */
|
|
216
156
|
const isSameEndpoint = (left, right) => left.chainId === right.chainId && left.address === right.address;
|
|
217
|
-
/**
|
|
218
|
-
* Normalizes a Relay quote into the provider-agnostic `RouteStep[]` shape.
|
|
219
|
-
*
|
|
220
|
-
* Relay's `steps` array holds only OPERATIONAL steps (`approve` and `deposit`) —
|
|
221
|
-
* what the user signs, not where the funds go. The route itself lives in
|
|
222
|
-
* `details.route`, which describes the journey as an origin leg and a
|
|
223
|
-
* destination leg, each with an input and an output currency. Reading those four
|
|
224
|
-
* endpoints in order and emitting a step between each consecutive pair recovers
|
|
225
|
-
* the real path, so a Bitcoin origin renders as "Bitcoin → United States Dollar
|
|
226
|
-
* Coin on Ethereum" rather than as nothing at all.
|
|
227
|
-
*
|
|
228
|
-
* Consecutive endpoints that name the same currency on the same chain are
|
|
229
|
-
* collapsed, because Relay repeats an endpoint when a leg performs no
|
|
230
|
-
* conversion. A step's kind follows from its chains: crossing chains is a
|
|
231
|
-
* `bridge`, staying put is a `swap`. Relay reports no wrap/unwrap distinction,
|
|
232
|
-
* so none is invented.
|
|
233
|
-
*
|
|
234
|
-
* Costs are route-level in Relay's response, not per leg, so they are attached
|
|
235
|
-
* to the FIRST step — the one the user actually signs and pays at. `gasUsd`
|
|
236
|
-
* carries `fees.gas` alone (the origin transaction's gas, paid on top in the
|
|
237
|
-
* network's native token), while `feeUsd` adds the relayer and application fees
|
|
238
|
-
* on top of it, matching how the LI.FI normalizer splits the two. The relayer
|
|
239
|
-
* fee's `relayerGas`/`relayerService` components are deliberately NOT summed:
|
|
240
|
-
* they add up to `relayer` exactly, so counting them would double the fee.
|
|
241
|
-
*
|
|
242
|
-
* @param quote - a raw Relay quote response body
|
|
243
|
-
* @returns ordered list of normalized steps; empty when no route is present
|
|
244
|
-
*/
|
|
245
157
|
export const normalizeRelaySteps = (quote) => {
|
|
246
158
|
const details = asRecord(asRecord(quote)?.details);
|
|
247
159
|
const route = asRecord(details?.route);
|
|
@@ -256,15 +168,12 @@ export const normalizeRelaySteps = (quote) => {
|
|
|
256
168
|
relayRouteEndpoint(destination?.inputCurrency),
|
|
257
169
|
relayRouteEndpoint(destination?.outputCurrency),
|
|
258
170
|
].filter((endpoint) => endpoint !== null);
|
|
259
|
-
// Collapse repeats so a leg that converts nothing does not become a step.
|
|
260
171
|
const path = endpoints.filter((endpoint, index) => index === 0 || !isSameEndpoint(endpoint, endpoints[index - 1]));
|
|
261
172
|
if (path.length < 2) {
|
|
262
173
|
return [];
|
|
263
174
|
}
|
|
264
175
|
const fees = asRecord(asRecord(quote)?.fees);
|
|
265
176
|
const gasUsd = relayFeeUsd(fees, 'gas');
|
|
266
|
-
// `relayer` is the full relayer charge; its `relayerGas` and `relayerService`
|
|
267
|
-
// components sum to it exactly, so only the total is counted here.
|
|
268
177
|
const totalUsd = gasUsd + relayFeeUsd(fees, 'relayer') + relayFeeUsd(fees, 'app');
|
|
269
178
|
const result = [];
|
|
270
179
|
for (let index = 1; index < path.length; index += 1) {
|
|
@@ -283,20 +192,11 @@ export const normalizeRelaySteps = (quote) => {
|
|
|
283
192
|
toToken: to.address,
|
|
284
193
|
fromTokenSymbol: from.symbol,
|
|
285
194
|
toTokenSymbol: to.symbol,
|
|
286
|
-
// Relay's currency metadata carries the TOKEN's own image, which is the only
|
|
287
|
-
// one available for a non-Ethereum endpoint (Bitcoin, Solana, Tron, The Open
|
|
288
|
-
// Network) — the image service cannot key those by a hex address.
|
|
289
195
|
fromTokenLogoUri: from.logoUri,
|
|
290
196
|
toTokenLogoUri: to.logoUri,
|
|
291
|
-
// Relay's `router` field on a route leg names the service ("relay"), not a
|
|
292
|
-
// contract address, so there is no router address to surface for the leg.
|
|
293
197
|
router: null,
|
|
294
|
-
// Relay supplies a logo for the currency, not for a per-leg tool, so the
|
|
295
|
-
// consuming interface falls back to the provider tile. Deliberately null
|
|
296
|
-
// rather than passing a token image off as a service-provider image.
|
|
297
198
|
providerLogoUri: null,
|
|
298
199
|
estimatedOut: to.amount,
|
|
299
|
-
// Route-level costs land on the signed step; later legs run inside it.
|
|
300
200
|
feeUsd: isFirstStep && totalUsd > 0 ? totalUsd : null,
|
|
301
201
|
gasUsd: isFirstStep && gasUsd > 0 ? gasUsd : null,
|
|
302
202
|
});
|
package/dist/routing/amounts.js
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
1
|
import { formatUnits, parseUnits } from 'viem';
|
|
2
2
|
import { amountAfterBridgeFee, bridgeCost } from '../settings.js';
|
|
3
|
-
/**
|
|
4
|
-
* The amount that actually LANDS on PulseChain for an aggregator candidate, net
|
|
5
|
-
* of the omnibridge crossing fee.
|
|
6
|
-
*
|
|
7
|
-
* The aggregator delivers `entryChainAmount` (the consolidated request's
|
|
8
|
-
* `toAmount`) onto the omnibridge entry chain, and the omnibridge entry call
|
|
9
|
-
* relays exactly that — but the omnibridge takes its foreign→home crossing fee on
|
|
10
|
-
* the way to PulseChain, so the user receives LESS than `entryChainAmount`. This
|
|
11
|
-
* nets that crossing fee out using the SDK's OWN fee primitives ({@link bridgeCost}
|
|
12
|
-
* and {@link amountAfterBridgeFee}) — the identical math the direct engine applies
|
|
13
|
-
* — so the displayed received amount is honest rather than overstated.
|
|
14
|
-
*
|
|
15
|
-
* The multiplexed (aggregator) swap fees are NOT subtracted here: the aggregator
|
|
16
|
-
* already quotes the source-chain `fromAmount` it needs to GUARANTEE
|
|
17
|
-
* `entryChainAmount` arrives, so those fees are already reflected in `fromAmount`.
|
|
18
|
-
* Only the omnibridge crossing fee remains to net out, which is this function's
|
|
19
|
-
* sole job.
|
|
20
|
-
*
|
|
21
|
-
* @param entryChainAmount - the amount arriving on the entry chain (request `toAmount`)
|
|
22
|
-
* @param omnibridgeFeePerEther - the omnibridge foreign→home fee rate (per 1e18), or null
|
|
23
|
-
* @returns the net amount delivered on PulseChain; equals `entryChainAmount` when the fee is unknown
|
|
24
|
-
*/
|
|
25
3
|
export const netDeliveredAmount = (entryChainAmount, omnibridgeFeePerEther) => {
|
|
26
|
-
// Defensive: the omnibridge percentage math runs ONLY when both operands are
|
|
27
|
-
// true bigints. A non-bigint fee (a value rehydrated from storage as a string,
|
|
28
|
-
// the project's documented BigInt-serialization gotcha) would throw inside
|
|
29
|
-
// `bridgeCost`'s mixed-type multiplication, so fall back to the un-netted
|
|
30
|
-
// entry-chain amount rather than crash the render. The `typeof` guards intentionally
|
|
31
|
-
// outlive the static `bigint` types because the runtime value can violate them.
|
|
32
4
|
if (typeof entryChainAmount !== 'bigint' || typeof omnibridgeFeePerEther !== 'bigint') {
|
|
33
5
|
return entryChainAmount;
|
|
34
6
|
}
|
|
@@ -36,25 +8,6 @@ export const netDeliveredAmount = (entryChainAmount, omnibridgeFeePerEther) => {
|
|
|
36
8
|
const net = amountAfterBridgeFee({ amountToBridge: entryChainAmount, bridgeCost: cost });
|
|
37
9
|
return net ?? entryChainAmount;
|
|
38
10
|
};
|
|
39
|
-
/**
|
|
40
|
-
* Restates an amount in a different token's decimals, preserving the QUANTITY.
|
|
41
|
-
*
|
|
42
|
-
* This is the honest degraded answer when {@link valueEquivalentAmount} cannot
|
|
43
|
-
* price a conversion. Losing the price must not also lose the decimals: the two
|
|
44
|
-
* omnibridge anchors hold the same asset bridged two ways, and the only
|
|
45
|
-
* difference between them is where the decimal point sits. Returning an
|
|
46
|
-
* unadjusted amount across that gap is wrong by a factor of 10^12, which is the
|
|
47
|
-
* exact error the value conversion exists to prevent.
|
|
48
|
-
*
|
|
49
|
-
* Scaling DOWN truncates, and must — a six-decimal token cannot hold the twelve
|
|
50
|
-
* extra digits an eighteen-decimal one carries. The lost remainder is smaller
|
|
51
|
-
* than one base unit of the target.
|
|
52
|
-
*
|
|
53
|
-
* @param params.amount - the amount, in `fromDecimals` base units
|
|
54
|
-
* @param params.fromDecimals - the decimals the amount is currently expressed in
|
|
55
|
-
* @param params.toDecimals - the decimals to restate it in
|
|
56
|
-
* @returns the same quantity, in `toDecimals` base units
|
|
57
|
-
*/
|
|
58
11
|
export const restateInDecimals = ({ amount, fromDecimals, toDecimals, }) => {
|
|
59
12
|
if (fromDecimals === toDecimals)
|
|
60
13
|
return amount;
|
|
@@ -62,47 +15,11 @@ export const restateInDecimals = ({ amount, fromDecimals, toDecimals, }) => {
|
|
|
62
15
|
return amount * 10n ** BigInt(toDecimals - fromDecimals);
|
|
63
16
|
return amount / 10n ** BigInt(fromDecimals - toDecimals);
|
|
64
17
|
};
|
|
65
|
-
/**
|
|
66
|
-
* Converts `amount` of a source token into the base-unit amount of a TARGET token
|
|
67
|
-
* carrying the SAME United States dollar value:
|
|
68
|
-
*
|
|
69
|
-
* valueUsd = amount / 10^fromDecimals × fromPrice
|
|
70
|
-
* targetAmount = valueUsd / toPrice × 10^toDecimals
|
|
71
|
-
*
|
|
72
|
-
* This is what lets the two omnibridge entry points be quoted for the SAME VALUE
|
|
73
|
-
* even when their anchor tokens differ in decimals — a PulseChain-bridged
|
|
74
|
-
* stablecoin is 6 decimals when bridged from Ethereum but 18 from Binance Smart
|
|
75
|
-
* Chain. Feeding one raw integer to both targeted quantities ~10^12 apart, so the
|
|
76
|
-
* routes were not comparable and the higher-decimal entry won on a bigger NUMBER
|
|
77
|
-
* rather than more value. Targeting equal value in each anchor's own decimals
|
|
78
|
-
* restores a fair, decimals-correct comparison.
|
|
79
|
-
*
|
|
80
|
-
* Returns null when either price is missing or non-positive, the scaled value is
|
|
81
|
-
* not finite, or the target cannot be parsed — the caller then has no
|
|
82
|
-
* value-equivalent target and must fall back rather than quote a bogus amount.
|
|
83
|
-
*
|
|
84
|
-
* @param params.amount - the source amount, in `fromDecimals` base units
|
|
85
|
-
* @param params.fromDecimals - the source token's decimals
|
|
86
|
-
* @param params.fromPrice - the source token's per-token United States dollar price, or null
|
|
87
|
-
* @param params.toDecimals - the target token's decimals
|
|
88
|
-
* @param params.toPrice - the target token's per-token United States dollar price, or null
|
|
89
|
-
* @returns the value-equivalent amount in the target token's base units, or null
|
|
90
|
-
*/
|
|
91
18
|
export const valueEquivalentAmount = ({ amount, fromDecimals, fromPrice, toDecimals, toPrice, }) => {
|
|
92
19
|
if (fromPrice === null || toPrice === null)
|
|
93
20
|
return null;
|
|
94
21
|
if (!(fromPrice > 0) || !(toPrice > 0))
|
|
95
22
|
return null;
|
|
96
|
-
// Equal prices make this a pure change of decimals, and integer rescaling is
|
|
97
|
-
// EXACT where the double round trip is not. `39.3` is not representable as a
|
|
98
|
-
// double, so `toFixed(18)` writes 39.299999999999997158 and the conversion
|
|
99
|
-
// comes back 2842 base units short of the 39.3 that went in. At the same
|
|
100
|
-
// decimals it lands one unit out (0.008802 returns as 8802000000000001).
|
|
101
|
-
//
|
|
102
|
-
// Both are negligible in value and neither is a reason to convert at all: with
|
|
103
|
-
// one price on both sides there is no value question to answer, only a decimal
|
|
104
|
-
// point to move. Moving it in integers keeps the same input producing the same
|
|
105
|
-
// target.
|
|
106
23
|
if (fromPrice === toPrice)
|
|
107
24
|
return restateInDecimals({ amount, fromDecimals, toDecimals });
|
|
108
25
|
const valueUsd = Number(formatUnits(amount, fromDecimals)) * fromPrice;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CandidateExecutionInputs } from '../execution.js';
|
|
1
2
|
import type { RouteCandidate } from './candidate.js';
|
|
2
3
|
import type { RouteData } from './route-data.js';
|
|
3
4
|
/**
|
|
@@ -37,7 +38,51 @@ export declare const buildRouteDataForCandidate: (candidate: RouteCandidate) =>
|
|
|
37
38
|
* site, so a surface that warns about a long journey and a surface that ranks
|
|
38
39
|
* routes cannot come to different counts.
|
|
39
40
|
*
|
|
41
|
+
* THIS COUNT LEAVES OUT APPROVALS. A token that is not the chain's native coin
|
|
42
|
+
* needs an `approve` transaction before its first section when the allowance
|
|
43
|
+
* is too small, and that approval is a signature too. Raw route data does not
|
|
44
|
+
* say who the spender is or what the account has approved already, so this
|
|
45
|
+
* function cannot count it. Use {@link routeSignatureCountForAccount} for the
|
|
46
|
+
* number the wallet will really ask for.
|
|
47
|
+
*
|
|
40
48
|
* @param route - the raw route data
|
|
41
|
-
* @returns the number of signatures, one per section
|
|
49
|
+
* @returns the number of signatures, one per section, with no approvals
|
|
42
50
|
*/
|
|
43
51
|
export declare const routeSignatureCount: (route: RouteData) => number;
|
|
52
|
+
/** What {@link routeSignatureCountForAccount} reports. */
|
|
53
|
+
export type RouteSignatureCount = {
|
|
54
|
+
/** Signatures for the sections themselves: one per planned section. */
|
|
55
|
+
readonly signatures: number;
|
|
56
|
+
/** Approval transactions the account must sign first, given what it has approved already. */
|
|
57
|
+
readonly approvals: number;
|
|
58
|
+
/** `signatures + approvals`: every wallet prompt the planned sections cause. */
|
|
59
|
+
readonly total: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* How many wallet signatures a route asks one account for, approvals included.
|
|
63
|
+
*
|
|
64
|
+
* {@link routeSignatureCount} counts sections only, so a USDC route reads "1"
|
|
65
|
+
* and then asks for two prompts: approve, then send. This function plans the
|
|
66
|
+
* sections with {@link planCandidateExecution} and counts what that plan
|
|
67
|
+
* contains, so it uses the same allowance rule and the same spender the
|
|
68
|
+
* execution planner uses. A native input needs no approval. An allowance that
|
|
69
|
+
* already covers the amount needs none. A smaller, non-zero allowance counts
|
|
70
|
+
* the reset and the new approval, the two transactions the planner emits. The
|
|
71
|
+
* spender is whatever the planner picks: the omnibridge mediator, a swap
|
|
72
|
+
* venue's router (including routers passed in `swapVenues`), or the
|
|
73
|
+
* aggregator's approval address.
|
|
74
|
+
*
|
|
75
|
+
* Pass every section of the route to count the whole route. Pass fewer to
|
|
76
|
+
* count only the window you plan now; each section is counted against the
|
|
77
|
+
* allowance on chain today, so a later section that spends a token an earlier
|
|
78
|
+
* section delivers is counted as if the approval is still needed.
|
|
79
|
+
*
|
|
80
|
+
* An aggregator section needs its `aggregator` inputs, a quote and the request
|
|
81
|
+
* it answered, exactly as {@link planCandidateExecution} does. Without them
|
|
82
|
+
* this function throws, because the approval spender comes from that quote.
|
|
83
|
+
*
|
|
84
|
+
* @param inputs - the same inputs {@link planCandidateExecution} takes
|
|
85
|
+
* @returns the signatures, approvals, and their total
|
|
86
|
+
* @throws when a section cannot be planned; the message names the section and the reason
|
|
87
|
+
*/
|
|
88
|
+
export declare const routeSignatureCountForAccount: (inputs: CandidateExecutionInputs) => Promise<RouteSignatureCount>;
|
|
@@ -1,45 +1,16 @@
|
|
|
1
1
|
import { buildNearIntentsTwoStepRouteData } from '../near-intents-route.js';
|
|
2
|
+
import { planCandidateExecution } from '../execution.js';
|
|
2
3
|
import { buildCandidateRouteData } from './producers.js';
|
|
3
|
-
/**
|
|
4
|
-
* One door in front of the two base route builders.
|
|
5
|
-
*
|
|
6
|
-
* WHY THIS EXISTS. A candidate is drawn by one of two builders and the choice
|
|
7
|
-
* is not the caller's to make by hand. {@link buildCandidateRouteData} draws a
|
|
8
|
-
* candidate as ONE signature, because the aggregator's executor performs the
|
|
9
|
-
* omnibridge entry call on arrival. {@link buildNearIntentsTwoStepRouteData}
|
|
10
|
-
* draws it as TWO, because the aggregator that carried it cannot execute
|
|
11
|
-
* anything on the chain it delivers to, so the reader signs the crossing
|
|
12
|
-
* themselves.
|
|
13
|
-
*
|
|
14
|
-
* Every composer that builds on top of a candidate — the PulseX conversion
|
|
15
|
-
* route is the first — has to pick between them, and picking wrong is not a
|
|
16
|
-
* cosmetic error. A two-signature candidate run through the one-signature
|
|
17
|
-
* builder produces a route that claims the crossing happens inside the
|
|
18
|
-
* transaction the reader already signed. It does not. The reader would select
|
|
19
|
-
* that route, sign once, and be left holding the anchor token on the entry
|
|
20
|
-
* chain with the interface reporting the journey finished.
|
|
21
|
-
*
|
|
22
|
-
* `candidate.deferredCrossing` is the fact that decides it, and it is the same
|
|
23
|
-
* field {@link buildNearIntentsTwoStepRouteData} refuses to build without. So
|
|
24
|
-
* the two agree by construction rather than by two callers remembering the
|
|
25
|
-
* same rule.
|
|
26
|
-
*
|
|
27
|
-
* @param candidate - the priced route candidate
|
|
28
|
-
* @returns the raw route data, or null when the candidate names a deferred
|
|
29
|
-
* crossing but no omnibridge provider to perform it
|
|
30
|
-
*/
|
|
31
4
|
export const buildRouteDataForCandidate = (candidate) => candidate.deferredCrossing
|
|
32
5
|
? buildNearIntentsTwoStepRouteData(candidate)
|
|
33
6
|
: buildCandidateRouteData(candidate);
|
|
34
|
-
/**
|
|
35
|
-
* How many wallet signatures a raw route asks the reader for.
|
|
36
|
-
*
|
|
37
|
-
* Counts the sections, because one section is one signature — that is what a
|
|
38
|
-
* section means. Named rather than left as `sections.length` at each call
|
|
39
|
-
* site, so a surface that warns about a long journey and a surface that ranks
|
|
40
|
-
* routes cannot come to different counts.
|
|
41
|
-
*
|
|
42
|
-
* @param route - the raw route data
|
|
43
|
-
* @returns the number of signatures, one per section
|
|
44
|
-
*/
|
|
45
7
|
export const routeSignatureCount = (route) => route.sections.length;
|
|
8
|
+
export const routeSignatureCountForAccount = async (inputs) => {
|
|
9
|
+
const plan = await planCandidateExecution(inputs);
|
|
10
|
+
if (plan.kind === 'refused') {
|
|
11
|
+
throw new Error(`Cannot count signatures: section ${plan.sectionIndex} cannot be planned (${JSON.stringify(plan.refusal)}).`);
|
|
12
|
+
}
|
|
13
|
+
const approvals = plan.transactions.filter((transaction) => transaction.kind === 'approve').length;
|
|
14
|
+
const signatures = plan.transactions.length - approvals;
|
|
15
|
+
return { signatures, approvals, total: plan.transactions.length };
|
|
16
|
+
};
|
|
@@ -1,39 +1,6 @@
|
|
|
1
1
|
import { formatUnits } from 'viem';
|
|
2
2
|
import { netDeliveredAmount } from './amounts.js';
|
|
3
3
|
import { omnibridgeProviderForEntryChain } from './providers.js';
|
|
4
|
-
/**
|
|
5
|
-
* Synthesizes a {@link RouteCandidate} for the SAME-CHAIN direct-omnibridge
|
|
6
|
-
* conversion case: when the origin chain IS one of the omnibridge entry chains
|
|
7
|
-
* (Ethereum chain 1 or Binance Smart Chain chain 56), there is no aggregator leg
|
|
8
|
-
* to quote — the user bridges directly from that chain to PulseChain. The
|
|
9
|
-
* same-chain skip in {@link useAggregatorRouteCandidate} is correct (nothing to
|
|
10
|
-
* quote), but it left the conversion path empty. This helper fills that gap by
|
|
11
|
-
* synthesizing a zero-step candidate. The user interface no longer calls this:
|
|
12
|
-
* the route engine serves a conversion now. The export stays as published
|
|
13
|
-
* interface until a separate decision removes it.
|
|
14
|
-
*
|
|
15
|
-
* The synthetic quote carries zero steps (no aggregator hops), a zero execution
|
|
16
|
-
* duration (the direct crossing needs no quote wait), and a null transaction
|
|
17
|
-
* request — this candidate is DISPLAY and RANKING only. The candidate's
|
|
18
|
-
* `deliveredAmount` is net of the omnibridge crossing fee, identical to the math
|
|
19
|
-
* {@link useAggregatorRouteCandidate} applies.
|
|
20
|
-
*
|
|
21
|
-
* This is a PURE exported helper (no hooks, no side effects) so it is
|
|
22
|
-
* unit-testable in isolation and can be called from two `useMemo` blocks in
|
|
23
|
-
* Bridge.tsx without duplicating the object literal.
|
|
24
|
-
*
|
|
25
|
-
* EXECUTION: when the route built from this candidate is selected, the
|
|
26
|
-
* consolidated (aggregator) execute path is deliberately NOT used — there is no
|
|
27
|
-
* aggregator leg for it to sign. `executesViaConsolidated`
|
|
28
|
-
* (`packages/ui/src/lib/state/bridgeExecution.ts`) recognizes the candidate's
|
|
29
|
-
* `entryChainId === originChainId` and routes the selection through the direct
|
|
30
|
-
* omnibridge sender instead, which builds its transaction from the live
|
|
31
|
-
* `useBridgeSettings` cascade rather than from this synthetic quote.
|
|
32
|
-
*
|
|
33
|
-
* @param inputs - the entry chain, origin/destination tokens, amount, and fee rate
|
|
34
|
-
* @returns the synthesized RouteCandidate, or throws when the entry chain is not
|
|
35
|
-
* a valid omnibridge entry point (caller must guard on `omnibridgeProviderForEntryChain`)
|
|
36
|
-
*/
|
|
37
4
|
export const buildDirectCrossingCandidate = ({ entryChainId, originChainId, originToken, destinationToken, amountIn, originUsdPrice, destinationUsdPrice, omnibridgeFeePerEther, }) => {
|
|
38
5
|
const provider = omnibridgeProviderForEntryChain(entryChainId);
|
|
39
6
|
if (provider === null) {
|
|
@@ -49,13 +16,6 @@ export const buildDirectCrossingCandidate = ({ entryChainId, originChainId, orig
|
|
|
49
16
|
: null;
|
|
50
17
|
const quoteId = `direct-omnibridge-${entryChainId}-${originChainId}`;
|
|
51
18
|
const syntheticQuote = {
|
|
52
|
-
// The synthetic quote wraps the omnibridge provider slug. ConsolidatedQuote's
|
|
53
|
-
// `provider` field is typed for the aggregator providers ('lifi' | 'relay'),
|
|
54
|
-
// but the functions that read it (`resolveProviderDisplayName`,
|
|
55
|
-
// `resolveProviderLogoUri`) accept any string at runtime. The cast is safe here
|
|
56
|
-
// because no aggregator-specific code path runs on a zero-step candidate —
|
|
57
|
-
// this synthetic quote is never itself signed; see the module doc comment
|
|
58
|
-
// above for where execution actually happens for the route built from it.
|
|
59
19
|
provider: provider,
|
|
60
20
|
quoteId,
|
|
61
21
|
tool: provider,
|
|
@@ -65,9 +25,6 @@ export const buildDirectCrossingCandidate = ({ entryChainId, originChainId, orig
|
|
|
65
25
|
approvalAddress: null,
|
|
66
26
|
executionDurationSeconds: 0,
|
|
67
27
|
steps: [],
|
|
68
|
-
// A direct crossing carries no aggregator, so there is no carrier fee to
|
|
69
|
-
// report. The omnibridge's own crossing fee is `omnibridgeFeeAmount`
|
|
70
|
-
// beside it, which the route surfaces separately.
|
|
71
28
|
fees: [],
|
|
72
29
|
originDeposit: {
|
|
73
30
|
ecosystem: 'evm',
|
|
@@ -88,9 +45,6 @@ export const buildDirectCrossingCandidate = ({ entryChainId, originChainId, orig
|
|
|
88
45
|
deliveredAmount: delivered,
|
|
89
46
|
omnibridgeFeeAmount,
|
|
90
47
|
destinationUsdPrice,
|
|
91
|
-
// A same-chain crossing has no aggregator leg to solve backward, so it spends
|
|
92
|
-
// exactly what was typed — requested and spent are the same figure by
|
|
93
|
-
// construction, and the route reports nothing unspent.
|
|
94
48
|
requestedAmount: amountIn,
|
|
95
49
|
};
|
|
96
50
|
};
|
|
@@ -1,37 +1,10 @@
|
|
|
1
1
|
import { Providers } from '../config.js';
|
|
2
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
3
|
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
4
|
const providerKeyByCarrierId = new Map([
|
|
14
5
|
...Object.values(Providers).map((provider) => [provider, provider]),
|
|
15
6
|
...providerRegistry.map((descriptor) => [descriptor.id, descriptor.id]),
|
|
16
7
|
['PulseX', 'PulseX'],
|
|
17
8
|
]);
|
|
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
9
|
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
10
|
export const carrierStepKind = (carrierId) => carrierId === WRAP_CARRIER_ID ? 'wrap' : null;
|