@hyperbridge/simplex 0.16.6 → 0.16.8
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/dist/bin/simplex.js +729 -250
- package/dist/bin/simplex.js.map +1 -1
- package/dist/index.cjs +380 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -56
- package/dist/index.d.ts +74 -56
- package/dist/index.js +380 -132
- package/dist/index.js.map +1 -1
- package/dist/sqlite.cjs +1 -1
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.js +1 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/{types-DUDABLPI.d.cts → types-C7rnnKWn.d.cts} +6 -4
- package/dist/{types-DUDABLPI.d.ts → types-C7rnnKWn.d.ts} +6 -4
- package/dist/ui/assets/index-BQu6BMxc.js +106 -0
- package/dist/ui/assets/index-C3T-gubp.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +2 -2
- package/dist/ui/assets/index-CsBo6xg3.js +0 -106
- package/dist/ui/assets/index-PIGVK8XG.css +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2459,6 +2459,7 @@ function closedKey(chain, vault) {
|
|
|
2459
2459
|
// src/config/external-links.json
|
|
2460
2460
|
var external_links_default = {
|
|
2461
2461
|
hyperfxApp: "https://app.hyperfx.finance",
|
|
2462
|
+
alchemyDashboard: "https://dashboard.alchemy.com/",
|
|
2462
2463
|
hyperbridgeExplorers: {
|
|
2463
2464
|
mainnet: "https://nexus.statescan.io",
|
|
2464
2465
|
testnet: "https://gargantua.statescan.io"
|
|
@@ -2478,6 +2479,52 @@ var external_links_default = {
|
|
|
2478
2479
|
};
|
|
2479
2480
|
|
|
2480
2481
|
// src/cli/init/chains.ts
|
|
2482
|
+
var FREE_RPC_URLS = {
|
|
2483
|
+
1: [
|
|
2484
|
+
"https://mainnet.gateway.tenderly.co",
|
|
2485
|
+
"https://rpc.mevblocker.io",
|
|
2486
|
+
"https://ethereum-rpc.publicnode.com",
|
|
2487
|
+
"https://ethereum.public.blockpi.network/v1/rpc/public",
|
|
2488
|
+
"https://eth.blockrazor.xyz",
|
|
2489
|
+
"https://eth.api.pocket.network",
|
|
2490
|
+
"https://ethereum-public.nodies.app",
|
|
2491
|
+
"https://eth-mainnet.public.blastapi.io",
|
|
2492
|
+
"https://public-eth.nownodes.io"
|
|
2493
|
+
],
|
|
2494
|
+
56: [
|
|
2495
|
+
"https://bsc-rpc.publicnode.com",
|
|
2496
|
+
"https://public-bsc-mainnet.fastnode.io",
|
|
2497
|
+
"https://public-bsc.nownodes.io",
|
|
2498
|
+
"https://bsc.blockrazor.xyz",
|
|
2499
|
+
"https://rpc-bsc.48.club",
|
|
2500
|
+
"https://bsc.api.pocket.network"
|
|
2501
|
+
],
|
|
2502
|
+
137: [
|
|
2503
|
+
"https://polygon-bor-rpc.publicnode.com",
|
|
2504
|
+
"https://polygon.drpc.org",
|
|
2505
|
+
"https://polygon.gateway.tenderly.co",
|
|
2506
|
+
"https://rpc.private.mev-x.com/polygon",
|
|
2507
|
+
"https://poly.api.pocket.network"
|
|
2508
|
+
],
|
|
2509
|
+
8453: [
|
|
2510
|
+
"https://base-rpc.publicnode.com",
|
|
2511
|
+
"https://base.gateway.tenderly.co",
|
|
2512
|
+
"https://base.public.blockpi.network/v1/rpc/public",
|
|
2513
|
+
"https://base.rpc.blxrbdn.com",
|
|
2514
|
+
"https://rpc.baseazul.dev",
|
|
2515
|
+
"https://mainnet.base.org",
|
|
2516
|
+
"https://base-public.nodies.app"
|
|
2517
|
+
],
|
|
2518
|
+
42161: [
|
|
2519
|
+
"https://arbitrum-one-rpc.publicnode.com",
|
|
2520
|
+
"https://arbitrum.gateway.tenderly.co",
|
|
2521
|
+
"https://arbitrum.drpc.org",
|
|
2522
|
+
"https://public-arb-mainnet.fastnode.io",
|
|
2523
|
+
"https://arb-one.api.pocket.network",
|
|
2524
|
+
"https://arbitrum-one-public.nodies.app",
|
|
2525
|
+
"https://arb1.arbitrum.io/rpc"
|
|
2526
|
+
]
|
|
2527
|
+
};
|
|
2481
2528
|
var CHAIN_NATIVE_SYMBOLS = {
|
|
2482
2529
|
1: "ETH",
|
|
2483
2530
|
56: "BNB",
|
|
@@ -2505,7 +2552,8 @@ var INIT_CHAINS = [
|
|
|
2505
2552
|
label: "Ethereum",
|
|
2506
2553
|
network: "mainnet",
|
|
2507
2554
|
alchemySubdomain: "eth-mainnet",
|
|
2508
|
-
explorerUrl: external_links_default.chainExplorers.ethereum
|
|
2555
|
+
explorerUrl: external_links_default.chainExplorers.ethereum,
|
|
2556
|
+
defaultRpcUrls: FREE_RPC_URLS[1]
|
|
2509
2557
|
},
|
|
2510
2558
|
{
|
|
2511
2559
|
chainId: 42161,
|
|
@@ -2513,7 +2561,8 @@ var INIT_CHAINS = [
|
|
|
2513
2561
|
label: "Arbitrum",
|
|
2514
2562
|
network: "mainnet",
|
|
2515
2563
|
alchemySubdomain: "arb-mainnet",
|
|
2516
|
-
explorerUrl: external_links_default.chainExplorers.arbitrum
|
|
2564
|
+
explorerUrl: external_links_default.chainExplorers.arbitrum,
|
|
2565
|
+
defaultRpcUrls: FREE_RPC_URLS[42161]
|
|
2517
2566
|
},
|
|
2518
2567
|
{
|
|
2519
2568
|
chainId: 8453,
|
|
@@ -2521,7 +2570,8 @@ var INIT_CHAINS = [
|
|
|
2521
2570
|
label: "Base",
|
|
2522
2571
|
network: "mainnet",
|
|
2523
2572
|
alchemySubdomain: "base-mainnet",
|
|
2524
|
-
explorerUrl: external_links_default.chainExplorers.base
|
|
2573
|
+
explorerUrl: external_links_default.chainExplorers.base,
|
|
2574
|
+
defaultRpcUrls: FREE_RPC_URLS[8453]
|
|
2525
2575
|
},
|
|
2526
2576
|
{
|
|
2527
2577
|
chainId: 137,
|
|
@@ -2529,7 +2579,8 @@ var INIT_CHAINS = [
|
|
|
2529
2579
|
label: "Polygon",
|
|
2530
2580
|
network: "mainnet",
|
|
2531
2581
|
alchemySubdomain: "polygon-mainnet",
|
|
2532
|
-
explorerUrl: external_links_default.chainExplorers.polygon
|
|
2582
|
+
explorerUrl: external_links_default.chainExplorers.polygon,
|
|
2583
|
+
defaultRpcUrls: FREE_RPC_URLS[137]
|
|
2533
2584
|
},
|
|
2534
2585
|
{
|
|
2535
2586
|
chainId: 56,
|
|
@@ -2537,7 +2588,8 @@ var INIT_CHAINS = [
|
|
|
2537
2588
|
label: "BNB Chain",
|
|
2538
2589
|
network: "mainnet",
|
|
2539
2590
|
alchemySubdomain: "bnb-mainnet",
|
|
2540
|
-
explorerUrl: external_links_default.chainExplorers.bnb
|
|
2591
|
+
explorerUrl: external_links_default.chainExplorers.bnb,
|
|
2592
|
+
defaultRpcUrls: FREE_RPC_URLS[56]
|
|
2541
2593
|
},
|
|
2542
2594
|
{
|
|
2543
2595
|
chainId: 11155111,
|
|
@@ -3025,14 +3077,16 @@ var LimitOrderService = class {
|
|
|
3025
3077
|
}
|
|
3026
3078
|
}
|
|
3027
3079
|
/**
|
|
3028
|
-
* The pairs an order can be written against,
|
|
3080
|
+
* The pairs an order can be written against, the smallest payout each token may carry, and
|
|
3081
|
+
* the tokens the orderbook registers on each chain.
|
|
3029
3082
|
*
|
|
3030
3083
|
* The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
|
|
3031
|
-
* operator is offered exactly these and no combination of symbols they could not post.
|
|
3084
|
+
* operator is offered exactly these and no combination of symbols they could not post. The
|
|
3085
|
+
* chains do the same for the source chains an order can accept.
|
|
3032
3086
|
*/
|
|
3033
3087
|
async books() {
|
|
3034
3088
|
const limits = await this.limits();
|
|
3035
|
-
return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes };
|
|
3089
|
+
return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes, chains: limits.chains ?? [] };
|
|
3036
3090
|
}
|
|
3037
3091
|
/**
|
|
3038
3092
|
* Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
|
|
@@ -3114,6 +3168,18 @@ var LimitOrderService = class {
|
|
|
3114
3168
|
);
|
|
3115
3169
|
}
|
|
3116
3170
|
}
|
|
3171
|
+
if (book.base !== book.quote) {
|
|
3172
|
+
const input = normalizeSymbol(request.tokenIn);
|
|
3173
|
+
const lacking = sources.filter((source) => {
|
|
3174
|
+
const listed = limits.chains?.find((chain) => chain.id === source);
|
|
3175
|
+
return listed && !listed.tokens.some((token) => normalizeSymbol(token.symbol) === input);
|
|
3176
|
+
});
|
|
3177
|
+
if (lacking.length > 0) {
|
|
3178
|
+
throw new LimitOrderValidationError(
|
|
3179
|
+
`The orderbook has no ${request.tokenIn} on ${lacking.join(", ")}, so swaps from there cannot pay this order`
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3117
3183
|
if (ttlSecs < limits.serverInfo.minOrderTtlSecs) {
|
|
3118
3184
|
throw new LimitOrderValidationError(
|
|
3119
3185
|
`ttlSecs must be at least the orderbook's minimum of ${limits.serverInfo.minOrderTtlSecs}; got ${ttlSecs}`
|
|
@@ -12984,14 +13050,10 @@ var RATE_LIMIT_RPC_CODES = /* @__PURE__ */ new Set([-32005, -32097, -32016, 429]
|
|
|
12984
13050
|
var RATE_LIMIT_SUSPENSION_MS = 5 * 6e4;
|
|
12985
13051
|
var SCAN_BUDGET = {
|
|
12986
13052
|
timeoutMs: 5e3,
|
|
12987
|
-
retryCount: 1,
|
|
12988
|
-
retryDelayMs: 500,
|
|
12989
13053
|
deadlineMs: 12e3
|
|
12990
13054
|
};
|
|
12991
13055
|
var CONFIRMATION_BUDGET = {
|
|
12992
13056
|
timeoutMs: 1e4,
|
|
12993
|
-
retryCount: 1,
|
|
12994
|
-
retryDelayMs: 500,
|
|
12995
13057
|
deadlineMs: 3e4
|
|
12996
13058
|
};
|
|
12997
13059
|
var MALFORMED_RESPONSE_SUSPENSION_MS = 3e4;
|
|
@@ -13083,8 +13145,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13083
13145
|
chain,
|
|
13084
13146
|
transport: (0, import_viem14.http)(url, {
|
|
13085
13147
|
timeout: budget.timeoutMs,
|
|
13086
|
-
|
|
13087
|
-
|
|
13148
|
+
// No transport-level retry. viem's retry sleeps for the provider's
|
|
13149
|
+
// `Retry-After` verbatim and uncapped, which reaches the socket
|
|
13150
|
+
// before {@link suspensionMsFor} can apply its clamp — and even
|
|
13151
|
+
// without that header, retrying inside the task is what makes a
|
|
13152
|
+
// task's worst case (timeout, backoff, timeout) approach the
|
|
13153
|
+
// deadline that is supposed to bound it. One attempt per endpoint
|
|
13154
|
+
// per call; `retryPromise` above the quorum owns the retry policy.
|
|
13155
|
+
retryCount: 0
|
|
13088
13156
|
})
|
|
13089
13157
|
})
|
|
13090
13158
|
);
|
|
@@ -13098,11 +13166,10 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13098
13166
|
return this.rpcUrls.filter((url) => (_QuorumPublicClient.benchedUntil.get(url) ?? 0) > now);
|
|
13099
13167
|
}
|
|
13100
13168
|
/**
|
|
13101
|
-
* The endpoints this call queries
|
|
13102
|
-
*
|
|
13103
|
-
*
|
|
13104
|
-
*
|
|
13105
|
-
* queried again; there is nobody left to prefer.
|
|
13169
|
+
* The endpoints this call queries: everyone not benched. A benched endpoint is
|
|
13170
|
+
* dropped outright: it answers nothing while throttled, so asking it could only
|
|
13171
|
+
* cost a request. With every endpoint benched, everyone is queried again; there
|
|
13172
|
+
* is nobody left to prefer.
|
|
13106
13173
|
*/
|
|
13107
13174
|
participants() {
|
|
13108
13175
|
const now = Date.now();
|
|
@@ -13110,12 +13177,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13110
13177
|
const active = all.filter(({ idx }) => (_QuorumPublicClient.benchedUntil.get(this.rpcUrls[idx]) ?? 0) <= now);
|
|
13111
13178
|
const allBenched = active.length === 0;
|
|
13112
13179
|
const queried = allBenched ? all : active;
|
|
13113
|
-
return {
|
|
13114
|
-
queried,
|
|
13115
|
-
skipped: all.length - queried.length,
|
|
13116
|
-
threshold: quorumThreshold(queried.length),
|
|
13117
|
-
allBenched
|
|
13118
|
-
};
|
|
13180
|
+
return { queried, skipped: all.length - queried.length, allBenched };
|
|
13119
13181
|
}
|
|
13120
13182
|
/**
|
|
13121
13183
|
* Records a task failure against its endpoint. Only the failures
|
|
@@ -13160,17 +13222,21 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13160
13222
|
* retries would otherwise stall every confirmation poll). Stragglers settle
|
|
13161
13223
|
* out of band and are ignored. `tryDecide` returns undefined to keep
|
|
13162
13224
|
* waiting; once every task has settled, `finalize` must decide (it throws
|
|
13163
|
-
* the QuorumError when the full response set still has no quorum).
|
|
13164
|
-
*
|
|
13165
|
-
*
|
|
13225
|
+
* the QuorumError when the full response set still has no quorum).
|
|
13226
|
+
*
|
|
13227
|
+
* Both are handed the bar: the BFT threshold over the endpoints that have not
|
|
13228
|
+
* failed, answered or still out. A failure lowers it rather than counting
|
|
13229
|
+
* against it. Counting the pending endpoints keeps an early decision to a bar
|
|
13230
|
+
* no lower than the full response set would get, so a vote it did not wait for
|
|
13231
|
+
* could never have reversed it.
|
|
13166
13232
|
*
|
|
13167
13233
|
* `deadlineMs` bounds the other direction: when the bar is NOT reachable
|
|
13168
13234
|
* from the fast responders, the call used to wait out every straggler, and
|
|
13169
13235
|
* the caller's own retry loop multiplied that into minutes of a chain not
|
|
13170
13236
|
* being scanned at all, silently. At the deadline the outstanding endpoints
|
|
13171
13237
|
* are treated as failures — named in the QuorumError, so the operator sees
|
|
13172
|
-
* which ones held the call up — and `finalize` decides
|
|
13173
|
-
*
|
|
13238
|
+
* which ones held the call up — and `finalize` decides over the endpoints
|
|
13239
|
+
* that answered.
|
|
13174
13240
|
*/
|
|
13175
13241
|
settleUntilQuorum(tasks, tryDecide, finalize) {
|
|
13176
13242
|
return new Promise((resolve, reject) => {
|
|
@@ -13188,13 +13254,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13188
13254
|
const evaluate = () => {
|
|
13189
13255
|
if (done) return;
|
|
13190
13256
|
try {
|
|
13191
|
-
const
|
|
13257
|
+
const threshold = quorumThreshold(tasks.length - failures.length);
|
|
13258
|
+
const early = tryDecide(fulfilled, threshold);
|
|
13192
13259
|
if (early !== void 0) {
|
|
13193
13260
|
finish(() => resolve(early));
|
|
13194
13261
|
return;
|
|
13195
13262
|
}
|
|
13196
13263
|
if (outstanding === 0) {
|
|
13197
|
-
const value = finalize(fulfilled, failures);
|
|
13264
|
+
const value = finalize(fulfilled, failures, threshold);
|
|
13198
13265
|
finish(() => resolve(value));
|
|
13199
13266
|
}
|
|
13200
13267
|
} catch (error) {
|
|
@@ -13212,7 +13279,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13212
13279
|
error: new Error(`no answer within the ${this.deadlineMs}ms quorum deadline`)
|
|
13213
13280
|
}));
|
|
13214
13281
|
try {
|
|
13215
|
-
const value = finalize(fulfilled, [...failures, ...stragglers]);
|
|
13282
|
+
const value = finalize(fulfilled, [...failures, ...stragglers], quorumThreshold(fulfilled.length));
|
|
13216
13283
|
finish(() => resolve(value));
|
|
13217
13284
|
} catch (error) {
|
|
13218
13285
|
finish(() => reject(error));
|
|
@@ -13239,22 +13306,22 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13239
13306
|
});
|
|
13240
13307
|
}
|
|
13241
13308
|
/**
|
|
13242
|
-
* Highest block head backed by a
|
|
13243
|
-
*
|
|
13309
|
+
* Highest block head backed by a quorum of the endpoints that answer. Throws
|
|
13310
|
+
* {@link QuorumError} when none do.
|
|
13244
13311
|
*/
|
|
13245
13312
|
async getBlockNumber() {
|
|
13246
|
-
const { queried, skipped,
|
|
13313
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13247
13314
|
return this.settleUntilQuorum(
|
|
13248
13315
|
queried.map(({ idx, client }) => ({ idx, task: client.getBlockNumber() })),
|
|
13249
13316
|
// Early exit as soon as a quorum is satisfiable. Late voters could
|
|
13250
13317
|
// only raise the reported head; the earlier (lower) head is
|
|
13251
13318
|
// conservative for every consumer (fewer confirmations counted,
|
|
13252
13319
|
// smaller scan windows).
|
|
13253
|
-
(fulfilled) => this.quorumHead(
|
|
13320
|
+
(fulfilled, threshold) => this.quorumHead(
|
|
13254
13321
|
fulfilled.map((f) => f.value),
|
|
13255
13322
|
threshold
|
|
13256
13323
|
) ?? void 0,
|
|
13257
|
-
(fulfilled, failures) => {
|
|
13324
|
+
(fulfilled, failures, threshold) => {
|
|
13258
13325
|
const head = this.quorumHead(
|
|
13259
13326
|
fulfilled.map((f) => f.value),
|
|
13260
13327
|
threshold
|
|
@@ -13295,7 +13362,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13295
13362
|
}
|
|
13296
13363
|
return views;
|
|
13297
13364
|
};
|
|
13298
|
-
const { queried, skipped,
|
|
13365
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13299
13366
|
return this.settleUntilQuorum(
|
|
13300
13367
|
queried.map(({ idx, client }) => ({
|
|
13301
13368
|
idx,
|
|
@@ -13314,8 +13381,8 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13314
13381
|
// Early exit only on a POSITIVE quorum: an agreeing inclusion group can
|
|
13315
13382
|
// only grow, so the first satisfiable quorum is final. Not-found votes
|
|
13316
13383
|
// never decide early — "not confirmed" needs the full response set.
|
|
13317
|
-
(fulfilled) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
13318
|
-
(fulfilled, failures) => {
|
|
13384
|
+
(fulfilled, threshold) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
13385
|
+
(fulfilled, failures, threshold) => {
|
|
13319
13386
|
const confirmations = aggregateConfirmations(toViews(fulfilled), threshold);
|
|
13320
13387
|
if (confirmations === null) {
|
|
13321
13388
|
throw new QuorumError(
|
|
@@ -13344,7 +13411,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13344
13411
|
}
|
|
13345
13412
|
return groups;
|
|
13346
13413
|
};
|
|
13347
|
-
const { queried, skipped,
|
|
13414
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13348
13415
|
return this.settleUntilQuorum(
|
|
13349
13416
|
queried.map(({ idx, client }) => ({
|
|
13350
13417
|
idx,
|
|
@@ -13356,13 +13423,13 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13356
13423
|
// two disjoint groups can't both hold one), so the first satisfiable
|
|
13357
13424
|
// agreement is final — no need to wait out stragglers that could
|
|
13358
13425
|
// only join or lose.
|
|
13359
|
-
(fulfilled) => {
|
|
13426
|
+
(fulfilled, threshold) => {
|
|
13360
13427
|
for (const group of groupOf(fulfilled).values()) {
|
|
13361
13428
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
13362
13429
|
}
|
|
13363
13430
|
return void 0;
|
|
13364
13431
|
},
|
|
13365
|
-
(fulfilled, failures) => {
|
|
13432
|
+
(fulfilled, failures, threshold) => {
|
|
13366
13433
|
const groups = groupOf(fulfilled);
|
|
13367
13434
|
for (const group of groups.values()) {
|
|
13368
13435
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
@@ -18575,20 +18642,32 @@ var ContractInteractionService = class {
|
|
|
18575
18642
|
} else {
|
|
18576
18643
|
this.logger.warn({ reason: pmResult.reason }, "No paymaster for bid UserOp; relying on EntryPoint deposit");
|
|
18577
18644
|
}
|
|
18578
|
-
const
|
|
18579
|
-
order,
|
|
18580
|
-
fillOptions,
|
|
18645
|
+
const bidGas = {
|
|
18581
18646
|
solverAccount: solverAccountAddress,
|
|
18582
|
-
solverSigner: sdkSigningAccount(this.signer),
|
|
18583
18647
|
nonce,
|
|
18584
18648
|
entryPointAddress,
|
|
18585
18649
|
callGasLimit,
|
|
18586
18650
|
verificationGasLimit: cachedEstimate.verificationGasLimit,
|
|
18587
|
-
preVerificationGas: cachedEstimate.preVerificationGas,
|
|
18588
18651
|
maxFeePerGas: cachedEstimate.maxFeePerGas,
|
|
18589
18652
|
maxPriorityFeePerGas: cachedEstimate.maxPriorityFeePerGas,
|
|
18590
18653
|
callData,
|
|
18591
18654
|
paymasterAndData
|
|
18655
|
+
};
|
|
18656
|
+
let preVerificationGas = cachedEstimate.preVerificationGas;
|
|
18657
|
+
try {
|
|
18658
|
+
preVerificationGas = await sdkHelper.estimateBidPreVerificationGas(bidGas);
|
|
18659
|
+
} catch (err) {
|
|
18660
|
+
this.logger.warn(
|
|
18661
|
+
{ orderId: order.id, err, preVerificationGas: preVerificationGas.toString() },
|
|
18662
|
+
"Could not estimate the bid's preVerificationGas; using the fill estimate's"
|
|
18663
|
+
);
|
|
18664
|
+
}
|
|
18665
|
+
const userOp = await sdkHelper.prepareSubmitBid({
|
|
18666
|
+
...bidGas,
|
|
18667
|
+
order,
|
|
18668
|
+
fillOptions,
|
|
18669
|
+
solverSigner: sdkSigningAccount(this.signer),
|
|
18670
|
+
preVerificationGas
|
|
18592
18671
|
});
|
|
18593
18672
|
const encodedUserOp = (0, import_sdk7.encodeUserOpScale)(userOp);
|
|
18594
18673
|
this.logger.info(
|
|
@@ -18597,6 +18676,7 @@ var ContractInteractionService = class {
|
|
|
18597
18676
|
solverAccount: solverAccountAddress,
|
|
18598
18677
|
nonce: nonce.toString(),
|
|
18599
18678
|
callGasLimit: callGasLimit.toString(),
|
|
18679
|
+
preVerificationGas: preVerificationGas.toString(),
|
|
18600
18680
|
maxFeePerGas: cachedEstimate.maxFeePerGas.toString()
|
|
18601
18681
|
},
|
|
18602
18682
|
"Prepared bid UserOp"
|
|
@@ -21275,8 +21355,15 @@ var IntentFiller = class {
|
|
|
21275
21355
|
const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
|
|
21276
21356
|
this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
|
|
21277
21357
|
if (validStrategies.length === 0) {
|
|
21278
|
-
|
|
21279
|
-
this.
|
|
21358
|
+
const priced = eligibleStrategies.some((s) => s !== null);
|
|
21359
|
+
this.logger.warn(
|
|
21360
|
+
{ orderId: order.id },
|
|
21361
|
+
priced ? "No profitable strategy found for order" : "No strategy can fill this order"
|
|
21362
|
+
);
|
|
21363
|
+
this.monitor.emit("orderSkipped", {
|
|
21364
|
+
orderId: order.id,
|
|
21365
|
+
reason: priced ? "No profitable strategy" : "No strategy can fill this order"
|
|
21366
|
+
});
|
|
21280
21367
|
return null;
|
|
21281
21368
|
}
|
|
21282
21369
|
this.logger.info(
|
|
@@ -21698,10 +21785,6 @@ var import_decimal6 = require("decimal.js");
|
|
|
21698
21785
|
function limitOrderLegs(order) {
|
|
21699
21786
|
return order.side === "BID" ? { input: order.base, output: order.quote } : { input: order.quote, output: order.base };
|
|
21700
21787
|
}
|
|
21701
|
-
function availableOn(order) {
|
|
21702
|
-
const available = BigInt(order.remaining) - BigInt(order.reserved);
|
|
21703
|
-
return available > 0n ? available : 0n;
|
|
21704
|
-
}
|
|
21705
21788
|
function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21706
21789
|
const qualifying = orders.filter((order) => serves(order, incoming, resolve, now)).map((order) => {
|
|
21707
21790
|
const offer = offerFor({
|
|
@@ -21710,7 +21793,7 @@ function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new D
|
|
|
21710
21793
|
price: BigInt(order.price),
|
|
21711
21794
|
outputDecimals: incoming.outputDecimals
|
|
21712
21795
|
});
|
|
21713
|
-
const available =
|
|
21796
|
+
const available = BigInt(order.remaining);
|
|
21714
21797
|
return { order, offer, available, payout: offer < available ? offer : available };
|
|
21715
21798
|
}).filter((candidate) => candidate.offer >= incoming.requestedOutput && candidate.payout > 0n).sort(byBestOfferFirst);
|
|
21716
21799
|
return qualifying;
|
|
@@ -21720,16 +21803,63 @@ function byBestOfferFirst(a, b) {
|
|
|
21720
21803
|
if (a.available !== b.available) return a.available > b.available ? -1 : 1;
|
|
21721
21804
|
return a.order.id < b.order.id ? -1 : 1;
|
|
21722
21805
|
}
|
|
21806
|
+
function whyUnmatched(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21807
|
+
let furthest = -1;
|
|
21808
|
+
for (const order of orders) {
|
|
21809
|
+
const failed = firstFailed(order, incoming, resolve, now);
|
|
21810
|
+
furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
|
|
21811
|
+
}
|
|
21812
|
+
const { source, destination, inputSymbol } = incoming;
|
|
21813
|
+
if (furthest < CHECKS.length) {
|
|
21814
|
+
switch (CHECKS[Math.max(furthest, 0)]) {
|
|
21815
|
+
case "open":
|
|
21816
|
+
return "no limit order is open";
|
|
21817
|
+
case "chain":
|
|
21818
|
+
return `no open limit order fills on ${destination}`;
|
|
21819
|
+
case "input":
|
|
21820
|
+
return `no limit order on ${destination} takes ${inputSymbol} in`;
|
|
21821
|
+
case "output":
|
|
21822
|
+
return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
|
|
21823
|
+
case "source":
|
|
21824
|
+
return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
|
|
21825
|
+
}
|
|
21826
|
+
}
|
|
21827
|
+
const priced = orders.filter((order) => firstFailed(order, incoming, resolve, now) === null).map((order) => ({
|
|
21828
|
+
order,
|
|
21829
|
+
offer: offerFor({
|
|
21830
|
+
side: order.side,
|
|
21831
|
+
inputAmount: incoming.inputNet,
|
|
21832
|
+
price: BigInt(order.price),
|
|
21833
|
+
outputDecimals: incoming.outputDecimals
|
|
21834
|
+
})
|
|
21835
|
+
}));
|
|
21836
|
+
if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
|
|
21837
|
+
return "the limit orders that meet its rate have nothing left to pay out";
|
|
21838
|
+
}
|
|
21839
|
+
const best = priced.reduce((a, b) => b.offer > a.offer ? b : a);
|
|
21840
|
+
const symbol = limitOrderLegs(best.order).output;
|
|
21841
|
+
return `it asks for ${readable(incoming.requestedOutput)} ${symbol}; the best limit order offers ${readable(best.offer)} ${symbol}`;
|
|
21842
|
+
}
|
|
21843
|
+
var CHECKS = ["open", "chain", "input", "output", "source"];
|
|
21723
21844
|
function serves(order, incoming, resolve, now) {
|
|
21724
|
-
|
|
21725
|
-
|
|
21726
|
-
|
|
21845
|
+
return firstFailed(order, incoming, resolve, now) === null;
|
|
21846
|
+
}
|
|
21847
|
+
function firstFailed(order, incoming, resolve, now) {
|
|
21848
|
+
if (order.status !== "open") return "open";
|
|
21849
|
+
if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
|
|
21850
|
+
if (order.fillChain !== incoming.destination) return "chain";
|
|
21727
21851
|
const legs = limitOrderLegs(order);
|
|
21728
|
-
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return
|
|
21852
|
+
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
|
|
21729
21853
|
const outputToken = resolve(legs.output, order.fillChain);
|
|
21730
|
-
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return
|
|
21731
|
-
if (incoming.source === incoming.destination) return
|
|
21732
|
-
return order.acceptedSources.includes(incoming.source);
|
|
21854
|
+
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
|
|
21855
|
+
if (incoming.source === incoming.destination) return null;
|
|
21856
|
+
return order.acceptedSources.includes(incoming.source) ? null : "source";
|
|
21857
|
+
}
|
|
21858
|
+
function readable(amount) {
|
|
21859
|
+
const [whole, fraction] = toHuman(amount).split(".");
|
|
21860
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
21861
|
+
const cut = fraction?.slice(0, 6).replace(/0+$/, "");
|
|
21862
|
+
return cut ? `${grouped}.${cut}` : grouped;
|
|
21733
21863
|
}
|
|
21734
21864
|
|
|
21735
21865
|
// src/orderbook/usd.ts
|
|
@@ -21871,15 +22001,20 @@ var FXFiller = class _FXFiller {
|
|
|
21871
22001
|
}
|
|
21872
22002
|
try {
|
|
21873
22003
|
if (order.inputs.length !== order.output.assets.length) {
|
|
21874
|
-
this.logger.
|
|
21875
|
-
{ inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
22004
|
+
this.logger.info(
|
|
22005
|
+
{ orderId: order.id, inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
21876
22006
|
"Order input/output length mismatch or empty"
|
|
21877
22007
|
);
|
|
21878
22008
|
return false;
|
|
21879
22009
|
}
|
|
21880
22010
|
if ((await this.matchOrder(order)).length === 0) {
|
|
21881
|
-
this.logger.
|
|
21882
|
-
{
|
|
22011
|
+
this.logger.info(
|
|
22012
|
+
{
|
|
22013
|
+
orderId: order.id,
|
|
22014
|
+
sourceChain: order.source,
|
|
22015
|
+
destChain: order.destination,
|
|
22016
|
+
reason: await this.explainUnmatched(order)
|
|
22017
|
+
},
|
|
21883
22018
|
"No limit order matches this order"
|
|
21884
22019
|
);
|
|
21885
22020
|
return false;
|
|
@@ -22421,30 +22556,53 @@ var FXFiller = class _FXFiller {
|
|
|
22421
22556
|
*/
|
|
22422
22557
|
async matchLeg(order, leg) {
|
|
22423
22558
|
if (!this.limitOrders) return [];
|
|
22559
|
+
const incoming = await this.incomingFor(order, leg);
|
|
22560
|
+
if (!incoming) return [];
|
|
22561
|
+
return matchLimitOrders(
|
|
22562
|
+
await this.limitOrders.open(),
|
|
22563
|
+
incoming,
|
|
22564
|
+
(symbol, chain) => this.registry.getAddress(symbol, chain)
|
|
22565
|
+
);
|
|
22566
|
+
}
|
|
22567
|
+
/** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
|
|
22568
|
+
async explainUnmatched(order) {
|
|
22569
|
+
if (!this.limitOrders) return "limit orders are not loaded";
|
|
22570
|
+
const open = await this.limitOrders.open();
|
|
22571
|
+
const reasons = [];
|
|
22572
|
+
for (let leg = 0; leg < order.inputs.length; leg++) {
|
|
22573
|
+
const incoming = await this.incomingFor(order, leg);
|
|
22574
|
+
const reason = incoming ? whyUnmatched(open, incoming, (symbol, chain) => this.registry.getAddress(symbol, chain)) : `its input token is not in the asset registry on ${order.source}`;
|
|
22575
|
+
reasons.push(order.inputs.length > 1 ? `leg ${leg}: ${reason}` : reason);
|
|
22576
|
+
}
|
|
22577
|
+
return reasons.join("; ");
|
|
22578
|
+
}
|
|
22579
|
+
/**
|
|
22580
|
+
* One leg as the matcher sees it, or null when its input token is not one the asset
|
|
22581
|
+
* registry knows on the source chain.
|
|
22582
|
+
*
|
|
22583
|
+
* Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
|
|
22584
|
+
*/
|
|
22585
|
+
async incomingFor(order, leg) {
|
|
22424
22586
|
const input = order.inputs[leg];
|
|
22425
22587
|
const output = order.output.assets[leg];
|
|
22426
|
-
if (!input || !output) return
|
|
22588
|
+
if (!input || !output) return null;
|
|
22427
22589
|
const inputToken = (0, import_sdk15.bytes32ToBytes20)(input.token);
|
|
22428
22590
|
const outputToken = (0, import_sdk15.bytes32ToBytes20)(output.token);
|
|
22429
22591
|
const inputSymbol = this.registry.symbolFor(inputToken, order.source);
|
|
22430
|
-
if (!inputSymbol) return
|
|
22592
|
+
if (!inputSymbol) return null;
|
|
22431
22593
|
const [inputDecimals, outputDecimals] = await Promise.all([
|
|
22432
22594
|
this.contractService.getTokenDecimals(inputToken, order.source),
|
|
22433
22595
|
this.contractService.getTokenDecimals(outputToken, order.destination)
|
|
22434
22596
|
]);
|
|
22435
|
-
return
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
outputDecimals
|
|
22445
|
-
},
|
|
22446
|
-
(symbol, chain) => this.registry.getAddress(symbol, chain)
|
|
22447
|
-
);
|
|
22597
|
+
return {
|
|
22598
|
+
source: order.source,
|
|
22599
|
+
destination: order.destination,
|
|
22600
|
+
inputSymbol,
|
|
22601
|
+
outputToken,
|
|
22602
|
+
inputNet: toScaled(input.amount, inputDecimals),
|
|
22603
|
+
requestedOutput: toScaled(output.amount, outputDecimals),
|
|
22604
|
+
outputDecimals
|
|
22605
|
+
};
|
|
22448
22606
|
}
|
|
22449
22607
|
/**
|
|
22450
22608
|
* The order's input in **USD**, or null when no limit order connects its input
|
|
@@ -23522,48 +23680,42 @@ async function bootFiller(config, options) {
|
|
|
23522
23680
|
assertPairSymbolsResolve(config.pairs, assetRegistry, configuredChainNames);
|
|
23523
23681
|
}
|
|
23524
23682
|
const adminStrategies = [];
|
|
23525
|
-
const
|
|
23526
|
-
|
|
23527
|
-
|
|
23528
|
-
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
|
|
23536
|
-
|
|
23537
|
-
token1: pair.token1,
|
|
23538
|
-
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23539
|
-
});
|
|
23683
|
+
const tradingPairs = (config.pairs ?? []).map((pair) => ({
|
|
23684
|
+
token0: pair.token0,
|
|
23685
|
+
token1: pair.token1
|
|
23686
|
+
}));
|
|
23687
|
+
tradingPairs.forEach((pair, pairIndex) => {
|
|
23688
|
+
adminStrategies.push({
|
|
23689
|
+
index: pairIndex,
|
|
23690
|
+
pairIndex,
|
|
23691
|
+
exotic: `${pair.token0}/${pair.token1}`,
|
|
23692
|
+
token0: pair.token0,
|
|
23693
|
+
token1: pair.token1,
|
|
23694
|
+
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23540
23695
|
});
|
|
23541
|
-
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
)
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
23696
|
+
});
|
|
23697
|
+
const confirmationPolicy = assertConfirmationCoverage(
|
|
23698
|
+
config.confirmationPolicies,
|
|
23699
|
+
resolvedChains.map((c) => c.chainId)
|
|
23700
|
+
);
|
|
23701
|
+
const fundingVenues = vaultVenue ? [vaultVenue] : [];
|
|
23702
|
+
const engine = new FXFiller(
|
|
23703
|
+
runtimeSigner,
|
|
23704
|
+
configService,
|
|
23705
|
+
chainClientManager,
|
|
23706
|
+
contractService,
|
|
23707
|
+
tradingPairs,
|
|
23708
|
+
assetRegistry,
|
|
23709
|
+
{ confirmationPolicy, fundingVenues, limitOrders: options.data.limitOrders }
|
|
23710
|
+
);
|
|
23711
|
+
logger.info("Hydrating funding venue state...");
|
|
23712
|
+
await engine.initialise();
|
|
23713
|
+
const strategies = [engine];
|
|
23559
23714
|
const haltControls = strategies.map((engine2, index) => ({
|
|
23560
23715
|
index,
|
|
23561
23716
|
isHalted: () => engine2.isHalted(),
|
|
23562
23717
|
resetHalt: () => engine2.resetHalt()
|
|
23563
23718
|
}));
|
|
23564
|
-
if (vaultVenue) {
|
|
23565
|
-
await vaultVenue.initialise(runtimeSigner.address);
|
|
23566
|
-
}
|
|
23567
23719
|
let rebalancingService;
|
|
23568
23720
|
const rebalancingConfig = configService.getRebalancingConfig();
|
|
23569
23721
|
if (rebalancingConfig?.triggerPercentage !== void 0 && rebalancingConfig?.baseBalances) {
|
|
@@ -24080,8 +24232,8 @@ var MemoryLimitOrderStore = class {
|
|
|
24080
24232
|
async reserve(id, amount) {
|
|
24081
24233
|
const order = this.orders.get(id);
|
|
24082
24234
|
if (!order || order.status !== "open") return false;
|
|
24235
|
+
if (BigInt(amount) > BigInt(order.remaining)) return false;
|
|
24083
24236
|
const reserved = BigInt(order.reserved) + BigInt(amount);
|
|
24084
|
-
if (reserved > BigInt(order.remaining)) return false;
|
|
24085
24237
|
this.patch(id, { reserved: reserved.toString() });
|
|
24086
24238
|
return true;
|
|
24087
24239
|
}
|
|
@@ -24157,6 +24309,8 @@ var MAX_BLOCK_RANGE = 1000n;
|
|
|
24157
24309
|
var PUBLISH_CHUNK = 250;
|
|
24158
24310
|
var yieldToConsumers = () => new Promise((resolve) => setImmediate(resolve));
|
|
24159
24311
|
var STOP_DRAIN_TIMEOUT_MS = 5e3;
|
|
24312
|
+
var STALL_THRESHOLD_MS = 45e3;
|
|
24313
|
+
var STALL_REPORT_INTERVAL_MS = 6e4;
|
|
24160
24314
|
var ChainScanner = class {
|
|
24161
24315
|
constructor(target, loggers = defaultLoggerContext(), scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS) {
|
|
24162
24316
|
this.target = target;
|
|
@@ -24170,6 +24324,18 @@ var ChainScanner = class {
|
|
|
24170
24324
|
this.errorHandler = () => {
|
|
24171
24325
|
};
|
|
24172
24326
|
this.stopped = false;
|
|
24327
|
+
/**
|
|
24328
|
+
* How far past the cursor the next pass reads, as `toBlock - fromBlock`:
|
|
24329
|
+
* halved after a failed read, doubled after a good one, between 0 (one block)
|
|
24330
|
+
* and {@link MAX_BLOCK_RANGE}.
|
|
24331
|
+
*
|
|
24332
|
+
* Free endpoints cap `eth_getLogs` ranges far below the full span — on
|
|
24333
|
+
* Arbitrum three of seven refused 100 blocks — and the cursor never moves
|
|
24334
|
+
* past a range it failed to read. At a fixed span a catch-up that outgrew
|
|
24335
|
+
* those caps was stuck for good: the range only grew, so the capped endpoints
|
|
24336
|
+
* never rejoined and the quorum never formed.
|
|
24337
|
+
*/
|
|
24338
|
+
this.span = MAX_BLOCK_RANGE;
|
|
24173
24339
|
this.logger = loggers.get("chain-scanner");
|
|
24174
24340
|
this.quorumClient = new QuorumPublicClient(target.chainId, target.rpcUrls, loggers);
|
|
24175
24341
|
if (this.quorumClient.size > 1) {
|
|
@@ -24212,8 +24378,9 @@ var ChainScanner = class {
|
|
|
24212
24378
|
if (this.timer || this.stopped) return;
|
|
24213
24379
|
this.logger.info({ chainId: this.target.chainId }, "Block scanner started");
|
|
24214
24380
|
this.timer = setInterval(() => {
|
|
24215
|
-
if (this.mutex.isLocked()) return;
|
|
24381
|
+
if (this.mutex.isLocked()) return this.reportStall();
|
|
24216
24382
|
void this.mutex.runExclusive(async () => {
|
|
24383
|
+
this.pass = { phase: "head", startedAt: Date.now(), reportedAt: 0 };
|
|
24217
24384
|
try {
|
|
24218
24385
|
await this.scan();
|
|
24219
24386
|
} catch (error) {
|
|
@@ -24227,10 +24394,66 @@ var ChainScanner = class {
|
|
|
24227
24394
|
"Error in block scanner"
|
|
24228
24395
|
);
|
|
24229
24396
|
this.errorHandler(error);
|
|
24397
|
+
} finally {
|
|
24398
|
+
this.notePassEnd();
|
|
24230
24399
|
}
|
|
24231
24400
|
});
|
|
24232
24401
|
}, this.scanIntervalMs);
|
|
24233
24402
|
}
|
|
24403
|
+
/**
|
|
24404
|
+
* Records which await the current pass is sitting on, for the watchdog.
|
|
24405
|
+
*
|
|
24406
|
+
* A no-op when no pass is registered — `scan()` is driven directly by tests
|
|
24407
|
+
* and by nothing else, so there is no watchdog to inform.
|
|
24408
|
+
*/
|
|
24409
|
+
enter(phase) {
|
|
24410
|
+
if (this.pass) this.pass.phase = phase;
|
|
24411
|
+
}
|
|
24412
|
+
/**
|
|
24413
|
+
* Says that a pass has outlived the quorum budgets and names the phase it is
|
|
24414
|
+
* stuck in, then repeats every {@link STALL_REPORT_INTERVAL_MS} until it ends.
|
|
24415
|
+
*
|
|
24416
|
+
* Observation only: the pass is left alone. Aborting it would need a cancel
|
|
24417
|
+
* path through the quorum client that does not exist, and the cursor is safe
|
|
24418
|
+
* either way — a pass that never completes never advances it.
|
|
24419
|
+
*/
|
|
24420
|
+
reportStall() {
|
|
24421
|
+
const pass = this.pass;
|
|
24422
|
+
if (!pass) return;
|
|
24423
|
+
const now = Date.now();
|
|
24424
|
+
const heldMs = now - pass.startedAt;
|
|
24425
|
+
if (heldMs < STALL_THRESHOLD_MS) return;
|
|
24426
|
+
if (pass.reportedAt !== 0 && now - pass.reportedAt < STALL_REPORT_INTERVAL_MS) return;
|
|
24427
|
+
pass.reportedAt = now;
|
|
24428
|
+
this.logger.warn(
|
|
24429
|
+
{
|
|
24430
|
+
chainId: this.target.chainId,
|
|
24431
|
+
phase: pass.phase,
|
|
24432
|
+
heldMs,
|
|
24433
|
+
cursor: this.cursor?.toString(),
|
|
24434
|
+
rpcUrls: this.target.rpcUrls
|
|
24435
|
+
},
|
|
24436
|
+
"Block scan pass has outlived its budget \u2014 this chain is not being scanned"
|
|
24437
|
+
);
|
|
24438
|
+
}
|
|
24439
|
+
/**
|
|
24440
|
+
* Closes out a pass, and says so if the watchdog complained about it — without
|
|
24441
|
+
* this the log shows a chain going quiet and never shows it coming back.
|
|
24442
|
+
*/
|
|
24443
|
+
notePassEnd() {
|
|
24444
|
+
const pass = this.pass;
|
|
24445
|
+
this.pass = void 0;
|
|
24446
|
+
if (!pass || pass.reportedAt === 0) return;
|
|
24447
|
+
this.logger.warn(
|
|
24448
|
+
{
|
|
24449
|
+
chainId: this.target.chainId,
|
|
24450
|
+
phase: pass.phase,
|
|
24451
|
+
heldMs: Date.now() - pass.startedAt,
|
|
24452
|
+
cursor: this.cursor?.toString()
|
|
24453
|
+
},
|
|
24454
|
+
"Block scan pass finished after outliving its budget \u2014 scanning has resumed"
|
|
24455
|
+
);
|
|
24456
|
+
}
|
|
24234
24457
|
/**
|
|
24235
24458
|
* Points this loop at different endpoints, keeping the cursor.
|
|
24236
24459
|
*
|
|
@@ -24272,11 +24495,12 @@ var ChainScanner = class {
|
|
|
24272
24495
|
}
|
|
24273
24496
|
if (currentBlock <= this.cursor) return;
|
|
24274
24497
|
const fromBlock = this.cursor + 1n;
|
|
24275
|
-
const toBlock = fromBlock +
|
|
24498
|
+
const toBlock = fromBlock + this.span > currentBlock ? currentBlock : fromBlock + this.span;
|
|
24276
24499
|
this.logger.debug(
|
|
24277
24500
|
{ chainId: this.target.chainId, fromBlock, toBlock, gap: Number(toBlock - fromBlock) },
|
|
24278
24501
|
"Scanning blocks"
|
|
24279
24502
|
);
|
|
24503
|
+
this.enter("logs");
|
|
24280
24504
|
let logs;
|
|
24281
24505
|
try {
|
|
24282
24506
|
logs = await (0, import_sdk17.retryPromise)(
|
|
@@ -24298,6 +24522,7 @@ var ChainScanner = class {
|
|
|
24298
24522
|
);
|
|
24299
24523
|
} catch (error) {
|
|
24300
24524
|
if (isBlockRangeError(error)) return;
|
|
24525
|
+
this.narrow(toBlock - fromBlock);
|
|
24301
24526
|
throw error;
|
|
24302
24527
|
}
|
|
24303
24528
|
if (this.stopped) return;
|
|
@@ -24308,6 +24533,7 @@ var ChainScanner = class {
|
|
|
24308
24533
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: placed.length },
|
|
24309
24534
|
"Found OrderPlaced events in block scan"
|
|
24310
24535
|
);
|
|
24536
|
+
this.enter("publish-orders");
|
|
24311
24537
|
await this.publishOrders(placed);
|
|
24312
24538
|
}
|
|
24313
24539
|
if (filled.length > 0) {
|
|
@@ -24315,10 +24541,36 @@ var ChainScanner = class {
|
|
|
24315
24541
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: filled.length },
|
|
24316
24542
|
"Found OrderFilled events in block scan"
|
|
24317
24543
|
);
|
|
24544
|
+
this.enter("publish-fills");
|
|
24318
24545
|
await this.publishFills(filled);
|
|
24319
24546
|
}
|
|
24320
24547
|
if (this.stopped) return;
|
|
24321
24548
|
this.cursor = toBlock;
|
|
24549
|
+
this.widen();
|
|
24550
|
+
}
|
|
24551
|
+
/**
|
|
24552
|
+
* Halves the span below the range that just failed, so the next pass asks
|
|
24553
|
+
* for something a range-capped endpoint will answer. Halving the attempted
|
|
24554
|
+
* range rather than the span matters near the head, where a pass reads fewer
|
|
24555
|
+
* blocks than the span allows.
|
|
24556
|
+
*/
|
|
24557
|
+
narrow(attempted) {
|
|
24558
|
+
const next = attempted / 2n;
|
|
24559
|
+
if (next >= this.span) return;
|
|
24560
|
+
this.span = next;
|
|
24561
|
+
this.logger.warn(
|
|
24562
|
+
{ chainId: this.target.chainId, span: Number(next) },
|
|
24563
|
+
"Narrowing the block scan range after a failed read"
|
|
24564
|
+
);
|
|
24565
|
+
}
|
|
24566
|
+
/** Doubles the span back toward {@link MAX_BLOCK_RANGE} after a good read. */
|
|
24567
|
+
widen() {
|
|
24568
|
+
if (this.span >= MAX_BLOCK_RANGE) return;
|
|
24569
|
+
const next = this.span === 0n ? 1n : this.span * 2n;
|
|
24570
|
+
this.span = next > MAX_BLOCK_RANGE ? MAX_BLOCK_RANGE : next;
|
|
24571
|
+
if (this.span === MAX_BLOCK_RANGE) {
|
|
24572
|
+
this.logger.info({ chainId: this.target.chainId }, "Block scan range back to full");
|
|
24573
|
+
}
|
|
24322
24574
|
}
|
|
24323
24575
|
async publishOrders(logs) {
|
|
24324
24576
|
const rebuilt = reconstructOrdersFromLogs(logs, {
|
|
@@ -24685,7 +24937,7 @@ var LimitOrderController = class {
|
|
|
24685
24937
|
]);
|
|
24686
24938
|
return { order, fills, bids };
|
|
24687
24939
|
}
|
|
24688
|
-
/** The pairs the orderbook lists,
|
|
24940
|
+
/** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
|
|
24689
24941
|
books() {
|
|
24690
24942
|
return this.service.books();
|
|
24691
24943
|
}
|
|
@@ -24733,10 +24985,7 @@ var PairController = class {
|
|
|
24733
24985
|
* the pair's symbols have to resolve before the market can be hydrated.
|
|
24734
24986
|
*/
|
|
24735
24987
|
async add(pair, options) {
|
|
24736
|
-
const { engine,
|
|
24737
|
-
if (!engine || !tradingPairs) {
|
|
24738
|
-
throw new Error("No trading engine is running \u2014 this filler was started without pairs");
|
|
24739
|
-
}
|
|
24988
|
+
const { engine, assetRegistry, adminStrategies, balanceTokens, config } = this.runtime;
|
|
24740
24989
|
const assets = options?.assets;
|
|
24741
24990
|
const nextAssets = { ...config.assets ?? {}, ...assets ?? {} };
|
|
24742
24991
|
const nextPairs = [...this.pairs, pair];
|
|
@@ -24775,7 +25024,6 @@ var PairController = class {
|
|
|
24775
25024
|
/** Closes a market. Orders already in flight against it still complete. */
|
|
24776
25025
|
async remove(index) {
|
|
24777
25026
|
const { engine, tradingPairs, adminStrategies, config } = this.runtime;
|
|
24778
|
-
if (!engine || !tradingPairs) throw new Error("No trading engine is running");
|
|
24779
25027
|
if (index < 0 || index >= this.pairs.length) throw new Error(`Unknown pair ${index}`);
|
|
24780
25028
|
engine.removePair(tradingPairs[index]);
|
|
24781
25029
|
config.pairs = this.pairs.filter((_, i) => i !== index);
|
|
@@ -24787,7 +25035,7 @@ var PairController = class {
|
|
|
24787
25035
|
await this.persist();
|
|
24788
25036
|
}
|
|
24789
25037
|
livePair(index) {
|
|
24790
|
-
const pair = this.runtime.tradingPairs
|
|
25038
|
+
const pair = this.runtime.tradingPairs[index];
|
|
24791
25039
|
if (!pair) throw new Error(`Unknown pair ${index}`);
|
|
24792
25040
|
return pair;
|
|
24793
25041
|
}
|
|
@@ -24879,9 +25127,9 @@ var ChainController = class {
|
|
|
24879
25127
|
throw error;
|
|
24880
25128
|
}
|
|
24881
25129
|
const curve = confirmationPolicies[String(chainId)];
|
|
24882
|
-
if (curve) this.runtime.confirmationPolicy
|
|
24883
|
-
else if (!this.runtime.confirmationPolicy
|
|
24884
|
-
this.runtime.confirmationPolicy
|
|
25130
|
+
if (curve) this.runtime.confirmationPolicy.add(chainId, curve);
|
|
25131
|
+
else if (!this.runtime.confirmationPolicy.has(chainId)) {
|
|
25132
|
+
this.runtime.confirmationPolicy.adopt(chainId, resolvedPolicy);
|
|
24885
25133
|
}
|
|
24886
25134
|
this.runtime.resolvedChains.push(resolved);
|
|
24887
25135
|
config.chains = [...config.chains, { rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl }];
|
|
@@ -24909,7 +25157,7 @@ var ChainController = class {
|
|
|
24909
25157
|
await intentFiller.removeChain(chainId);
|
|
24910
25158
|
if (this.ownsScanner) await this.scanner.removeChain(chainId);
|
|
24911
25159
|
configService.removeChain(chainId);
|
|
24912
|
-
this.runtime.confirmationPolicy
|
|
25160
|
+
this.runtime.confirmationPolicy.remove(chainId);
|
|
24913
25161
|
this.runtime.chainClientManager.invalidate(chainKey);
|
|
24914
25162
|
this.runtime.resolvedChains.splice(index, 1);
|
|
24915
25163
|
config.chains = config.chains.filter((_, i) => i !== index);
|