@gibs/bridge-sdk 1.13.0 → 1.17.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 +244 -18
- package/abis/package.json +6 -0
- package/bridge-limits/package.json +6 -0
- package/chain-info/package.json +6 -0
- package/chain-names/package.json +6 -0
- package/chains/package.json +6 -0
- package/claims/package.json +6 -0
- package/config/package.json +6 -0
- package/consolidated-quote/package.json +6 -0
- package/destination-calldata/package.json +6 -0
- package/dist/chain-info.d.ts +1 -1
- package/dist/chain-info.js +11 -12
- package/dist/chains.d.ts +25 -0
- package/dist/chains.js +55 -20
- package/dist/cjs/abis.d.ts +1 -0
- package/dist/cjs/abis.js +19 -0
- package/dist/cjs/bridge-limits.d.ts +103 -0
- package/dist/cjs/bridge-limits.js +89 -0
- package/dist/cjs/chain-info.d.ts +169 -0
- package/dist/cjs/chain-info.js +452 -0
- package/dist/cjs/chain-names.d.ts +160 -0
- package/dist/cjs/chain-names.js +207 -0
- package/dist/cjs/chains.d.ts +90 -0
- package/dist/cjs/chains.js +357 -0
- package/dist/cjs/claims.d.ts +198 -0
- package/dist/cjs/claims.js +182 -0
- package/dist/cjs/config.d.ts +240 -0
- package/dist/cjs/config.js +537 -0
- package/dist/cjs/consolidated-quote.d.ts +648 -0
- package/dist/cjs/consolidated-quote.js +1048 -0
- package/dist/cjs/destination-calldata.d.ts +118 -0
- package/dist/cjs/destination-calldata.js +156 -0
- package/dist/cjs/ecosystems.d.ts +217 -0
- package/dist/cjs/ecosystems.js +298 -0
- package/dist/cjs/execution.d.ts +346 -0
- package/dist/cjs/execution.js +468 -0
- package/dist/cjs/fee-type.d.ts +10 -0
- package/dist/cjs/fee-type.js +10 -0
- package/dist/cjs/hyperliquid-execution.d.ts +152 -0
- package/dist/cjs/hyperliquid-execution.js +175 -0
- package/dist/cjs/image-links.d.ts +117 -0
- package/dist/cjs/image-links.js +153 -0
- package/dist/cjs/index.d.ts +23 -0
- package/dist/cjs/index.js +39 -0
- package/dist/cjs/lifi-execution-guard.d.ts +210 -0
- package/dist/cjs/lifi-execution-guard.js +409 -0
- package/dist/cjs/near-intents-conformance.d.ts +85 -0
- package/dist/cjs/near-intents-conformance.js +337 -0
- package/dist/cjs/near-intents-route.d.ts +47 -0
- package/dist/cjs/near-intents-route.js +303 -0
- package/dist/cjs/near-intents.d.ts +524 -0
- package/dist/cjs/near-intents.js +1071 -0
- package/dist/cjs/origin-deposit.d.ts +326 -0
- package/dist/cjs/origin-deposit.js +82 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pre-quote.d.ts +198 -0
- package/dist/cjs/pre-quote.js +180 -0
- package/dist/cjs/prices.d.ts +107 -0
- package/dist/cjs/prices.js +172 -0
- package/dist/cjs/providers.d.ts +238 -0
- package/dist/cjs/providers.js +171 -0
- package/dist/cjs/quote-fees.d.ts +102 -0
- package/dist/cjs/quote-fees.js +201 -0
- package/dist/cjs/referral.d.ts +85 -0
- package/dist/cjs/referral.js +85 -0
- package/dist/cjs/relay-currencies.d.ts +47 -0
- package/dist/cjs/relay-currencies.js +95 -0
- package/dist/cjs/relay-depository.d.ts +2 -0
- package/dist/cjs/relay-depository.js +85 -0
- package/dist/cjs/relay-quote.d.ts +301 -0
- package/dist/cjs/relay-quote.js +996 -0
- package/dist/cjs/route-steps.d.ts +138 -0
- package/dist/cjs/route-steps.js +310 -0
- package/dist/cjs/routing/amounts.d.ts +80 -0
- package/dist/cjs/routing/amounts.js +126 -0
- package/dist/cjs/routing/candidate-route.d.ts +43 -0
- package/dist/cjs/routing/candidate-route.js +50 -0
- package/dist/cjs/routing/candidate.d.ts +237 -0
- package/dist/cjs/routing/candidate.js +100 -0
- package/dist/cjs/routing/carriers/ask-answer.d.ts +77 -0
- package/dist/cjs/routing/carriers/ask-answer.js +2 -0
- package/dist/cjs/routing/carriers/omnibridge.d.ts +137 -0
- package/dist/cjs/routing/carriers/omnibridge.js +459 -0
- package/dist/cjs/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/cjs/routing/carriers/pulsex-paths.js +97 -0
- package/dist/cjs/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/cjs/routing/carriers/pulsex-split.js +193 -0
- package/dist/cjs/routing/carriers/pulsex.d.ts +159 -0
- package/dist/cjs/routing/carriers/pulsex.js +565 -0
- package/dist/cjs/routing/carriers/wrap.d.ts +25 -0
- package/dist/cjs/routing/carriers/wrap.js +119 -0
- package/dist/cjs/routing/composed-route-quality.d.ts +125 -0
- package/dist/cjs/routing/composed-route-quality.js +144 -0
- package/dist/cjs/routing/entry-chain-anchor.d.ts +88 -0
- package/dist/cjs/routing/entry-chain-anchor.js +59 -0
- package/dist/cjs/routing/format.d.ts +31 -0
- package/dist/cjs/routing/format.js +64 -0
- package/dist/cjs/routing/index.d.ts +38 -0
- package/dist/cjs/routing/index.js +134 -0
- package/dist/cjs/routing/producers.d.ts +770 -0
- package/dist/cjs/routing/producers.js +1617 -0
- package/dist/cjs/routing/providers.d.ts +31 -0
- package/dist/cjs/routing/providers.js +48 -0
- package/dist/cjs/routing/route-data.d.ts +355 -0
- package/dist/cjs/routing/route-data.js +2 -0
- package/dist/cjs/routing/search/compose.d.ts +168 -0
- package/dist/cjs/routing/search/compose.js +104 -0
- package/dist/cjs/routing/search/graph.d.ts +668 -0
- package/dist/cjs/routing/search/graph.js +461 -0
- package/dist/cjs/routing/search/plan-route-data.d.ts +269 -0
- package/dist/cjs/routing/search/plan-route-data.js +848 -0
- package/dist/cjs/routing/search/score.d.ts +356 -0
- package/dist/cjs/routing/search/score.js +445 -0
- package/dist/cjs/routing/swap-transactions.d.ts +91 -0
- package/dist/cjs/routing/swap-transactions.js +131 -0
- package/dist/cjs/routing/usd-value.d.ts +17 -0
- package/dist/cjs/routing/usd-value.js +29 -0
- package/dist/cjs/settings.d.ts +785 -0
- package/dist/cjs/settings.js +1074 -0
- package/dist/cjs/ton-connect.d.ts +98 -0
- package/dist/cjs/ton-connect.js +206 -0
- package/dist/cjs/ton-execution.d.ts +7 -0
- package/dist/cjs/ton-execution.js +23 -0
- package/dist/cjs/ton.d.ts +37 -0
- package/dist/cjs/ton.js +83 -0
- package/dist/cjs/tron-execution.d.ts +146 -0
- package/dist/cjs/tron-execution.js +248 -0
- package/dist/cjs/types.d.ts +51 -0
- package/dist/cjs/types.js +2 -0
- package/dist/claims.d.ts +198 -0
- package/dist/claims.js +140 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +39 -5
- package/dist/consolidated-quote.d.ts +109 -15
- package/dist/consolidated-quote.js +310 -52
- package/dist/execution.d.ts +346 -0
- package/dist/execution.js +428 -0
- package/dist/hyperliquid-execution.js +2 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/lifi-execution-guard.d.ts +13 -2
- package/dist/lifi-execution-guard.js +4 -0
- package/dist/near-intents.js +2 -3
- package/dist/pre-quote.d.ts +6 -6
- package/dist/pre-quote.js +10 -14
- package/dist/providers.d.ts +17 -0
- package/dist/providers.js +6 -0
- package/dist/referral.d.ts +12 -4
- package/dist/referral.js +12 -4
- package/dist/relay-depository.d.ts +2 -0
- package/dist/relay-depository.js +82 -0
- package/dist/relay-quote.js +2 -3
- package/dist/routing/candidate.d.ts +3 -3
- package/dist/routing/candidate.js +3 -3
- package/dist/routing/carriers/ask-answer.d.ts +19 -0
- package/dist/routing/carriers/omnibridge.js +23 -21
- package/dist/routing/carriers/pulsex-paths.d.ts +80 -0
- package/dist/routing/carriers/pulsex-paths.js +92 -0
- package/dist/routing/carriers/pulsex-split.d.ts +175 -0
- package/dist/routing/carriers/pulsex-split.js +185 -0
- package/dist/routing/carriers/pulsex.d.ts +69 -21
- package/dist/routing/carriers/pulsex.js +184 -65
- package/dist/routing/composed-route-quality.d.ts +28 -6
- package/dist/routing/composed-route-quality.js +36 -5
- package/dist/routing/entry-chain-anchor.d.ts +1 -1
- package/dist/routing/entry-chain-anchor.js +1 -1
- package/dist/routing/index.d.ts +7 -3
- package/dist/routing/index.js +7 -3
- package/dist/routing/producers.js +1 -1
- package/dist/routing/providers.d.ts +5 -0
- package/dist/routing/providers.js +5 -0
- package/dist/routing/search/graph.d.ts +29 -0
- package/dist/routing/search/plan-route-data.d.ts +25 -0
- package/dist/routing/search/plan-route-data.js +43 -7
- package/dist/routing/search/score.d.ts +33 -1
- package/dist/routing/search/score.js +41 -6
- package/dist/routing/swap-transactions.d.ts +91 -0
- package/dist/routing/swap-transactions.js +125 -0
- package/dist/settings.d.ts +94 -1
- package/dist/settings.js +95 -3
- package/dist/ton-connect.d.ts +98 -0
- package/dist/ton-connect.js +200 -0
- package/dist/ton-execution.d.ts +4 -114
- package/dist/ton-execution.js +4 -244
- package/dist/ton.d.ts +37 -0
- package/dist/ton.js +65 -0
- package/docs/examples/example-a-ether-to-pulse.ts +300 -0
- package/docs/examples/example-b-token-pulsechain-to-ethereum.ts +270 -0
- package/docs/examples/shared.ts +261 -0
- package/docs/examples/tsconfig.json +18 -0
- package/docs/integration-guide.md +617 -0
- package/ecosystems/package.json +6 -0
- package/execution/package.json +6 -0
- package/fee-type/package.json +6 -0
- package/hyperliquid-execution/package.json +6 -0
- package/image-links/package.json +6 -0
- package/lifi-execution-guard/package.json +6 -0
- package/llms.txt +234 -0
- package/near-intents/package.json +6 -0
- package/near-intents-route/package.json +6 -0
- package/origin-deposit/package.json +6 -0
- package/package.json +818 -10
- package/pre-quote/package.json +6 -0
- package/prices/package.json +6 -0
- package/providers/package.json +6 -0
- package/quote-fees/package.json +6 -0
- package/referral/package.json +6 -0
- package/relay-currencies/package.json +6 -0
- package/relay-quote/package.json +6 -0
- package/route-steps/package.json +6 -0
- package/routing/package.json +6 -0
- package/settings/package.json +6 -0
- package/ton/package.json +6 -0
- package/ton-connect/package.json +6 -0
- package/ton-execution/package.json +6 -0
- package/tron-execution/package.json +6 -0
- package/types/package.json +6 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { Hex } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* The semantic kind of a single route step. These map to the provider's
|
|
4
|
+
* action type as faithfully as the response data allows.
|
|
5
|
+
*
|
|
6
|
+
* - `bridge` — cross-chain transfer (different `fromChainId` and `toChainId`)
|
|
7
|
+
* - `swap` — same-chain token exchange via a decentralized exchange
|
|
8
|
+
* - `wrap` — native asset → wrapped native (e.g. ETH → WETH)
|
|
9
|
+
* - `unwrap` — wrapped native → native asset
|
|
10
|
+
* - `send` — plain transfer with no token conversion
|
|
11
|
+
*/
|
|
12
|
+
export type RouteStepKind = 'bridge' | 'swap' | 'wrap' | 'unwrap' | 'send';
|
|
13
|
+
/**
|
|
14
|
+
* A normalized, provider-agnostic description of one leg in a bridge route.
|
|
15
|
+
* The UI renders these in order to show the user what will happen to their
|
|
16
|
+
* funds at each stage of the journey.
|
|
17
|
+
*/
|
|
18
|
+
export type RouteStep = {
|
|
19
|
+
/** Semantic category of this step. */
|
|
20
|
+
kind: RouteStepKind;
|
|
21
|
+
/**
|
|
22
|
+
* Human-readable, fully-spelled-out label for this step (no acronyms).
|
|
23
|
+
* Example: "Bridge to Ethereum" or "Swap on Uniswap V3".
|
|
24
|
+
*/
|
|
25
|
+
label: string;
|
|
26
|
+
/** Chain the step starts on, as a decimal chain id. */
|
|
27
|
+
fromChainId: number;
|
|
28
|
+
/** Chain the step lands on, as a decimal chain id. */
|
|
29
|
+
toChainId: number;
|
|
30
|
+
/**
|
|
31
|
+
* Token address the step consumes.
|
|
32
|
+
*
|
|
33
|
+
* Typed as `string` rather than `Hex` because a leg can start on a
|
|
34
|
+
* non-Ethereum chain, where the token identifier is not `0x`-prefixed — a
|
|
35
|
+
* Bitcoin address, a Solana or Tron base58 address, a The Open Network
|
|
36
|
+
* user-friendly address. Treat it as an opaque identifier for display and
|
|
37
|
+
* prefer {@link fromTokenSymbol} when showing it.
|
|
38
|
+
*/
|
|
39
|
+
fromToken: string;
|
|
40
|
+
/** Token address the step produces. `string` for the same reason as {@link fromToken}. */
|
|
41
|
+
toToken: string;
|
|
42
|
+
/**
|
|
43
|
+
* Ticker symbol of the consumed token (e.g. "USDC"), when the provider
|
|
44
|
+
* supplies it. Null when the response omitted the token's symbol — callers
|
|
45
|
+
* showing a conversion should fall back to a shortened address rather than the
|
|
46
|
+
* full address, which is unreadable.
|
|
47
|
+
*/
|
|
48
|
+
fromTokenSymbol: string | null;
|
|
49
|
+
/** Ticker symbol of the produced token, or null when the provider omits it. */
|
|
50
|
+
toTokenSymbol: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Logo image url for the CONSUMED token, when the provider supplies one. This is
|
|
53
|
+
* the token's own image, not the service provider's ({@link providerLogoUri}).
|
|
54
|
+
* Carried because a step can start on a non-Ethereum chain, where the image
|
|
55
|
+
* service has no entry keyed by a checksummed hex address and the provider's url
|
|
56
|
+
* is the only image available. Null when the provider omitted it.
|
|
57
|
+
*/
|
|
58
|
+
fromTokenLogoUri: string | null;
|
|
59
|
+
/** Logo image url for the PRODUCED token. Null for the same reasons as {@link fromTokenLogoUri}. */
|
|
60
|
+
toTokenLogoUri: string | null;
|
|
61
|
+
/**
|
|
62
|
+
* The router or contract this step calls, when the provider supplies it.
|
|
63
|
+
* Null when the provider does not expose a contract address for this leg.
|
|
64
|
+
*/
|
|
65
|
+
router: Hex | null;
|
|
66
|
+
/**
|
|
67
|
+
* Logo image url for the service provider that performs this step (for a
|
|
68
|
+
* LI.FI route this is the per-step tool: the decentralized exchange or bridge
|
|
69
|
+
* the aggregator routes through, for example Uniswap or Stargate). Taken from
|
|
70
|
+
* the provider's per-step tool details when present. Null when the provider
|
|
71
|
+
* supplied no logo url for the step, in which case the consuming interface
|
|
72
|
+
* falls back to a provider-initials tile rather than a broken image.
|
|
73
|
+
*/
|
|
74
|
+
providerLogoUri: string | null;
|
|
75
|
+
/**
|
|
76
|
+
* Estimated output of this step in base units, when the provider supplies it.
|
|
77
|
+
* Null when the provider does not report an estimated output for this leg.
|
|
78
|
+
*/
|
|
79
|
+
estimatedOut: bigint | null;
|
|
80
|
+
/**
|
|
81
|
+
* Estimated United States dollar cost of THIS step — the sum of the provider's
|
|
82
|
+
* per-step protocol fees (`feeCosts`) and gas costs (`gasCosts`) — so the UI can
|
|
83
|
+
* show a fee on every hop rather than only a single route total. Null when the
|
|
84
|
+
* provider reported no per-step costs (the UI then shows no fee for the hop).
|
|
85
|
+
*/
|
|
86
|
+
feeUsd: number | null;
|
|
87
|
+
/**
|
|
88
|
+
* Estimated United States dollar GAS cost of THIS step — the provider's
|
|
89
|
+
* `gasCosts` ALONE, with the protocol-fee portion ({@link feeUsd}'s `feeCosts`
|
|
90
|
+
* component) excluded. Surfaced separately from {@link feeUsd} because the two
|
|
91
|
+
* costs enter route comparison differently: protocol fees and slippage are
|
|
92
|
+
* already embedded in the quote's `fromAmount` (the backward-quoted origin
|
|
93
|
+
* token), whereas gas is paid ON TOP in the network's native token. Route
|
|
94
|
+
* ranking therefore charges only this gas figure on top of the paid-versus-
|
|
95
|
+
* delivered spread, so it must not double-count the protocol fees. Null when the
|
|
96
|
+
* provider reported no gas costs for the leg.
|
|
97
|
+
*/
|
|
98
|
+
gasUsd: number | null;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Normalizes a LI.FI `includedSteps` array into the provider-agnostic
|
|
102
|
+
* `RouteStep[]` shape. Steps with missing or non-hex token addresses are
|
|
103
|
+
* silently dropped rather than causing the whole normalization to fail.
|
|
104
|
+
*
|
|
105
|
+
* @param steps - the raw `includedSteps` value from a LI.FI quote response,
|
|
106
|
+
* or `undefined` when the field was absent (trimmed responses)
|
|
107
|
+
* @returns ordered list of normalized steps; empty when no usable legs are present
|
|
108
|
+
*/
|
|
109
|
+
export declare const normalizeLifiSteps: (steps: unknown) => RouteStep[];
|
|
110
|
+
/**
|
|
111
|
+
* Normalizes a Relay quote into the provider-agnostic `RouteStep[]` shape.
|
|
112
|
+
*
|
|
113
|
+
* Relay's `steps` array holds only OPERATIONAL steps (`approve` and `deposit`) —
|
|
114
|
+
* what the user signs, not where the funds go. The route itself lives in
|
|
115
|
+
* `details.route`, which describes the journey as an origin leg and a
|
|
116
|
+
* destination leg, each with an input and an output currency. Reading those four
|
|
117
|
+
* endpoints in order and emitting a step between each consecutive pair recovers
|
|
118
|
+
* the real path, so a Bitcoin origin renders as "Bitcoin → United States Dollar
|
|
119
|
+
* Coin on Ethereum" rather than as nothing at all.
|
|
120
|
+
*
|
|
121
|
+
* Consecutive endpoints that name the same currency on the same chain are
|
|
122
|
+
* collapsed, because Relay repeats an endpoint when a leg performs no
|
|
123
|
+
* conversion. A step's kind follows from its chains: crossing chains is a
|
|
124
|
+
* `bridge`, staying put is a `swap`. Relay reports no wrap/unwrap distinction,
|
|
125
|
+
* so none is invented.
|
|
126
|
+
*
|
|
127
|
+
* Costs are route-level in Relay's response, not per leg, so they are attached
|
|
128
|
+
* to the FIRST step — the one the user actually signs and pays at. `gasUsd`
|
|
129
|
+
* carries `fees.gas` alone (the origin transaction's gas, paid on top in the
|
|
130
|
+
* network's native token), while `feeUsd` adds the relayer and application fees
|
|
131
|
+
* on top of it, matching how the LI.FI normalizer splits the two. The relayer
|
|
132
|
+
* fee's `relayerGas`/`relayerService` components are deliberately NOT summed:
|
|
133
|
+
* they add up to `relayer` exactly, so counting them would double the fee.
|
|
134
|
+
*
|
|
135
|
+
* @param quote - a raw Relay quote response body
|
|
136
|
+
* @returns ordered list of normalized steps; empty when no route is present
|
|
137
|
+
*/
|
|
138
|
+
export declare const normalizeRelaySteps: (quote: unknown) => RouteStep[];
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeRelaySteps = exports.normalizeLifiSteps = void 0;
|
|
4
|
+
const chain_names_js_1 = require("./chain-names.js");
|
|
5
|
+
const isHexString = (value) => typeof value === 'string' && value.startsWith('0x');
|
|
6
|
+
/**
|
|
7
|
+
* Sums the `amountUSD` field across a LI.FI cost array (`feeCosts` or
|
|
8
|
+
* `gasCosts`), tolerating missing/malformed entries by skipping them. LI.FI
|
|
9
|
+
* reports each cost's dollar value as a numeric string; entries without a finite
|
|
10
|
+
* `amountUSD` contribute nothing rather than poisoning the sum with `NaN`.
|
|
11
|
+
*
|
|
12
|
+
* @param costs - a raw `feeCosts`/`gasCosts` value from a step estimate
|
|
13
|
+
* @returns the total United States dollar cost; zero when none are present
|
|
14
|
+
*/
|
|
15
|
+
const sumUsdCosts = (costs) => {
|
|
16
|
+
if (!Array.isArray(costs)) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
return costs.reduce((total, raw) => {
|
|
20
|
+
const cost = asRecord(raw);
|
|
21
|
+
const amountUsd = typeof cost?.amountUSD === 'string' ? Number(cost.amountUSD) : Number.NaN;
|
|
22
|
+
return Number.isFinite(amountUsd) ? total + amountUsd : total;
|
|
23
|
+
}, 0);
|
|
24
|
+
};
|
|
25
|
+
const asRecord = (value) => {
|
|
26
|
+
if (typeof value !== 'object' || value === null) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Maps a LI.FI step type string to the normalized `RouteStepKind`.
|
|
33
|
+
*
|
|
34
|
+
* LI.FI reports `"cross"` for bridge legs and `"swap"` for same-chain
|
|
35
|
+
* exchanges. `"wrap"` and `"unwrap"` map directly. Unknown types fall back to
|
|
36
|
+
* `"send"` rather than failing the whole normalization.
|
|
37
|
+
*/
|
|
38
|
+
const lifiTypeToKind = (type) => {
|
|
39
|
+
if (type === 'cross')
|
|
40
|
+
return 'bridge';
|
|
41
|
+
if (type === 'swap')
|
|
42
|
+
return 'swap';
|
|
43
|
+
if (type === 'wrap')
|
|
44
|
+
return 'wrap';
|
|
45
|
+
if (type === 'unwrap')
|
|
46
|
+
return 'unwrap';
|
|
47
|
+
return 'send';
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Builds a human-readable label for a LI.FI step.
|
|
51
|
+
*
|
|
52
|
+
* Labels are fully spelled out (no acronyms) so they can be displayed
|
|
53
|
+
* directly in the UI without any post-processing.
|
|
54
|
+
*/
|
|
55
|
+
const buildLifiLabel = (kind, toChainId, toolName) => {
|
|
56
|
+
if (kind === 'bridge')
|
|
57
|
+
return `Bridge to ${(0, chain_names_js_1.chainDisplayName)(toChainId)}`;
|
|
58
|
+
if (kind === 'swap')
|
|
59
|
+
return `Swap on ${toolName}`;
|
|
60
|
+
if (kind === 'wrap')
|
|
61
|
+
return `Wrap to native token on ${(0, chain_names_js_1.chainDisplayName)(toChainId)}`;
|
|
62
|
+
if (kind === 'unwrap')
|
|
63
|
+
return `Unwrap to native on ${(0, chain_names_js_1.chainDisplayName)(toChainId)}`;
|
|
64
|
+
return `Transfer on ${(0, chain_names_js_1.chainDisplayName)(toChainId)}`;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Normalizes a LI.FI `includedSteps` array into the provider-agnostic
|
|
68
|
+
* `RouteStep[]` shape. Steps with missing or non-hex token addresses are
|
|
69
|
+
* silently dropped rather than causing the whole normalization to fail.
|
|
70
|
+
*
|
|
71
|
+
* @param steps - the raw `includedSteps` value from a LI.FI quote response,
|
|
72
|
+
* or `undefined` when the field was absent (trimmed responses)
|
|
73
|
+
* @returns ordered list of normalized steps; empty when no usable legs are present
|
|
74
|
+
*/
|
|
75
|
+
const normalizeLifiSteps = (steps) => {
|
|
76
|
+
if (!Array.isArray(steps) || steps.length === 0) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const result = [];
|
|
80
|
+
for (const raw of steps) {
|
|
81
|
+
const step = asRecord(raw);
|
|
82
|
+
if (!step) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const type = typeof step.type === 'string' ? step.type : 'send';
|
|
86
|
+
const action = asRecord(step.action);
|
|
87
|
+
if (!action) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const fromChainId = typeof action.fromChainId === 'number' ? action.fromChainId : null;
|
|
91
|
+
const toChainId = typeof action.toChainId === 'number' ? action.toChainId : null;
|
|
92
|
+
if (fromChainId === null || toChainId === null) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// A step that changes chains IS a bridge, whatever the provider called its
|
|
96
|
+
// type. LI.FI labels most cross-chain legs `"cross"`, but some (e.g. an Across
|
|
97
|
+
// leg, or a `"protocol"`/`"lifi"`-typed leg) would otherwise fall through to
|
|
98
|
+
// the generic `"send"` kind and read as "Transfer on Ethereum" — misleading,
|
|
99
|
+
// since the funds are actually being BRIDGED there. Deciding from the chains
|
|
100
|
+
// rather than the type string keeps the label honest.
|
|
101
|
+
const typeKind = lifiTypeToKind(type);
|
|
102
|
+
const kind = typeKind === 'send' && fromChainId !== toChainId ? 'bridge' : typeKind;
|
|
103
|
+
const fromTokenRecord = asRecord(action.fromToken);
|
|
104
|
+
const toTokenRecord = asRecord(action.toToken);
|
|
105
|
+
const fromToken = isHexString(fromTokenRecord?.address) ? fromTokenRecord.address : null;
|
|
106
|
+
const toToken = isHexString(toTokenRecord?.address) ? toTokenRecord.address : null;
|
|
107
|
+
if (!fromToken || !toToken) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const fromTokenSymbol = typeof fromTokenRecord?.symbol === 'string' ? fromTokenRecord.symbol : null;
|
|
111
|
+
const toTokenSymbol = typeof toTokenRecord?.symbol === 'string' ? toTokenRecord.symbol : null;
|
|
112
|
+
const fromTokenLogoUri = typeof fromTokenRecord?.logoURI === 'string' ? fromTokenRecord.logoURI : null;
|
|
113
|
+
const toTokenLogoUri = typeof toTokenRecord?.logoURI === 'string' ? toTokenRecord.logoURI : null;
|
|
114
|
+
const toolDetails = asRecord(step.toolDetails);
|
|
115
|
+
const toolName = typeof toolDetails?.name === 'string'
|
|
116
|
+
? toolDetails.name
|
|
117
|
+
: typeof step.tool === 'string'
|
|
118
|
+
? step.tool
|
|
119
|
+
: 'unknown';
|
|
120
|
+
// The per-step tool's logo, when the provider supplies one. This is the best
|
|
121
|
+
// source for a real service-provider image on the consuming interface (for
|
|
122
|
+
// example the Uniswap or Stargate logo for the leg the aggregator routes
|
|
123
|
+
// through). Null when absent, so the interface falls back to an initials tile.
|
|
124
|
+
const providerLogoUri = typeof toolDetails?.logoURI === 'string' ? toolDetails.logoURI : null;
|
|
125
|
+
const label = buildLifiLabel(kind, toChainId, toolName);
|
|
126
|
+
const estimate = asRecord(step.estimate);
|
|
127
|
+
const estimatedOut = typeof estimate?.toAmount === 'string' && estimate.toAmount !== ''
|
|
128
|
+
? (() => {
|
|
129
|
+
try {
|
|
130
|
+
return BigInt(estimate.toAmount);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
})()
|
|
136
|
+
: null;
|
|
137
|
+
const integrationData = asRecord(step.integrationData);
|
|
138
|
+
const router = isHexString(integrationData?.callToAddress)
|
|
139
|
+
? integrationData.callToAddress
|
|
140
|
+
: null;
|
|
141
|
+
// Per-step cost = protocol fees + gas, both reported in United States dollars.
|
|
142
|
+
// Null when the provider reported neither, so the UI omits the hop's fee.
|
|
143
|
+
const protocolFeeUsd = sumUsdCosts(estimate?.feeCosts);
|
|
144
|
+
const gasCostUsd = sumUsdCosts(estimate?.gasCosts);
|
|
145
|
+
const stepCostUsd = protocolFeeUsd + gasCostUsd;
|
|
146
|
+
const feeUsd = stepCostUsd > 0 ? stepCostUsd : null;
|
|
147
|
+
// Gas alone, for route ranking (which charges gas on top of the paid-versus-
|
|
148
|
+
// delivered spread and must not double-count the protocol fees already baked
|
|
149
|
+
// into the quote's `fromAmount`). Null when no gas was reported.
|
|
150
|
+
const gasUsd = gasCostUsd > 0 ? gasCostUsd : null;
|
|
151
|
+
result.push({
|
|
152
|
+
kind,
|
|
153
|
+
label,
|
|
154
|
+
fromChainId,
|
|
155
|
+
toChainId,
|
|
156
|
+
fromToken,
|
|
157
|
+
toToken,
|
|
158
|
+
fromTokenSymbol,
|
|
159
|
+
toTokenSymbol,
|
|
160
|
+
fromTokenLogoUri,
|
|
161
|
+
toTokenLogoUri,
|
|
162
|
+
router,
|
|
163
|
+
estimatedOut,
|
|
164
|
+
feeUsd,
|
|
165
|
+
gasUsd,
|
|
166
|
+
providerLogoUri,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
};
|
|
171
|
+
exports.normalizeLifiSteps = normalizeLifiSteps;
|
|
172
|
+
/**
|
|
173
|
+
* Parses one `{ currency, amount }` entry from Relay's `details.route`.
|
|
174
|
+
*
|
|
175
|
+
* @param value - a raw `inputCurrency`/`outputCurrency` value
|
|
176
|
+
* @returns the endpoint, or null when the chain id or address is unusable
|
|
177
|
+
*/
|
|
178
|
+
const relayRouteEndpoint = (value) => {
|
|
179
|
+
const entry = asRecord(value);
|
|
180
|
+
const currency = asRecord(entry?.currency);
|
|
181
|
+
if (!currency) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const chainId = typeof currency.chainId === 'number' ? currency.chainId : null;
|
|
185
|
+
const address = typeof currency.address === 'string' ? currency.address : null;
|
|
186
|
+
// Without a chain and an identifier there is no leg to describe. Both are
|
|
187
|
+
// always present in Relay's verified shape; bail rather than invent one.
|
|
188
|
+
if (chainId === null || address === null) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const metadata = asRecord(currency.metadata);
|
|
192
|
+
let amount = null;
|
|
193
|
+
if (typeof entry?.amount === 'string' && entry.amount !== '') {
|
|
194
|
+
try {
|
|
195
|
+
amount = BigInt(entry.amount);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
amount = null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
chainId,
|
|
203
|
+
address,
|
|
204
|
+
symbol: typeof currency.symbol === 'string' ? currency.symbol : null,
|
|
205
|
+
logoUri: typeof metadata?.logoURI === 'string' ? metadata.logoURI : null,
|
|
206
|
+
amount,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Reads a `{ amountUsd }` entry from Relay's `fees` object as a number.
|
|
211
|
+
* Relay reports dollar values as numeric strings; anything non-finite
|
|
212
|
+
* contributes nothing rather than poisoning the sum with `NaN`.
|
|
213
|
+
*/
|
|
214
|
+
const relayFeeUsd = (fees, key) => {
|
|
215
|
+
const fee = asRecord(fees?.[key]);
|
|
216
|
+
const amountUsd = typeof fee?.amountUsd === 'string' ? Number(fee.amountUsd) : Number.NaN;
|
|
217
|
+
return Number.isFinite(amountUsd) ? amountUsd : 0;
|
|
218
|
+
};
|
|
219
|
+
/** True when two endpoints are the same currency on the same chain. */
|
|
220
|
+
const isSameEndpoint = (left, right) => left.chainId === right.chainId && left.address === right.address;
|
|
221
|
+
/**
|
|
222
|
+
* Normalizes a Relay quote into the provider-agnostic `RouteStep[]` shape.
|
|
223
|
+
*
|
|
224
|
+
* Relay's `steps` array holds only OPERATIONAL steps (`approve` and `deposit`) —
|
|
225
|
+
* what the user signs, not where the funds go. The route itself lives in
|
|
226
|
+
* `details.route`, which describes the journey as an origin leg and a
|
|
227
|
+
* destination leg, each with an input and an output currency. Reading those four
|
|
228
|
+
* endpoints in order and emitting a step between each consecutive pair recovers
|
|
229
|
+
* the real path, so a Bitcoin origin renders as "Bitcoin → United States Dollar
|
|
230
|
+
* Coin on Ethereum" rather than as nothing at all.
|
|
231
|
+
*
|
|
232
|
+
* Consecutive endpoints that name the same currency on the same chain are
|
|
233
|
+
* collapsed, because Relay repeats an endpoint when a leg performs no
|
|
234
|
+
* conversion. A step's kind follows from its chains: crossing chains is a
|
|
235
|
+
* `bridge`, staying put is a `swap`. Relay reports no wrap/unwrap distinction,
|
|
236
|
+
* so none is invented.
|
|
237
|
+
*
|
|
238
|
+
* Costs are route-level in Relay's response, not per leg, so they are attached
|
|
239
|
+
* to the FIRST step — the one the user actually signs and pays at. `gasUsd`
|
|
240
|
+
* carries `fees.gas` alone (the origin transaction's gas, paid on top in the
|
|
241
|
+
* network's native token), while `feeUsd` adds the relayer and application fees
|
|
242
|
+
* on top of it, matching how the LI.FI normalizer splits the two. The relayer
|
|
243
|
+
* fee's `relayerGas`/`relayerService` components are deliberately NOT summed:
|
|
244
|
+
* they add up to `relayer` exactly, so counting them would double the fee.
|
|
245
|
+
*
|
|
246
|
+
* @param quote - a raw Relay quote response body
|
|
247
|
+
* @returns ordered list of normalized steps; empty when no route is present
|
|
248
|
+
*/
|
|
249
|
+
const normalizeRelaySteps = (quote) => {
|
|
250
|
+
const details = asRecord(asRecord(quote)?.details);
|
|
251
|
+
const route = asRecord(details?.route);
|
|
252
|
+
if (!route) {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
const origin = asRecord(route.origin);
|
|
256
|
+
const destination = asRecord(route.destination);
|
|
257
|
+
const endpoints = [
|
|
258
|
+
relayRouteEndpoint(origin?.inputCurrency),
|
|
259
|
+
relayRouteEndpoint(origin?.outputCurrency),
|
|
260
|
+
relayRouteEndpoint(destination?.inputCurrency),
|
|
261
|
+
relayRouteEndpoint(destination?.outputCurrency),
|
|
262
|
+
].filter((endpoint) => endpoint !== null);
|
|
263
|
+
// Collapse repeats so a leg that converts nothing does not become a step.
|
|
264
|
+
const path = endpoints.filter((endpoint, index) => index === 0 || !isSameEndpoint(endpoint, endpoints[index - 1]));
|
|
265
|
+
if (path.length < 2) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
const fees = asRecord(asRecord(quote)?.fees);
|
|
269
|
+
const gasUsd = relayFeeUsd(fees, 'gas');
|
|
270
|
+
// `relayer` is the full relayer charge; its `relayerGas` and `relayerService`
|
|
271
|
+
// components sum to it exactly, so only the total is counted here.
|
|
272
|
+
const totalUsd = gasUsd + relayFeeUsd(fees, 'relayer') + relayFeeUsd(fees, 'app');
|
|
273
|
+
const result = [];
|
|
274
|
+
for (let index = 1; index < path.length; index += 1) {
|
|
275
|
+
const from = path[index - 1];
|
|
276
|
+
const to = path[index];
|
|
277
|
+
const kind = from.chainId === to.chainId ? 'swap' : 'bridge';
|
|
278
|
+
const isFirstStep = index === 1;
|
|
279
|
+
result.push({
|
|
280
|
+
kind,
|
|
281
|
+
label: kind === 'bridge'
|
|
282
|
+
? `Bridge to ${(0, chain_names_js_1.chainDisplayName)(to.chainId)}`
|
|
283
|
+
: `Swap on ${(0, chain_names_js_1.chainDisplayName)(to.chainId)}`,
|
|
284
|
+
fromChainId: from.chainId,
|
|
285
|
+
toChainId: to.chainId,
|
|
286
|
+
fromToken: from.address,
|
|
287
|
+
toToken: to.address,
|
|
288
|
+
fromTokenSymbol: from.symbol,
|
|
289
|
+
toTokenSymbol: to.symbol,
|
|
290
|
+
// Relay's currency metadata carries the TOKEN's own image, which is the only
|
|
291
|
+
// one available for a non-Ethereum endpoint (Bitcoin, Solana, Tron, The Open
|
|
292
|
+
// Network) — the image service cannot key those by a hex address.
|
|
293
|
+
fromTokenLogoUri: from.logoUri,
|
|
294
|
+
toTokenLogoUri: to.logoUri,
|
|
295
|
+
// Relay's `router` field on a route leg names the service ("relay"), not a
|
|
296
|
+
// contract address, so there is no router address to surface for the leg.
|
|
297
|
+
router: null,
|
|
298
|
+
// Relay supplies a logo for the currency, not for a per-leg tool, so the
|
|
299
|
+
// consuming interface falls back to the provider tile. Deliberately null
|
|
300
|
+
// rather than passing a token image off as a service-provider image.
|
|
301
|
+
providerLogoUri: null,
|
|
302
|
+
estimatedOut: to.amount,
|
|
303
|
+
// Route-level costs land on the signed step; later legs run inside it.
|
|
304
|
+
feeUsd: isFirstStep && totalUsd > 0 ? totalUsd : null,
|
|
305
|
+
gasUsd: isFirstStep && gasUsd > 0 ? gasUsd : null,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
};
|
|
310
|
+
exports.normalizeRelaySteps = normalizeRelaySteps;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The amount that actually LANDS on PulseChain for an aggregator candidate, net
|
|
3
|
+
* of the omnibridge crossing fee.
|
|
4
|
+
*
|
|
5
|
+
* The aggregator delivers `entryChainAmount` (the consolidated request's
|
|
6
|
+
* `toAmount`) onto the omnibridge entry chain, and the omnibridge entry call
|
|
7
|
+
* relays exactly that — but the omnibridge takes its foreign→home crossing fee on
|
|
8
|
+
* the way to PulseChain, so the user receives LESS than `entryChainAmount`. This
|
|
9
|
+
* nets that crossing fee out using the SDK's OWN fee primitives ({@link bridgeCost}
|
|
10
|
+
* and {@link amountAfterBridgeFee}) — the identical math the direct engine applies
|
|
11
|
+
* — so the displayed received amount is honest rather than overstated.
|
|
12
|
+
*
|
|
13
|
+
* The multiplexed (aggregator) swap fees are NOT subtracted here: the aggregator
|
|
14
|
+
* already quotes the source-chain `fromAmount` it needs to GUARANTEE
|
|
15
|
+
* `entryChainAmount` arrives, so those fees are already reflected in `fromAmount`.
|
|
16
|
+
* Only the omnibridge crossing fee remains to net out, which is this function's
|
|
17
|
+
* sole job.
|
|
18
|
+
*
|
|
19
|
+
* @param entryChainAmount - the amount arriving on the entry chain (request `toAmount`)
|
|
20
|
+
* @param omnibridgeFeePerEther - the omnibridge foreign→home fee rate (per 1e18), or null
|
|
21
|
+
* @returns the net amount delivered on PulseChain; equals `entryChainAmount` when the fee is unknown
|
|
22
|
+
*/
|
|
23
|
+
export declare const netDeliveredAmount: (entryChainAmount: bigint, omnibridgeFeePerEther: bigint | null) => bigint;
|
|
24
|
+
/**
|
|
25
|
+
* Restates an amount in a different token's decimals, preserving the QUANTITY.
|
|
26
|
+
*
|
|
27
|
+
* This is the honest degraded answer when {@link valueEquivalentAmount} cannot
|
|
28
|
+
* price a conversion. Losing the price must not also lose the decimals: the two
|
|
29
|
+
* omnibridge anchors hold the same asset bridged two ways, and the only
|
|
30
|
+
* difference between them is where the decimal point sits. Returning an
|
|
31
|
+
* unadjusted amount across that gap is wrong by a factor of 10^12, which is the
|
|
32
|
+
* exact error the value conversion exists to prevent.
|
|
33
|
+
*
|
|
34
|
+
* Scaling DOWN truncates, and must — a six-decimal token cannot hold the twelve
|
|
35
|
+
* extra digits an eighteen-decimal one carries. The lost remainder is smaller
|
|
36
|
+
* than one base unit of the target.
|
|
37
|
+
*
|
|
38
|
+
* @param params.amount - the amount, in `fromDecimals` base units
|
|
39
|
+
* @param params.fromDecimals - the decimals the amount is currently expressed in
|
|
40
|
+
* @param params.toDecimals - the decimals to restate it in
|
|
41
|
+
* @returns the same quantity, in `toDecimals` base units
|
|
42
|
+
*/
|
|
43
|
+
export declare const restateInDecimals: ({ amount, fromDecimals, toDecimals, }: {
|
|
44
|
+
amount: bigint;
|
|
45
|
+
fromDecimals: number;
|
|
46
|
+
toDecimals: number;
|
|
47
|
+
}) => bigint;
|
|
48
|
+
/**
|
|
49
|
+
* Converts `amount` of a source token into the base-unit amount of a TARGET token
|
|
50
|
+
* carrying the SAME United States dollar value:
|
|
51
|
+
*
|
|
52
|
+
* valueUsd = amount / 10^fromDecimals × fromPrice
|
|
53
|
+
* targetAmount = valueUsd / toPrice × 10^toDecimals
|
|
54
|
+
*
|
|
55
|
+
* This is what lets the two omnibridge entry points be quoted for the SAME VALUE
|
|
56
|
+
* even when their anchor tokens differ in decimals — a PulseChain-bridged
|
|
57
|
+
* stablecoin is 6 decimals when bridged from Ethereum but 18 from Binance Smart
|
|
58
|
+
* Chain. Feeding one raw integer to both targeted quantities ~10^12 apart, so the
|
|
59
|
+
* routes were not comparable and the higher-decimal entry won on a bigger NUMBER
|
|
60
|
+
* rather than more value. Targeting equal value in each anchor's own decimals
|
|
61
|
+
* restores a fair, decimals-correct comparison.
|
|
62
|
+
*
|
|
63
|
+
* Returns null when either price is missing or non-positive, the scaled value is
|
|
64
|
+
* not finite, or the target cannot be parsed — the caller then has no
|
|
65
|
+
* value-equivalent target and must fall back rather than quote a bogus amount.
|
|
66
|
+
*
|
|
67
|
+
* @param params.amount - the source amount, in `fromDecimals` base units
|
|
68
|
+
* @param params.fromDecimals - the source token's decimals
|
|
69
|
+
* @param params.fromPrice - the source token's per-token United States dollar price, or null
|
|
70
|
+
* @param params.toDecimals - the target token's decimals
|
|
71
|
+
* @param params.toPrice - the target token's per-token United States dollar price, or null
|
|
72
|
+
* @returns the value-equivalent amount in the target token's base units, or null
|
|
73
|
+
*/
|
|
74
|
+
export declare const valueEquivalentAmount: ({ amount, fromDecimals, fromPrice, toDecimals, toPrice, }: {
|
|
75
|
+
amount: bigint;
|
|
76
|
+
fromDecimals: number;
|
|
77
|
+
fromPrice: number | null;
|
|
78
|
+
toDecimals: number;
|
|
79
|
+
toPrice: number | null;
|
|
80
|
+
}) => bigint | null;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.valueEquivalentAmount = exports.restateInDecimals = exports.netDeliveredAmount = void 0;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
const settings_js_1 = require("../settings.js");
|
|
6
|
+
/**
|
|
7
|
+
* The amount that actually LANDS on PulseChain for an aggregator candidate, net
|
|
8
|
+
* of the omnibridge crossing fee.
|
|
9
|
+
*
|
|
10
|
+
* The aggregator delivers `entryChainAmount` (the consolidated request's
|
|
11
|
+
* `toAmount`) onto the omnibridge entry chain, and the omnibridge entry call
|
|
12
|
+
* relays exactly that — but the omnibridge takes its foreign→home crossing fee on
|
|
13
|
+
* the way to PulseChain, so the user receives LESS than `entryChainAmount`. This
|
|
14
|
+
* nets that crossing fee out using the SDK's OWN fee primitives ({@link bridgeCost}
|
|
15
|
+
* and {@link amountAfterBridgeFee}) — the identical math the direct engine applies
|
|
16
|
+
* — so the displayed received amount is honest rather than overstated.
|
|
17
|
+
*
|
|
18
|
+
* The multiplexed (aggregator) swap fees are NOT subtracted here: the aggregator
|
|
19
|
+
* already quotes the source-chain `fromAmount` it needs to GUARANTEE
|
|
20
|
+
* `entryChainAmount` arrives, so those fees are already reflected in `fromAmount`.
|
|
21
|
+
* Only the omnibridge crossing fee remains to net out, which is this function's
|
|
22
|
+
* sole job.
|
|
23
|
+
*
|
|
24
|
+
* @param entryChainAmount - the amount arriving on the entry chain (request `toAmount`)
|
|
25
|
+
* @param omnibridgeFeePerEther - the omnibridge foreign→home fee rate (per 1e18), or null
|
|
26
|
+
* @returns the net amount delivered on PulseChain; equals `entryChainAmount` when the fee is unknown
|
|
27
|
+
*/
|
|
28
|
+
const netDeliveredAmount = (entryChainAmount, omnibridgeFeePerEther) => {
|
|
29
|
+
// Defensive: the omnibridge percentage math runs ONLY when both operands are
|
|
30
|
+
// true bigints. A non-bigint fee (a value rehydrated from storage as a string,
|
|
31
|
+
// the project's documented BigInt-serialization gotcha) would throw inside
|
|
32
|
+
// `bridgeCost`'s mixed-type multiplication, so fall back to the un-netted
|
|
33
|
+
// entry-chain amount rather than crash the render. The `typeof` guards intentionally
|
|
34
|
+
// outlive the static `bigint` types because the runtime value can violate them.
|
|
35
|
+
if (typeof entryChainAmount !== 'bigint' || typeof omnibridgeFeePerEther !== 'bigint') {
|
|
36
|
+
return entryChainAmount;
|
|
37
|
+
}
|
|
38
|
+
const cost = (0, settings_js_1.bridgeCost)({ amountToBridge: entryChainAmount, bridgeFee: omnibridgeFeePerEther });
|
|
39
|
+
const net = (0, settings_js_1.amountAfterBridgeFee)({ amountToBridge: entryChainAmount, bridgeCost: cost });
|
|
40
|
+
return net ?? entryChainAmount;
|
|
41
|
+
};
|
|
42
|
+
exports.netDeliveredAmount = netDeliveredAmount;
|
|
43
|
+
/**
|
|
44
|
+
* Restates an amount in a different token's decimals, preserving the QUANTITY.
|
|
45
|
+
*
|
|
46
|
+
* This is the honest degraded answer when {@link valueEquivalentAmount} cannot
|
|
47
|
+
* price a conversion. Losing the price must not also lose the decimals: the two
|
|
48
|
+
* omnibridge anchors hold the same asset bridged two ways, and the only
|
|
49
|
+
* difference between them is where the decimal point sits. Returning an
|
|
50
|
+
* unadjusted amount across that gap is wrong by a factor of 10^12, which is the
|
|
51
|
+
* exact error the value conversion exists to prevent.
|
|
52
|
+
*
|
|
53
|
+
* Scaling DOWN truncates, and must — a six-decimal token cannot hold the twelve
|
|
54
|
+
* extra digits an eighteen-decimal one carries. The lost remainder is smaller
|
|
55
|
+
* than one base unit of the target.
|
|
56
|
+
*
|
|
57
|
+
* @param params.amount - the amount, in `fromDecimals` base units
|
|
58
|
+
* @param params.fromDecimals - the decimals the amount is currently expressed in
|
|
59
|
+
* @param params.toDecimals - the decimals to restate it in
|
|
60
|
+
* @returns the same quantity, in `toDecimals` base units
|
|
61
|
+
*/
|
|
62
|
+
const restateInDecimals = ({ amount, fromDecimals, toDecimals, }) => {
|
|
63
|
+
if (fromDecimals === toDecimals)
|
|
64
|
+
return amount;
|
|
65
|
+
if (toDecimals > fromDecimals)
|
|
66
|
+
return amount * 10n ** BigInt(toDecimals - fromDecimals);
|
|
67
|
+
return amount / 10n ** BigInt(fromDecimals - toDecimals);
|
|
68
|
+
};
|
|
69
|
+
exports.restateInDecimals = restateInDecimals;
|
|
70
|
+
/**
|
|
71
|
+
* Converts `amount` of a source token into the base-unit amount of a TARGET token
|
|
72
|
+
* carrying the SAME United States dollar value:
|
|
73
|
+
*
|
|
74
|
+
* valueUsd = amount / 10^fromDecimals × fromPrice
|
|
75
|
+
* targetAmount = valueUsd / toPrice × 10^toDecimals
|
|
76
|
+
*
|
|
77
|
+
* This is what lets the two omnibridge entry points be quoted for the SAME VALUE
|
|
78
|
+
* even when their anchor tokens differ in decimals — a PulseChain-bridged
|
|
79
|
+
* stablecoin is 6 decimals when bridged from Ethereum but 18 from Binance Smart
|
|
80
|
+
* Chain. Feeding one raw integer to both targeted quantities ~10^12 apart, so the
|
|
81
|
+
* routes were not comparable and the higher-decimal entry won on a bigger NUMBER
|
|
82
|
+
* rather than more value. Targeting equal value in each anchor's own decimals
|
|
83
|
+
* restores a fair, decimals-correct comparison.
|
|
84
|
+
*
|
|
85
|
+
* Returns null when either price is missing or non-positive, the scaled value is
|
|
86
|
+
* not finite, or the target cannot be parsed — the caller then has no
|
|
87
|
+
* value-equivalent target and must fall back rather than quote a bogus amount.
|
|
88
|
+
*
|
|
89
|
+
* @param params.amount - the source amount, in `fromDecimals` base units
|
|
90
|
+
* @param params.fromDecimals - the source token's decimals
|
|
91
|
+
* @param params.fromPrice - the source token's per-token United States dollar price, or null
|
|
92
|
+
* @param params.toDecimals - the target token's decimals
|
|
93
|
+
* @param params.toPrice - the target token's per-token United States dollar price, or null
|
|
94
|
+
* @returns the value-equivalent amount in the target token's base units, or null
|
|
95
|
+
*/
|
|
96
|
+
const valueEquivalentAmount = ({ amount, fromDecimals, fromPrice, toDecimals, toPrice, }) => {
|
|
97
|
+
if (fromPrice === null || toPrice === null)
|
|
98
|
+
return null;
|
|
99
|
+
if (!(fromPrice > 0) || !(toPrice > 0))
|
|
100
|
+
return null;
|
|
101
|
+
// Equal prices make this a pure change of decimals, and integer rescaling is
|
|
102
|
+
// EXACT where the double round trip is not. `39.3` is not representable as a
|
|
103
|
+
// double, so `toFixed(18)` writes 39.299999999999997158 and the conversion
|
|
104
|
+
// comes back 2842 base units short of the 39.3 that went in. At the same
|
|
105
|
+
// decimals it lands one unit out (0.008802 returns as 8802000000000001).
|
|
106
|
+
//
|
|
107
|
+
// Both are negligible in value and neither is a reason to convert at all: with
|
|
108
|
+
// one price on both sides there is no value question to answer, only a decimal
|
|
109
|
+
// point to move. Moving it in integers keeps the same input producing the same
|
|
110
|
+
// target.
|
|
111
|
+
if (fromPrice === toPrice)
|
|
112
|
+
return (0, exports.restateInDecimals)({ amount, fromDecimals, toDecimals });
|
|
113
|
+
const valueUsd = Number((0, viem_1.formatUnits)(amount, fromDecimals)) * fromPrice;
|
|
114
|
+
if (!Number.isFinite(valueUsd))
|
|
115
|
+
return null;
|
|
116
|
+
const targetWhole = valueUsd / toPrice;
|
|
117
|
+
if (!Number.isFinite(targetWhole) || targetWhole < 0)
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
return (0, viem_1.parseUnits)(targetWhole.toFixed(toDecimals), toDecimals);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
exports.valueEquivalentAmount = valueEquivalentAmount;
|