@gibs/bridge-sdk 1.13.1 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -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 +255 -0
- package/dist/cjs/config.js +569 -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 +359 -0
- package/dist/cjs/execution.js +495 -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 +24 -0
- package/dist/cjs/index.js +40 -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 +117 -0
- package/dist/cjs/prices.js +224 -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/carrier-provider-key.d.ts +31 -0
- package/dist/cjs/routing/carrier-provider-key.js +42 -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 +184 -0
- package/dist/cjs/routing/carriers/pulsex.js +615 -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 +42 -0
- package/dist/cjs/routing/index.js +142 -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 +397 -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 +285 -0
- package/dist/cjs/routing/search/plan-route-data.js +871 -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 +95 -0
- package/dist/cjs/routing/swap-transactions.js +133 -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/swap-venues.d.ts +43 -0
- package/dist/cjs/swap-venues.js +29 -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 +42 -3
- package/dist/config.js +87 -21
- package/dist/consolidated-quote.d.ts +109 -15
- package/dist/consolidated-quote.js +310 -52
- package/dist/execution.d.ts +359 -0
- package/dist/execution.js +455 -0
- package/dist/hyperliquid-execution.js +2 -3
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -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/prices.d.ts +10 -0
- package/dist/prices.js +52 -1
- 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/carrier-provider-key.d.ts +31 -0
- package/dist/routing/carrier-provider-key.js +37 -0
- package/dist/routing/carriers/ask-answer.d.ts +17 -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 +81 -46
- package/dist/routing/carriers/pulsex.js +267 -148
- 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 +11 -3
- package/dist/routing/index.js +8 -2
- 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/route-data.d.ts +47 -5
- package/dist/routing/search/graph.d.ts +15 -0
- package/dist/routing/search/plan-route-data.d.ts +42 -1
- package/dist/routing/search/plan-route-data.js +68 -9
- package/dist/routing/search/score.d.ts +3 -0
- package/dist/routing/search/score.js +11 -3
- package/dist/routing/swap-transactions.d.ts +95 -0
- package/dist/routing/swap-transactions.js +127 -0
- package/dist/settings.d.ts +94 -1
- package/dist/settings.js +95 -3
- package/dist/swap-venues.d.ts +43 -0
- package/dist/swap-venues.js +24 -0
- 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 +75 -10
- 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 +817 -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,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bestPulseXPathQuote = exports.candidatePulseXPaths = exports.pulseXHubTokens = void 0;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
const config_js_1 = require("../../config.js");
|
|
6
|
+
const prices_js_1 = require("../../prices.js");
|
|
7
|
+
/** Wrapped PLS: the first hub, and the middle token of every two-hub path. */
|
|
8
|
+
const wrappedPulseAddress = (0, viem_1.getAddress)(config_js_1.nativeAssetOut[config_js_1.Chains.PLS]);
|
|
9
|
+
/** The Ethereum-bridged WETH on PulseChain, read off the Ethereum pathway's own config. Undefined only if that pathway is removed. */
|
|
10
|
+
const bridgedWrappedEtherAddress = config_js_1.pathways[config_js_1.Providers.PULSECHAIN][config_js_1.Chains.PLS]?.[config_js_1.Chains.ETH]?.bridgedNativeAssetOut;
|
|
11
|
+
/**
|
|
12
|
+
* The tokens a PulseX swap may route through, Wrapped PLS first. Each address
|
|
13
|
+
* comes from a constant this package already holds; none is written here:
|
|
14
|
+
*
|
|
15
|
+
* - WPLS — `nativeAssetOut[Chains.PLS]` (`config.ts`).
|
|
16
|
+
* - PLSX, HEX, USDC, USDT — `pulsechainTokenAddresses` (`config.ts`).
|
|
17
|
+
* - DAI (from Ethereum) — `PULSECHAIN_USD_ANCHOR_ADDRESS` (`prices.ts`).
|
|
18
|
+
* - WETH (from Ethereum) — the Ethereum pathway's `bridgedNativeAssetOut` (`config.ts`).
|
|
19
|
+
*
|
|
20
|
+
* KEPT SMALL ON PURPOSE. Each hub adds pools to the one batched reserve read
|
|
21
|
+
* the carrier makes per search, and adds paths to every price it computes.
|
|
22
|
+
* Seven hubs give at most 20 candidate paths per hop (see
|
|
23
|
+
* {@link candidatePulseXPaths}).
|
|
24
|
+
*/
|
|
25
|
+
exports.pulseXHubTokens = Object.freeze([
|
|
26
|
+
{ symbol: 'WPLS', address: wrappedPulseAddress },
|
|
27
|
+
{ symbol: 'PLSX', address: config_js_1.pulsechainTokenAddresses.PLSX },
|
|
28
|
+
{ symbol: 'HEX', address: config_js_1.pulsechainTokenAddresses.HEX },
|
|
29
|
+
{ symbol: 'DAI', address: (0, viem_1.getAddress)(prices_js_1.PULSECHAIN_USD_ANCHOR_ADDRESS) },
|
|
30
|
+
{ symbol: 'USDC', address: config_js_1.pulsechainTokenAddresses.USDC },
|
|
31
|
+
{ symbol: 'USDT', address: config_js_1.pulsechainTokenAddresses.USDT },
|
|
32
|
+
...(bridgedWrappedEtherAddress === undefined ? [] : [{ symbol: 'WETH', address: (0, viem_1.getAddress)(bridgedWrappedEtherAddress) }]),
|
|
33
|
+
].map((hub) => Object.freeze(hub)));
|
|
34
|
+
/** Whether a path names any token twice, compared without regard to letter case. */
|
|
35
|
+
const repeatsAToken = (path) => new Set(path.map((address) => address.toLowerCase())).size !== path.length;
|
|
36
|
+
/** Whether two addresses are the same token, compared without regard to letter case. */
|
|
37
|
+
const sameToken = (left, right) => left.toLowerCase() === right.toLowerCase();
|
|
38
|
+
/**
|
|
39
|
+
* Every swap path a PulseX hop from `from` to `to` may take:
|
|
40
|
+
*
|
|
41
|
+
* - direct: `[from, to]`;
|
|
42
|
+
* - one hub: `[from, hub, to]` for each hub, Wrapped PLS among them;
|
|
43
|
+
* - two hubs: `[from, wrappedNative, hub, to]` and `[from, hub, wrappedNative, to]`
|
|
44
|
+
* for each other hub — Wrapped PLS holds the deepest pools on PulseX, so it
|
|
45
|
+
* is the one middle token worth pairing with every other hub.
|
|
46
|
+
*
|
|
47
|
+
* A path that names a token twice is dropped. That removes every path whose
|
|
48
|
+
* hub IS `from` or `to`, and the same-token hop when `from` equals `to`.
|
|
49
|
+
*
|
|
50
|
+
* The fixed path this carrier used before — `[from, WPLS, to]`, or
|
|
51
|
+
* `[from, to]` when one side is WPLS — is always in the result, so the pick
|
|
52
|
+
* can only match or beat it.
|
|
53
|
+
*
|
|
54
|
+
* @param options.from - the pool token the swap spends
|
|
55
|
+
* @param options.to - the pool token the swap buys
|
|
56
|
+
* @param options.wrappedNative - Wrapped PLS
|
|
57
|
+
* @param options.hubs - the hub token addresses, Wrapped PLS included
|
|
58
|
+
* @returns the candidate paths, direct first, each at least two tokens long
|
|
59
|
+
*/
|
|
60
|
+
const candidatePulseXPaths = (options) => {
|
|
61
|
+
const { from, to, wrappedNative, hubs } = options;
|
|
62
|
+
const oneHubPaths = hubs.map((hub) => [from, hub, to]);
|
|
63
|
+
const twoHubPaths = hubs
|
|
64
|
+
.filter((hub) => !sameToken(hub, wrappedNative))
|
|
65
|
+
.flatMap((hub) => [
|
|
66
|
+
[from, wrappedNative, hub, to],
|
|
67
|
+
[from, hub, wrappedNative, to],
|
|
68
|
+
]);
|
|
69
|
+
return [[from, to], ...oneHubPaths, ...twoHubPaths].filter((path) => !repeatsAToken(path));
|
|
70
|
+
};
|
|
71
|
+
exports.candidatePulseXPaths = candidatePulseXPaths;
|
|
72
|
+
/**
|
|
73
|
+
* Whether `candidate` should replace `best`: it delivers more, or it delivers
|
|
74
|
+
* the same and crosses fewer pools. A tie goes to the shorter path because
|
|
75
|
+
* each extra pool costs gas, and nothing else separates the two.
|
|
76
|
+
*/
|
|
77
|
+
const beats = (candidate, best) => {
|
|
78
|
+
if (candidate.deliveredAmount !== best.deliveredAmount)
|
|
79
|
+
return candidate.deliveredAmount > best.deliveredAmount;
|
|
80
|
+
return candidate.path.length < best.path.length;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The priced path that delivers the most. A path that delivers nothing is
|
|
84
|
+
* never chosen. Among equal outputs the path with fewer pools wins, and among
|
|
85
|
+
* equal lengths the earlier candidate wins, so the pick is deterministic.
|
|
86
|
+
*
|
|
87
|
+
* Gas is not weighed beyond that tie-break: the search charges one flat gas
|
|
88
|
+
* figure per swap signature, whatever its path length (`route-search-gas.ts`
|
|
89
|
+
* in the interface), so there is no per-pool gas figure to subtract here.
|
|
90
|
+
*
|
|
91
|
+
* @param quotes - the priced candidates; paths with a missing pool are already left out
|
|
92
|
+
* @returns the best candidate, or null when none delivers anything
|
|
93
|
+
*/
|
|
94
|
+
const bestPulseXPathQuote = (quotes) => quotes
|
|
95
|
+
.filter((quote) => quote.deliveredAmount > 0n)
|
|
96
|
+
.reduce((best, quote) => (best === null || beats(quote, best) ? quote : best), null);
|
|
97
|
+
exports.bestPulseXPathQuote = bestPulseXPathQuote;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { Hex } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* PRICING ONLY. NOTHING SIGNS A SPLIT, SO NOTHING SERVES ONE.
|
|
4
|
+
*
|
|
5
|
+
* A split swap sends part of one trade along one PulseX path and the rest
|
|
6
|
+
* along others, often on both venues (V1 and V2). That lowers price impact on
|
|
7
|
+
* a large trade. But a split is several router calls, and on PulseChain no
|
|
8
|
+
* contract this application deploys or calls can run several router calls
|
|
9
|
+
* from the reader's tokens in one transaction:
|
|
10
|
+
*
|
|
11
|
+
* - The PulseX routers have no split call. Each call trades one path on one
|
|
12
|
+
* factory.
|
|
13
|
+
* - The Gibs destination routers (`TokenOmnibridgeRouter`) only unwrap and pay
|
|
14
|
+
* delivery fees in `onTokenBridged`. They make no arbitrary call.
|
|
15
|
+
* - Multicall3 forwards native value, but it cannot pull the reader's tokens
|
|
16
|
+
* safely: an allowance to Multicall3 is open to anyone who calls it.
|
|
17
|
+
* - PulseChain has no EIP-7702, so an ordinary wallet cannot batch calls
|
|
18
|
+
* atomically through `wallet_sendCalls`.
|
|
19
|
+
*
|
|
20
|
+
* So this module stays out of the package index and out of the carrier's
|
|
21
|
+
* `exact`. It exists so the figures can be measured and compared. Wiring it
|
|
22
|
+
* into the engine needs an execution path first: see the design note in the
|
|
23
|
+
* report that introduced this file.
|
|
24
|
+
*/
|
|
25
|
+
/** One pool a split leg crosses, read in the direction of travel, with the identity shared pools are matched by. */
|
|
26
|
+
export type PulseXSplitStep = {
|
|
27
|
+
/** The pool's identity: its venue and token pair. Two legs that name the same `pool` trade against the same reserves. */
|
|
28
|
+
readonly pool: string;
|
|
29
|
+
/** The token flowing into the pool. */
|
|
30
|
+
readonly tokenIn: Hex;
|
|
31
|
+
/** The token flowing out of the pool. */
|
|
32
|
+
readonly tokenOut: Hex;
|
|
33
|
+
/** The pool's reserve of `tokenIn` before any leg of the split trades. */
|
|
34
|
+
readonly reserveIn: bigint;
|
|
35
|
+
/** The pool's reserve of `tokenOut` before any leg of the split trades. */
|
|
36
|
+
readonly reserveOut: bigint;
|
|
37
|
+
};
|
|
38
|
+
/** One path a split may send part of the trade along. */
|
|
39
|
+
export type PulseXSplitCandidate = {
|
|
40
|
+
/** The router the leg would be signed on. */
|
|
41
|
+
readonly venue: Hex;
|
|
42
|
+
/** The tokens the leg passes through, in order. */
|
|
43
|
+
readonly path: readonly Hex[];
|
|
44
|
+
/** The pools the leg crosses, in order. */
|
|
45
|
+
readonly steps: readonly PulseXSplitStep[];
|
|
46
|
+
};
|
|
47
|
+
/** One leg of a priced split, in execution order. */
|
|
48
|
+
export type PulseXSplitLeg = {
|
|
49
|
+
readonly venue: Hex;
|
|
50
|
+
readonly path: readonly Hex[];
|
|
51
|
+
/** The part of the trade this leg spends. The legs' inputs sum to the whole trade. */
|
|
52
|
+
readonly amountIn: bigint;
|
|
53
|
+
/** What this leg delivers, after every earlier leg has moved the pools it shares. */
|
|
54
|
+
readonly deliveredAmount: bigint;
|
|
55
|
+
};
|
|
56
|
+
/** A priced split: the legs, in the order they must execute, and the combined figures. */
|
|
57
|
+
export type PulseXSplitQuote = {
|
|
58
|
+
readonly legs: readonly PulseXSplitLeg[];
|
|
59
|
+
/** What all legs deliver together. */
|
|
60
|
+
readonly deliveredAmount: bigint;
|
|
61
|
+
/** What the best single path delivers for the same trade, for comparison. */
|
|
62
|
+
readonly singlePathDeliveredAmount: bigint;
|
|
63
|
+
/**
|
|
64
|
+
* Price impact of the COMBINED trade, in percent, against the best marginal
|
|
65
|
+
* rate any candidate offers (see {@link splitPriceImpact}). Null when the
|
|
66
|
+
* reference trade rounds away.
|
|
67
|
+
*/
|
|
68
|
+
readonly priceImpactPercent: number | null;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* How many equal chunks the search divides the trade into: 20, so legs move
|
|
72
|
+
* in 5% steps. Finer steps buy a little output for proportionally more work.
|
|
73
|
+
*/
|
|
74
|
+
export declare const PULSEX_SPLIT_CHUNKS = 20;
|
|
75
|
+
/** The most legs one split may have. Each leg is one more router call to sign or batch, and one more swap's gas. */
|
|
76
|
+
export declare const PULSEX_SPLIT_MAX_LEGS = 3;
|
|
77
|
+
/** How many candidate paths, best single-path output first, the search may spread the trade over. */
|
|
78
|
+
export declare const PULSEX_SPLIT_CANDIDATE_LIMIT = 4;
|
|
79
|
+
/**
|
|
80
|
+
* The gain a split must show over the best single path before it is chosen,
|
|
81
|
+
* in basis points of the single path's output: 25, or 0.25%.
|
|
82
|
+
*
|
|
83
|
+
* GAS IS REAL. Each extra leg costs roughly one more swap's gas, and a
|
|
84
|
+
* batching contract adds its own overhead. A split that wins by a rounding
|
|
85
|
+
* margin buys the reader nothing but a larger fee. The figure is a judgement,
|
|
86
|
+
* not a measurement; revisit it once an execution path has a real gas cost.
|
|
87
|
+
*/
|
|
88
|
+
export declare const PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS = 25n;
|
|
89
|
+
/**
|
|
90
|
+
* Executes legs one after another against shared pool state, exactly as
|
|
91
|
+
* sequential router calls would. A leg that crosses a pool an earlier leg
|
|
92
|
+
* already traded sees that pool's moved reserves, in whichever direction the
|
|
93
|
+
* earlier leg traded it.
|
|
94
|
+
*
|
|
95
|
+
* Each trade adds its full input to the pool and removes its output, the
|
|
96
|
+
* Uniswap-V2 accounting. See the open risk in the report: if PulseX moves
|
|
97
|
+
* part of its fee out of the pool during a swap, a shared pool is a few
|
|
98
|
+
* hundredths of a percent shallower here than on chain.
|
|
99
|
+
*
|
|
100
|
+
* @param legs - each candidate with the amount it spends, in execution order
|
|
101
|
+
* @returns what each leg delivers, in the same order
|
|
102
|
+
*/
|
|
103
|
+
export declare const executePulseXSplitInOrder: (legs: readonly {
|
|
104
|
+
readonly candidate: PulseXSplitCandidate;
|
|
105
|
+
readonly amountIn: bigint;
|
|
106
|
+
}[]) => readonly bigint[];
|
|
107
|
+
/**
|
|
108
|
+
* The candidates a split may use: the {@link PULSEX_SPLIT_CANDIDATE_LIMIT}
|
|
109
|
+
* best by single-path output for the whole trade. A candidate that delivers
|
|
110
|
+
* nothing is dropped. Ties go to the shorter path, then the earlier
|
|
111
|
+
* candidate, so the ranking is deterministic.
|
|
112
|
+
*
|
|
113
|
+
* @param candidates - every priced path, on every venue
|
|
114
|
+
* @param amountIn - the whole trade
|
|
115
|
+
* @returns the ranked candidates, best first, each with its single-path output
|
|
116
|
+
*/
|
|
117
|
+
export declare const rankPulseXSplitCandidates: (candidates: readonly PulseXSplitCandidate[], amountIn: bigint) => readonly {
|
|
118
|
+
readonly candidate: PulseXSplitCandidate;
|
|
119
|
+
readonly deliveredAmount: bigint;
|
|
120
|
+
}[];
|
|
121
|
+
/**
|
|
122
|
+
* Spreads a trade over ranked candidates, one chunk at a time. Each chunk goes
|
|
123
|
+
* to the candidate whose share, once raised by that chunk, makes the WHOLE
|
|
124
|
+
* split deliver the most — the split is re-executed in full each time
|
|
125
|
+
* ({@link executePulseXSplitInOrder}), so a shared pool is always priced with
|
|
126
|
+
* the reserves every leg leaves behind. A candidate not yet used is skipped
|
|
127
|
+
* once {@link PULSEX_SPLIT_MAX_LEGS} legs exist. Ties go to the earlier
|
|
128
|
+
* candidate.
|
|
129
|
+
*
|
|
130
|
+
* BOUNDED AND DETERMINISTIC. Work per quote is at most
|
|
131
|
+
* `PULSEX_SPLIT_CHUNKS × PULSEX_SPLIT_CANDIDATE_LIMIT` full executions (80),
|
|
132
|
+
* each of at most `PULSEX_SPLIT_MAX_LEGS` legs of at most three pools: under
|
|
133
|
+
* 750 constant-product evaluations, no network call.
|
|
134
|
+
*
|
|
135
|
+
* On pools that share nothing, each candidate's output is concave in its
|
|
136
|
+
* share, so this greedy fill reaches the best allocation on the chunk grid.
|
|
137
|
+
* Shared pools break that guarantee; {@link bestPulseXSplitQuote} still never
|
|
138
|
+
* returns a split worse than the best single path.
|
|
139
|
+
*
|
|
140
|
+
* @param candidates - ranked candidates, best first
|
|
141
|
+
* @param amountIn - the whole trade
|
|
142
|
+
* @returns the share of the trade each candidate spends, in candidate order
|
|
143
|
+
*/
|
|
144
|
+
export declare const allocatePulseXSplit: (candidates: readonly PulseXSplitCandidate[], amountIn: bigint) => readonly bigint[];
|
|
145
|
+
/**
|
|
146
|
+
* Price impact of a combined trade, in percent, measured with the same
|
|
147
|
+
* reference size the single-path figure uses
|
|
148
|
+
* (`amountIn / PULSEX_MARGINAL_REFERENCE_DIVISOR`).
|
|
149
|
+
*
|
|
150
|
+
* THE REFERENCE IS THE BEST MARGINAL RATE OF ANY CANDIDATE, not a blend of
|
|
151
|
+
* the legs' own rates. A split that leans on a path with a worse spot price
|
|
152
|
+
* pays for that, and the figure shows it. A blend would hide it.
|
|
153
|
+
*
|
|
154
|
+
* @param options.candidates - every ranked candidate
|
|
155
|
+
* @param options.amountIn - the whole trade
|
|
156
|
+
* @param options.deliveredAmount - what the combined trade delivers
|
|
157
|
+
* @returns the impact in percent, or null when the reference trade rounds away
|
|
158
|
+
*/
|
|
159
|
+
export declare const splitPriceImpact: (options: {
|
|
160
|
+
readonly candidates: readonly PulseXSplitCandidate[];
|
|
161
|
+
readonly amountIn: bigint;
|
|
162
|
+
readonly deliveredAmount: bigint;
|
|
163
|
+
}) => number | null;
|
|
164
|
+
/**
|
|
165
|
+
* The best split of a PulseX trade, or null when no split beats the best
|
|
166
|
+
* single path by {@link PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS}.
|
|
167
|
+
*
|
|
168
|
+
* NEVER WORSE THAN ONE PATH. A null answer means "take the best single path";
|
|
169
|
+
* this never returns a split that delivers less than that path.
|
|
170
|
+
*
|
|
171
|
+
* @param candidates - every priced path, on every venue
|
|
172
|
+
* @param amountIn - the whole trade
|
|
173
|
+
* @returns the split with its legs in execution order, or null
|
|
174
|
+
*/
|
|
175
|
+
export declare const bestPulseXSplitQuote: (candidates: readonly PulseXSplitCandidate[], amountIn: bigint) => PulseXSplitQuote | null;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bestPulseXSplitQuote = exports.splitPriceImpact = exports.allocatePulseXSplit = exports.rankPulseXSplitCandidates = exports.executePulseXSplitInOrder = exports.PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS = exports.PULSEX_SPLIT_CANDIDATE_LIMIT = exports.PULSEX_SPLIT_MAX_LEGS = exports.PULSEX_SPLIT_CHUNKS = void 0;
|
|
4
|
+
const producers_js_1 = require("../producers.js");
|
|
5
|
+
const pulsex_js_1 = require("./pulsex.js");
|
|
6
|
+
/**
|
|
7
|
+
* How many equal chunks the search divides the trade into: 20, so legs move
|
|
8
|
+
* in 5% steps. Finer steps buy a little output for proportionally more work.
|
|
9
|
+
*/
|
|
10
|
+
exports.PULSEX_SPLIT_CHUNKS = 20;
|
|
11
|
+
/** The most legs one split may have. Each leg is one more router call to sign or batch, and one more swap's gas. */
|
|
12
|
+
exports.PULSEX_SPLIT_MAX_LEGS = 3;
|
|
13
|
+
/** How many candidate paths, best single-path output first, the search may spread the trade over. */
|
|
14
|
+
exports.PULSEX_SPLIT_CANDIDATE_LIMIT = 4;
|
|
15
|
+
/**
|
|
16
|
+
* The gain a split must show over the best single path before it is chosen,
|
|
17
|
+
* in basis points of the single path's output: 25, or 0.25%.
|
|
18
|
+
*
|
|
19
|
+
* GAS IS REAL. Each extra leg costs roughly one more swap's gas, and a
|
|
20
|
+
* batching contract adds its own overhead. A split that wins by a rounding
|
|
21
|
+
* margin buys the reader nothing but a larger fee. The figure is a judgement,
|
|
22
|
+
* not a measurement; revisit it once an execution path has a real gas cost.
|
|
23
|
+
*/
|
|
24
|
+
exports.PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS = 25n;
|
|
25
|
+
const BASIS_POINTS = 10000n;
|
|
26
|
+
/** The key one token's reserve in one pool is held under while a split executes. */
|
|
27
|
+
const reserveKey = (pool, token) => `${pool}|${token.toLowerCase()}`;
|
|
28
|
+
/**
|
|
29
|
+
* Executes legs one after another against shared pool state, exactly as
|
|
30
|
+
* sequential router calls would. A leg that crosses a pool an earlier leg
|
|
31
|
+
* already traded sees that pool's moved reserves, in whichever direction the
|
|
32
|
+
* earlier leg traded it.
|
|
33
|
+
*
|
|
34
|
+
* Each trade adds its full input to the pool and removes its output, the
|
|
35
|
+
* Uniswap-V2 accounting. See the open risk in the report: if PulseX moves
|
|
36
|
+
* part of its fee out of the pool during a swap, a shared pool is a few
|
|
37
|
+
* hundredths of a percent shallower here than on chain.
|
|
38
|
+
*
|
|
39
|
+
* @param legs - each candidate with the amount it spends, in execution order
|
|
40
|
+
* @returns what each leg delivers, in the same order
|
|
41
|
+
*/
|
|
42
|
+
const executePulseXSplitInOrder = (legs) => {
|
|
43
|
+
const reserves = new Map();
|
|
44
|
+
const reserveOf = (pool, token, initial) => reserves.get(reserveKey(pool, token)) ?? initial;
|
|
45
|
+
return legs.map(({ candidate, amountIn }) => candidate.steps.reduce((runningAmount, step) => {
|
|
46
|
+
const reserveIn = reserveOf(step.pool, step.tokenIn, step.reserveIn);
|
|
47
|
+
const reserveOut = reserveOf(step.pool, step.tokenOut, step.reserveOut);
|
|
48
|
+
const amountOut = (0, pulsex_js_1.amountOutAlongPath)(runningAmount, [{ reserveIn, reserveOut }]);
|
|
49
|
+
reserves.set(reserveKey(step.pool, step.tokenIn), reserveIn + runningAmount);
|
|
50
|
+
reserves.set(reserveKey(step.pool, step.tokenOut), reserveOut - amountOut);
|
|
51
|
+
return amountOut;
|
|
52
|
+
}, amountIn));
|
|
53
|
+
};
|
|
54
|
+
exports.executePulseXSplitInOrder = executePulseXSplitInOrder;
|
|
55
|
+
/** What one candidate alone delivers for the whole trade. */
|
|
56
|
+
const singlePathOutput = (candidate, amountIn) => (0, exports.executePulseXSplitInOrder)([{ candidate, amountIn }])[0] ?? 0n;
|
|
57
|
+
/**
|
|
58
|
+
* The candidates a split may use: the {@link PULSEX_SPLIT_CANDIDATE_LIMIT}
|
|
59
|
+
* best by single-path output for the whole trade. A candidate that delivers
|
|
60
|
+
* nothing is dropped. Ties go to the shorter path, then the earlier
|
|
61
|
+
* candidate, so the ranking is deterministic.
|
|
62
|
+
*
|
|
63
|
+
* @param candidates - every priced path, on every venue
|
|
64
|
+
* @param amountIn - the whole trade
|
|
65
|
+
* @returns the ranked candidates, best first, each with its single-path output
|
|
66
|
+
*/
|
|
67
|
+
const rankPulseXSplitCandidates = (candidates, amountIn) => candidates
|
|
68
|
+
.map((candidate, index) => ({ candidate, index, deliveredAmount: singlePathOutput(candidate, amountIn) }))
|
|
69
|
+
.filter((ranked) => ranked.deliveredAmount > 0n)
|
|
70
|
+
.sort((left, right) => {
|
|
71
|
+
if (left.deliveredAmount !== right.deliveredAmount)
|
|
72
|
+
return left.deliveredAmount > right.deliveredAmount ? -1 : 1;
|
|
73
|
+
if (left.candidate.path.length !== right.candidate.path.length)
|
|
74
|
+
return left.candidate.path.length - right.candidate.path.length;
|
|
75
|
+
return left.index - right.index;
|
|
76
|
+
})
|
|
77
|
+
.slice(0, exports.PULSEX_SPLIT_CANDIDATE_LIMIT)
|
|
78
|
+
.map(({ candidate, deliveredAmount }) => ({ candidate, deliveredAmount }));
|
|
79
|
+
exports.rankPulseXSplitCandidates = rankPulseXSplitCandidates;
|
|
80
|
+
/** The trade divided into {@link PULSEX_SPLIT_CHUNKS} chunks; the last one takes the remainder, so the chunks sum to the trade exactly. */
|
|
81
|
+
const chunksOf = (amountIn) => {
|
|
82
|
+
const count = BigInt(exports.PULSEX_SPLIT_CHUNKS);
|
|
83
|
+
const chunk = amountIn / count;
|
|
84
|
+
return Array.from({ length: exports.PULSEX_SPLIT_CHUNKS }, (_, index) => index === exports.PULSEX_SPLIT_CHUNKS - 1 ? amountIn - chunk * (count - 1n) : chunk);
|
|
85
|
+
};
|
|
86
|
+
/** Each candidate with a positive allocation, in candidate order — the order the legs execute in. */
|
|
87
|
+
const legsOf = (candidates, allocation) => candidates.flatMap((candidate, index) => {
|
|
88
|
+
const amountIn = allocation[index] ?? 0n;
|
|
89
|
+
return amountIn > 0n ? [{ candidate, amountIn }] : [];
|
|
90
|
+
});
|
|
91
|
+
/** What an allocation delivers in total, executed in candidate order. */
|
|
92
|
+
const allocationOutput = (candidates, allocation) => (0, exports.executePulseXSplitInOrder)(legsOf(candidates, allocation)).reduce((total, amount) => total + amount, 0n);
|
|
93
|
+
/**
|
|
94
|
+
* Spreads a trade over ranked candidates, one chunk at a time. Each chunk goes
|
|
95
|
+
* to the candidate whose share, once raised by that chunk, makes the WHOLE
|
|
96
|
+
* split deliver the most — the split is re-executed in full each time
|
|
97
|
+
* ({@link executePulseXSplitInOrder}), so a shared pool is always priced with
|
|
98
|
+
* the reserves every leg leaves behind. A candidate not yet used is skipped
|
|
99
|
+
* once {@link PULSEX_SPLIT_MAX_LEGS} legs exist. Ties go to the earlier
|
|
100
|
+
* candidate.
|
|
101
|
+
*
|
|
102
|
+
* BOUNDED AND DETERMINISTIC. Work per quote is at most
|
|
103
|
+
* `PULSEX_SPLIT_CHUNKS × PULSEX_SPLIT_CANDIDATE_LIMIT` full executions (80),
|
|
104
|
+
* each of at most `PULSEX_SPLIT_MAX_LEGS` legs of at most three pools: under
|
|
105
|
+
* 750 constant-product evaluations, no network call.
|
|
106
|
+
*
|
|
107
|
+
* On pools that share nothing, each candidate's output is concave in its
|
|
108
|
+
* share, so this greedy fill reaches the best allocation on the chunk grid.
|
|
109
|
+
* Shared pools break that guarantee; {@link bestPulseXSplitQuote} still never
|
|
110
|
+
* returns a split worse than the best single path.
|
|
111
|
+
*
|
|
112
|
+
* @param candidates - ranked candidates, best first
|
|
113
|
+
* @param amountIn - the whole trade
|
|
114
|
+
* @returns the share of the trade each candidate spends, in candidate order
|
|
115
|
+
*/
|
|
116
|
+
const allocatePulseXSplit = (candidates, amountIn) => chunksOf(amountIn).reduce((allocation, chunk) => {
|
|
117
|
+
const usedLegs = allocation.filter((amount) => amount > 0n).length;
|
|
118
|
+
const trials = candidates.flatMap((_, index) => {
|
|
119
|
+
const unused = (allocation[index] ?? 0n) === 0n;
|
|
120
|
+
if (unused && usedLegs >= exports.PULSEX_SPLIT_MAX_LEGS)
|
|
121
|
+
return [];
|
|
122
|
+
const trial = allocation.map((amount, trialIndex) => (trialIndex === index ? amount + chunk : amount));
|
|
123
|
+
return [{ trial, output: allocationOutput(candidates, trial) }];
|
|
124
|
+
});
|
|
125
|
+
const best = trials.reduce((leader, entry) => (leader === null || entry.output > leader.output ? entry : leader), null);
|
|
126
|
+
return best === null ? allocation : best.trial;
|
|
127
|
+
}, candidates.map(() => 0n));
|
|
128
|
+
exports.allocatePulseXSplit = allocatePulseXSplit;
|
|
129
|
+
/**
|
|
130
|
+
* Price impact of a combined trade, in percent, measured with the same
|
|
131
|
+
* reference size the single-path figure uses
|
|
132
|
+
* (`amountIn / PULSEX_MARGINAL_REFERENCE_DIVISOR`).
|
|
133
|
+
*
|
|
134
|
+
* THE REFERENCE IS THE BEST MARGINAL RATE OF ANY CANDIDATE, not a blend of
|
|
135
|
+
* the legs' own rates. A split that leans on a path with a worse spot price
|
|
136
|
+
* pays for that, and the figure shows it. A blend would hide it.
|
|
137
|
+
*
|
|
138
|
+
* @param options.candidates - every ranked candidate
|
|
139
|
+
* @param options.amountIn - the whole trade
|
|
140
|
+
* @param options.deliveredAmount - what the combined trade delivers
|
|
141
|
+
* @returns the impact in percent, or null when the reference trade rounds away
|
|
142
|
+
*/
|
|
143
|
+
const splitPriceImpact = (options) => {
|
|
144
|
+
const { candidates, amountIn, deliveredAmount } = options;
|
|
145
|
+
const referenceAmountIn = amountIn / producers_js_1.PULSEX_MARGINAL_REFERENCE_DIVISOR;
|
|
146
|
+
const referenceAmountOut = candidates.reduce((best, candidate) => {
|
|
147
|
+
const output = singlePathOutput(candidate, referenceAmountIn);
|
|
148
|
+
return output > best ? output : best;
|
|
149
|
+
}, 0n);
|
|
150
|
+
return (0, pulsex_js_1.priceImpactAgainstMarginal)({ amountIn, amountOut: deliveredAmount, referenceAmountIn, referenceAmountOut });
|
|
151
|
+
};
|
|
152
|
+
exports.splitPriceImpact = splitPriceImpact;
|
|
153
|
+
/**
|
|
154
|
+
* The best split of a PulseX trade, or null when no split beats the best
|
|
155
|
+
* single path by {@link PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS}.
|
|
156
|
+
*
|
|
157
|
+
* NEVER WORSE THAN ONE PATH. A null answer means "take the best single path";
|
|
158
|
+
* this never returns a split that delivers less than that path.
|
|
159
|
+
*
|
|
160
|
+
* @param candidates - every priced path, on every venue
|
|
161
|
+
* @param amountIn - the whole trade
|
|
162
|
+
* @returns the split with its legs in execution order, or null
|
|
163
|
+
*/
|
|
164
|
+
const bestPulseXSplitQuote = (candidates, amountIn) => {
|
|
165
|
+
if (amountIn <= 0n)
|
|
166
|
+
return null;
|
|
167
|
+
const ranked = (0, exports.rankPulseXSplitCandidates)(candidates, amountIn);
|
|
168
|
+
const singlePathDeliveredAmount = ranked[0]?.deliveredAmount;
|
|
169
|
+
if (singlePathDeliveredAmount === undefined)
|
|
170
|
+
return null;
|
|
171
|
+
const rankedCandidates = ranked.map(({ candidate }) => candidate);
|
|
172
|
+
const allocation = (0, exports.allocatePulseXSplit)(rankedCandidates, amountIn);
|
|
173
|
+
const legs = legsOf(rankedCandidates, allocation);
|
|
174
|
+
if (legs.length < 2)
|
|
175
|
+
return null;
|
|
176
|
+
const legOutputs = (0, exports.executePulseXSplitInOrder)(legs);
|
|
177
|
+
const deliveredAmount = legOutputs.reduce((total, amount) => total + amount, 0n);
|
|
178
|
+
const requiredAmount = singlePathDeliveredAmount + (singlePathDeliveredAmount * exports.PULSEX_SPLIT_MINIMUM_GAIN_BASIS_POINTS) / BASIS_POINTS;
|
|
179
|
+
if (deliveredAmount < requiredAmount)
|
|
180
|
+
return null;
|
|
181
|
+
return {
|
|
182
|
+
legs: legs.map(({ candidate, amountIn: legAmountIn }, index) => ({
|
|
183
|
+
venue: candidate.venue,
|
|
184
|
+
path: candidate.path,
|
|
185
|
+
amountIn: legAmountIn,
|
|
186
|
+
deliveredAmount: legOutputs[index] ?? 0n,
|
|
187
|
+
})),
|
|
188
|
+
deliveredAmount,
|
|
189
|
+
singlePathDeliveredAmount,
|
|
190
|
+
priceImpactPercent: (0, exports.splitPriceImpact)({ candidates: rankedCandidates, amountIn, deliveredAmount }),
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
exports.bestPulseXSplitQuote = bestPulseXSplitQuote;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
import type { PublicClient } from 'viem';
|
|
3
|
+
import { type SwapVenue, type SwapVenues } from '../../swap-venues.js';
|
|
4
|
+
import { type AssetNode, type HopEdge, type PlannedHop } from '../search/graph.js';
|
|
5
|
+
import type { CarrierAskAnswer, CarrierExactResult } from './ask-answer.js';
|
|
6
|
+
/**
|
|
7
|
+
* One swap deployment a swap can execute on: the router the reader signs and
|
|
8
|
+
* the factory whose pools that router trades against. A Uniswap-V2-style
|
|
9
|
+
* router resolves every pool through its OWN factory, so one router call can
|
|
10
|
+
* only ever touch that one deployment's pools — a path can never mix them.
|
|
11
|
+
*
|
|
12
|
+
* Same type as {@link SwapVenue}; the old name stays for existing callers.
|
|
13
|
+
*/
|
|
14
|
+
export type PulseXVenue = SwapVenue;
|
|
15
|
+
/**
|
|
16
|
+
* Every PulseX deployment on PulseChain, V1 first: the PulseChain entry of
|
|
17
|
+
* {@link defaultSwapVenues}. Frozen. It lists the DEFAULTS only — a carrier
|
|
18
|
+
* built with its own `venues` offers those instead.
|
|
19
|
+
*/
|
|
20
|
+
export declare const pulseXVenues: readonly PulseXVenue[];
|
|
21
|
+
/**
|
|
22
|
+
* The {@link SwapVenue} a swap edge runs on: the one its figure was quoted
|
|
23
|
+
* against. Reads the edge's own {@link HopEdge.venue}; an edge with none runs
|
|
24
|
+
* on the FIRST venue of its chain (V1 for PulseX) — every swap edge built
|
|
25
|
+
* before venues existed was quoted and signed there, and the same-chain swap
|
|
26
|
+
* sender's own hand-built edge still is.
|
|
27
|
+
*
|
|
28
|
+
* NEVER FALLS BACK TO ANOTHER CHAIN OR ANOTHER ROUTER. An edge that names a
|
|
29
|
+
* router `venues` does not list gets null, so the caller can refuse it.
|
|
30
|
+
*
|
|
31
|
+
* @param venues - the venues the caller allows
|
|
32
|
+
* @param edge - the edge to sign; `from` names the chain, and defaults to PulseChain when absent
|
|
33
|
+
* @returns the venue, or null when the edge is not a swap edge or names a router that is not in `venues`
|
|
34
|
+
*/
|
|
35
|
+
export declare const venueForEdge: (venues: SwapVenues, edge: Pick<HopEdge, "carrier" | "venue"> & {
|
|
36
|
+
readonly from?: Pick<AssetNode, "chainId">;
|
|
37
|
+
}) => SwapVenue | null;
|
|
38
|
+
/**
|
|
39
|
+
* The router a PulseX edge must be signed on, judged against the DEFAULT
|
|
40
|
+
* venues only. Prefer {@link venueForEdge} with the venues your host passes.
|
|
41
|
+
*
|
|
42
|
+
* @param edge - the edge to sign
|
|
43
|
+
* @returns the router, or null when the edge is not a swap edge or names a router that is not a default
|
|
44
|
+
*/
|
|
45
|
+
export declare const pulseXRouterForEdge: (edge: Pick<HopEdge, "carrier" | "venue"> & {
|
|
46
|
+
readonly from?: Pick<AssetNode, "chainId">;
|
|
47
|
+
}) => Hex | null;
|
|
48
|
+
/**
|
|
49
|
+
* The fixed Wrapped-PLS path from `from` to `to` — `[from, WPLS, to]`, or
|
|
50
|
+
* `[from, to]` when one side is WPLS — or null when both ends resolve to the
|
|
51
|
+
* same pool token (PLS<->WPLS is the wrap carrier's job).
|
|
52
|
+
*
|
|
53
|
+
* NOT THE PATH A HOP IS SIGNED ALONG. The carrier's own `exact` picks the
|
|
54
|
+
* best of many paths ({@link candidatePulseXPaths}) and records the winner on
|
|
55
|
+
* the hop's outcome; the signer reads it back with {@link pulseXSwapPathForHop}.
|
|
56
|
+
* This fixed path serves only the carrier's asynchronous `ask` — the
|
|
57
|
+
* interface's `getAmountsOut` read, which the route search never calls for a
|
|
58
|
+
* PulseX hop, because the search only asks aggregators live.
|
|
59
|
+
*
|
|
60
|
+
* Native PLS resolves to WPLS first ({@link poolTokenAddress}), so a path
|
|
61
|
+
* never starts or ends on the zero address, which has no pool.
|
|
62
|
+
*
|
|
63
|
+
* @param from - the hop's origin node
|
|
64
|
+
* @param to - the hop's destination node
|
|
65
|
+
* @returns the fixed path, or null when there is nothing for PulseX to swap
|
|
66
|
+
*/
|
|
67
|
+
export declare const pulseXWrappedNativePath: (from: AssetNode, to: AssetNode) => readonly Hex[] | null;
|
|
68
|
+
/**
|
|
69
|
+
* The swap path a planned PulseX hop must be signed along: the path its
|
|
70
|
+
* outcome records ({@link HopOutcome.swapPath}), which is the path the
|
|
71
|
+
* carrier's `exact` priced the figure on. NEVER REBUILT HERE — rebuilding a
|
|
72
|
+
* path at signing time is how a quote on one path becomes a signature on
|
|
73
|
+
* another.
|
|
74
|
+
*
|
|
75
|
+
* The recorded path is checked against the hop's own edge before it is
|
|
76
|
+
* returned: it must start at the edge's input pool token, end at its output
|
|
77
|
+
* pool token, hold only addresses, and name no token twice. A path that fails
|
|
78
|
+
* any check is refused (null) rather than signed.
|
|
79
|
+
*
|
|
80
|
+
* @param hop - the planned hop to sign
|
|
81
|
+
* @returns the path to sign, or null when the hop is not a PulseX hop, records no path, or records one that does not fit its edge
|
|
82
|
+
*/
|
|
83
|
+
export declare const pulseXSwapPathForHop: (hop: Pick<PlannedHop, "edge" | "outcome">) => readonly Hex[] | null;
|
|
84
|
+
/** One pool a swap path crosses, read in the direction of travel. */
|
|
85
|
+
export type PulseXPathStep = {
|
|
86
|
+
/** The pool's reserve of the token flowing in. */
|
|
87
|
+
readonly reserveIn: bigint;
|
|
88
|
+
/** The pool's reserve of the token flowing out. */
|
|
89
|
+
readonly reserveOut: bigint;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* What `amountIn` delivers after crossing every pool of a path in order,
|
|
93
|
+
* chaining {@link constantProductAmountOut}.
|
|
94
|
+
*
|
|
95
|
+
* @param amountIn - the amount entering the first pool
|
|
96
|
+
* @param steps - the pools in order of travel
|
|
97
|
+
* @param feeBasisPoints - the venue's swap fee in basis points; PulseX's 29 when omitted
|
|
98
|
+
* @returns the amount leaving the last pool; 0 when any pool cannot pay out
|
|
99
|
+
*/
|
|
100
|
+
export declare const amountOutAlongPath: (amountIn: bigint, steps: readonly PulseXPathStep[], feeBasisPoints?: number) => bigint;
|
|
101
|
+
/**
|
|
102
|
+
* How far a trade's executed rate falls below the marginal (near-spot) rate,
|
|
103
|
+
* in percent. The marginal rate is a tiny reference trade through the same
|
|
104
|
+
* pools (`amountIn / PULSEX_MARGINAL_REFERENCE_DIVISOR`). The swap fee rides
|
|
105
|
+
* in both rates, so it cancels and the gap is price impact alone. A path
|
|
106
|
+
* through several pools composes by itself: the reference trade crosses the
|
|
107
|
+
* same pools in the same order.
|
|
108
|
+
*
|
|
109
|
+
* impact = (1 - (amountOut / amountIn) / (referenceAmountOut / referenceAmountIn)) * 100
|
|
110
|
+
*
|
|
111
|
+
* @param options.amountIn - the full trade's input
|
|
112
|
+
* @param options.amountOut - what the full trade delivers
|
|
113
|
+
* @param options.referenceAmountIn - the reference trade's input
|
|
114
|
+
* @param options.referenceAmountOut - what the reference trade delivers
|
|
115
|
+
* @returns the impact in percent, never below 0; null when a reference figure is zero (impact cannot be measured)
|
|
116
|
+
*/
|
|
117
|
+
export declare const priceImpactAgainstMarginal: (options: {
|
|
118
|
+
readonly amountIn: bigint;
|
|
119
|
+
readonly amountOut: bigint;
|
|
120
|
+
readonly referenceAmountIn: bigint;
|
|
121
|
+
readonly referenceAmountOut: bigint;
|
|
122
|
+
}) => number | null;
|
|
123
|
+
/** What {@link createPulseXCarrier} needs. */
|
|
124
|
+
export type CreatePulseXCarrierOptions = {
|
|
125
|
+
/**
|
|
126
|
+
* Reads PulseX's `getAmountsOut` for one PulseX edge and an input amount,
|
|
127
|
+
* in the edge's `to` token's base units. Returns null when the path has no
|
|
128
|
+
* liquidity.
|
|
129
|
+
*
|
|
130
|
+
* MUST READ THE EDGE'S OWN ROUTER ({@link venueForEdge}). A V2 edge
|
|
131
|
+
* answered from V1 would carry V1's figure into a V2 signature.
|
|
132
|
+
*/
|
|
133
|
+
readonly readAmountOut: (edge: HopEdge, inputAmount: bigint) => Promise<bigint | null>;
|
|
134
|
+
/**
|
|
135
|
+
* Returns a client for a chain, for {@link Carrier.prepare}'s own batched
|
|
136
|
+
* reserve reads — never called by `ask`, which keeps using
|
|
137
|
+
* {@link readAmountOut} unchanged.
|
|
138
|
+
*
|
|
139
|
+
* OPTIONAL, ON PURPOSE — see `omnibridge.ts`'s identical `clientForChain`
|
|
140
|
+
* field for why: a host that has not wired this yet gets the carrier's
|
|
141
|
+
* original behavior, `prepare` a no-op and `exact` always `null`.
|
|
142
|
+
*
|
|
143
|
+
* @param chainId - a chain that has swap venues
|
|
144
|
+
* @returns a client, or null when this host has none
|
|
145
|
+
*/
|
|
146
|
+
readonly clientForChain?: (chainId: number) => PublicClient | null;
|
|
147
|
+
/**
|
|
148
|
+
* The swap venues to offer. Merged over {@link defaultSwapVenues} by
|
|
149
|
+
* {@link resolveSwapVenues}: a chain named here REPLACES that chain's
|
|
150
|
+
* default list, a chain left out keeps its defaults, and an empty list
|
|
151
|
+
* switches swaps off on that chain. A venue on ANY chain builds swap edges
|
|
152
|
+
* there.
|
|
153
|
+
*/
|
|
154
|
+
readonly venues?: SwapVenues;
|
|
155
|
+
/**
|
|
156
|
+
* Narrows the offered venues to these routers. An edge is always signed on
|
|
157
|
+
* the router it was quoted against ({@link venueForEdge}), so a router the
|
|
158
|
+
* host cannot sign on must never be quoted either. A router that is in no
|
|
159
|
+
* offered venue is a configuration error and throws at construction.
|
|
160
|
+
*
|
|
161
|
+
* Defaults to every offered venue.
|
|
162
|
+
*/
|
|
163
|
+
readonly routers?: readonly Hex[];
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Builds the PulseX conversion swap as a plug-in carrier.
|
|
167
|
+
*
|
|
168
|
+
* @param options - see {@link CreatePulseXCarrierOptions}
|
|
169
|
+
* @returns the carrier — `id: 'PulseX'`
|
|
170
|
+
*/
|
|
171
|
+
export declare const createPulseXCarrier: (options: CreatePulseXCarrierOptions) => {
|
|
172
|
+
id: string;
|
|
173
|
+
edgesFrom: (from: AssetNode, to: AssetNode) => readonly HopEdge[];
|
|
174
|
+
ask: ({ edge, inputAmount }: {
|
|
175
|
+
edge: HopEdge;
|
|
176
|
+
inputAmount: bigint;
|
|
177
|
+
}) => Promise<CarrierAskAnswer>;
|
|
178
|
+
prepare: (nodes: readonly AssetNode[]) => Promise<void>;
|
|
179
|
+
exact: (edge: HopEdge, amountIn: bigint) => CarrierExactResult | null;
|
|
180
|
+
isRateLimited: boolean;
|
|
181
|
+
depositMode: "wallet-signed";
|
|
182
|
+
sender: "swap";
|
|
183
|
+
settlementSeconds: number;
|
|
184
|
+
};
|