@anywalletkit/protocol-swap2-router 0.0.0-stage → 0.2.1
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/LICENSE +202 -0
- package/README.md +70 -2
- package/dist/chunk-6WXEWTIF.js +296 -0
- package/dist/chunk-6WXEWTIF.js.map +1 -0
- package/dist/chunk-ATQMEXTG.js +98 -0
- package/dist/chunk-ATQMEXTG.js.map +1 -0
- package/dist/chunk-VRUHVDAX.js +78 -0
- package/dist/chunk-VRUHVDAX.js.map +1 -0
- package/dist/index.cjs +487 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/router.cjs +316 -0
- package/dist/router.cjs.map +1 -0
- package/dist/router.d.cts +118 -0
- package/dist/router.d.ts +118 -0
- package/dist/router.js +7 -0
- package/dist/router.js.map +1 -0
- package/dist/swap2/router.cjs +386 -0
- package/dist/swap2/router.cjs.map +1 -0
- package/dist/swap2/router.d.cts +100 -0
- package/dist/swap2/router.d.ts +100 -0
- package/dist/swap2/router.js +8 -0
- package/dist/swap2/router.js.map +1 -0
- package/dist/value.cjs +118 -0
- package/dist/value.cjs.map +1 -0
- package/dist/value.d.cts +51 -0
- package/dist/value.d.ts +51 -0
- package/dist/value.js +9 -0
- package/dist/value.js.map +1 -0
- package/module.json +20 -0
- package/package.json +69 -3
package/dist/router.cjs
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/router.ts
|
|
21
|
+
var router_exports = {};
|
|
22
|
+
__export(router_exports, {
|
|
23
|
+
CrossChainRouter: () => CrossChainRouter
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(router_exports);
|
|
26
|
+
var import_validate = require("@anywalletkit/protocol-swap2-core/validate");
|
|
27
|
+
var DEFAULT_WEIGHTS = { price: 0.6, speed: 0.3, reliability: 0.1 };
|
|
28
|
+
var DEFAULT_TIE_BAND = 2e-3;
|
|
29
|
+
var QUOTE_TIMEOUT_MS = 8e3;
|
|
30
|
+
var SOLO_QUOTE_TIMEOUT_MS = 25e3;
|
|
31
|
+
function parseWeights(spec) {
|
|
32
|
+
if (!spec) return DEFAULT_WEIGHTS;
|
|
33
|
+
const w = { ...DEFAULT_WEIGHTS };
|
|
34
|
+
for (const part of spec.split(",")) {
|
|
35
|
+
const [k, v] = part.split(":");
|
|
36
|
+
const n = Number(v);
|
|
37
|
+
if ((k === "price" || k === "speed" || k === "reliability") && Number.isFinite(n)) {
|
|
38
|
+
w[k] = Math.max(0, n);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return w;
|
|
42
|
+
}
|
|
43
|
+
async function withTimeout(p, ms) {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const timer = setTimeout(() => reject(new Error(`quote timed out after ${ms}ms`)), ms);
|
|
46
|
+
p.then(resolve, reject).finally(() => clearTimeout(timer));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
var CrossChainRouter = class _CrossChainRouter {
|
|
50
|
+
providers;
|
|
51
|
+
/** Why the venues of the LAST getBestQuote call sat out — for error surfaces. */
|
|
52
|
+
_lastRejections = [];
|
|
53
|
+
/** Why providers sat out the last auction, one line each. */
|
|
54
|
+
get lastRejections() {
|
|
55
|
+
return this._lastRejections;
|
|
56
|
+
}
|
|
57
|
+
weights;
|
|
58
|
+
valueUsd;
|
|
59
|
+
tieBand;
|
|
60
|
+
options;
|
|
61
|
+
constructor(providers, options) {
|
|
62
|
+
const opts = typeof options === "string" ? { weights: options } : { ...options ?? {} };
|
|
63
|
+
if (opts.tieBand !== void 0 && !(Number.isFinite(opts.tieBand) && opts.tieBand >= 0 && opts.tieBand < 1)) {
|
|
64
|
+
throw new Error("cross-chain router: tieBand must be a fraction in [0, 1)");
|
|
65
|
+
}
|
|
66
|
+
const names = /* @__PURE__ */ new Set();
|
|
67
|
+
for (const provider of providers) {
|
|
68
|
+
if (typeof provider.name !== "string" || !/^[A-Za-z0-9_-]+$/.test(provider.name) || names.has(provider.name)) {
|
|
69
|
+
throw new Error(`cross-chain router: invalid or duplicate provider name '${String(provider.name)}'`);
|
|
70
|
+
}
|
|
71
|
+
names.add(provider.name);
|
|
72
|
+
}
|
|
73
|
+
this.providers = providers.map((provider) => ({
|
|
74
|
+
name: provider.name,
|
|
75
|
+
enabled: provider.enabled.bind(provider),
|
|
76
|
+
supportsRoute: provider.supportsRoute.bind(provider),
|
|
77
|
+
getQuote: provider.getQuote.bind(provider),
|
|
78
|
+
buildDeposit: provider.buildDeposit.bind(provider),
|
|
79
|
+
trackIntent: provider.trackIntent.bind(provider),
|
|
80
|
+
...provider.preflightDeposit ? { preflightDeposit: provider.preflightDeposit.bind(provider) } : {},
|
|
81
|
+
...provider.notifyDeposit ? { notifyDeposit: provider.notifyDeposit.bind(provider) } : {},
|
|
82
|
+
...provider.registerSignedOrder ? { registerSignedOrder: provider.registerSignedOrder.bind(provider) } : {},
|
|
83
|
+
// Two-phase routes (CCTP): the destination claim. Missing here, the engine
|
|
84
|
+
// and the server both reported "provider 'cctp' has no claim step" (2026-10-13).
|
|
85
|
+
...provider.buildClaim ? { buildClaim: provider.buildClaim.bind(provider) } : {},
|
|
86
|
+
...provider.close ? { close: provider.close.bind(provider) } : {},
|
|
87
|
+
...provider.supportsChain ? { supportsChain: provider.supportsChain.bind(provider) } : {},
|
|
88
|
+
...provider.supportsChainPair ? { supportsChainPair: provider.supportsChainPair.bind(provider) } : {},
|
|
89
|
+
...provider.describeActivity ? { describeActivity: provider.describeActivity.bind(provider) } : {},
|
|
90
|
+
...provider.supportsSameChain !== void 0 ? { supportsSameChain: provider.supportsSameChain } : {}
|
|
91
|
+
}));
|
|
92
|
+
this.weights = parseWeights(opts.weights);
|
|
93
|
+
this.valueUsd = opts.valueUsd;
|
|
94
|
+
this.tieBand = opts.tieBand ?? DEFAULT_TIE_BAND;
|
|
95
|
+
this.options = opts;
|
|
96
|
+
}
|
|
97
|
+
/** The same auction restricted to one provider — to execute the route a user picked. */
|
|
98
|
+
only(name) {
|
|
99
|
+
const provider = this.getProvider(name);
|
|
100
|
+
if (!provider) throw new Error(`cross-chain router: provider '${name}' is not registered`);
|
|
101
|
+
return new _CrossChainRouter([provider], this.options);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Activity recognition across the installed providers: the first one that
|
|
105
|
+
* claims the transaction describes it. Null when none does (or none is held).
|
|
106
|
+
*/
|
|
107
|
+
describeActivity(tx, ctx) {
|
|
108
|
+
for (const provider of this.providers) {
|
|
109
|
+
const item = provider.describeActivity?.(tx, ctx);
|
|
110
|
+
if (item) return item;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
/** A provider by name. */
|
|
115
|
+
getProvider(name) {
|
|
116
|
+
return this.providers.find((p) => p.name === name);
|
|
117
|
+
}
|
|
118
|
+
/** True when a provider of that name is installed. */
|
|
119
|
+
hasProvider(name) {
|
|
120
|
+
return this.providers.some((p) => p.name === name);
|
|
121
|
+
}
|
|
122
|
+
/** True when that provider is installed and enabled. */
|
|
123
|
+
hasEnabledProvider(name) {
|
|
124
|
+
return this.providers.some((p) => {
|
|
125
|
+
try {
|
|
126
|
+
return p.name === name && p.enabled();
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** True when any enabled provider declares the chain. */
|
|
133
|
+
supportsChain(caip2) {
|
|
134
|
+
return this.providers.some((p) => {
|
|
135
|
+
try {
|
|
136
|
+
return p.enabled() && !!p.supportsChain?.(caip2);
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/** True when any enabled provider declares the chain pair. */
|
|
143
|
+
supportsChainPair(fromChain, toChain) {
|
|
144
|
+
return this.providers.some((p) => {
|
|
145
|
+
try {
|
|
146
|
+
return p.enabled() && (p.supportsChainPair?.(fromChain, toChain) ?? false);
|
|
147
|
+
} catch {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The auction's pick: the best price (net of native fees and gas when
|
|
154
|
+
* `valueUsd` is set; faster, then more reliable, among equal prices), or
|
|
155
|
+
* the fastest route with `prefer: "fastest"`.
|
|
156
|
+
*/
|
|
157
|
+
async getBestQuote(params, options) {
|
|
158
|
+
const set = await this.getQuotes(params, options);
|
|
159
|
+
if (!set) return null;
|
|
160
|
+
const pick = options?.prefer === "fastest" ? set.fastest : set.best;
|
|
161
|
+
const { netValueUsd: _n, relativeValue: _r, tags: _t, ...quote } = pick;
|
|
162
|
+
return {
|
|
163
|
+
...quote,
|
|
164
|
+
routeScore: {
|
|
165
|
+
score: pick.relativeValue,
|
|
166
|
+
weights: this.weights,
|
|
167
|
+
candidates: set.quotes.map((q) => ({
|
|
168
|
+
provider: q.provider,
|
|
169
|
+
sourceAmount: q.sourceAmount,
|
|
170
|
+
etaSeconds: q.etaSeconds,
|
|
171
|
+
score: q.relativeValue,
|
|
172
|
+
minDestAmount: q.minDestAmount,
|
|
173
|
+
netValueUsd: q.netValueUsd
|
|
174
|
+
}))
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Every validated route, ranked — for a route picker that shows the best and
|
|
180
|
+
* the fastest and lets the user choose. Providers run in parallel; with
|
|
181
|
+
* `onUpdate` each one that answers re-publishes the ranked set at once (the
|
|
182
|
+
* first price shows in the time of the fastest venue, not the slowest).
|
|
183
|
+
*/
|
|
184
|
+
async getQuotes(params, options) {
|
|
185
|
+
const rejections = [];
|
|
186
|
+
this._lastRejections = rejections;
|
|
187
|
+
const snapshot = Object.freeze({ ...params });
|
|
188
|
+
const from = Object.freeze({ chain: snapshot.sourceChain, token: snapshot.sourceToken });
|
|
189
|
+
const to = Object.freeze({ chain: snapshot.destChain, token: snapshot.destToken });
|
|
190
|
+
let eligible = this.providers.filter((p) => {
|
|
191
|
+
try {
|
|
192
|
+
return p.enabled() && p.supportsRoute(from, to);
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
if (snapshot.sourceChain === snapshot.destChain) {
|
|
198
|
+
eligible = eligible.filter((p) => p.supportsSameChain === true);
|
|
199
|
+
}
|
|
200
|
+
if (eligible.length === 0) return null;
|
|
201
|
+
const timeoutMs = eligible.length === 1 ? SOLO_QUOTE_TIMEOUT_MS : QUOTE_TIMEOUT_MS;
|
|
202
|
+
const refusals = [];
|
|
203
|
+
const accepted = [];
|
|
204
|
+
const pending = new Set(eligible.map((p) => p.name));
|
|
205
|
+
const publish = () => {
|
|
206
|
+
if (!options?.onUpdate || accepted.length === 0) return;
|
|
207
|
+
const set = this.rank(accepted, snapshot, [...pending], rejections);
|
|
208
|
+
try {
|
|
209
|
+
options.onUpdate(set);
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
await Promise.all(eligible.map(async (p, i) => {
|
|
214
|
+
const controller = new AbortController();
|
|
215
|
+
const own = Object.freeze({ ...snapshot, onRefusal: (reason) => {
|
|
216
|
+
refusals[i] ??= reason.startsWith(`${p.name}:`) ? reason : `${p.name}: ${reason}`;
|
|
217
|
+
try {
|
|
218
|
+
snapshot.onRefusal?.(reason);
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
} });
|
|
222
|
+
let q = null;
|
|
223
|
+
try {
|
|
224
|
+
q = await withTimeout(p.getQuote(own, controller.signal), timeoutMs);
|
|
225
|
+
if (!q) {
|
|
226
|
+
rejections.push(refusals[i] ?? `${p.name}: returned no quote`);
|
|
227
|
+
} else if (!(0, import_validate.validQuote)(q, snapshot, p.name)) {
|
|
228
|
+
const reason = `${p.name}: rejected by validation (minDest=${q.minDestAmount} vs floor=${snapshot.minAmountOut ?? "-"}, validUntil=${q.validUntilMs}, now=${Date.now()})`;
|
|
229
|
+
rejections.push(reason);
|
|
230
|
+
console.warn(`swap2-router: ${reason}`);
|
|
231
|
+
q = null;
|
|
232
|
+
} else if (options?.requireImmediateExecution && !(q.depositAddress.length > 0 || q.txRequest !== void 0)) {
|
|
233
|
+
q = null;
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
const reason = `${p.name}: ${err instanceof Error ? err.message.slice(0, 160) : String(err).slice(0, 160)}`;
|
|
237
|
+
rejections.push(reason);
|
|
238
|
+
console.warn(`swap2-router: quote failed \u2014 ${reason}`);
|
|
239
|
+
q = null;
|
|
240
|
+
} finally {
|
|
241
|
+
controller.abort();
|
|
242
|
+
}
|
|
243
|
+
pending.delete(p.name);
|
|
244
|
+
if (q) accepted.push({ ...q, provider: p.name });
|
|
245
|
+
publish();
|
|
246
|
+
}));
|
|
247
|
+
if (accepted.length === 0) return null;
|
|
248
|
+
return this.rank(accepted, snapshot, [], rejections);
|
|
249
|
+
}
|
|
250
|
+
/** Status from the named provider; `failed` when it is not installed. */
|
|
251
|
+
async trackIntent(providerName, ref) {
|
|
252
|
+
const provider = this.getProvider(providerName);
|
|
253
|
+
if (!provider) return { state: "failed" };
|
|
254
|
+
return provider.trackIntent(ref);
|
|
255
|
+
}
|
|
256
|
+
/** Forward a source-broadcast notification to the intent's provider. */
|
|
257
|
+
async notifyDeposit(providerName, intentId, txHash) {
|
|
258
|
+
await this.getProvider(providerName)?.notifyDeposit?.(intentId, txHash);
|
|
259
|
+
}
|
|
260
|
+
/** Release every provider's resources. */
|
|
261
|
+
close() {
|
|
262
|
+
for (const provider of this.providers) provider.close?.();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Price first: each quote's value is its net USD (`valueUsd`) — or, when any
|
|
266
|
+
* quote cannot be priced, its guaranteed output — against the best one.
|
|
267
|
+
* Quotes within `tieBand` of the best are the same price; among those the
|
|
268
|
+
* weights order speed and reliability. A $0.01 edge no longer beats a route
|
|
269
|
+
* that lands in seconds instead of twenty minutes, and a route that charges
|
|
270
|
+
* a native bridge fee on top no longer wins on its gross output.
|
|
271
|
+
*/
|
|
272
|
+
rank(quotes, params, pending, rejections) {
|
|
273
|
+
const usd = this.valueUsd ? quotes.map((q) => {
|
|
274
|
+
try {
|
|
275
|
+
const v = this.valueUsd(q, params);
|
|
276
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
277
|
+
} catch {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
}) : quotes.map(() => null);
|
|
281
|
+
const priced = usd.every((v) => v !== null);
|
|
282
|
+
let relative;
|
|
283
|
+
if (priced) {
|
|
284
|
+
const values = usd;
|
|
285
|
+
const max = Math.max(...values);
|
|
286
|
+
relative = values.map((v) => max > 0 ? Math.max(0, v) / max : v >= max ? 1 : 0);
|
|
287
|
+
} else {
|
|
288
|
+
const outs = quotes.map((q) => BigInt(q.minDestAmount));
|
|
289
|
+
const max = outs.reduce((a, b) => b > a ? b : a);
|
|
290
|
+
relative = outs.map((o) => fraction(o, max));
|
|
291
|
+
}
|
|
292
|
+
const ranked = quotes.map((q, i) => ({
|
|
293
|
+
...q,
|
|
294
|
+
netValueUsd: priced ? usd[i] : null,
|
|
295
|
+
relativeValue: relative[i],
|
|
296
|
+
tags: []
|
|
297
|
+
}));
|
|
298
|
+
const tier = ranked.filter((q) => q.relativeValue >= 1 - this.tieBand - 1e-12);
|
|
299
|
+
const etas = tier.map((q) => q.etaSeconds);
|
|
300
|
+
const minEta = Math.min(...etas);
|
|
301
|
+
const maxEta = Math.max(...etas);
|
|
302
|
+
const w = this.weights;
|
|
303
|
+
const tieScore = (q) => w.price * q.relativeValue + w.speed * (maxEta === minEta ? 1 : (maxEta - q.etaSeconds) / (maxEta - minEta)) + w.reliability * q.reliability;
|
|
304
|
+
const best = tier.reduce((a, b) => tieScore(b) > tieScore(a) ? b : a);
|
|
305
|
+
const fastest = ranked.reduce((a, b) => b.etaSeconds < a.etaSeconds || b.etaSeconds === a.etaSeconds && b.relativeValue > a.relativeValue ? b : a);
|
|
306
|
+
best.tags.push("best");
|
|
307
|
+
fastest.tags.push("fastest");
|
|
308
|
+
ranked.sort((a, b) => a === best ? -1 : b === best ? 1 : b.relativeValue - a.relativeValue || a.etaSeconds - b.etaSeconds);
|
|
309
|
+
return { quotes: ranked, best, fastest, pending, rejections: [...rejections] };
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
function fraction(num, den) {
|
|
313
|
+
if (den === 0n) return 1;
|
|
314
|
+
return Number(num * 10000n / den) / 1e4;
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=router.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/router.ts"],"sourcesContent":["/**\n * The auction engine of the optional aggregator: routes a cross-chain delivery\n * over whichever providers the caller installed and passed in. Protocol\n * modules never need it — each one runs standalone on @anywalletkit/protocol-swap2-core.\n */\nimport type {\n CrossChainProvider,\n IntentRef,\n IntentStatus,\n Quote,\n QuoteParams,\n RoutedQuote,\n ScoreWeights,\n} from \"@anywalletkit/protocol-swap2-core/types\";\nimport {validQuote} from \"@anywalletkit/protocol-swap2-core/validate\";\nimport type {ActivityItem, ChainTx, DescribeActivityContext} from \"@anywalletkit/core\";\n\nconst DEFAULT_WEIGHTS: ScoreWeights = {price: 0.6, speed: 0.3, reliability: 0.1};\n/** Quotes within 0.2% of the best net value count as the same price. */\nconst DEFAULT_TIE_BAND = 0.002;\n\nexport interface RouterOptions {\n /**\n * \"price:0.6,speed:0.3,reliability:0.1\" — how quotes inside the tie band\n * (same price, within `tieBand`) are ordered. Price itself always leads.\n */\n weights?: string;\n /**\n * What a quote is worth to the user in USD: its guaranteed output minus what\n * the route charges on top (a native bridge fee, the source gas). Return null\n * when it cannot be priced — the auction then compares guaranteed outputs.\n * `usdValue(prices)` builds one from a symbol → USD price map.\n */\n valueUsd?: (quote: Quote, params: QuoteParams) => number | null;\n /** Fraction of the best net value inside which quotes tie on price (default 0.002). */\n tieBand?: number;\n}\n\n/** A validated quote with its place in the auction. */\nexport interface RankedQuote extends Quote {\n /** Net USD value (`valueUsd`), or null when the auction compared raw outputs. */\n netValueUsd: number | null;\n /** This quote's value against the best one, 0..1 (1 = the best price). */\n relativeValue: number;\n /** \"best\": the pick (best price; faster/more reliable among equal prices). \"fastest\": the lowest ETA. */\n tags: (\"best\" | \"fastest\")[];\n}\n\n/** Every validated route for one request, ranked by value. */\nexport interface RouteSet {\n /** Every validated route, best first. */\n quotes: RankedQuote[];\n /** Most delivered value (net of fees when priced) within the tie band. */\n best: RankedQuote;\n /** Lowest ETA. */\n fastest: RankedQuote;\n /** Providers still quoting (only non-empty in progressive `onUpdate` snapshots). */\n pending: string[];\n /** Why the venues that sat out did (same as `lastRejections`). */\n rejections: string[];\n}\n// LI.FI's /quote routinely takes ~3.5s (it builds executable calldata) — 4s\n// raced it out of every round. All providers still run in parallel.\nconst QUOTE_TIMEOUT_MS = 8000;\n/** Pinned single-venue (execution) budget — see the comment at the call site. */\nconst SOLO_QUOTE_TIMEOUT_MS = 25_000;\n\nfunction parseWeights(spec?: string): ScoreWeights {\n if (!spec) return DEFAULT_WEIGHTS;\n const w = {...DEFAULT_WEIGHTS};\n for (const part of spec.split(\",\")) {\n const [k, v] = part.split(\":\");\n const n = Number(v);\n if ((k === \"price\" || k === \"speed\" || k === \"reliability\") && Number.isFinite(n)) {\n w[k] = Math.max(0, n);\n }\n }\n return w;\n}\n\nasync function withTimeout<T>(p: Promise<T>, ms: number): Promise<T | null> {\n return new Promise((resolve, reject) => {\n // Timeouts and failures must REJECT: the old `() => resolve(null)` arm\n // swallowed every provider error — including forwarded venue refusals —\n // into a null indistinguishable from \"no route\", which made execution\n // failures undiagnosable from the surface (the '(c2)' hunt of 2026-08-31).\n const timer = setTimeout(() => reject(new Error(`quote timed out after ${ms}ms`)), ms);\n p.then(resolve, reject).finally(() => clearTimeout(timer));\n });\n}\n\n/**\n * Routes a cross-chain delivery over pluggable providers: queries all that\n * support the route in parallel (with a timeout so a slow provider can't block),\n * normalizes their quotes, scores by configurable weights, and returns the best\n * tagged with `routeScore`. A failed/slow provider simply doesn't participate.\n */\nexport class CrossChainRouter {\n private readonly providers: CrossChainProvider[];\n /** Why the venues of the LAST getBestQuote call sat out — for error surfaces. */\n private _lastRejections: string[] = [];\n /** Why providers sat out the last auction, one line each. */\n get lastRejections(): readonly string[] {\n return this._lastRejections;\n }\n private readonly weights: ScoreWeights;\n private readonly valueUsd: RouterOptions[\"valueUsd\"];\n private readonly tieBand: number;\n private readonly options: RouterOptions;\n\n constructor(providers: CrossChainProvider[], options?: string | RouterOptions) {\n const opts: RouterOptions = typeof options === \"string\" ? {weights: options} : {...(options ?? {})};\n if (opts.tieBand !== undefined && !(Number.isFinite(opts.tieBand) && opts.tieBand >= 0 && opts.tieBand < 1)) {\n throw new Error(\"cross-chain router: tieBand must be a fraction in [0, 1)\");\n }\n const names = new Set<string>();\n for (const provider of providers) {\n if (typeof provider.name !== \"string\" || !/^[A-Za-z0-9_-]+$/.test(provider.name) || names.has(provider.name)) {\n throw new Error(`cross-chain router: invalid or duplicate provider name '${String(provider.name)}'`);\n }\n names.add(provider.name);\n }\n // Snapshot the dispatch surface. A caller mutating provider.name or method\n // properties after construction must not redirect quote→build/status.\n this.providers = providers.map((provider) => ({\n name: provider.name,\n enabled: provider.enabled.bind(provider),\n supportsRoute: provider.supportsRoute.bind(provider),\n getQuote: provider.getQuote.bind(provider),\n buildDeposit: provider.buildDeposit.bind(provider),\n trackIntent: provider.trackIntent.bind(provider),\n ...(provider.preflightDeposit ? {preflightDeposit: provider.preflightDeposit.bind(provider)} : {}),\n ...(provider.notifyDeposit ? {notifyDeposit: provider.notifyDeposit.bind(provider)} : {}),\n ...(provider.registerSignedOrder ? {registerSignedOrder: provider.registerSignedOrder.bind(provider)} : {}),\n // Two-phase routes (CCTP): the destination claim. Missing here, the engine\n // and the server both reported \"provider 'cctp' has no claim step\" (2026-10-13).\n ...(provider.buildClaim ? {buildClaim: provider.buildClaim.bind(provider)} : {}),\n ...(provider.close ? {close: provider.close.bind(provider)} : {}),\n ...(provider.supportsChain ? {supportsChain: provider.supportsChain.bind(provider)} : {}),\n ...(provider.supportsChainPair ? {supportsChainPair: provider.supportsChainPair.bind(provider)} : {}),\n ...(provider.describeActivity ? {describeActivity: provider.describeActivity.bind(provider)} : {}),\n ...(provider.supportsSameChain !== undefined ? {supportsSameChain: provider.supportsSameChain} : {}),\n }));\n this.weights = parseWeights(opts.weights);\n this.valueUsd = opts.valueUsd;\n this.tieBand = opts.tieBand ?? DEFAULT_TIE_BAND;\n this.options = opts;\n }\n\n /** The same auction restricted to one provider — to execute the route a user picked. */\n only(name: string): CrossChainRouter {\n const provider = this.getProvider(name);\n if (!provider) throw new Error(`cross-chain router: provider '${name}' is not registered`);\n return new CrossChainRouter([provider], this.options);\n }\n\n /**\n * Activity recognition across the installed providers: the first one that\n * claims the transaction describes it. Null when none does (or none is held).\n */\n describeActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null {\n for (const provider of this.providers) {\n const item = provider.describeActivity?.(tx, ctx);\n if (item) return item;\n }\n return null;\n }\n\n /** A provider by name. */\n getProvider(name: string): CrossChainProvider | undefined {\n return this.providers.find((p) => p.name === name);\n }\n\n /** True when a provider of that name is installed. */\n hasProvider(name: string): boolean {\n return this.providers.some((p) => p.name === name);\n }\n\n /** True when that provider is installed and enabled. */\n hasEnabledProvider(name: string): boolean {\n return this.providers.some((p) => {\n try { return p.name === name && p.enabled(); } catch { return false; }\n });\n }\n\n /** True when any enabled provider declares the chain. */\n supportsChain(caip2: string): boolean {\n return this.providers.some((p) => {\n try { return p.enabled() && !!p.supportsChain?.(caip2); } catch { return false; }\n });\n }\n\n /** True when any enabled provider declares the chain pair. */\n supportsChainPair(fromChain: string, toChain: string): boolean {\n return this.providers.some((p) => {\n try {\n return p.enabled() && (p.supportsChainPair?.(fromChain, toChain) ?? false);\n } catch { return false; }\n });\n }\n\n /**\n * The auction's pick: the best price (net of native fees and gas when\n * `valueUsd` is set; faster, then more reliable, among equal prices), or\n * the fastest route with `prefer: \"fastest\"`.\n */\n async getBestQuote(\n params: QuoteParams,\n options?: {requireImmediateExecution?: boolean; prefer?: \"best\" | \"fastest\"},\n ): Promise<RoutedQuote | null> {\n const set = await this.getQuotes(params, options);\n if (!set) return null;\n const pick = options?.prefer === \"fastest\" ? set.fastest : set.best;\n const {netValueUsd: _n, relativeValue: _r, tags: _t, ...quote} = pick;\n return {\n ...quote,\n routeScore: {\n score: pick.relativeValue,\n weights: this.weights,\n candidates: set.quotes.map((q) => ({\n provider: q.provider,\n sourceAmount: q.sourceAmount,\n etaSeconds: q.etaSeconds,\n score: q.relativeValue,\n minDestAmount: q.minDestAmount,\n netValueUsd: q.netValueUsd,\n })),\n },\n };\n }\n\n /**\n * Every validated route, ranked — for a route picker that shows the best and\n * the fastest and lets the user choose. Providers run in parallel; with\n * `onUpdate` each one that answers re-publishes the ranked set at once (the\n * first price shows in the time of the fastest venue, not the slowest).\n */\n async getQuotes(\n params: QuoteParams,\n options?: {requireImmediateExecution?: boolean; onUpdate?: (set: RouteSet) => void},\n ): Promise<RouteSet | null> {\n // A public engine caller — or one provider running in the same auction —\n // must not be able to rewrite another provider's request while async quote\n // work is in flight. QuoteParams is intentionally flat, so a frozen copy is\n // a complete execution snapshot.\n const rejections: string[] = [];\n this._lastRejections = rejections;\n const snapshot = Object.freeze({...params});\n const from = Object.freeze({chain: snapshot.sourceChain, token: snapshot.sourceToken});\n const to = Object.freeze({chain: snapshot.destChain, token: snapshot.destToken});\n // Only enabled (validated) providers that can serve the route participate.\n let eligible = this.providers.filter((p) => {\n try { return p.enabled() && p.supportsRoute(from, to); } catch { return false; }\n });\n if (snapshot.sourceChain === snapshot.destChain) {\n eligible = eligible.filter((p) => p.supportsSameChain === true);\n }\n if (eligible.length === 0) return null;\n\n // The short timeout protects the AUCTION — one slow venue must not hold\n // the others hostage. With a single eligible provider (a pinned execution\n // re-quote) there is nobody to protect, and a live deposit-creating quote\n // legitimately takes longer than a dry one (serverless hop + upstream\n // retries) — starving it surfaced as a terminal \"no route\" on execution.\n const timeoutMs = eligible.length === 1 ? SOLO_QUOTE_TIMEOUT_MS : QUOTE_TIMEOUT_MS;\n // Each venue names its own refusal (per call, so concurrent quotes cannot race).\n const refusals: (string | undefined)[] = [];\n const accepted: Quote[] = [];\n const pending = new Set(eligible.map((p) => p.name));\n const publish = () => {\n if (!options?.onUpdate || accepted.length === 0) return;\n const set = this.rank(accepted, snapshot, [...pending], rejections);\n try { options.onUpdate(set); } catch { /* an observer must not break the auction */ }\n };\n await Promise.all(eligible.map(async (p, i) => {\n const controller = new AbortController();\n const own = Object.freeze({...snapshot, onRefusal: (reason: string) => {\n refusals[i] ??= reason.startsWith(`${p.name}:`) ? reason : `${p.name}: ${reason}`;\n try { snapshot.onRefusal?.(reason); } catch { /* diagnostics only */ }\n }});\n let q: Quote | null = null;\n try {\n q = await withTimeout(p.getQuote(own, controller.signal), timeoutMs);\n if (!q) {\n // A provider RETURNING null (vs throwing) was the one silent path\n // left — record it so the surfaced no-route error names the venue.\n rejections.push(refusals[i] ?? `${p.name}: returned no quote`);\n } else if (!validQuote(q, snapshot, p.name)) {\n const reason = `${p.name}: rejected by validation (minDest=${q.minDestAmount} vs floor=${snapshot.minAmountOut ?? \"-\"}, validUntil=${q.validUntilMs}, now=${Date.now()})`;\n rejections.push(reason);\n console.warn(`swap2-router: ${reason}`);\n q = null;\n } else if (options?.requireImmediateExecution && !(q.depositAddress.length > 0 || q.txRequest !== undefined)) {\n // Legacy deposit-address screens cannot execute order-based quotes that\n // require a separate build/sign/register lifecycle.\n q = null;\n }\n } catch (err) {\n // A failed venue sitting out silently is indistinguishable from \"no\n // route\" — record why (surfaced through lastRejections) and say it in\n // the console (same posture as onModuleSkipped).\n const reason = `${p.name}: ${err instanceof Error ? err.message.slice(0, 160) : String(err).slice(0, 160)}`;\n rejections.push(reason);\n console.warn(`swap2-router: quote failed — ${reason}`);\n q = null;\n } finally {\n controller.abort();\n }\n pending.delete(p.name);\n // Provider identity is an execution security boundary. Never trust the\n // provider field returned by an untrusted adapter; bind it to its slot.\n if (q) accepted.push({...q, provider: p.name});\n publish();\n }));\n if (accepted.length === 0) return null;\n return this.rank(accepted, snapshot, [], rejections);\n }\n\n /** Status from the named provider; `failed` when it is not installed. */\n async trackIntent(providerName: string, ref: IntentRef): Promise<IntentStatus> {\n const provider = this.getProvider(providerName);\n if (!provider) return {state: \"failed\"};\n return provider.trackIntent(ref);\n }\n\n /** Forward a source-broadcast notification to the intent's provider. */\n async notifyDeposit(providerName: string, intentId: string, txHash: string): Promise<void> {\n await this.getProvider(providerName)?.notifyDeposit?.(intentId, txHash);\n }\n\n /** Release every provider's resources. */\n close(): void {\n for (const provider of this.providers) provider.close?.();\n }\n\n /**\n * Price first: each quote's value is its net USD (`valueUsd`) — or, when any\n * quote cannot be priced, its guaranteed output — against the best one.\n * Quotes within `tieBand` of the best are the same price; among those the\n * weights order speed and reliability. A $0.01 edge no longer beats a route\n * that lands in seconds instead of twenty minutes, and a route that charges\n * a native bridge fee on top no longer wins on its gross output.\n */\n private rank(quotes: Quote[], params: QuoteParams, pending: string[], rejections: string[]): RouteSet {\n const usd = this.valueUsd ? quotes.map((q) => {\n try {\n const v = this.valueUsd!(q, params);\n return typeof v === \"number\" && Number.isFinite(v) ? v : null;\n } catch { return null; }\n }) : quotes.map(() => null);\n const priced = usd.every((v) => v !== null);\n let relative: number[];\n if (priced) {\n const values = usd as number[];\n const max = Math.max(...values);\n relative = values.map((v) => (max > 0 ? Math.max(0, v) / max : v >= max ? 1 : 0));\n } else {\n const outs = quotes.map((q) => BigInt(q.minDestAmount));\n const max = outs.reduce((a, b) => (b > a ? b : a));\n relative = outs.map((o) => fraction(o, max));\n }\n const ranked: RankedQuote[] = quotes.map((q, i) => ({\n ...q,\n netValueUsd: priced ? usd[i]! : null,\n relativeValue: relative[i]!,\n tags: [],\n }));\n\n const tier = ranked.filter((q) => q.relativeValue >= 1 - this.tieBand - 1e-12);\n const etas = tier.map((q) => q.etaSeconds);\n const minEta = Math.min(...etas);\n const maxEta = Math.max(...etas);\n const w = this.weights;\n const tieScore = (q: RankedQuote) =>\n w.price * q.relativeValue +\n w.speed * (maxEta === minEta ? 1 : (maxEta - q.etaSeconds) / (maxEta - minEta)) +\n w.reliability * q.reliability;\n const best = tier.reduce((a, b) => (tieScore(b) > tieScore(a) ? b : a));\n const fastest = ranked.reduce((a, b) =>\n b.etaSeconds < a.etaSeconds || (b.etaSeconds === a.etaSeconds && b.relativeValue > a.relativeValue) ? b : a);\n best.tags.push(\"best\");\n fastest.tags.push(\"fastest\");\n\n // The list reads top-down by value; the pick leads its price tier.\n ranked.sort((a, b) => (a === best ? -1 : b === best ? 1 : b.relativeValue - a.relativeValue || a.etaSeconds - b.etaSeconds));\n return {quotes: ranked, best, fastest, pending, rejections: [...rejections]};\n }\n}\n\n/** Ratio of two non-negative bigints as a 0..1 number. */\nfunction fraction(num: bigint, den: bigint): number {\n if (den === 0n) return 1;\n // scale to keep precision, then to float\n return Number((num * 10_000n) / den) / 10_000;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { CrossChainProvider, Quote, QuoteParams, RoutedQuote, IntentRef, IntentStatus } from '@anywalletkit/protocol-swap2-core/types';
|
|
2
|
+
import { ChainTx, DescribeActivityContext, ActivityItem } from '@anywalletkit/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The auction engine of the optional aggregator: routes a cross-chain delivery
|
|
6
|
+
* over whichever providers the caller installed and passed in. Protocol
|
|
7
|
+
* modules never need it — each one runs standalone on @anywalletkit/protocol-swap2-core.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface RouterOptions {
|
|
11
|
+
/**
|
|
12
|
+
* "price:0.6,speed:0.3,reliability:0.1" — how quotes inside the tie band
|
|
13
|
+
* (same price, within `tieBand`) are ordered. Price itself always leads.
|
|
14
|
+
*/
|
|
15
|
+
weights?: string;
|
|
16
|
+
/**
|
|
17
|
+
* What a quote is worth to the user in USD: its guaranteed output minus what
|
|
18
|
+
* the route charges on top (a native bridge fee, the source gas). Return null
|
|
19
|
+
* when it cannot be priced — the auction then compares guaranteed outputs.
|
|
20
|
+
* `usdValue(prices)` builds one from a symbol → USD price map.
|
|
21
|
+
*/
|
|
22
|
+
valueUsd?: (quote: Quote, params: QuoteParams) => number | null;
|
|
23
|
+
/** Fraction of the best net value inside which quotes tie on price (default 0.002). */
|
|
24
|
+
tieBand?: number;
|
|
25
|
+
}
|
|
26
|
+
/** A validated quote with its place in the auction. */
|
|
27
|
+
interface RankedQuote extends Quote {
|
|
28
|
+
/** Net USD value (`valueUsd`), or null when the auction compared raw outputs. */
|
|
29
|
+
netValueUsd: number | null;
|
|
30
|
+
/** This quote's value against the best one, 0..1 (1 = the best price). */
|
|
31
|
+
relativeValue: number;
|
|
32
|
+
/** "best": the pick (best price; faster/more reliable among equal prices). "fastest": the lowest ETA. */
|
|
33
|
+
tags: ("best" | "fastest")[];
|
|
34
|
+
}
|
|
35
|
+
/** Every validated route for one request, ranked by value. */
|
|
36
|
+
interface RouteSet {
|
|
37
|
+
/** Every validated route, best first. */
|
|
38
|
+
quotes: RankedQuote[];
|
|
39
|
+
/** Most delivered value (net of fees when priced) within the tie band. */
|
|
40
|
+
best: RankedQuote;
|
|
41
|
+
/** Lowest ETA. */
|
|
42
|
+
fastest: RankedQuote;
|
|
43
|
+
/** Providers still quoting (only non-empty in progressive `onUpdate` snapshots). */
|
|
44
|
+
pending: string[];
|
|
45
|
+
/** Why the venues that sat out did (same as `lastRejections`). */
|
|
46
|
+
rejections: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Routes a cross-chain delivery over pluggable providers: queries all that
|
|
50
|
+
* support the route in parallel (with a timeout so a slow provider can't block),
|
|
51
|
+
* normalizes their quotes, scores by configurable weights, and returns the best
|
|
52
|
+
* tagged with `routeScore`. A failed/slow provider simply doesn't participate.
|
|
53
|
+
*/
|
|
54
|
+
declare class CrossChainRouter {
|
|
55
|
+
private readonly providers;
|
|
56
|
+
/** Why the venues of the LAST getBestQuote call sat out — for error surfaces. */
|
|
57
|
+
private _lastRejections;
|
|
58
|
+
/** Why providers sat out the last auction, one line each. */
|
|
59
|
+
get lastRejections(): readonly string[];
|
|
60
|
+
private readonly weights;
|
|
61
|
+
private readonly valueUsd;
|
|
62
|
+
private readonly tieBand;
|
|
63
|
+
private readonly options;
|
|
64
|
+
constructor(providers: CrossChainProvider[], options?: string | RouterOptions);
|
|
65
|
+
/** The same auction restricted to one provider — to execute the route a user picked. */
|
|
66
|
+
only(name: string): CrossChainRouter;
|
|
67
|
+
/**
|
|
68
|
+
* Activity recognition across the installed providers: the first one that
|
|
69
|
+
* claims the transaction describes it. Null when none does (or none is held).
|
|
70
|
+
*/
|
|
71
|
+
describeActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;
|
|
72
|
+
/** A provider by name. */
|
|
73
|
+
getProvider(name: string): CrossChainProvider | undefined;
|
|
74
|
+
/** True when a provider of that name is installed. */
|
|
75
|
+
hasProvider(name: string): boolean;
|
|
76
|
+
/** True when that provider is installed and enabled. */
|
|
77
|
+
hasEnabledProvider(name: string): boolean;
|
|
78
|
+
/** True when any enabled provider declares the chain. */
|
|
79
|
+
supportsChain(caip2: string): boolean;
|
|
80
|
+
/** True when any enabled provider declares the chain pair. */
|
|
81
|
+
supportsChainPair(fromChain: string, toChain: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The auction's pick: the best price (net of native fees and gas when
|
|
84
|
+
* `valueUsd` is set; faster, then more reliable, among equal prices), or
|
|
85
|
+
* the fastest route with `prefer: "fastest"`.
|
|
86
|
+
*/
|
|
87
|
+
getBestQuote(params: QuoteParams, options?: {
|
|
88
|
+
requireImmediateExecution?: boolean;
|
|
89
|
+
prefer?: "best" | "fastest";
|
|
90
|
+
}): Promise<RoutedQuote | null>;
|
|
91
|
+
/**
|
|
92
|
+
* Every validated route, ranked — for a route picker that shows the best and
|
|
93
|
+
* the fastest and lets the user choose. Providers run in parallel; with
|
|
94
|
+
* `onUpdate` each one that answers re-publishes the ranked set at once (the
|
|
95
|
+
* first price shows in the time of the fastest venue, not the slowest).
|
|
96
|
+
*/
|
|
97
|
+
getQuotes(params: QuoteParams, options?: {
|
|
98
|
+
requireImmediateExecution?: boolean;
|
|
99
|
+
onUpdate?: (set: RouteSet) => void;
|
|
100
|
+
}): Promise<RouteSet | null>;
|
|
101
|
+
/** Status from the named provider; `failed` when it is not installed. */
|
|
102
|
+
trackIntent(providerName: string, ref: IntentRef): Promise<IntentStatus>;
|
|
103
|
+
/** Forward a source-broadcast notification to the intent's provider. */
|
|
104
|
+
notifyDeposit(providerName: string, intentId: string, txHash: string): Promise<void>;
|
|
105
|
+
/** Release every provider's resources. */
|
|
106
|
+
close(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Price first: each quote's value is its net USD (`valueUsd`) — or, when any
|
|
109
|
+
* quote cannot be priced, its guaranteed output — against the best one.
|
|
110
|
+
* Quotes within `tieBand` of the best are the same price; among those the
|
|
111
|
+
* weights order speed and reliability. A $0.01 edge no longer beats a route
|
|
112
|
+
* that lands in seconds instead of twenty minutes, and a route that charges
|
|
113
|
+
* a native bridge fee on top no longer wins on its gross output.
|
|
114
|
+
*/
|
|
115
|
+
private rank;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { CrossChainRouter, type RankedQuote, type RouteSet, type RouterOptions };
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { CrossChainProvider, Quote, QuoteParams, RoutedQuote, IntentRef, IntentStatus } from '@anywalletkit/protocol-swap2-core/types';
|
|
2
|
+
import { ChainTx, DescribeActivityContext, ActivityItem } from '@anywalletkit/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The auction engine of the optional aggregator: routes a cross-chain delivery
|
|
6
|
+
* over whichever providers the caller installed and passed in. Protocol
|
|
7
|
+
* modules never need it — each one runs standalone on @anywalletkit/protocol-swap2-core.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface RouterOptions {
|
|
11
|
+
/**
|
|
12
|
+
* "price:0.6,speed:0.3,reliability:0.1" — how quotes inside the tie band
|
|
13
|
+
* (same price, within `tieBand`) are ordered. Price itself always leads.
|
|
14
|
+
*/
|
|
15
|
+
weights?: string;
|
|
16
|
+
/**
|
|
17
|
+
* What a quote is worth to the user in USD: its guaranteed output minus what
|
|
18
|
+
* the route charges on top (a native bridge fee, the source gas). Return null
|
|
19
|
+
* when it cannot be priced — the auction then compares guaranteed outputs.
|
|
20
|
+
* `usdValue(prices)` builds one from a symbol → USD price map.
|
|
21
|
+
*/
|
|
22
|
+
valueUsd?: (quote: Quote, params: QuoteParams) => number | null;
|
|
23
|
+
/** Fraction of the best net value inside which quotes tie on price (default 0.002). */
|
|
24
|
+
tieBand?: number;
|
|
25
|
+
}
|
|
26
|
+
/** A validated quote with its place in the auction. */
|
|
27
|
+
interface RankedQuote extends Quote {
|
|
28
|
+
/** Net USD value (`valueUsd`), or null when the auction compared raw outputs. */
|
|
29
|
+
netValueUsd: number | null;
|
|
30
|
+
/** This quote's value against the best one, 0..1 (1 = the best price). */
|
|
31
|
+
relativeValue: number;
|
|
32
|
+
/** "best": the pick (best price; faster/more reliable among equal prices). "fastest": the lowest ETA. */
|
|
33
|
+
tags: ("best" | "fastest")[];
|
|
34
|
+
}
|
|
35
|
+
/** Every validated route for one request, ranked by value. */
|
|
36
|
+
interface RouteSet {
|
|
37
|
+
/** Every validated route, best first. */
|
|
38
|
+
quotes: RankedQuote[];
|
|
39
|
+
/** Most delivered value (net of fees when priced) within the tie band. */
|
|
40
|
+
best: RankedQuote;
|
|
41
|
+
/** Lowest ETA. */
|
|
42
|
+
fastest: RankedQuote;
|
|
43
|
+
/** Providers still quoting (only non-empty in progressive `onUpdate` snapshots). */
|
|
44
|
+
pending: string[];
|
|
45
|
+
/** Why the venues that sat out did (same as `lastRejections`). */
|
|
46
|
+
rejections: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Routes a cross-chain delivery over pluggable providers: queries all that
|
|
50
|
+
* support the route in parallel (with a timeout so a slow provider can't block),
|
|
51
|
+
* normalizes their quotes, scores by configurable weights, and returns the best
|
|
52
|
+
* tagged with `routeScore`. A failed/slow provider simply doesn't participate.
|
|
53
|
+
*/
|
|
54
|
+
declare class CrossChainRouter {
|
|
55
|
+
private readonly providers;
|
|
56
|
+
/** Why the venues of the LAST getBestQuote call sat out — for error surfaces. */
|
|
57
|
+
private _lastRejections;
|
|
58
|
+
/** Why providers sat out the last auction, one line each. */
|
|
59
|
+
get lastRejections(): readonly string[];
|
|
60
|
+
private readonly weights;
|
|
61
|
+
private readonly valueUsd;
|
|
62
|
+
private readonly tieBand;
|
|
63
|
+
private readonly options;
|
|
64
|
+
constructor(providers: CrossChainProvider[], options?: string | RouterOptions);
|
|
65
|
+
/** The same auction restricted to one provider — to execute the route a user picked. */
|
|
66
|
+
only(name: string): CrossChainRouter;
|
|
67
|
+
/**
|
|
68
|
+
* Activity recognition across the installed providers: the first one that
|
|
69
|
+
* claims the transaction describes it. Null when none does (or none is held).
|
|
70
|
+
*/
|
|
71
|
+
describeActivity(tx: ChainTx, ctx: DescribeActivityContext): ActivityItem | null;
|
|
72
|
+
/** A provider by name. */
|
|
73
|
+
getProvider(name: string): CrossChainProvider | undefined;
|
|
74
|
+
/** True when a provider of that name is installed. */
|
|
75
|
+
hasProvider(name: string): boolean;
|
|
76
|
+
/** True when that provider is installed and enabled. */
|
|
77
|
+
hasEnabledProvider(name: string): boolean;
|
|
78
|
+
/** True when any enabled provider declares the chain. */
|
|
79
|
+
supportsChain(caip2: string): boolean;
|
|
80
|
+
/** True when any enabled provider declares the chain pair. */
|
|
81
|
+
supportsChainPair(fromChain: string, toChain: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The auction's pick: the best price (net of native fees and gas when
|
|
84
|
+
* `valueUsd` is set; faster, then more reliable, among equal prices), or
|
|
85
|
+
* the fastest route with `prefer: "fastest"`.
|
|
86
|
+
*/
|
|
87
|
+
getBestQuote(params: QuoteParams, options?: {
|
|
88
|
+
requireImmediateExecution?: boolean;
|
|
89
|
+
prefer?: "best" | "fastest";
|
|
90
|
+
}): Promise<RoutedQuote | null>;
|
|
91
|
+
/**
|
|
92
|
+
* Every validated route, ranked — for a route picker that shows the best and
|
|
93
|
+
* the fastest and lets the user choose. Providers run in parallel; with
|
|
94
|
+
* `onUpdate` each one that answers re-publishes the ranked set at once (the
|
|
95
|
+
* first price shows in the time of the fastest venue, not the slowest).
|
|
96
|
+
*/
|
|
97
|
+
getQuotes(params: QuoteParams, options?: {
|
|
98
|
+
requireImmediateExecution?: boolean;
|
|
99
|
+
onUpdate?: (set: RouteSet) => void;
|
|
100
|
+
}): Promise<RouteSet | null>;
|
|
101
|
+
/** Status from the named provider; `failed` when it is not installed. */
|
|
102
|
+
trackIntent(providerName: string, ref: IntentRef): Promise<IntentStatus>;
|
|
103
|
+
/** Forward a source-broadcast notification to the intent's provider. */
|
|
104
|
+
notifyDeposit(providerName: string, intentId: string, txHash: string): Promise<void>;
|
|
105
|
+
/** Release every provider's resources. */
|
|
106
|
+
close(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Price first: each quote's value is its net USD (`valueUsd`) — or, when any
|
|
109
|
+
* quote cannot be priced, its guaranteed output — against the best one.
|
|
110
|
+
* Quotes within `tieBand` of the best are the same price; among those the
|
|
111
|
+
* weights order speed and reliability. A $0.01 edge no longer beats a route
|
|
112
|
+
* that lands in seconds instead of twenty minutes, and a route that charges
|
|
113
|
+
* a native bridge fee on top no longer wins on its gross output.
|
|
114
|
+
*/
|
|
115
|
+
private rank;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { CrossChainRouter, type RankedQuote, type RouteSet, type RouterOptions };
|
package/dist/router.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|