@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
package/dist/referral.js
CHANGED
|
@@ -33,7 +33,11 @@ export const referralIntegrator = 'gibs.finance';
|
|
|
33
33
|
*/
|
|
34
34
|
export const resolveAttribution = (override, packageDefault) => (override === null ? undefined : (override ?? packageDefault));
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Gibs Finance's payout address for Relay application fees.
|
|
37
|
+
*
|
|
38
|
+
* NO BUILDER APPLIES IT. A Relay request carries an `appFees` entry only when
|
|
39
|
+
* the caller passes `relayAppFee`. The Gibs interface passes this address
|
|
40
|
+
* explicitly; any other caller passes its own, or nothing.
|
|
37
41
|
*
|
|
38
42
|
* Set to the owner's receiving wallet. Setting it back to `null` is a working
|
|
39
43
|
* state rather than a broken one, and is the way to stop collecting: Relay
|
|
@@ -52,9 +56,13 @@ export const resolveAttribution = (override, packageDefault) => (override === nu
|
|
|
52
56
|
*/
|
|
53
57
|
export const relayReferralRecipient = '0xAF2ce0189f46f5663715b0b9ED2a10eA924AB9B0';
|
|
54
58
|
/**
|
|
55
|
-
*
|
|
56
|
-
* percent. The interface can override it per
|
|
57
|
-
* is the value a reader who has never touched
|
|
59
|
+
* The application fee the Gibs interface charges, in basis points. Ten basis
|
|
60
|
+
* points is one tenth of one percent. The interface can override it per
|
|
61
|
+
* provider in stored settings; this is the value a reader who has never touched
|
|
62
|
+
* the setting gets.
|
|
63
|
+
*
|
|
64
|
+
* NO BUILDER APPLIES IT. It reaches a request only when a caller passes it as
|
|
65
|
+
* `relayAppFee.basisPoints` (Relay) or converts it to `lifiFee` (LI.FI).
|
|
58
66
|
*/
|
|
59
67
|
export const defaultReferralFeeBasisPoints = 10n;
|
|
60
68
|
/**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* Relay's deposit contract (its "depository") on each Ethereum-style chain:
|
|
4
|
+
* the ONLY address a Relay origin deposit may be sent to, and the only
|
|
5
|
+
* address it may ask the reader to approve.
|
|
6
|
+
*
|
|
7
|
+
* WHY A FIXED TABLE. A quote that names its own spender cannot be trusted to
|
|
8
|
+
* name an honest one: a compromised quote host could name any contract. This
|
|
9
|
+
* table ships with the code and changes only through a release.
|
|
10
|
+
*
|
|
11
|
+
* SOURCE. Relay's Chains API, `GET https://api.relay.link/chains`, field
|
|
12
|
+
* `chains[].protocol.v2.depository`, read 2026-10-07 for every chain whose
|
|
13
|
+
* `vmType` is `evm`. Relay's documentation says its depository addresses are
|
|
14
|
+
* published only through that API
|
|
15
|
+
* (https://docs.relay.link/references/protocol/addresses). A live quote the
|
|
16
|
+
* same day (Ethereum USDC to Base, `POST /quote/v2`) approved and deposited to
|
|
17
|
+
* exactly the Ethereum entry below.
|
|
18
|
+
*
|
|
19
|
+
* NOT THE `contracts` BLOCK. The same API also lists `relayReceiver`,
|
|
20
|
+
* `approvalProxy` and `erc20Router` under `chains[].contracts`. The live
|
|
21
|
+
* quote used none of them, so they are left out: an address the quote does
|
|
22
|
+
* not use is not one this table should accept.
|
|
23
|
+
*
|
|
24
|
+
* A CHAIN NOT LISTED IS REFUSED. If Relay adds a chain or moves a depository,
|
|
25
|
+
* deposits there are refused until this table is updated.
|
|
26
|
+
*/
|
|
27
|
+
const common = getAddress('0x4cd00e387622c35bddb9b4c962c136462338bc31');
|
|
28
|
+
const alternate = getAddress('0x59916da825d2d2ec1bf878d71c88826f6633ecca');
|
|
29
|
+
export const relayDepositoryByChainId = new Map([
|
|
30
|
+
[1, common], // ethereum
|
|
31
|
+
[10, common], // optimism
|
|
32
|
+
[25, alternate], // cronos
|
|
33
|
+
[56, common], // bsc
|
|
34
|
+
[100, common], // gnosis
|
|
35
|
+
[130, common], // unichain
|
|
36
|
+
[137, common], // polygon
|
|
37
|
+
[143, common], // monad
|
|
38
|
+
[146, common], // sonic
|
|
39
|
+
[169, common], // manta-pacific
|
|
40
|
+
[196, common], // xlayer
|
|
41
|
+
[288, common], // boba
|
|
42
|
+
[324, common], // zksync
|
|
43
|
+
[360, common], // shape
|
|
44
|
+
[480, common], // world-chain
|
|
45
|
+
[747, common], // flow-evm
|
|
46
|
+
[988, common], // stable
|
|
47
|
+
[999, common], // hyperevm
|
|
48
|
+
[1088, alternate], // metis
|
|
49
|
+
[1135, common], // lisk
|
|
50
|
+
[1868, common], // soneium
|
|
51
|
+
[2020, common], // ronin
|
|
52
|
+
[2741, common], // abstract
|
|
53
|
+
[2818, common], // morph
|
|
54
|
+
[4217, common], // tempo
|
|
55
|
+
[4326, common], // megaeth
|
|
56
|
+
[4663, common], // robinhood
|
|
57
|
+
[5000, alternate], // mantle
|
|
58
|
+
[5031, common], // somnia
|
|
59
|
+
[5042, common], // arc
|
|
60
|
+
[5330, common], // superseed
|
|
61
|
+
[8453, common], // base
|
|
62
|
+
[9745, common], // plasma
|
|
63
|
+
[33139, common], // apechain
|
|
64
|
+
[34443, common], // mode
|
|
65
|
+
[42018, common], // mythos
|
|
66
|
+
[42161, common], // arbitrum
|
|
67
|
+
[42220, common], // celo
|
|
68
|
+
[43114, common], // avalanche
|
|
69
|
+
[48900, common], // zircuit
|
|
70
|
+
[57073, common], // ink
|
|
71
|
+
[59144, alternate], // linea
|
|
72
|
+
[60808, common], // bob
|
|
73
|
+
[80094, common], // berachain
|
|
74
|
+
[81457, common], // blast
|
|
75
|
+
[97477, common], // doma
|
|
76
|
+
[98866, common], // plume
|
|
77
|
+
[534352, common], // scroll
|
|
78
|
+
[685689, common], // gensyn
|
|
79
|
+
[747474, common], // katana
|
|
80
|
+
[5064014, common], // ethereal
|
|
81
|
+
[7777777, common], // zora
|
|
82
|
+
]);
|
package/dist/relay-quote.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildUrl } from '@gibs/common/url';
|
|
1
2
|
import { decodeFunctionData, encodeFunctionData, erc20Abi, maxUint256, zeroAddress, } from 'viem';
|
|
2
3
|
import { Chains } from './config.js';
|
|
3
4
|
import { isNonEthereumVirtualMachineOrigin, } from './consolidated-quote.js';
|
|
@@ -861,9 +862,7 @@ export const parseRelayQuote = (payload) => {
|
|
|
861
862
|
* than trust it — see `checkConsolidatedRecord`.
|
|
862
863
|
*/
|
|
863
864
|
export const relayStatusUrl = ({ quoteId }) => {
|
|
864
|
-
|
|
865
|
-
url.searchParams.set('id', quoteId);
|
|
866
|
-
return url.toString();
|
|
865
|
+
return buildUrl(relayRequestsUrl, { id: quoteId });
|
|
867
866
|
};
|
|
868
867
|
/**
|
|
869
868
|
* Relay's request lifecycle states. Terminal states are `success` (the
|
|
@@ -208,9 +208,9 @@ export type DirectCrossingCandidateInputs = {
|
|
|
208
208
|
* to quote — the user bridges directly from that chain to PulseChain. The
|
|
209
209
|
* same-chain skip in {@link useAggregatorRouteCandidate} is correct (nothing to
|
|
210
210
|
* quote), but it left the conversion path empty. This helper fills that gap by
|
|
211
|
-
* synthesizing a zero-step candidate
|
|
212
|
-
*
|
|
213
|
-
*
|
|
211
|
+
* synthesizing a zero-step candidate. The user interface no longer calls this:
|
|
212
|
+
* the route engine serves a conversion now. The export stays as published
|
|
213
|
+
* interface until a separate decision removes it.
|
|
214
214
|
*
|
|
215
215
|
* The synthetic quote carries zero steps (no aggregator hops), a zero execution
|
|
216
216
|
* duration (the direct crossing needs no quote wait), and a null transaction
|
|
@@ -8,9 +8,9 @@ import { omnibridgeProviderForEntryChain } from './providers.js';
|
|
|
8
8
|
* to quote — the user bridges directly from that chain to PulseChain. The
|
|
9
9
|
* same-chain skip in {@link useAggregatorRouteCandidate} is correct (nothing to
|
|
10
10
|
* quote), but it left the conversion path empty. This helper fills that gap by
|
|
11
|
-
* synthesizing a zero-step candidate
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* synthesizing a zero-step candidate. The user interface no longer calls this:
|
|
12
|
+
* the route engine serves a conversion now. The export stays as published
|
|
13
|
+
* interface until a separate decision removes it.
|
|
14
14
|
*
|
|
15
15
|
* The synthetic quote carries zero steps (no aggregator hops), a zero execution
|
|
16
16
|
* duration (the direct crossing needs no quote wait), and a null transaction
|
|
@@ -29,6 +29,11 @@ export type CarrierAskAnswer = {
|
|
|
29
29
|
readonly reason: string | null;
|
|
30
30
|
/** A minimum amount this refusal names, in `edge.from`'s base units, or null. */
|
|
31
31
|
readonly minimumAmount: bigint | null;
|
|
32
|
+
/**
|
|
33
|
+
* What the carrier can still take today, in `edge.from`'s base units,
|
|
34
|
+
* when the refusal is `'above-available'`. Absent otherwise.
|
|
35
|
+
*/
|
|
36
|
+
readonly availableAmount?: bigint;
|
|
32
37
|
};
|
|
33
38
|
};
|
|
34
39
|
/**
|
|
@@ -46,6 +51,15 @@ export type CarrierExactResult = {
|
|
|
46
51
|
readonly outcome: 'delivered';
|
|
47
52
|
/** What this edge delivers for exactly the requested input amount, in `edge.to`'s base units. */
|
|
48
53
|
readonly deliveredAmount: bigint;
|
|
54
|
+
/** How far this edge's executed rate falls below its marginal rate, in percent; absent when the carrier cannot measure it. */
|
|
55
|
+
readonly priceImpactPercent?: number;
|
|
56
|
+
/**
|
|
57
|
+
* The token addresses the carrier's own execution passes through, in
|
|
58
|
+
* order, when it chose among several (a PulseX swap path). The figure
|
|
59
|
+
* above was priced along exactly this path, so it is the path that must
|
|
60
|
+
* be signed. Absent when the carrier makes no such choice.
|
|
61
|
+
*/
|
|
62
|
+
readonly swapPath?: readonly string[];
|
|
49
63
|
} | {
|
|
50
64
|
readonly outcome: 'refused';
|
|
51
65
|
readonly refusal: {
|
|
@@ -54,5 +68,10 @@ export type CarrierExactResult = {
|
|
|
54
68
|
readonly reason: string | null;
|
|
55
69
|
/** A minimum amount this refusal names, in `edge.from`'s base units, or null. */
|
|
56
70
|
readonly minimumAmount: bigint | null;
|
|
71
|
+
/**
|
|
72
|
+
* What the carrier can still take today, in `edge.from`'s base units,
|
|
73
|
+
* when the refusal is `'above-available'`. Absent otherwise.
|
|
74
|
+
*/
|
|
75
|
+
readonly availableAmount?: bigint;
|
|
57
76
|
};
|
|
58
77
|
};
|
|
@@ -277,9 +277,27 @@ const prepareOmnibridgeReads = async ({ nodes, edgesFrom, isProductionNetwork, c
|
|
|
277
277
|
}
|
|
278
278
|
return preparedByEdgeKey;
|
|
279
279
|
};
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
/**
|
|
281
|
+
* The refusal an entry mediator's own bound produces, carrying the bound
|
|
282
|
+
* itself as a figure: `minimumAmount` below the floor, `availableAmount`
|
|
283
|
+
* above what is left today. A reader is told the number to type, so the
|
|
284
|
+
* number must survive here rather than live only inside the reason string.
|
|
285
|
+
*
|
|
286
|
+
* @param verdict - the crossed bound, in the entry token's base units
|
|
287
|
+
* @returns the refusal, in `edge.from`'s base units
|
|
288
|
+
*/
|
|
289
|
+
const entryAmountRefusal = (verdict) => verdict.reason === 'below-minimum'
|
|
290
|
+
? {
|
|
291
|
+
kind: 'below-minimum',
|
|
292
|
+
reason: `below the entry mediator's minimum of ${verdict.minimumPerTransaction} base units`,
|
|
293
|
+
minimumAmount: verdict.minimumPerTransaction,
|
|
294
|
+
}
|
|
295
|
+
: {
|
|
296
|
+
kind: 'above-available',
|
|
297
|
+
reason: `above what the entry mediator has left today (${verdict.availablePerTransaction} base units)`,
|
|
298
|
+
minimumAmount: null,
|
|
299
|
+
availableAmount: verdict.availablePerTransaction,
|
|
300
|
+
};
|
|
283
301
|
/**
|
|
284
302
|
* Builds the omnibridge crossing as a plug-in carrier.
|
|
285
303
|
*
|
|
@@ -316,15 +334,7 @@ export const createOmnibridgeCarrier = (options) => {
|
|
|
316
334
|
return null;
|
|
317
335
|
const verdict = checkEntryAmount({ amount: amountIn, limits: prepared.limits });
|
|
318
336
|
if (!verdict.fits) {
|
|
319
|
-
|
|
320
|
-
return {
|
|
321
|
-
outcome: 'refused',
|
|
322
|
-
refusal: {
|
|
323
|
-
kind: verdict.reason === 'below-minimum' ? 'below-minimum' : 'above-available',
|
|
324
|
-
reason: entryAmountVerdictReason(verdict),
|
|
325
|
-
minimumAmount,
|
|
326
|
-
},
|
|
327
|
-
};
|
|
337
|
+
return { outcome: 'refused', refusal: entryAmountRefusal(verdict) };
|
|
328
338
|
}
|
|
329
339
|
return { outcome: 'delivered', deliveredAmount: netDeliveredAmount(amountIn, prepared.feePerEther) };
|
|
330
340
|
};
|
|
@@ -332,15 +342,7 @@ export const createOmnibridgeCarrier = (options) => {
|
|
|
332
342
|
const limits = await readEntryLimits(edge);
|
|
333
343
|
const verdict = checkEntryAmount({ amount: inputAmount, limits });
|
|
334
344
|
if (!verdict.fits) {
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
ok: false,
|
|
338
|
-
refusal: {
|
|
339
|
-
kind: verdict.reason === 'below-minimum' ? 'below-minimum' : 'above-available',
|
|
340
|
-
reason: entryAmountVerdictReason(verdict),
|
|
341
|
-
minimumAmount,
|
|
342
|
-
},
|
|
343
|
-
};
|
|
345
|
+
return { ok: false, refusal: entryAmountRefusal(verdict) };
|
|
344
346
|
}
|
|
345
347
|
const feePerEther = await readFeePerEther(edge);
|
|
346
348
|
const deliveredAmount = netDeliveredAmount(inputAmount, feePerEther);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* Which swap path a PulseX hop takes. The hop no longer always goes through
|
|
4
|
+
* Wrapped PLS: the carrier prices every candidate path from the pool reserves
|
|
5
|
+
* it already read, and keeps the one that delivers the most. These are the
|
|
6
|
+
* pure parts of that choice — the hub list, the candidate paths, and the
|
|
7
|
+
* pick — kept apart from the carrier so each can be tested on its own.
|
|
8
|
+
*/
|
|
9
|
+
/** One token a PulseX swap may pass through on its way from the input token to the output token. */
|
|
10
|
+
export type PulseXHubToken = {
|
|
11
|
+
/** The token's on-chain `symbol()`, for logs and tests only. Never used to match a token. */
|
|
12
|
+
readonly symbol: string;
|
|
13
|
+
/** The token's checksummed PulseChain address. Tokens are always matched by this. */
|
|
14
|
+
readonly address: Hex;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The tokens a PulseX swap may route through, Wrapped PLS first. Each address
|
|
18
|
+
* comes from a constant this package already holds; none is written here:
|
|
19
|
+
*
|
|
20
|
+
* - WPLS — `nativeAssetOut[Chains.PLS]` (`config.ts`).
|
|
21
|
+
* - PLSX, HEX, USDC, USDT — `pulsechainTokenAddresses` (`config.ts`).
|
|
22
|
+
* - DAI (from Ethereum) — `PULSECHAIN_USD_ANCHOR_ADDRESS` (`prices.ts`).
|
|
23
|
+
* - WETH (from Ethereum) — the Ethereum pathway's `bridgedNativeAssetOut` (`config.ts`).
|
|
24
|
+
*
|
|
25
|
+
* KEPT SMALL ON PURPOSE. Each hub adds pools to the one batched reserve read
|
|
26
|
+
* the carrier makes per search, and adds paths to every price it computes.
|
|
27
|
+
* Seven hubs give at most 20 candidate paths per hop (see
|
|
28
|
+
* {@link candidatePulseXPaths}).
|
|
29
|
+
*/
|
|
30
|
+
export declare const pulseXHubTokens: readonly PulseXHubToken[];
|
|
31
|
+
/**
|
|
32
|
+
* Every swap path a PulseX hop from `from` to `to` may take:
|
|
33
|
+
*
|
|
34
|
+
* - direct: `[from, to]`;
|
|
35
|
+
* - one hub: `[from, hub, to]` for each hub, Wrapped PLS among them;
|
|
36
|
+
* - two hubs: `[from, wrappedNative, hub, to]` and `[from, hub, wrappedNative, to]`
|
|
37
|
+
* for each other hub — Wrapped PLS holds the deepest pools on PulseX, so it
|
|
38
|
+
* is the one middle token worth pairing with every other hub.
|
|
39
|
+
*
|
|
40
|
+
* A path that names a token twice is dropped. That removes every path whose
|
|
41
|
+
* hub IS `from` or `to`, and the same-token hop when `from` equals `to`.
|
|
42
|
+
*
|
|
43
|
+
* The fixed path this carrier used before — `[from, WPLS, to]`, or
|
|
44
|
+
* `[from, to]` when one side is WPLS — is always in the result, so the pick
|
|
45
|
+
* can only match or beat it.
|
|
46
|
+
*
|
|
47
|
+
* @param options.from - the pool token the swap spends
|
|
48
|
+
* @param options.to - the pool token the swap buys
|
|
49
|
+
* @param options.wrappedNative - Wrapped PLS
|
|
50
|
+
* @param options.hubs - the hub token addresses, Wrapped PLS included
|
|
51
|
+
* @returns the candidate paths, direct first, each at least two tokens long
|
|
52
|
+
*/
|
|
53
|
+
export declare const candidatePulseXPaths: (options: {
|
|
54
|
+
readonly from: Hex;
|
|
55
|
+
readonly to: Hex;
|
|
56
|
+
readonly wrappedNative: Hex;
|
|
57
|
+
readonly hubs: readonly Hex[];
|
|
58
|
+
}) => readonly (readonly Hex[])[];
|
|
59
|
+
/** One candidate path, priced. */
|
|
60
|
+
export type PulseXPathQuote<Step> = {
|
|
61
|
+
/** The tokens the swap passes through, in order. */
|
|
62
|
+
readonly path: readonly Hex[];
|
|
63
|
+
/** What the path delivers for the trade, in the last token's base units. */
|
|
64
|
+
readonly deliveredAmount: bigint;
|
|
65
|
+
/** The pools the path crosses, in order — kept so the caller can measure price impact on the same pools. */
|
|
66
|
+
readonly steps: readonly Step[];
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* The priced path that delivers the most. A path that delivers nothing is
|
|
70
|
+
* never chosen. Among equal outputs the path with fewer pools wins, and among
|
|
71
|
+
* equal lengths the earlier candidate wins, so the pick is deterministic.
|
|
72
|
+
*
|
|
73
|
+
* Gas is not weighed beyond that tie-break: the search charges one flat gas
|
|
74
|
+
* figure per swap signature, whatever its path length (`route-search-gas.ts`
|
|
75
|
+
* in the interface), so there is no per-pool gas figure to subtract here.
|
|
76
|
+
*
|
|
77
|
+
* @param quotes - the priced candidates; paths with a missing pool are already left out
|
|
78
|
+
* @returns the best candidate, or null when none delivers anything
|
|
79
|
+
*/
|
|
80
|
+
export declare const bestPulseXPathQuote: <Step>(quotes: readonly PulseXPathQuote<Step>[]) => PulseXPathQuote<Step> | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
import { Chains, Providers, nativeAssetOut, pathways, pulsechainTokenAddresses } from '../../config.js';
|
|
3
|
+
import { PULSECHAIN_USD_ANCHOR_ADDRESS } from '../../prices.js';
|
|
4
|
+
/** Wrapped PLS: the first hub, and the middle token of every two-hub path. */
|
|
5
|
+
const wrappedPulseAddress = getAddress(nativeAssetOut[Chains.PLS]);
|
|
6
|
+
/** The Ethereum-bridged WETH on PulseChain, read off the Ethereum pathway's own config. Undefined only if that pathway is removed. */
|
|
7
|
+
const bridgedWrappedEtherAddress = pathways[Providers.PULSECHAIN][Chains.PLS]?.[Chains.ETH]?.bridgedNativeAssetOut;
|
|
8
|
+
/**
|
|
9
|
+
* The tokens a PulseX swap may route through, Wrapped PLS first. Each address
|
|
10
|
+
* comes from a constant this package already holds; none is written here:
|
|
11
|
+
*
|
|
12
|
+
* - WPLS — `nativeAssetOut[Chains.PLS]` (`config.ts`).
|
|
13
|
+
* - PLSX, HEX, USDC, USDT — `pulsechainTokenAddresses` (`config.ts`).
|
|
14
|
+
* - DAI (from Ethereum) — `PULSECHAIN_USD_ANCHOR_ADDRESS` (`prices.ts`).
|
|
15
|
+
* - WETH (from Ethereum) — the Ethereum pathway's `bridgedNativeAssetOut` (`config.ts`).
|
|
16
|
+
*
|
|
17
|
+
* KEPT SMALL ON PURPOSE. Each hub adds pools to the one batched reserve read
|
|
18
|
+
* the carrier makes per search, and adds paths to every price it computes.
|
|
19
|
+
* Seven hubs give at most 20 candidate paths per hop (see
|
|
20
|
+
* {@link candidatePulseXPaths}).
|
|
21
|
+
*/
|
|
22
|
+
export const pulseXHubTokens = Object.freeze([
|
|
23
|
+
{ symbol: 'WPLS', address: wrappedPulseAddress },
|
|
24
|
+
{ symbol: 'PLSX', address: pulsechainTokenAddresses.PLSX },
|
|
25
|
+
{ symbol: 'HEX', address: pulsechainTokenAddresses.HEX },
|
|
26
|
+
{ symbol: 'DAI', address: getAddress(PULSECHAIN_USD_ANCHOR_ADDRESS) },
|
|
27
|
+
{ symbol: 'USDC', address: pulsechainTokenAddresses.USDC },
|
|
28
|
+
{ symbol: 'USDT', address: pulsechainTokenAddresses.USDT },
|
|
29
|
+
...(bridgedWrappedEtherAddress === undefined ? [] : [{ symbol: 'WETH', address: getAddress(bridgedWrappedEtherAddress) }]),
|
|
30
|
+
].map((hub) => Object.freeze(hub)));
|
|
31
|
+
/** Whether a path names any token twice, compared without regard to letter case. */
|
|
32
|
+
const repeatsAToken = (path) => new Set(path.map((address) => address.toLowerCase())).size !== path.length;
|
|
33
|
+
/** Whether two addresses are the same token, compared without regard to letter case. */
|
|
34
|
+
const sameToken = (left, right) => left.toLowerCase() === right.toLowerCase();
|
|
35
|
+
/**
|
|
36
|
+
* Every swap path a PulseX hop from `from` to `to` may take:
|
|
37
|
+
*
|
|
38
|
+
* - direct: `[from, to]`;
|
|
39
|
+
* - one hub: `[from, hub, to]` for each hub, Wrapped PLS among them;
|
|
40
|
+
* - two hubs: `[from, wrappedNative, hub, to]` and `[from, hub, wrappedNative, to]`
|
|
41
|
+
* for each other hub — Wrapped PLS holds the deepest pools on PulseX, so it
|
|
42
|
+
* is the one middle token worth pairing with every other hub.
|
|
43
|
+
*
|
|
44
|
+
* A path that names a token twice is dropped. That removes every path whose
|
|
45
|
+
* hub IS `from` or `to`, and the same-token hop when `from` equals `to`.
|
|
46
|
+
*
|
|
47
|
+
* The fixed path this carrier used before — `[from, WPLS, to]`, or
|
|
48
|
+
* `[from, to]` when one side is WPLS — is always in the result, so the pick
|
|
49
|
+
* can only match or beat it.
|
|
50
|
+
*
|
|
51
|
+
* @param options.from - the pool token the swap spends
|
|
52
|
+
* @param options.to - the pool token the swap buys
|
|
53
|
+
* @param options.wrappedNative - Wrapped PLS
|
|
54
|
+
* @param options.hubs - the hub token addresses, Wrapped PLS included
|
|
55
|
+
* @returns the candidate paths, direct first, each at least two tokens long
|
|
56
|
+
*/
|
|
57
|
+
export const candidatePulseXPaths = (options) => {
|
|
58
|
+
const { from, to, wrappedNative, hubs } = options;
|
|
59
|
+
const oneHubPaths = hubs.map((hub) => [from, hub, to]);
|
|
60
|
+
const twoHubPaths = hubs
|
|
61
|
+
.filter((hub) => !sameToken(hub, wrappedNative))
|
|
62
|
+
.flatMap((hub) => [
|
|
63
|
+
[from, wrappedNative, hub, to],
|
|
64
|
+
[from, hub, wrappedNative, to],
|
|
65
|
+
]);
|
|
66
|
+
return [[from, to], ...oneHubPaths, ...twoHubPaths].filter((path) => !repeatsAToken(path));
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Whether `candidate` should replace `best`: it delivers more, or it delivers
|
|
70
|
+
* the same and crosses fewer pools. A tie goes to the shorter path because
|
|
71
|
+
* each extra pool costs gas, and nothing else separates the two.
|
|
72
|
+
*/
|
|
73
|
+
const beats = (candidate, best) => {
|
|
74
|
+
if (candidate.deliveredAmount !== best.deliveredAmount)
|
|
75
|
+
return candidate.deliveredAmount > best.deliveredAmount;
|
|
76
|
+
return candidate.path.length < best.path.length;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The priced path that delivers the most. A path that delivers nothing is
|
|
80
|
+
* never chosen. Among equal outputs the path with fewer pools wins, and among
|
|
81
|
+
* equal lengths the earlier candidate wins, so the pick is deterministic.
|
|
82
|
+
*
|
|
83
|
+
* Gas is not weighed beyond that tie-break: the search charges one flat gas
|
|
84
|
+
* figure per swap signature, whatever its path length (`route-search-gas.ts`
|
|
85
|
+
* in the interface), so there is no per-pool gas figure to subtract here.
|
|
86
|
+
*
|
|
87
|
+
* @param quotes - the priced candidates; paths with a missing pool are already left out
|
|
88
|
+
* @returns the best candidate, or null when none delivers anything
|
|
89
|
+
*/
|
|
90
|
+
export const bestPulseXPathQuote = (quotes) => quotes
|
|
91
|
+
.filter((quote) => quote.deliveredAmount > 0n)
|
|
92
|
+
.reduce((best, quote) => (best === null || beats(quote, best) ? quote : best), null);
|
|
@@ -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;
|