@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
package/dist/prices.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatUnits } from 'viem';
|
|
2
|
-
import { Chains, nativeAssetOut } from './config.js';
|
|
2
|
+
import { Chains, nativeAssetOut, toChain } from './config.js';
|
|
3
3
|
/**
|
|
4
4
|
* PulseChain mainnet numeric chain id (`Chains.PLS` = `0x171`). Tokens on this
|
|
5
5
|
* chain (and its testnet sibling below) are priced on-chain via PulseX rather
|
|
@@ -138,6 +138,57 @@ export const computeUsdPriceFromAmountsOut = ({ results, stableDecimals = PULSEC
|
|
|
138
138
|
* @returns the USD price as a number, or null when no source can price it
|
|
139
139
|
*/
|
|
140
140
|
export const resolveTokenPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
|
|
141
|
+
const wrappedNative = sameChainWrappedNative(token);
|
|
142
|
+
if (wrappedNative !== null) {
|
|
143
|
+
return resolveNativeFamilyPriceUsd(token, wrappedNative, { pulsexPrices, lifiPrices });
|
|
144
|
+
}
|
|
145
|
+
return resolveOwnPriceUsd(token, { pulsexPrices, lifiPrices });
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* The chain's canonical wrapped native address when `token` is the native
|
|
149
|
+
* coin (zero address) or that wrapper itself, and null for every other token.
|
|
150
|
+
* Only the SAME chain's pair is returned: a bridged copy of a wrapper on
|
|
151
|
+
* another chain is a different token and keeps its own price.
|
|
152
|
+
*
|
|
153
|
+
* @param token - the token to test
|
|
154
|
+
* @returns the wrapped native address on the token's own chain, or null
|
|
155
|
+
*/
|
|
156
|
+
export const sameChainWrappedNative = (token) => {
|
|
157
|
+
const wrapped = nativeAssetOut[toChain(token.chainId)];
|
|
158
|
+
if (wrapped === undefined) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const isNativeCoin = sameAddress(token.address, NATIVE_TOKEN_ADDRESS);
|
|
162
|
+
return isNativeCoin || sameAddress(token.address, wrapped) ? wrapped : null;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* One price for a native coin and its canonical wrapper. The contract wraps
|
|
166
|
+
* and unwraps 1:1, so two prices would show a value loss on a wrap step that
|
|
167
|
+
* loses nothing.
|
|
168
|
+
*
|
|
169
|
+
* Off PulseChain the native coin's market feed (LI.FI) wins. The wrapper's
|
|
170
|
+
* own price there is the PulseX price of its bridged copy (it carries a
|
|
171
|
+
* PulseChain origin link), which is a derivative market with its own
|
|
172
|
+
* premium or discount. The coin's feed prices the underlying asset. When the
|
|
173
|
+
* feed has no native price, the wrapper's feed price, then its own resolved
|
|
174
|
+
* price, stand in.
|
|
175
|
+
*
|
|
176
|
+
* On PulseChain both are priced by PulseX through the WPLS path, so the
|
|
177
|
+
* wrapper's entry is used and the coin's entry stands in when the wrapper was
|
|
178
|
+
* not in the priced set.
|
|
179
|
+
*/
|
|
180
|
+
const resolveNativeFamilyPriceUsd = (token, wrappedNative, maps) => {
|
|
181
|
+
const wrapped = { ...token, address: wrappedNative };
|
|
182
|
+
if (isPulsechain(token.chainId)) {
|
|
183
|
+
return (resolveOwnPriceUsd(wrapped, maps) ??
|
|
184
|
+
resolveOwnPriceUsd({ ...token, address: NATIVE_TOKEN_ADDRESS }, maps));
|
|
185
|
+
}
|
|
186
|
+
return (maps.lifiPrices.get(lifiPriceKey(token.chainId, NATIVE_TOKEN_ADDRESS)) ??
|
|
187
|
+
maps.lifiPrices.get(lifiPriceKey(token.chainId, wrappedNative)) ??
|
|
188
|
+
resolveOwnPriceUsd(wrapped, maps));
|
|
189
|
+
};
|
|
190
|
+
/** The token's price by its own address, with no native-wrapper pairing. */
|
|
191
|
+
const resolveOwnPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
|
|
141
192
|
if (isPulsechain(token.chainId)) {
|
|
142
193
|
if (sameAddress(token.address, PULSECHAIN_USD_ANCHOR_ADDRESS)) {
|
|
143
194
|
return 1;
|
package/dist/providers.d.ts
CHANGED
|
@@ -132,6 +132,23 @@ export type ProviderDescriptor = {
|
|
|
132
132
|
* `providers.test.ts`).
|
|
133
133
|
*/
|
|
134
134
|
destinationCallCarriesNativeValue: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* How this provider takes an Ethereum token from the reader's wallet.
|
|
137
|
+
*
|
|
138
|
+
* - `'allowance'` — the provider's contract pulls the token, so the wallet
|
|
139
|
+
* must first approve that contract as a spender. The execution quote names
|
|
140
|
+
* the spender (`ConsolidatedQuote.approvalAddress`), and the executor
|
|
141
|
+
* raises the allowance before it sends.
|
|
142
|
+
* - `'transfer'` — the reader sends the token to an address the provider
|
|
143
|
+
* gives. A plain transfer needs no approval, and the parser leaves
|
|
144
|
+
* `approvalAddress` null.
|
|
145
|
+
*
|
|
146
|
+
* A DISPLAY FACT, NEVER A SIGNING ONE. The press reads the live quote's own
|
|
147
|
+
* `approvalAddress` and the live allowance. This field lets a route say, BEFORE
|
|
148
|
+
* any live quote exists, that its wallet may ask for an approval first.
|
|
149
|
+
* The native coin never needs one, whatever this says.
|
|
150
|
+
*/
|
|
151
|
+
originTokenSpend: 'allowance' | 'transfer';
|
|
135
152
|
/**
|
|
136
153
|
* Phase one: the plain forward-solving origin-to-anchor swap pre-quote.
|
|
137
154
|
*
|
package/dist/providers.js
CHANGED
|
@@ -11,6 +11,8 @@ const lifiDescriptor = {
|
|
|
11
11
|
executesDestinationCalls: true,
|
|
12
12
|
destinationCallOriginChainIds: [Number(Chains.ETH)],
|
|
13
13
|
destinationCallCarriesNativeValue: false,
|
|
14
|
+
// Its quote names the Diamond as `estimate.approvalAddress`.
|
|
15
|
+
originTokenSpend: 'allowance',
|
|
14
16
|
preQuote: {
|
|
15
17
|
method: 'GET',
|
|
16
18
|
build: buildLifiPreQuoteRequest,
|
|
@@ -33,6 +35,8 @@ const relayDescriptor = {
|
|
|
33
35
|
executesDestinationCalls: true,
|
|
34
36
|
destinationCallOriginChainIds: 'any',
|
|
35
37
|
destinationCallCarriesNativeValue: true,
|
|
38
|
+
// Its quote carries an approve step; the parser lifts its spender.
|
|
39
|
+
originTokenSpend: 'allowance',
|
|
36
40
|
preQuote: {
|
|
37
41
|
method: 'POST',
|
|
38
42
|
build: buildRelayPreQuoteRequest,
|
|
@@ -61,6 +65,8 @@ const nearIntentsDescriptor = {
|
|
|
61
65
|
executesDestinationCalls: false,
|
|
62
66
|
destinationCallOriginChainIds: [],
|
|
63
67
|
destinationCallCarriesNativeValue: false,
|
|
68
|
+
// The reader sends the token to a deposit address.
|
|
69
|
+
originTokenSpend: 'transfer',
|
|
64
70
|
preQuote: {
|
|
65
71
|
method: 'POST',
|
|
66
72
|
build: buildNearIntentsPreQuoteRequest,
|
package/dist/referral.d.ts
CHANGED
|
@@ -34,7 +34,11 @@ export declare const referralIntegrator = "gibs.finance";
|
|
|
34
34
|
*/
|
|
35
35
|
export declare const resolveAttribution: (override: string | null | undefined, packageDefault: string | undefined) => string | undefined;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Gibs Finance's payout address for Relay application fees.
|
|
38
|
+
*
|
|
39
|
+
* NO BUILDER APPLIES IT. A Relay request carries an `appFees` entry only when
|
|
40
|
+
* the caller passes `relayAppFee`. The Gibs interface passes this address
|
|
41
|
+
* explicitly; any other caller passes its own, or nothing.
|
|
38
42
|
*
|
|
39
43
|
* Set to the owner's receiving wallet. Setting it back to `null` is a working
|
|
40
44
|
* state rather than a broken one, and is the way to stop collecting: Relay
|
|
@@ -53,9 +57,13 @@ export declare const resolveAttribution: (override: string | null | undefined, p
|
|
|
53
57
|
*/
|
|
54
58
|
export declare const relayReferralRecipient: Hex | null;
|
|
55
59
|
/**
|
|
56
|
-
*
|
|
57
|
-
* percent. The interface can override it per
|
|
58
|
-
* is the value a reader who has never touched
|
|
60
|
+
* The application fee the Gibs interface charges, in basis points. Ten basis
|
|
61
|
+
* points is one tenth of one percent. The interface can override it per
|
|
62
|
+
* provider in stored settings; this is the value a reader who has never touched
|
|
63
|
+
* the setting gets.
|
|
64
|
+
*
|
|
65
|
+
* NO BUILDER APPLIES IT. It reaches a request only when a caller passes it as
|
|
66
|
+
* `relayAppFee.basisPoints` (Relay) or converts it to `lifiFee` (LI.FI).
|
|
59
67
|
*/
|
|
60
68
|
export declare const defaultReferralFeeBasisPoints = 10n;
|
|
61
69
|
/**
|
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
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ProviderKey } from '../providers.js';
|
|
2
|
+
/**
|
|
3
|
+
* The carrier id of the native-coin wrap and unwrap step
|
|
4
|
+
* (`routing/carriers/wrap.ts`).
|
|
5
|
+
*/
|
|
6
|
+
export declare const WRAP_CARRIER_ID = "wrap";
|
|
7
|
+
/**
|
|
8
|
+
* The steps a route can take that no third-party provider performs. A step of
|
|
9
|
+
* this kind is drawn from its own mark, never from a provider's.
|
|
10
|
+
*/
|
|
11
|
+
export type CarrierStepKind = 'wrap';
|
|
12
|
+
/**
|
|
13
|
+
* The one place an engine carrier id becomes a {@link ProviderKey}.
|
|
14
|
+
*
|
|
15
|
+
* AN ENGINE CARRIER ID IS NOT A PROVIDER KEY. The engine is a plug-in host: a
|
|
16
|
+
* carrier id is any string, and one of them, `'wrap'`, names a step that no
|
|
17
|
+
* provider performs. Handing a raw id to the logo, glyph, or name tables drew a
|
|
18
|
+
* blank mark and logged an error. Route data resolves the key here, once, and
|
|
19
|
+
* every display site reads the resolved key.
|
|
20
|
+
*
|
|
21
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
22
|
+
* @returns the provider key, or null when no registered provider carries that id
|
|
23
|
+
*/
|
|
24
|
+
export declare const carrierProviderKey: (carrierId: string) => ProviderKey | null;
|
|
25
|
+
/**
|
|
26
|
+
* The kind of provider-less step a carrier id performs.
|
|
27
|
+
*
|
|
28
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
29
|
+
* @returns the step kind, or null when the carrier is a provider or unknown
|
|
30
|
+
*/
|
|
31
|
+
export declare const carrierStepKind: (carrierId: string) => CarrierStepKind | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Providers } from '../config.js';
|
|
2
|
+
import { providerRegistry } from '../providers.js';
|
|
3
|
+
/**
|
|
4
|
+
* The carrier id of the native-coin wrap and unwrap step
|
|
5
|
+
* (`routing/carriers/wrap.ts`).
|
|
6
|
+
*/
|
|
7
|
+
export const WRAP_CARRIER_ID = 'wrap';
|
|
8
|
+
/**
|
|
9
|
+
* Every engine carrier id that IS a provider, mapped to its {@link ProviderKey}.
|
|
10
|
+
* The omnibridge crossings carry the provider value itself; the aggregators
|
|
11
|
+
* carry their registry id; the PulseChain exchange carries its own literal.
|
|
12
|
+
*/
|
|
13
|
+
const providerKeyByCarrierId = new Map([
|
|
14
|
+
...Object.values(Providers).map((provider) => [provider, provider]),
|
|
15
|
+
...providerRegistry.map((descriptor) => [descriptor.id, descriptor.id]),
|
|
16
|
+
['PulseX', 'PulseX'],
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* The one place an engine carrier id becomes a {@link ProviderKey}.
|
|
20
|
+
*
|
|
21
|
+
* AN ENGINE CARRIER ID IS NOT A PROVIDER KEY. The engine is a plug-in host: a
|
|
22
|
+
* carrier id is any string, and one of them, `'wrap'`, names a step that no
|
|
23
|
+
* provider performs. Handing a raw id to the logo, glyph, or name tables drew a
|
|
24
|
+
* blank mark and logged an error. Route data resolves the key here, once, and
|
|
25
|
+
* every display site reads the resolved key.
|
|
26
|
+
*
|
|
27
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
28
|
+
* @returns the provider key, or null when no registered provider carries that id
|
|
29
|
+
*/
|
|
30
|
+
export const carrierProviderKey = (carrierId) => providerKeyByCarrierId.get(carrierId) ?? null;
|
|
31
|
+
/**
|
|
32
|
+
* The kind of provider-less step a carrier id performs.
|
|
33
|
+
*
|
|
34
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
35
|
+
* @returns the step kind, or null when the carrier is a provider or unknown
|
|
36
|
+
*/
|
|
37
|
+
export const carrierStepKind = (carrierId) => carrierId === WRAP_CARRIER_ID ? 'wrap' : null;
|
|
@@ -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
|
/**
|
|
@@ -48,6 +53,13 @@ export type CarrierExactResult = {
|
|
|
48
53
|
readonly deliveredAmount: bigint;
|
|
49
54
|
/** How far this edge's executed rate falls below its marginal rate, in percent; absent when the carrier cannot measure it. */
|
|
50
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[];
|
|
51
63
|
} | {
|
|
52
64
|
readonly outcome: 'refused';
|
|
53
65
|
readonly refusal: {
|
|
@@ -56,5 +68,10 @@ export type CarrierExactResult = {
|
|
|
56
68
|
readonly reason: string | null;
|
|
57
69
|
/** A minimum amount this refusal names, in `edge.from`'s base units, or null. */
|
|
58
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;
|
|
59
76
|
};
|
|
60
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);
|