@gibs/bridge-sdk 1.17.0 → 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 +52 -1
- package/dist/cjs/config.d.ts +18 -3
- package/dist/cjs/config.js +49 -17
- package/dist/cjs/execution.d.ts +17 -4
- package/dist/cjs/execution.js +40 -13
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/prices.d.ts +10 -0
- package/dist/cjs/prices.js +54 -2
- 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/pulsex.d.ts +50 -25
- package/dist/cjs/routing/carriers/pulsex.js +135 -85
- package/dist/cjs/routing/index.d.ts +6 -2
- package/dist/cjs/routing/index.js +9 -1
- package/dist/cjs/routing/route-data.d.ts +47 -5
- package/dist/cjs/routing/search/plan-route-data.d.ts +17 -1
- package/dist/cjs/routing/search/plan-route-data.js +30 -7
- package/dist/cjs/routing/swap-transactions.d.ts +7 -3
- package/dist/cjs/routing/swap-transactions.js +6 -4
- package/dist/cjs/swap-venues.d.ts +43 -0
- package/dist/cjs/swap-venues.js +29 -0
- package/dist/config.d.ts +18 -3
- package/dist/config.js +48 -16
- package/dist/execution.d.ts +17 -4
- package/dist/execution.js +41 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/prices.d.ts +10 -0
- package/dist/prices.js +52 -1
- package/dist/routing/carrier-provider-key.d.ts +31 -0
- package/dist/routing/carrier-provider-key.js +37 -0
- package/dist/routing/carriers/pulsex.d.ts +50 -25
- package/dist/routing/carriers/pulsex.js +133 -84
- package/dist/routing/index.d.ts +6 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/route-data.d.ts +47 -5
- package/dist/routing/search/plan-route-data.d.ts +17 -1
- package/dist/routing/search/plan-route-data.js +30 -7
- package/dist/routing/swap-transactions.d.ts +7 -3
- package/dist/routing/swap-transactions.js +7 -5
- package/dist/swap-venues.d.ts +43 -0
- package/dist/swap-venues.js +24 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -432,7 +432,8 @@ if (plan.kind === 'refused') throw new Error(plan.refusal.reason);
|
|
|
432
432
|
non-zero allowance is reset to zero first, because some tokens refuse to change
|
|
433
433
|
one non-zero allowance into another.
|
|
434
434
|
- **Spenders come from fixed tables.** The plan approves only the omnibridge
|
|
435
|
-
mediator and the
|
|
435
|
+
mediator and the swap routers in the swap venues (PulseX by default, see
|
|
436
|
+
"Overriding swap routers"), LI.FI's
|
|
436
437
|
Diamond (`lifiDiamondAddressByChainId`, plus the LI.FI execution guard), and
|
|
437
438
|
Relay's depository (`relayDepositoryByChainId`). A quote that names any other
|
|
438
439
|
spender or deposit target is refused. NEAR Intents may name no spender at all.
|
|
@@ -440,6 +441,56 @@ if (plan.kind === 'refused') throw new Error(plan.refusal.reason);
|
|
|
440
441
|
asked for your wallet, in `sections[i].aggregator`. The candidate's own quote
|
|
441
442
|
priced the route on screen and is never signed.
|
|
442
443
|
|
|
444
|
+
## Overriding swap routers
|
|
445
|
+
|
|
446
|
+
The package swaps on PulseX V1 and V2 on PulseChain by default. You can swap on
|
|
447
|
+
other routers, on any chain. Describe each router as a `SwapVenue` and pass the
|
|
448
|
+
venues to the route search and to execution. Today the only `kind` is
|
|
449
|
+
`'uniswap-v2'`: a router with `getAmountsOut` and the three
|
|
450
|
+
`swapExact...` functions, a factory with `getPair`, and pairs with
|
|
451
|
+
`getReserves`.
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
import {
|
|
455
|
+
type SwapVenues,
|
|
456
|
+
defaultSwapVenues,
|
|
457
|
+
planCandidateExecution,
|
|
458
|
+
resolveSwapVenues,
|
|
459
|
+
} from '@gibs/bridge-sdk';
|
|
460
|
+
|
|
461
|
+
const swapVenues: SwapVenues = {
|
|
462
|
+
// Swap on Ethereum, on Uniswap V2.
|
|
463
|
+
1: [{
|
|
464
|
+
kind: 'uniswap-v2',
|
|
465
|
+
chainId: 1,
|
|
466
|
+
router: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
|
|
467
|
+
factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f',
|
|
468
|
+
feeBasisPoints: 30, // 29 for PulseX, 30 for Uniswap V2 forks
|
|
469
|
+
label: 'Uniswap V2',
|
|
470
|
+
}],
|
|
471
|
+
// An empty list switches swaps off on PulseChain.
|
|
472
|
+
369: [],
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// What the engine will use: your chains replace the defaults, other chains keep them.
|
|
476
|
+
const venues = resolveSwapVenues(swapVenues);
|
|
477
|
+
console.log(Object.keys(defaultSwapVenues)); // ['369']
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
- **A chain you name replaces that chain's default list.** A chain you leave out
|
|
481
|
+
keeps its defaults. An empty list disables swaps on that chain.
|
|
482
|
+
- **A venue on any chain builds swap steps there.** The rule that aggregators
|
|
483
|
+
never receive PulseChain is separate and does not change.
|
|
484
|
+
- **Each swap step records the venue it was priced on.** Signing uses that
|
|
485
|
+
venue's router, also as the approval spender. Pass the same `swapVenues` to
|
|
486
|
+
`planExecution`, `planCandidateExecution` and `readPulseXSectionQuote` that
|
|
487
|
+
you gave the search. A router that is not in the venues throws an error. The
|
|
488
|
+
package never falls back to PulseX.
|
|
489
|
+
- **The LI.FI execution guard does not change.** It checks the LI.FI Diamond
|
|
490
|
+
address and has no override.
|
|
491
|
+
- `uniV2Routers` and `uniV2Factories` stay exported. They are frozen and derive
|
|
492
|
+
from `defaultSwapVenues`.
|
|
493
|
+
|
|
443
494
|
## Manual claims
|
|
444
495
|
|
|
445
496
|
A crossing from PulseChain to Ethereum or BNB Smart Chain ends with a release on
|
package/dist/cjs/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Hex } from 'viem';
|
|
2
|
+
import type { SwapVenues } from './swap-venues.js';
|
|
2
3
|
import type { BridgeKey, Token, TokenOut } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Add here your chain id as hex, be sure to add your chain in the chainsMetadata object too
|
|
@@ -59,8 +60,22 @@ export declare const pulsechainTokenAddresses: Readonly<{
|
|
|
59
60
|
}>;
|
|
60
61
|
/** A map of chains to deprecated native asset out addresses */
|
|
61
62
|
export declare const deprecatedNativeAssetOut: Partial<Record<Chains, Set<Hex>>>;
|
|
62
|
-
/**
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The swap venues the package ships with: PulseX V1 then V2 on PulseChain.
|
|
65
|
+
* Frozen. A caller who wants other routers passes its own
|
|
66
|
+
* {@link SwapVenues} (see `resolveSwapVenues`); this table is never mutated.
|
|
67
|
+
*
|
|
68
|
+
* Each factory is the router's own `router.factory()` read on chain — see
|
|
69
|
+
* {@link uniV2Factories}'s doc. The fee is 29 basis points for both
|
|
70
|
+
* deployments (measured, same doc).
|
|
71
|
+
*/
|
|
72
|
+
export declare const defaultSwapVenues: SwapVenues;
|
|
73
|
+
/**
|
|
74
|
+
* A map of chains to uni v2 router addresses. Frozen. PulseChain's entries
|
|
75
|
+
* derive from {@link defaultSwapVenues}, so the two cannot drift. Kept for
|
|
76
|
+
* compatibility; to swap on other routers pass `swapVenues` instead.
|
|
77
|
+
*/
|
|
78
|
+
export declare const uniV2Routers: Readonly<Record<Chains, readonly Hex[]>>;
|
|
64
79
|
/**
|
|
65
80
|
* Each {@link uniV2Routers} entry's OWN factory address, same index, same
|
|
66
81
|
* chain — `router.factory()` read live from `one.valve.city` on 2026-09-24.
|
|
@@ -85,7 +100,7 @@ export declare const uniV2Routers: Record<Chains, Hex[]>;
|
|
|
85
100
|
* salt collision) and worth this note so a future reader does not assume a
|
|
86
101
|
* transcription error and "fix" it back to something wrong.
|
|
87
102
|
*/
|
|
88
|
-
export declare const uniV2Factories: Record<Chains, Hex[]
|
|
103
|
+
export declare const uniV2Factories: Readonly<Record<Chains, readonly Hex[]>>;
|
|
89
104
|
/** A map of chains to native token symbols */
|
|
90
105
|
export declare const nativeTokenSymbol: Record<Chains, string>;
|
|
91
106
|
/** A map of chains to native token names */
|
package/dist/cjs/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FOREIGN_TO_HOME_FEE = exports.HOME_TO_FOREIGN_FEE = exports.bridgeConfigs = exports.startBlocks = exports.chainIds = exports.chains = exports.canChangeUnwrap = exports.isUnwrappable = exports.isNative = exports.defaultAssetIn = exports.pathway = exports.pathwayGroup = exports.inferBridgeKey = exports.isTestnetChain = exports.validBridgeKeys = exports.testnetPathways = exports.pathways = exports.nativeTokenName = exports.nativeTokenSymbol = exports.uniV2Factories = exports.uniV2Routers = exports.deprecatedNativeAssetOut = exports.pulsechainTokenAddresses = exports.nativeAssetOut = exports.Providers = exports.toChainKey = exports.toChain = exports.chainIdToKey = exports.Chains = void 0;
|
|
3
|
+
exports.FOREIGN_TO_HOME_FEE = exports.HOME_TO_FOREIGN_FEE = exports.bridgeConfigs = exports.startBlocks = exports.chainIds = exports.chains = exports.canChangeUnwrap = exports.isUnwrappable = exports.isNative = exports.defaultAssetIn = exports.pathway = exports.pathwayGroup = exports.inferBridgeKey = exports.isTestnetChain = exports.validBridgeKeys = exports.testnetPathways = exports.pathways = exports.nativeTokenName = exports.nativeTokenSymbol = exports.uniV2Factories = exports.uniV2Routers = exports.defaultSwapVenues = exports.deprecatedNativeAssetOut = exports.pulsechainTokenAddresses = exports.nativeAssetOut = exports.Providers = exports.toChainKey = exports.toChain = exports.chainIdToKey = exports.Chains = void 0;
|
|
4
4
|
const viem_1 = require("viem");
|
|
5
5
|
(function (Chains) {
|
|
6
6
|
Chains["PLS"] = "0x171";
|
|
@@ -72,12 +72,13 @@ exports.pulsechainTokenAddresses = Object.freeze({
|
|
|
72
72
|
exports.deprecatedNativeAssetOut = {
|
|
73
73
|
[exports.Chains.PLS]: new Set(['0x97Ac4a2439A47c07ad535bb1188c989dae755341']),
|
|
74
74
|
};
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Routers on chains where this package knows the router but has not verified
|
|
77
|
+
* a factory, so no {@link SwapVenue} can be built for them. They stay in
|
|
78
|
+
* {@link uniV2Routers} for the native-price reads (`chain-info.ts`,
|
|
79
|
+
* `readAmountOut`) that need only a router.
|
|
80
|
+
*/
|
|
81
|
+
const routersWithoutVenue = {
|
|
81
82
|
[exports.Chains.ETH]: ['0x7a250d5630b4cf539739df2c5dacb4c659f2488d'],
|
|
82
83
|
[exports.Chains.BNB]: ['0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'],
|
|
83
84
|
[exports.Chains.SEP]: ['0xeE567Fe1712Faf6149d80dA1E6934E354124CfE3'],
|
|
@@ -86,6 +87,43 @@ exports.uniV2Routers = {
|
|
|
86
87
|
'0x636f6407B90661b73b1C0F7e24F4C79f624d0738',
|
|
87
88
|
],
|
|
88
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* The swap venues the package ships with: PulseX V1 then V2 on PulseChain.
|
|
92
|
+
* Frozen. A caller who wants other routers passes its own
|
|
93
|
+
* {@link SwapVenues} (see `resolveSwapVenues`); this table is never mutated.
|
|
94
|
+
*
|
|
95
|
+
* Each factory is the router's own `router.factory()` read on chain — see
|
|
96
|
+
* {@link uniV2Factories}'s doc. The fee is 29 basis points for both
|
|
97
|
+
* deployments (measured, same doc).
|
|
98
|
+
*/
|
|
99
|
+
exports.defaultSwapVenues = Object.freeze({
|
|
100
|
+
[Number(exports.Chains.PLS)]: Object.freeze([
|
|
101
|
+
Object.freeze({
|
|
102
|
+
kind: 'uniswap-v2',
|
|
103
|
+
chainId: Number(exports.Chains.PLS),
|
|
104
|
+
router: '0x98bf93ebf5c380C0e6Ae8e192A7e2AE08edAcc02',
|
|
105
|
+
factory: '0x1715a3E4A142d8b698131108995174F37aEBA10D',
|
|
106
|
+
feeBasisPoints: 29,
|
|
107
|
+
label: 'PulseX V1',
|
|
108
|
+
}),
|
|
109
|
+
Object.freeze({
|
|
110
|
+
kind: 'uniswap-v2',
|
|
111
|
+
chainId: Number(exports.Chains.PLS),
|
|
112
|
+
router: '0x165C3410fC91EF562C50559f7d2289fEbed552d9',
|
|
113
|
+
factory: '0x29eA7545DEf87022BAdc76323F373EA1e707C523',
|
|
114
|
+
feeBasisPoints: 29,
|
|
115
|
+
label: 'PulseX V2',
|
|
116
|
+
}),
|
|
117
|
+
]),
|
|
118
|
+
});
|
|
119
|
+
/** One chain's routers: its default venues' routers, else the router-only entries. */
|
|
120
|
+
const defaultRoutersFor = (chain) => exports.defaultSwapVenues[Number(chain)]?.map((venue) => venue.router) ?? routersWithoutVenue[chain] ?? [];
|
|
121
|
+
/**
|
|
122
|
+
* A map of chains to uni v2 router addresses. Frozen. PulseChain's entries
|
|
123
|
+
* derive from {@link defaultSwapVenues}, so the two cannot drift. Kept for
|
|
124
|
+
* compatibility; to swap on other routers pass `swapVenues` instead.
|
|
125
|
+
*/
|
|
126
|
+
exports.uniV2Routers = Object.freeze(Object.fromEntries(Object.values(exports.Chains).map((chain) => [chain, Object.freeze([...defaultRoutersFor(chain)])])));
|
|
89
127
|
/**
|
|
90
128
|
* Each {@link uniV2Routers} entry's OWN factory address, same index, same
|
|
91
129
|
* chain — `router.factory()` read live from `one.valve.city` on 2026-09-24.
|
|
@@ -110,16 +148,10 @@ exports.uniV2Routers = {
|
|
|
110
148
|
* salt collision) and worth this note so a future reader does not assume a
|
|
111
149
|
* transcription error and "fix" it back to something wrong.
|
|
112
150
|
*/
|
|
113
|
-
exports.uniV2Factories =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
],
|
|
118
|
-
[exports.Chains.ETH]: [],
|
|
119
|
-
[exports.Chains.BNB]: [],
|
|
120
|
-
[exports.Chains.SEP]: [],
|
|
121
|
-
[exports.Chains.V4PLS]: [],
|
|
122
|
-
};
|
|
151
|
+
exports.uniV2Factories = Object.freeze(Object.fromEntries(Object.values(exports.Chains).map((chain) => [
|
|
152
|
+
chain,
|
|
153
|
+
Object.freeze((exports.defaultSwapVenues[Number(chain)] ?? []).map((venue) => venue.factory)),
|
|
154
|
+
])));
|
|
123
155
|
/** A map of chains to native token symbols */
|
|
124
156
|
exports.nativeTokenSymbol = {
|
|
125
157
|
[exports.Chains.PLS]: 'PLS',
|
package/dist/cjs/execution.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Hex, type PublicClient } from 'viem';
|
|
2
2
|
import type { TokenBridgeInfo } from './chain-info.js';
|
|
3
|
+
import { type SwapVenues } from './swap-venues.js';
|
|
3
4
|
import { type AggregatorOriginRefusal, type ConsolidatedQuote, type ConsolidatedQuoteRequest } from './consolidated-quote.js';
|
|
4
5
|
import type { FeeType } from './fee-type.js';
|
|
5
6
|
import type { RouteCandidate } from './routing/candidate.js';
|
|
@@ -20,7 +21,7 @@ import type { BridgeKey, Token } from './types.js';
|
|
|
20
21
|
* EVERY SPENDER IS ONE THIS CODE ALREADY KNOWS. Approvals are for the exact
|
|
21
22
|
* amount spent, never unlimited, and go only to:
|
|
22
23
|
* - the omnibridge pathway's `from` mediator, read from `config.ts`;
|
|
23
|
-
* - the
|
|
24
|
+
* - the swap router the edge was quoted on, found in the swap venues (the defaults in `config.ts` unless the caller passes its own);
|
|
24
25
|
* - an aggregator spender that `aggregatorOriginCalls` checked against a
|
|
25
26
|
* fixed table for its provider and chain (LI.FI's Diamond, Relay's
|
|
26
27
|
* depository). A quote cannot name its own spender.
|
|
@@ -212,12 +213,15 @@ export type ExecutionPlan = {
|
|
|
212
213
|
* @param options.account - the account that signs every transaction
|
|
213
214
|
* @param options.legs - the legs, in the order the wallet signs them
|
|
214
215
|
* @param options.readAllowance - reads an allowance on chain; see {@link AllowanceReader}
|
|
216
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults; a swap whose router is in none of them throws
|
|
215
217
|
* @returns the ordered transactions, or the first leg that cannot be planned
|
|
218
|
+
* @throws when a swap leg's router is not in the swap venues
|
|
216
219
|
*/
|
|
217
|
-
export declare const planExecution: ({ account, legs, readAllowance, }: {
|
|
220
|
+
export declare const planExecution: ({ account, legs, readAllowance, swapVenues, }: {
|
|
218
221
|
readonly account: Hex;
|
|
219
222
|
readonly legs: readonly ExecutionLeg[];
|
|
220
223
|
readonly readAllowance: AllowanceReader;
|
|
224
|
+
readonly swapVenues?: SwapVenues;
|
|
221
225
|
}) => Promise<ExecutionPlan>;
|
|
222
226
|
/**
|
|
223
227
|
* What the caller supplies for one signature section of a candidate. Each
|
|
@@ -270,6 +274,12 @@ export type CandidateExecutionInputs = {
|
|
|
270
274
|
readonly readAllowance: AllowanceReader;
|
|
271
275
|
/** The resolver the candidate was built with; turns a plan's graph node into its token. */
|
|
272
276
|
readonly resolveToken: ResolveNodeToken;
|
|
277
|
+
/**
|
|
278
|
+
* The routers swaps may sign on, merged over the defaults: a chain named
|
|
279
|
+
* here replaces that chain's default list. Pass the SAME venues the route
|
|
280
|
+
* search used. A swap whose router is in none of them throws.
|
|
281
|
+
*/
|
|
282
|
+
readonly swapVenues?: SwapVenues;
|
|
273
283
|
/**
|
|
274
284
|
* The index of the section that `sections[0]` plans. It defaults to 0.
|
|
275
285
|
* Pass it to plan a later section after the earlier ones landed, with the
|
|
@@ -337,10 +347,13 @@ export declare const planCandidateExecution: (inputs: CandidateExecutionInputs)
|
|
|
337
347
|
* @param options.hop - the swap hop; its recorded path and edge name what to read
|
|
338
348
|
* @param options.amountIn - the amount the swap spends, in base units
|
|
339
349
|
* @param options.client - a client that can call `readContract`
|
|
340
|
-
* @
|
|
350
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults
|
|
351
|
+
* @returns the amount the last token of the path delivers, or null when the hop records no path, the amount is not positive, or the read fails or returns nothing
|
|
352
|
+
* @throws when the hop records a path but its router is not in the swap venues
|
|
341
353
|
*/
|
|
342
|
-
export declare const readPulseXSectionQuote: ({ hop, amountIn, client, }: {
|
|
354
|
+
export declare const readPulseXSectionQuote: ({ hop, amountIn, client, swapVenues, }: {
|
|
343
355
|
readonly hop: Pick<PlannedHop, "edge" | "outcome">;
|
|
344
356
|
readonly amountIn: bigint;
|
|
345
357
|
readonly client: Pick<PublicClient, "readContract">;
|
|
358
|
+
readonly swapVenues?: SwapVenues;
|
|
346
359
|
}) => Promise<bigint | null>;
|
package/dist/cjs/execution.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.readPulseXSectionQuote = exports.planCandidateExecution = exports.planEx
|
|
|
37
37
|
const viem_1 = require("viem");
|
|
38
38
|
const abis = __importStar(require("./abis.js"));
|
|
39
39
|
const config_js_1 = require("./config.js");
|
|
40
|
+
const swap_venues_js_1 = require("./swap-venues.js");
|
|
40
41
|
const consolidated_quote_js_1 = require("./consolidated-quote.js");
|
|
41
42
|
const pulsex_js_1 = require("./routing/carriers/pulsex.js");
|
|
42
43
|
const swap_transactions_js_1 = require("./routing/swap-transactions.js");
|
|
@@ -131,10 +132,30 @@ const omnibridgeCalls = (leg, account) => {
|
|
|
131
132
|
delivery: deliveryTermsOf(leg.delivery),
|
|
132
133
|
};
|
|
133
134
|
};
|
|
134
|
-
|
|
135
|
+
/**
|
|
136
|
+
* The venue a swap edge was priced on, from the venues execution was given.
|
|
137
|
+
* A router that is not in those venues THROWS: signing on another router, on
|
|
138
|
+
* PulseX most of all, would trade against pools the reader was never shown.
|
|
139
|
+
*
|
|
140
|
+
* @param venues - the venues passed to execution
|
|
141
|
+
* @param edge - the swap edge
|
|
142
|
+
* @returns the venue
|
|
143
|
+
* @throws when the edge's router is not in `venues`, or its chain has no venue
|
|
144
|
+
*/
|
|
145
|
+
const requireSwapVenue = (venues, edge) => {
|
|
146
|
+
const venue = (0, pulsex_js_1.venueForEdge)(venues, edge);
|
|
147
|
+
if (venue !== null)
|
|
148
|
+
return venue;
|
|
149
|
+
const router = edge.venue ?? 'the default router';
|
|
150
|
+
throw new Error(`The swap was priced on ${router} on chain ${edge.from.chainId}, but that router is not in the swap venues passed to execution. ` +
|
|
151
|
+
'Pass the same swapVenues the route search used.');
|
|
152
|
+
};
|
|
153
|
+
const pulseXSwapCalls = (leg, venues) => {
|
|
135
154
|
const { edge } = leg.hop;
|
|
136
155
|
const chainId = edge.from.chainId;
|
|
137
|
-
|
|
156
|
+
if (edge.carrier !== 'PulseX')
|
|
157
|
+
return refusal({ reason: 'swap-refused' });
|
|
158
|
+
const routerAddress = requireSwapVenue(venues, edge).router;
|
|
138
159
|
// The builder refuses a hop that records no priced path, a router other
|
|
139
160
|
// than the edge's own, and a leg on screen that disagrees.
|
|
140
161
|
const call = (0, swap_transactions_js_1.buildPulseXSwapTransaction)({
|
|
@@ -146,8 +167,9 @@ const pulseXSwapCalls = (leg) => {
|
|
|
146
167
|
signedLeg: { kind: 'swap', chainId },
|
|
147
168
|
chainId,
|
|
148
169
|
routerAddress,
|
|
170
|
+
venues,
|
|
149
171
|
});
|
|
150
|
-
if (call === null
|
|
172
|
+
if (call === null)
|
|
151
173
|
return refusal({ reason: 'swap-refused' });
|
|
152
174
|
const spendsNative = edge.from.address.toLowerCase() === viem_1.zeroAddress;
|
|
153
175
|
return {
|
|
@@ -191,11 +213,11 @@ const aggregatorCalls = (leg) => {
|
|
|
191
213
|
delivery: null,
|
|
192
214
|
};
|
|
193
215
|
};
|
|
194
|
-
const legCalls = (leg, account) => {
|
|
216
|
+
const legCalls = (leg, account, venues) => {
|
|
195
217
|
if (leg.kind === 'omnibridge')
|
|
196
218
|
return omnibridgeCalls(leg, account);
|
|
197
219
|
if (leg.kind === 'pulsex-swap')
|
|
198
|
-
return pulseXSwapCalls(leg);
|
|
220
|
+
return pulseXSwapCalls(leg, venues);
|
|
199
221
|
if (leg.kind === 'wrap')
|
|
200
222
|
return wrapCalls(leg);
|
|
201
223
|
return aggregatorCalls(leg);
|
|
@@ -220,10 +242,13 @@ const approvalTransactions = async ({ approval, account, readAllowance, }) => {
|
|
|
220
242
|
* @param options.account - the account that signs every transaction
|
|
221
243
|
* @param options.legs - the legs, in the order the wallet signs them
|
|
222
244
|
* @param options.readAllowance - reads an allowance on chain; see {@link AllowanceReader}
|
|
245
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults; a swap whose router is in none of them throws
|
|
223
246
|
* @returns the ordered transactions, or the first leg that cannot be planned
|
|
247
|
+
* @throws when a swap leg's router is not in the swap venues
|
|
224
248
|
*/
|
|
225
|
-
const planExecution = async ({ account, legs, readAllowance, }) => {
|
|
226
|
-
const
|
|
249
|
+
const planExecution = async ({ account, legs, readAllowance, swapVenues, }) => {
|
|
250
|
+
const venues = (0, swap_venues_js_1.resolveSwapVenues)(swapVenues);
|
|
251
|
+
const calls = legs.map((leg) => legCalls(leg, account, venues));
|
|
227
252
|
const refusedIndex = calls.findIndex((entry) => entry.kind === 'refused');
|
|
228
253
|
const refused = calls[refusedIndex];
|
|
229
254
|
if (refused !== undefined && refused.kind === 'refused') {
|
|
@@ -401,7 +426,7 @@ const sectionWindowProblem = ({ firstSectionIndex, plannedCount, sectionCount, }
|
|
|
401
426
|
* @returns the ordered transactions with the deliveries they buy, or the first section that cannot be planned
|
|
402
427
|
*/
|
|
403
428
|
const planCandidateExecution = async (inputs) => {
|
|
404
|
-
const { candidate, account, readAllowance, resolveToken, sections } = inputs;
|
|
429
|
+
const { candidate, account, readAllowance, resolveToken, sections, swapVenues } = inputs;
|
|
405
430
|
const firstSectionIndex = inputs.firstSectionIndex ?? 0;
|
|
406
431
|
const plan = candidate.plan;
|
|
407
432
|
const allSections = plan === undefined ? null : planSections(plan);
|
|
@@ -424,7 +449,7 @@ const planCandidateExecution = async (inputs) => {
|
|
|
424
449
|
return { kind: 'refused', sectionIndex: firstSectionIndex + refusedIndex, refusal: refused.refusal };
|
|
425
450
|
}
|
|
426
451
|
const legs = sectionLegs.flatMap((entry) => (entry.kind === 'leg' ? [entry.leg] : []));
|
|
427
|
-
const planned = await (0, exports.planExecution)({ account, legs, readAllowance });
|
|
452
|
+
const planned = await (0, exports.planExecution)({ account, legs, readAllowance, ...(swapVenues === undefined ? {} : { swapVenues }) });
|
|
428
453
|
if (planned.kind === 'refused') {
|
|
429
454
|
return { kind: 'refused', sectionIndex: firstSectionIndex + planned.legIndex, refusal: planned.refusal };
|
|
430
455
|
}
|
|
@@ -445,13 +470,15 @@ exports.planCandidateExecution = planCandidateExecution;
|
|
|
445
470
|
* @param options.hop - the swap hop; its recorded path and edge name what to read
|
|
446
471
|
* @param options.amountIn - the amount the swap spends, in base units
|
|
447
472
|
* @param options.client - a client that can call `readContract`
|
|
448
|
-
* @
|
|
473
|
+
* @param options.swapVenues - the routers swaps may sign on, merged over the defaults
|
|
474
|
+
* @returns the amount the last token of the path delivers, or null when the hop records no path, the amount is not positive, or the read fails or returns nothing
|
|
475
|
+
* @throws when the hop records a path but its router is not in the swap venues
|
|
449
476
|
*/
|
|
450
|
-
const readPulseXSectionQuote = async ({ hop, amountIn, client, }) => {
|
|
477
|
+
const readPulseXSectionQuote = async ({ hop, amountIn, client, swapVenues, }) => {
|
|
451
478
|
const path = (0, pulsex_js_1.pulseXSwapPathForHop)(hop);
|
|
452
|
-
|
|
453
|
-
if (path === null || router === null || amountIn <= 0n)
|
|
479
|
+
if (path === null || amountIn <= 0n)
|
|
454
480
|
return null;
|
|
481
|
+
const router = requireSwapVenue((0, swap_venues_js_1.resolveSwapVenues)(swapVenues), hop.edge).router;
|
|
455
482
|
try {
|
|
456
483
|
const amounts = await client.readContract({
|
|
457
484
|
address: router,
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from './referral.js';
|
|
|
17
17
|
export * from './route-steps.js';
|
|
18
18
|
export * from './routing/index.js';
|
|
19
19
|
export * from './settings.js';
|
|
20
|
+
export * from './swap-venues.js';
|
|
20
21
|
export * from './types.js';
|
|
21
22
|
export * from './claims.js';
|
|
22
23
|
export * from './execution.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __exportStar(require("./referral.js"), exports);
|
|
|
33
33
|
__exportStar(require("./route-steps.js"), exports);
|
|
34
34
|
__exportStar(require("./routing/index.js"), exports);
|
|
35
35
|
__exportStar(require("./settings.js"), exports);
|
|
36
|
+
__exportStar(require("./swap-venues.js"), exports);
|
|
36
37
|
__exportStar(require("./types.js"), exports);
|
|
37
38
|
__exportStar(require("./claims.js"), exports);
|
|
38
39
|
__exportStar(require("./execution.js"), exports);
|
package/dist/cjs/prices.d.ts
CHANGED
|
@@ -105,3 +105,13 @@ export declare const resolveTokenPriceUsd: (token: Token, { pulsexPrices, lifiPr
|
|
|
105
105
|
pulsexPrices: Map<string, number>;
|
|
106
106
|
lifiPrices: Map<string, number>;
|
|
107
107
|
}) => number | null;
|
|
108
|
+
/**
|
|
109
|
+
* The chain's canonical wrapped native address when `token` is the native
|
|
110
|
+
* coin (zero address) or that wrapper itself, and null for every other token.
|
|
111
|
+
* Only the SAME chain's pair is returned: a bridged copy of a wrapper on
|
|
112
|
+
* another chain is a different token and keeps its own price.
|
|
113
|
+
*
|
|
114
|
+
* @param token - the token to test
|
|
115
|
+
* @returns the wrapped native address on the token's own chain, or null
|
|
116
|
+
*/
|
|
117
|
+
export declare const sameChainWrappedNative: (token: Token) => Hex | null;
|
package/dist/cjs/prices.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveTokenPriceUsd = exports.computeUsdPriceFromAmountsOut = exports.pulsexPricePaths = exports.pulsechainOriginAddress = exports.isPulsechain = exports.lifiPriceKey = exports.lowercaseAddress = exports.WPLS_ADDRESS = exports.PULSECHAIN_USD_ANCHOR_DECIMALS = exports.PULSECHAIN_USD_ANCHOR_ADDRESS = exports.PULSECHAIN_TESTNET_CHAIN_ID = exports.PULSECHAIN_CHAIN_ID = void 0;
|
|
3
|
+
exports.sameChainWrappedNative = exports.resolveTokenPriceUsd = exports.computeUsdPriceFromAmountsOut = exports.pulsexPricePaths = exports.pulsechainOriginAddress = exports.isPulsechain = exports.lifiPriceKey = exports.lowercaseAddress = exports.WPLS_ADDRESS = exports.PULSECHAIN_USD_ANCHOR_DECIMALS = exports.PULSECHAIN_USD_ANCHOR_ADDRESS = exports.PULSECHAIN_TESTNET_CHAIN_ID = exports.PULSECHAIN_CHAIN_ID = void 0;
|
|
4
4
|
const viem_1 = require("viem");
|
|
5
5
|
const config_js_1 = require("./config.js");
|
|
6
6
|
/**
|
|
@@ -147,6 +147,59 @@ exports.computeUsdPriceFromAmountsOut = computeUsdPriceFromAmountsOut;
|
|
|
147
147
|
* @returns the USD price as a number, or null when no source can price it
|
|
148
148
|
*/
|
|
149
149
|
const resolveTokenPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
|
|
150
|
+
const wrappedNative = (0, exports.sameChainWrappedNative)(token);
|
|
151
|
+
if (wrappedNative !== null) {
|
|
152
|
+
return resolveNativeFamilyPriceUsd(token, wrappedNative, { pulsexPrices, lifiPrices });
|
|
153
|
+
}
|
|
154
|
+
return resolveOwnPriceUsd(token, { pulsexPrices, lifiPrices });
|
|
155
|
+
};
|
|
156
|
+
exports.resolveTokenPriceUsd = resolveTokenPriceUsd;
|
|
157
|
+
/**
|
|
158
|
+
* The chain's canonical wrapped native address when `token` is the native
|
|
159
|
+
* coin (zero address) or that wrapper itself, and null for every other token.
|
|
160
|
+
* Only the SAME chain's pair is returned: a bridged copy of a wrapper on
|
|
161
|
+
* another chain is a different token and keeps its own price.
|
|
162
|
+
*
|
|
163
|
+
* @param token - the token to test
|
|
164
|
+
* @returns the wrapped native address on the token's own chain, or null
|
|
165
|
+
*/
|
|
166
|
+
const sameChainWrappedNative = (token) => {
|
|
167
|
+
const wrapped = config_js_1.nativeAssetOut[(0, config_js_1.toChain)(token.chainId)];
|
|
168
|
+
if (wrapped === undefined) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const isNativeCoin = sameAddress(token.address, NATIVE_TOKEN_ADDRESS);
|
|
172
|
+
return isNativeCoin || sameAddress(token.address, wrapped) ? wrapped : null;
|
|
173
|
+
};
|
|
174
|
+
exports.sameChainWrappedNative = sameChainWrappedNative;
|
|
175
|
+
/**
|
|
176
|
+
* One price for a native coin and its canonical wrapper. The contract wraps
|
|
177
|
+
* and unwraps 1:1, so two prices would show a value loss on a wrap step that
|
|
178
|
+
* loses nothing.
|
|
179
|
+
*
|
|
180
|
+
* Off PulseChain the native coin's market feed (LI.FI) wins. The wrapper's
|
|
181
|
+
* own price there is the PulseX price of its bridged copy (it carries a
|
|
182
|
+
* PulseChain origin link), which is a derivative market with its own
|
|
183
|
+
* premium or discount. The coin's feed prices the underlying asset. When the
|
|
184
|
+
* feed has no native price, the wrapper's feed price, then its own resolved
|
|
185
|
+
* price, stand in.
|
|
186
|
+
*
|
|
187
|
+
* On PulseChain both are priced by PulseX through the WPLS path, so the
|
|
188
|
+
* wrapper's entry is used and the coin's entry stands in when the wrapper was
|
|
189
|
+
* not in the priced set.
|
|
190
|
+
*/
|
|
191
|
+
const resolveNativeFamilyPriceUsd = (token, wrappedNative, maps) => {
|
|
192
|
+
const wrapped = { ...token, address: wrappedNative };
|
|
193
|
+
if ((0, exports.isPulsechain)(token.chainId)) {
|
|
194
|
+
return (resolveOwnPriceUsd(wrapped, maps) ??
|
|
195
|
+
resolveOwnPriceUsd({ ...token, address: NATIVE_TOKEN_ADDRESS }, maps));
|
|
196
|
+
}
|
|
197
|
+
return (maps.lifiPrices.get((0, exports.lifiPriceKey)(token.chainId, NATIVE_TOKEN_ADDRESS)) ??
|
|
198
|
+
maps.lifiPrices.get((0, exports.lifiPriceKey)(token.chainId, wrappedNative)) ??
|
|
199
|
+
resolveOwnPriceUsd(wrapped, maps));
|
|
200
|
+
};
|
|
201
|
+
/** The token's price by its own address, with no native-wrapper pairing. */
|
|
202
|
+
const resolveOwnPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
|
|
150
203
|
if ((0, exports.isPulsechain)(token.chainId)) {
|
|
151
204
|
if (sameAddress(token.address, exports.PULSECHAIN_USD_ANCHOR_ADDRESS)) {
|
|
152
205
|
return 1;
|
|
@@ -169,4 +222,3 @@ const resolveTokenPriceUsd = (token, { pulsexPrices, lifiPrices, }) => {
|
|
|
169
222
|
}
|
|
170
223
|
return lifiPrices.get((0, exports.lifiPriceKey)(token.chainId, token.address)) ?? null;
|
|
171
224
|
};
|
|
172
|
-
exports.resolveTokenPriceUsd = resolveTokenPriceUsd;
|
|
@@ -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,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.carrierStepKind = exports.carrierProviderKey = exports.WRAP_CARRIER_ID = void 0;
|
|
4
|
+
const config_js_1 = require("../config.js");
|
|
5
|
+
const providers_js_1 = require("../providers.js");
|
|
6
|
+
/**
|
|
7
|
+
* The carrier id of the native-coin wrap and unwrap step
|
|
8
|
+
* (`routing/carriers/wrap.ts`).
|
|
9
|
+
*/
|
|
10
|
+
exports.WRAP_CARRIER_ID = 'wrap';
|
|
11
|
+
/**
|
|
12
|
+
* Every engine carrier id that IS a provider, mapped to its {@link ProviderKey}.
|
|
13
|
+
* The omnibridge crossings carry the provider value itself; the aggregators
|
|
14
|
+
* carry their registry id; the PulseChain exchange carries its own literal.
|
|
15
|
+
*/
|
|
16
|
+
const providerKeyByCarrierId = new Map([
|
|
17
|
+
...Object.values(config_js_1.Providers).map((provider) => [provider, provider]),
|
|
18
|
+
...providers_js_1.providerRegistry.map((descriptor) => [descriptor.id, descriptor.id]),
|
|
19
|
+
['PulseX', 'PulseX'],
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* The one place an engine carrier id becomes a {@link ProviderKey}.
|
|
23
|
+
*
|
|
24
|
+
* AN ENGINE CARRIER ID IS NOT A PROVIDER KEY. The engine is a plug-in host: a
|
|
25
|
+
* carrier id is any string, and one of them, `'wrap'`, names a step that no
|
|
26
|
+
* provider performs. Handing a raw id to the logo, glyph, or name tables drew a
|
|
27
|
+
* blank mark and logged an error. Route data resolves the key here, once, and
|
|
28
|
+
* every display site reads the resolved key.
|
|
29
|
+
*
|
|
30
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
31
|
+
* @returns the provider key, or null when no registered provider carries that id
|
|
32
|
+
*/
|
|
33
|
+
const carrierProviderKey = (carrierId) => providerKeyByCarrierId.get(carrierId) ?? null;
|
|
34
|
+
exports.carrierProviderKey = carrierProviderKey;
|
|
35
|
+
/**
|
|
36
|
+
* The kind of provider-less step a carrier id performs.
|
|
37
|
+
*
|
|
38
|
+
* @param carrierId - the `carrier` of a planned hop's edge
|
|
39
|
+
* @returns the step kind, or null when the carrier is a provider or unknown
|
|
40
|
+
*/
|
|
41
|
+
const carrierStepKind = (carrierId) => carrierId === exports.WRAP_CARRIER_ID ? 'wrap' : null;
|
|
42
|
+
exports.carrierStepKind = carrierStepKind;
|