@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.js
CHANGED
|
@@ -2402,6 +2402,7 @@ function closedKey(chain, vault) {
|
|
|
2402
2402
|
// src/config/external-links.json
|
|
2403
2403
|
var external_links_default = {
|
|
2404
2404
|
hyperfxApp: "https://app.hyperfx.finance",
|
|
2405
|
+
alchemyDashboard: "https://dashboard.alchemy.com/",
|
|
2405
2406
|
hyperbridgeExplorers: {
|
|
2406
2407
|
mainnet: "https://nexus.statescan.io",
|
|
2407
2408
|
testnet: "https://gargantua.statescan.io"
|
|
@@ -2421,6 +2422,52 @@ var external_links_default = {
|
|
|
2421
2422
|
};
|
|
2422
2423
|
|
|
2423
2424
|
// src/cli/init/chains.ts
|
|
2425
|
+
var FREE_RPC_URLS = {
|
|
2426
|
+
1: [
|
|
2427
|
+
"https://mainnet.gateway.tenderly.co",
|
|
2428
|
+
"https://rpc.mevblocker.io",
|
|
2429
|
+
"https://ethereum-rpc.publicnode.com",
|
|
2430
|
+
"https://ethereum.public.blockpi.network/v1/rpc/public",
|
|
2431
|
+
"https://eth.blockrazor.xyz",
|
|
2432
|
+
"https://eth.api.pocket.network",
|
|
2433
|
+
"https://ethereum-public.nodies.app",
|
|
2434
|
+
"https://eth-mainnet.public.blastapi.io",
|
|
2435
|
+
"https://public-eth.nownodes.io"
|
|
2436
|
+
],
|
|
2437
|
+
56: [
|
|
2438
|
+
"https://bsc-rpc.publicnode.com",
|
|
2439
|
+
"https://public-bsc-mainnet.fastnode.io",
|
|
2440
|
+
"https://public-bsc.nownodes.io",
|
|
2441
|
+
"https://bsc.blockrazor.xyz",
|
|
2442
|
+
"https://rpc-bsc.48.club",
|
|
2443
|
+
"https://bsc.api.pocket.network"
|
|
2444
|
+
],
|
|
2445
|
+
137: [
|
|
2446
|
+
"https://polygon-bor-rpc.publicnode.com",
|
|
2447
|
+
"https://polygon.drpc.org",
|
|
2448
|
+
"https://polygon.gateway.tenderly.co",
|
|
2449
|
+
"https://rpc.private.mev-x.com/polygon",
|
|
2450
|
+
"https://poly.api.pocket.network"
|
|
2451
|
+
],
|
|
2452
|
+
8453: [
|
|
2453
|
+
"https://base-rpc.publicnode.com",
|
|
2454
|
+
"https://base.gateway.tenderly.co",
|
|
2455
|
+
"https://base.public.blockpi.network/v1/rpc/public",
|
|
2456
|
+
"https://base.rpc.blxrbdn.com",
|
|
2457
|
+
"https://rpc.baseazul.dev",
|
|
2458
|
+
"https://mainnet.base.org",
|
|
2459
|
+
"https://base-public.nodies.app"
|
|
2460
|
+
],
|
|
2461
|
+
42161: [
|
|
2462
|
+
"https://arbitrum-one-rpc.publicnode.com",
|
|
2463
|
+
"https://arbitrum.gateway.tenderly.co",
|
|
2464
|
+
"https://arbitrum.drpc.org",
|
|
2465
|
+
"https://public-arb-mainnet.fastnode.io",
|
|
2466
|
+
"https://arb-one.api.pocket.network",
|
|
2467
|
+
"https://arbitrum-one-public.nodies.app",
|
|
2468
|
+
"https://arb1.arbitrum.io/rpc"
|
|
2469
|
+
]
|
|
2470
|
+
};
|
|
2424
2471
|
var CHAIN_NATIVE_SYMBOLS = {
|
|
2425
2472
|
1: "ETH",
|
|
2426
2473
|
56: "BNB",
|
|
@@ -2448,7 +2495,8 @@ var INIT_CHAINS = [
|
|
|
2448
2495
|
label: "Ethereum",
|
|
2449
2496
|
network: "mainnet",
|
|
2450
2497
|
alchemySubdomain: "eth-mainnet",
|
|
2451
|
-
explorerUrl: external_links_default.chainExplorers.ethereum
|
|
2498
|
+
explorerUrl: external_links_default.chainExplorers.ethereum,
|
|
2499
|
+
defaultRpcUrls: FREE_RPC_URLS[1]
|
|
2452
2500
|
},
|
|
2453
2501
|
{
|
|
2454
2502
|
chainId: 42161,
|
|
@@ -2456,7 +2504,8 @@ var INIT_CHAINS = [
|
|
|
2456
2504
|
label: "Arbitrum",
|
|
2457
2505
|
network: "mainnet",
|
|
2458
2506
|
alchemySubdomain: "arb-mainnet",
|
|
2459
|
-
explorerUrl: external_links_default.chainExplorers.arbitrum
|
|
2507
|
+
explorerUrl: external_links_default.chainExplorers.arbitrum,
|
|
2508
|
+
defaultRpcUrls: FREE_RPC_URLS[42161]
|
|
2460
2509
|
},
|
|
2461
2510
|
{
|
|
2462
2511
|
chainId: 8453,
|
|
@@ -2464,7 +2513,8 @@ var INIT_CHAINS = [
|
|
|
2464
2513
|
label: "Base",
|
|
2465
2514
|
network: "mainnet",
|
|
2466
2515
|
alchemySubdomain: "base-mainnet",
|
|
2467
|
-
explorerUrl: external_links_default.chainExplorers.base
|
|
2516
|
+
explorerUrl: external_links_default.chainExplorers.base,
|
|
2517
|
+
defaultRpcUrls: FREE_RPC_URLS[8453]
|
|
2468
2518
|
},
|
|
2469
2519
|
{
|
|
2470
2520
|
chainId: 137,
|
|
@@ -2472,7 +2522,8 @@ var INIT_CHAINS = [
|
|
|
2472
2522
|
label: "Polygon",
|
|
2473
2523
|
network: "mainnet",
|
|
2474
2524
|
alchemySubdomain: "polygon-mainnet",
|
|
2475
|
-
explorerUrl: external_links_default.chainExplorers.polygon
|
|
2525
|
+
explorerUrl: external_links_default.chainExplorers.polygon,
|
|
2526
|
+
defaultRpcUrls: FREE_RPC_URLS[137]
|
|
2476
2527
|
},
|
|
2477
2528
|
{
|
|
2478
2529
|
chainId: 56,
|
|
@@ -2480,7 +2531,8 @@ var INIT_CHAINS = [
|
|
|
2480
2531
|
label: "BNB Chain",
|
|
2481
2532
|
network: "mainnet",
|
|
2482
2533
|
alchemySubdomain: "bnb-mainnet",
|
|
2483
|
-
explorerUrl: external_links_default.chainExplorers.bnb
|
|
2534
|
+
explorerUrl: external_links_default.chainExplorers.bnb,
|
|
2535
|
+
defaultRpcUrls: FREE_RPC_URLS[56]
|
|
2484
2536
|
},
|
|
2485
2537
|
{
|
|
2486
2538
|
chainId: 11155111,
|
|
@@ -2968,14 +3020,16 @@ var LimitOrderService = class {
|
|
|
2968
3020
|
}
|
|
2969
3021
|
}
|
|
2970
3022
|
/**
|
|
2971
|
-
* The pairs an order can be written against,
|
|
3023
|
+
* The pairs an order can be written against, the smallest payout each token may carry, and
|
|
3024
|
+
* the tokens the orderbook registers on each chain.
|
|
2972
3025
|
*
|
|
2973
3026
|
* The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
|
|
2974
|
-
* operator is offered exactly these and no combination of symbols they could not post.
|
|
3027
|
+
* operator is offered exactly these and no combination of symbols they could not post. The
|
|
3028
|
+
* chains do the same for the source chains an order can accept.
|
|
2975
3029
|
*/
|
|
2976
3030
|
async books() {
|
|
2977
3031
|
const limits = await this.limits();
|
|
2978
|
-
return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes };
|
|
3032
|
+
return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes, chains: limits.chains ?? [] };
|
|
2979
3033
|
}
|
|
2980
3034
|
/**
|
|
2981
3035
|
* Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
|
|
@@ -3057,6 +3111,18 @@ var LimitOrderService = class {
|
|
|
3057
3111
|
);
|
|
3058
3112
|
}
|
|
3059
3113
|
}
|
|
3114
|
+
if (book.base !== book.quote) {
|
|
3115
|
+
const input = normalizeSymbol(request.tokenIn);
|
|
3116
|
+
const lacking = sources.filter((source) => {
|
|
3117
|
+
const listed = limits.chains?.find((chain) => chain.id === source);
|
|
3118
|
+
return listed && !listed.tokens.some((token) => normalizeSymbol(token.symbol) === input);
|
|
3119
|
+
});
|
|
3120
|
+
if (lacking.length > 0) {
|
|
3121
|
+
throw new LimitOrderValidationError(
|
|
3122
|
+
`The orderbook has no ${request.tokenIn} on ${lacking.join(", ")}, so swaps from there cannot pay this order`
|
|
3123
|
+
);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3060
3126
|
if (ttlSecs < limits.serverInfo.minOrderTtlSecs) {
|
|
3061
3127
|
throw new LimitOrderValidationError(
|
|
3062
3128
|
`ttlSecs must be at least the orderbook's minimum of ${limits.serverInfo.minOrderTtlSecs}; got ${ttlSecs}`
|
|
@@ -12942,14 +13008,10 @@ var RATE_LIMIT_RPC_CODES = /* @__PURE__ */ new Set([-32005, -32097, -32016, 429]
|
|
|
12942
13008
|
var RATE_LIMIT_SUSPENSION_MS = 5 * 6e4;
|
|
12943
13009
|
var SCAN_BUDGET = {
|
|
12944
13010
|
timeoutMs: 5e3,
|
|
12945
|
-
retryCount: 1,
|
|
12946
|
-
retryDelayMs: 500,
|
|
12947
13011
|
deadlineMs: 12e3
|
|
12948
13012
|
};
|
|
12949
13013
|
var CONFIRMATION_BUDGET = {
|
|
12950
13014
|
timeoutMs: 1e4,
|
|
12951
|
-
retryCount: 1,
|
|
12952
|
-
retryDelayMs: 500,
|
|
12953
13015
|
deadlineMs: 3e4
|
|
12954
13016
|
};
|
|
12955
13017
|
var MALFORMED_RESPONSE_SUSPENSION_MS = 3e4;
|
|
@@ -13041,8 +13103,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13041
13103
|
chain,
|
|
13042
13104
|
transport: http(url, {
|
|
13043
13105
|
timeout: budget.timeoutMs,
|
|
13044
|
-
|
|
13045
|
-
|
|
13106
|
+
// No transport-level retry. viem's retry sleeps for the provider's
|
|
13107
|
+
// `Retry-After` verbatim and uncapped, which reaches the socket
|
|
13108
|
+
// before {@link suspensionMsFor} can apply its clamp — and even
|
|
13109
|
+
// without that header, retrying inside the task is what makes a
|
|
13110
|
+
// task's worst case (timeout, backoff, timeout) approach the
|
|
13111
|
+
// deadline that is supposed to bound it. One attempt per endpoint
|
|
13112
|
+
// per call; `retryPromise` above the quorum owns the retry policy.
|
|
13113
|
+
retryCount: 0
|
|
13046
13114
|
})
|
|
13047
13115
|
})
|
|
13048
13116
|
);
|
|
@@ -13056,11 +13124,10 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13056
13124
|
return this.rpcUrls.filter((url) => (_QuorumPublicClient.benchedUntil.get(url) ?? 0) > now);
|
|
13057
13125
|
}
|
|
13058
13126
|
/**
|
|
13059
|
-
* The endpoints this call queries
|
|
13060
|
-
*
|
|
13061
|
-
*
|
|
13062
|
-
*
|
|
13063
|
-
* queried again; there is nobody left to prefer.
|
|
13127
|
+
* The endpoints this call queries: everyone not benched. A benched endpoint is
|
|
13128
|
+
* dropped outright: it answers nothing while throttled, so asking it could only
|
|
13129
|
+
* cost a request. With every endpoint benched, everyone is queried again; there
|
|
13130
|
+
* is nobody left to prefer.
|
|
13064
13131
|
*/
|
|
13065
13132
|
participants() {
|
|
13066
13133
|
const now = Date.now();
|
|
@@ -13068,12 +13135,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13068
13135
|
const active = all.filter(({ idx }) => (_QuorumPublicClient.benchedUntil.get(this.rpcUrls[idx]) ?? 0) <= now);
|
|
13069
13136
|
const allBenched = active.length === 0;
|
|
13070
13137
|
const queried = allBenched ? all : active;
|
|
13071
|
-
return {
|
|
13072
|
-
queried,
|
|
13073
|
-
skipped: all.length - queried.length,
|
|
13074
|
-
threshold: quorumThreshold(queried.length),
|
|
13075
|
-
allBenched
|
|
13076
|
-
};
|
|
13138
|
+
return { queried, skipped: all.length - queried.length, allBenched };
|
|
13077
13139
|
}
|
|
13078
13140
|
/**
|
|
13079
13141
|
* Records a task failure against its endpoint. Only the failures
|
|
@@ -13118,17 +13180,21 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13118
13180
|
* retries would otherwise stall every confirmation poll). Stragglers settle
|
|
13119
13181
|
* out of band and are ignored. `tryDecide` returns undefined to keep
|
|
13120
13182
|
* waiting; once every task has settled, `finalize` must decide (it throws
|
|
13121
|
-
* the QuorumError when the full response set still has no quorum).
|
|
13122
|
-
*
|
|
13123
|
-
*
|
|
13183
|
+
* the QuorumError when the full response set still has no quorum).
|
|
13184
|
+
*
|
|
13185
|
+
* Both are handed the bar: the BFT threshold over the endpoints that have not
|
|
13186
|
+
* failed, answered or still out. A failure lowers it rather than counting
|
|
13187
|
+
* against it. Counting the pending endpoints keeps an early decision to a bar
|
|
13188
|
+
* no lower than the full response set would get, so a vote it did not wait for
|
|
13189
|
+
* could never have reversed it.
|
|
13124
13190
|
*
|
|
13125
13191
|
* `deadlineMs` bounds the other direction: when the bar is NOT reachable
|
|
13126
13192
|
* from the fast responders, the call used to wait out every straggler, and
|
|
13127
13193
|
* the caller's own retry loop multiplied that into minutes of a chain not
|
|
13128
13194
|
* being scanned at all, silently. At the deadline the outstanding endpoints
|
|
13129
13195
|
* are treated as failures — named in the QuorumError, so the operator sees
|
|
13130
|
-
* which ones held the call up — and `finalize` decides
|
|
13131
|
-
*
|
|
13196
|
+
* which ones held the call up — and `finalize` decides over the endpoints
|
|
13197
|
+
* that answered.
|
|
13132
13198
|
*/
|
|
13133
13199
|
settleUntilQuorum(tasks, tryDecide, finalize) {
|
|
13134
13200
|
return new Promise((resolve, reject) => {
|
|
@@ -13146,13 +13212,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13146
13212
|
const evaluate = () => {
|
|
13147
13213
|
if (done) return;
|
|
13148
13214
|
try {
|
|
13149
|
-
const
|
|
13215
|
+
const threshold = quorumThreshold(tasks.length - failures.length);
|
|
13216
|
+
const early = tryDecide(fulfilled, threshold);
|
|
13150
13217
|
if (early !== void 0) {
|
|
13151
13218
|
finish(() => resolve(early));
|
|
13152
13219
|
return;
|
|
13153
13220
|
}
|
|
13154
13221
|
if (outstanding === 0) {
|
|
13155
|
-
const value = finalize(fulfilled, failures);
|
|
13222
|
+
const value = finalize(fulfilled, failures, threshold);
|
|
13156
13223
|
finish(() => resolve(value));
|
|
13157
13224
|
}
|
|
13158
13225
|
} catch (error) {
|
|
@@ -13170,7 +13237,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13170
13237
|
error: new Error(`no answer within the ${this.deadlineMs}ms quorum deadline`)
|
|
13171
13238
|
}));
|
|
13172
13239
|
try {
|
|
13173
|
-
const value = finalize(fulfilled, [...failures, ...stragglers]);
|
|
13240
|
+
const value = finalize(fulfilled, [...failures, ...stragglers], quorumThreshold(fulfilled.length));
|
|
13174
13241
|
finish(() => resolve(value));
|
|
13175
13242
|
} catch (error) {
|
|
13176
13243
|
finish(() => reject(error));
|
|
@@ -13197,22 +13264,22 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13197
13264
|
});
|
|
13198
13265
|
}
|
|
13199
13266
|
/**
|
|
13200
|
-
* Highest block head backed by a
|
|
13201
|
-
*
|
|
13267
|
+
* Highest block head backed by a quorum of the endpoints that answer. Throws
|
|
13268
|
+
* {@link QuorumError} when none do.
|
|
13202
13269
|
*/
|
|
13203
13270
|
async getBlockNumber() {
|
|
13204
|
-
const { queried, skipped,
|
|
13271
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13205
13272
|
return this.settleUntilQuorum(
|
|
13206
13273
|
queried.map(({ idx, client }) => ({ idx, task: client.getBlockNumber() })),
|
|
13207
13274
|
// Early exit as soon as a quorum is satisfiable. Late voters could
|
|
13208
13275
|
// only raise the reported head; the earlier (lower) head is
|
|
13209
13276
|
// conservative for every consumer (fewer confirmations counted,
|
|
13210
13277
|
// smaller scan windows).
|
|
13211
|
-
(fulfilled) => this.quorumHead(
|
|
13278
|
+
(fulfilled, threshold) => this.quorumHead(
|
|
13212
13279
|
fulfilled.map((f) => f.value),
|
|
13213
13280
|
threshold
|
|
13214
13281
|
) ?? void 0,
|
|
13215
|
-
(fulfilled, failures) => {
|
|
13282
|
+
(fulfilled, failures, threshold) => {
|
|
13216
13283
|
const head = this.quorumHead(
|
|
13217
13284
|
fulfilled.map((f) => f.value),
|
|
13218
13285
|
threshold
|
|
@@ -13253,7 +13320,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13253
13320
|
}
|
|
13254
13321
|
return views;
|
|
13255
13322
|
};
|
|
13256
|
-
const { queried, skipped,
|
|
13323
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13257
13324
|
return this.settleUntilQuorum(
|
|
13258
13325
|
queried.map(({ idx, client }) => ({
|
|
13259
13326
|
idx,
|
|
@@ -13272,8 +13339,8 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13272
13339
|
// Early exit only on a POSITIVE quorum: an agreeing inclusion group can
|
|
13273
13340
|
// only grow, so the first satisfiable quorum is final. Not-found votes
|
|
13274
13341
|
// never decide early — "not confirmed" needs the full response set.
|
|
13275
|
-
(fulfilled) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
13276
|
-
(fulfilled, failures) => {
|
|
13342
|
+
(fulfilled, threshold) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
13343
|
+
(fulfilled, failures, threshold) => {
|
|
13277
13344
|
const confirmations = aggregateConfirmations(toViews(fulfilled), threshold);
|
|
13278
13345
|
if (confirmations === null) {
|
|
13279
13346
|
throw new QuorumError(
|
|
@@ -13302,7 +13369,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13302
13369
|
}
|
|
13303
13370
|
return groups;
|
|
13304
13371
|
};
|
|
13305
|
-
const { queried, skipped,
|
|
13372
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
13306
13373
|
return this.settleUntilQuorum(
|
|
13307
13374
|
queried.map(({ idx, client }) => ({
|
|
13308
13375
|
idx,
|
|
@@ -13314,13 +13381,13 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13314
13381
|
// two disjoint groups can't both hold one), so the first satisfiable
|
|
13315
13382
|
// agreement is final — no need to wait out stragglers that could
|
|
13316
13383
|
// only join or lose.
|
|
13317
|
-
(fulfilled) => {
|
|
13384
|
+
(fulfilled, threshold) => {
|
|
13318
13385
|
for (const group of groupOf(fulfilled).values()) {
|
|
13319
13386
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
13320
13387
|
}
|
|
13321
13388
|
return void 0;
|
|
13322
13389
|
},
|
|
13323
|
-
(fulfilled, failures) => {
|
|
13390
|
+
(fulfilled, failures, threshold) => {
|
|
13324
13391
|
const groups = groupOf(fulfilled);
|
|
13325
13392
|
for (const group of groups.values()) {
|
|
13326
13393
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
@@ -18563,20 +18630,32 @@ var ContractInteractionService = class {
|
|
|
18563
18630
|
} else {
|
|
18564
18631
|
this.logger.warn({ reason: pmResult.reason }, "No paymaster for bid UserOp; relying on EntryPoint deposit");
|
|
18565
18632
|
}
|
|
18566
|
-
const
|
|
18567
|
-
order,
|
|
18568
|
-
fillOptions,
|
|
18633
|
+
const bidGas = {
|
|
18569
18634
|
solverAccount: solverAccountAddress,
|
|
18570
|
-
solverSigner: sdkSigningAccount(this.signer),
|
|
18571
18635
|
nonce,
|
|
18572
18636
|
entryPointAddress,
|
|
18573
18637
|
callGasLimit,
|
|
18574
18638
|
verificationGasLimit: cachedEstimate.verificationGasLimit,
|
|
18575
|
-
preVerificationGas: cachedEstimate.preVerificationGas,
|
|
18576
18639
|
maxFeePerGas: cachedEstimate.maxFeePerGas,
|
|
18577
18640
|
maxPriorityFeePerGas: cachedEstimate.maxPriorityFeePerGas,
|
|
18578
18641
|
callData,
|
|
18579
18642
|
paymasterAndData
|
|
18643
|
+
};
|
|
18644
|
+
let preVerificationGas = cachedEstimate.preVerificationGas;
|
|
18645
|
+
try {
|
|
18646
|
+
preVerificationGas = await sdkHelper.estimateBidPreVerificationGas(bidGas);
|
|
18647
|
+
} catch (err) {
|
|
18648
|
+
this.logger.warn(
|
|
18649
|
+
{ orderId: order.id, err, preVerificationGas: preVerificationGas.toString() },
|
|
18650
|
+
"Could not estimate the bid's preVerificationGas; using the fill estimate's"
|
|
18651
|
+
);
|
|
18652
|
+
}
|
|
18653
|
+
const userOp = await sdkHelper.prepareSubmitBid({
|
|
18654
|
+
...bidGas,
|
|
18655
|
+
order,
|
|
18656
|
+
fillOptions,
|
|
18657
|
+
solverSigner: sdkSigningAccount(this.signer),
|
|
18658
|
+
preVerificationGas
|
|
18580
18659
|
});
|
|
18581
18660
|
const encodedUserOp = encodeUserOpScale(userOp);
|
|
18582
18661
|
this.logger.info(
|
|
@@ -18585,6 +18664,7 @@ var ContractInteractionService = class {
|
|
|
18585
18664
|
solverAccount: solverAccountAddress,
|
|
18586
18665
|
nonce: nonce.toString(),
|
|
18587
18666
|
callGasLimit: callGasLimit.toString(),
|
|
18667
|
+
preVerificationGas: preVerificationGas.toString(),
|
|
18588
18668
|
maxFeePerGas: cachedEstimate.maxFeePerGas.toString()
|
|
18589
18669
|
},
|
|
18590
18670
|
"Prepared bid UserOp"
|
|
@@ -21276,8 +21356,15 @@ var IntentFiller = class {
|
|
|
21276
21356
|
const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
|
|
21277
21357
|
this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
|
|
21278
21358
|
if (validStrategies.length === 0) {
|
|
21279
|
-
|
|
21280
|
-
this.
|
|
21359
|
+
const priced = eligibleStrategies.some((s) => s !== null);
|
|
21360
|
+
this.logger.warn(
|
|
21361
|
+
{ orderId: order.id },
|
|
21362
|
+
priced ? "No profitable strategy found for order" : "No strategy can fill this order"
|
|
21363
|
+
);
|
|
21364
|
+
this.monitor.emit("orderSkipped", {
|
|
21365
|
+
orderId: order.id,
|
|
21366
|
+
reason: priced ? "No profitable strategy" : "No strategy can fill this order"
|
|
21367
|
+
});
|
|
21281
21368
|
return null;
|
|
21282
21369
|
}
|
|
21283
21370
|
this.logger.info(
|
|
@@ -21702,10 +21789,6 @@ import { Decimal as Decimal6 } from "decimal.js";
|
|
|
21702
21789
|
function limitOrderLegs(order) {
|
|
21703
21790
|
return order.side === "BID" ? { input: order.base, output: order.quote } : { input: order.quote, output: order.base };
|
|
21704
21791
|
}
|
|
21705
|
-
function availableOn(order) {
|
|
21706
|
-
const available = BigInt(order.remaining) - BigInt(order.reserved);
|
|
21707
|
-
return available > 0n ? available : 0n;
|
|
21708
|
-
}
|
|
21709
21792
|
function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21710
21793
|
const qualifying = orders.filter((order) => serves(order, incoming, resolve, now)).map((order) => {
|
|
21711
21794
|
const offer = offerFor({
|
|
@@ -21714,7 +21797,7 @@ function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new D
|
|
|
21714
21797
|
price: BigInt(order.price),
|
|
21715
21798
|
outputDecimals: incoming.outputDecimals
|
|
21716
21799
|
});
|
|
21717
|
-
const available =
|
|
21800
|
+
const available = BigInt(order.remaining);
|
|
21718
21801
|
return { order, offer, available, payout: offer < available ? offer : available };
|
|
21719
21802
|
}).filter((candidate) => candidate.offer >= incoming.requestedOutput && candidate.payout > 0n).sort(byBestOfferFirst);
|
|
21720
21803
|
return qualifying;
|
|
@@ -21724,16 +21807,63 @@ function byBestOfferFirst(a, b) {
|
|
|
21724
21807
|
if (a.available !== b.available) return a.available > b.available ? -1 : 1;
|
|
21725
21808
|
return a.order.id < b.order.id ? -1 : 1;
|
|
21726
21809
|
}
|
|
21810
|
+
function whyUnmatched(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21811
|
+
let furthest = -1;
|
|
21812
|
+
for (const order of orders) {
|
|
21813
|
+
const failed = firstFailed(order, incoming, resolve, now);
|
|
21814
|
+
furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
|
|
21815
|
+
}
|
|
21816
|
+
const { source, destination, inputSymbol } = incoming;
|
|
21817
|
+
if (furthest < CHECKS.length) {
|
|
21818
|
+
switch (CHECKS[Math.max(furthest, 0)]) {
|
|
21819
|
+
case "open":
|
|
21820
|
+
return "no limit order is open";
|
|
21821
|
+
case "chain":
|
|
21822
|
+
return `no open limit order fills on ${destination}`;
|
|
21823
|
+
case "input":
|
|
21824
|
+
return `no limit order on ${destination} takes ${inputSymbol} in`;
|
|
21825
|
+
case "output":
|
|
21826
|
+
return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
|
|
21827
|
+
case "source":
|
|
21828
|
+
return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
|
|
21829
|
+
}
|
|
21830
|
+
}
|
|
21831
|
+
const priced = orders.filter((order) => firstFailed(order, incoming, resolve, now) === null).map((order) => ({
|
|
21832
|
+
order,
|
|
21833
|
+
offer: offerFor({
|
|
21834
|
+
side: order.side,
|
|
21835
|
+
inputAmount: incoming.inputNet,
|
|
21836
|
+
price: BigInt(order.price),
|
|
21837
|
+
outputDecimals: incoming.outputDecimals
|
|
21838
|
+
})
|
|
21839
|
+
}));
|
|
21840
|
+
if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
|
|
21841
|
+
return "the limit orders that meet its rate have nothing left to pay out";
|
|
21842
|
+
}
|
|
21843
|
+
const best = priced.reduce((a, b) => b.offer > a.offer ? b : a);
|
|
21844
|
+
const symbol = limitOrderLegs(best.order).output;
|
|
21845
|
+
return `it asks for ${readable(incoming.requestedOutput)} ${symbol}; the best limit order offers ${readable(best.offer)} ${symbol}`;
|
|
21846
|
+
}
|
|
21847
|
+
var CHECKS = ["open", "chain", "input", "output", "source"];
|
|
21727
21848
|
function serves(order, incoming, resolve, now) {
|
|
21728
|
-
|
|
21729
|
-
|
|
21730
|
-
|
|
21849
|
+
return firstFailed(order, incoming, resolve, now) === null;
|
|
21850
|
+
}
|
|
21851
|
+
function firstFailed(order, incoming, resolve, now) {
|
|
21852
|
+
if (order.status !== "open") return "open";
|
|
21853
|
+
if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
|
|
21854
|
+
if (order.fillChain !== incoming.destination) return "chain";
|
|
21731
21855
|
const legs = limitOrderLegs(order);
|
|
21732
|
-
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return
|
|
21856
|
+
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
|
|
21733
21857
|
const outputToken = resolve(legs.output, order.fillChain);
|
|
21734
|
-
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return
|
|
21735
|
-
if (incoming.source === incoming.destination) return
|
|
21736
|
-
return order.acceptedSources.includes(incoming.source);
|
|
21858
|
+
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
|
|
21859
|
+
if (incoming.source === incoming.destination) return null;
|
|
21860
|
+
return order.acceptedSources.includes(incoming.source) ? null : "source";
|
|
21861
|
+
}
|
|
21862
|
+
function readable(amount) {
|
|
21863
|
+
const [whole, fraction] = toHuman(amount).split(".");
|
|
21864
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
21865
|
+
const cut = fraction?.slice(0, 6).replace(/0+$/, "");
|
|
21866
|
+
return cut ? `${grouped}.${cut}` : grouped;
|
|
21737
21867
|
}
|
|
21738
21868
|
|
|
21739
21869
|
// src/orderbook/usd.ts
|
|
@@ -21875,15 +22005,20 @@ var FXFiller = class _FXFiller {
|
|
|
21875
22005
|
}
|
|
21876
22006
|
try {
|
|
21877
22007
|
if (order.inputs.length !== order.output.assets.length) {
|
|
21878
|
-
this.logger.
|
|
21879
|
-
{ inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
22008
|
+
this.logger.info(
|
|
22009
|
+
{ orderId: order.id, inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
21880
22010
|
"Order input/output length mismatch or empty"
|
|
21881
22011
|
);
|
|
21882
22012
|
return false;
|
|
21883
22013
|
}
|
|
21884
22014
|
if ((await this.matchOrder(order)).length === 0) {
|
|
21885
|
-
this.logger.
|
|
21886
|
-
{
|
|
22015
|
+
this.logger.info(
|
|
22016
|
+
{
|
|
22017
|
+
orderId: order.id,
|
|
22018
|
+
sourceChain: order.source,
|
|
22019
|
+
destChain: order.destination,
|
|
22020
|
+
reason: await this.explainUnmatched(order)
|
|
22021
|
+
},
|
|
21887
22022
|
"No limit order matches this order"
|
|
21888
22023
|
);
|
|
21889
22024
|
return false;
|
|
@@ -22425,30 +22560,53 @@ var FXFiller = class _FXFiller {
|
|
|
22425
22560
|
*/
|
|
22426
22561
|
async matchLeg(order, leg) {
|
|
22427
22562
|
if (!this.limitOrders) return [];
|
|
22563
|
+
const incoming = await this.incomingFor(order, leg);
|
|
22564
|
+
if (!incoming) return [];
|
|
22565
|
+
return matchLimitOrders(
|
|
22566
|
+
await this.limitOrders.open(),
|
|
22567
|
+
incoming,
|
|
22568
|
+
(symbol, chain) => this.registry.getAddress(symbol, chain)
|
|
22569
|
+
);
|
|
22570
|
+
}
|
|
22571
|
+
/** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
|
|
22572
|
+
async explainUnmatched(order) {
|
|
22573
|
+
if (!this.limitOrders) return "limit orders are not loaded";
|
|
22574
|
+
const open = await this.limitOrders.open();
|
|
22575
|
+
const reasons = [];
|
|
22576
|
+
for (let leg = 0; leg < order.inputs.length; leg++) {
|
|
22577
|
+
const incoming = await this.incomingFor(order, leg);
|
|
22578
|
+
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}`;
|
|
22579
|
+
reasons.push(order.inputs.length > 1 ? `leg ${leg}: ${reason}` : reason);
|
|
22580
|
+
}
|
|
22581
|
+
return reasons.join("; ");
|
|
22582
|
+
}
|
|
22583
|
+
/**
|
|
22584
|
+
* One leg as the matcher sees it, or null when its input token is not one the asset
|
|
22585
|
+
* registry knows on the source chain.
|
|
22586
|
+
*
|
|
22587
|
+
* Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
|
|
22588
|
+
*/
|
|
22589
|
+
async incomingFor(order, leg) {
|
|
22428
22590
|
const input = order.inputs[leg];
|
|
22429
22591
|
const output = order.output.assets[leg];
|
|
22430
|
-
if (!input || !output) return
|
|
22592
|
+
if (!input || !output) return null;
|
|
22431
22593
|
const inputToken = bytes32ToBytes204(input.token);
|
|
22432
22594
|
const outputToken = bytes32ToBytes204(output.token);
|
|
22433
22595
|
const inputSymbol = this.registry.symbolFor(inputToken, order.source);
|
|
22434
|
-
if (!inputSymbol) return
|
|
22596
|
+
if (!inputSymbol) return null;
|
|
22435
22597
|
const [inputDecimals, outputDecimals] = await Promise.all([
|
|
22436
22598
|
this.contractService.getTokenDecimals(inputToken, order.source),
|
|
22437
22599
|
this.contractService.getTokenDecimals(outputToken, order.destination)
|
|
22438
22600
|
]);
|
|
22439
|
-
return
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
outputDecimals
|
|
22449
|
-
},
|
|
22450
|
-
(symbol, chain) => this.registry.getAddress(symbol, chain)
|
|
22451
|
-
);
|
|
22601
|
+
return {
|
|
22602
|
+
source: order.source,
|
|
22603
|
+
destination: order.destination,
|
|
22604
|
+
inputSymbol,
|
|
22605
|
+
outputToken,
|
|
22606
|
+
inputNet: toScaled(input.amount, inputDecimals),
|
|
22607
|
+
requestedOutput: toScaled(output.amount, outputDecimals),
|
|
22608
|
+
outputDecimals
|
|
22609
|
+
};
|
|
22452
22610
|
}
|
|
22453
22611
|
/**
|
|
22454
22612
|
* The order's input in **USD**, or null when no limit order connects its input
|
|
@@ -23526,48 +23684,42 @@ async function bootFiller(config, options) {
|
|
|
23526
23684
|
assertPairSymbolsResolve(config.pairs, assetRegistry, configuredChainNames);
|
|
23527
23685
|
}
|
|
23528
23686
|
const adminStrategies = [];
|
|
23529
|
-
const
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
|
|
23536
|
-
|
|
23537
|
-
|
|
23538
|
-
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
token1: pair.token1,
|
|
23542
|
-
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23543
|
-
});
|
|
23687
|
+
const tradingPairs = (config.pairs ?? []).map((pair) => ({
|
|
23688
|
+
token0: pair.token0,
|
|
23689
|
+
token1: pair.token1
|
|
23690
|
+
}));
|
|
23691
|
+
tradingPairs.forEach((pair, pairIndex) => {
|
|
23692
|
+
adminStrategies.push({
|
|
23693
|
+
index: pairIndex,
|
|
23694
|
+
pairIndex,
|
|
23695
|
+
exotic: `${pair.token0}/${pair.token1}`,
|
|
23696
|
+
token0: pair.token0,
|
|
23697
|
+
token1: pair.token1,
|
|
23698
|
+
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23544
23699
|
});
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
)
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
23561
|
-
|
|
23562
|
-
|
|
23700
|
+
});
|
|
23701
|
+
const confirmationPolicy = assertConfirmationCoverage(
|
|
23702
|
+
config.confirmationPolicies,
|
|
23703
|
+
resolvedChains.map((c) => c.chainId)
|
|
23704
|
+
);
|
|
23705
|
+
const fundingVenues = vaultVenue ? [vaultVenue] : [];
|
|
23706
|
+
const engine = new FXFiller(
|
|
23707
|
+
runtimeSigner,
|
|
23708
|
+
configService,
|
|
23709
|
+
chainClientManager,
|
|
23710
|
+
contractService,
|
|
23711
|
+
tradingPairs,
|
|
23712
|
+
assetRegistry,
|
|
23713
|
+
{ confirmationPolicy, fundingVenues, limitOrders: options.data.limitOrders }
|
|
23714
|
+
);
|
|
23715
|
+
logger.info("Hydrating funding venue state...");
|
|
23716
|
+
await engine.initialise();
|
|
23717
|
+
const strategies = [engine];
|
|
23563
23718
|
const haltControls = strategies.map((engine2, index) => ({
|
|
23564
23719
|
index,
|
|
23565
23720
|
isHalted: () => engine2.isHalted(),
|
|
23566
23721
|
resetHalt: () => engine2.resetHalt()
|
|
23567
23722
|
}));
|
|
23568
|
-
if (vaultVenue) {
|
|
23569
|
-
await vaultVenue.initialise(runtimeSigner.address);
|
|
23570
|
-
}
|
|
23571
23723
|
let rebalancingService;
|
|
23572
23724
|
const rebalancingConfig = configService.getRebalancingConfig();
|
|
23573
23725
|
if (rebalancingConfig?.triggerPercentage !== void 0 && rebalancingConfig?.baseBalances) {
|
|
@@ -24084,8 +24236,8 @@ var MemoryLimitOrderStore = class {
|
|
|
24084
24236
|
async reserve(id, amount) {
|
|
24085
24237
|
const order = this.orders.get(id);
|
|
24086
24238
|
if (!order || order.status !== "open") return false;
|
|
24239
|
+
if (BigInt(amount) > BigInt(order.remaining)) return false;
|
|
24087
24240
|
const reserved = BigInt(order.reserved) + BigInt(amount);
|
|
24088
|
-
if (reserved > BigInt(order.remaining)) return false;
|
|
24089
24241
|
this.patch(id, { reserved: reserved.toString() });
|
|
24090
24242
|
return true;
|
|
24091
24243
|
}
|
|
@@ -24161,6 +24313,8 @@ var MAX_BLOCK_RANGE = 1000n;
|
|
|
24161
24313
|
var PUBLISH_CHUNK = 250;
|
|
24162
24314
|
var yieldToConsumers = () => new Promise((resolve) => setImmediate(resolve));
|
|
24163
24315
|
var STOP_DRAIN_TIMEOUT_MS = 5e3;
|
|
24316
|
+
var STALL_THRESHOLD_MS = 45e3;
|
|
24317
|
+
var STALL_REPORT_INTERVAL_MS = 6e4;
|
|
24164
24318
|
var ChainScanner = class {
|
|
24165
24319
|
constructor(target, loggers = defaultLoggerContext(), scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS) {
|
|
24166
24320
|
this.target = target;
|
|
@@ -24174,6 +24328,18 @@ var ChainScanner = class {
|
|
|
24174
24328
|
this.errorHandler = () => {
|
|
24175
24329
|
};
|
|
24176
24330
|
this.stopped = false;
|
|
24331
|
+
/**
|
|
24332
|
+
* How far past the cursor the next pass reads, as `toBlock - fromBlock`:
|
|
24333
|
+
* halved after a failed read, doubled after a good one, between 0 (one block)
|
|
24334
|
+
* and {@link MAX_BLOCK_RANGE}.
|
|
24335
|
+
*
|
|
24336
|
+
* Free endpoints cap `eth_getLogs` ranges far below the full span — on
|
|
24337
|
+
* Arbitrum three of seven refused 100 blocks — and the cursor never moves
|
|
24338
|
+
* past a range it failed to read. At a fixed span a catch-up that outgrew
|
|
24339
|
+
* those caps was stuck for good: the range only grew, so the capped endpoints
|
|
24340
|
+
* never rejoined and the quorum never formed.
|
|
24341
|
+
*/
|
|
24342
|
+
this.span = MAX_BLOCK_RANGE;
|
|
24177
24343
|
this.logger = loggers.get("chain-scanner");
|
|
24178
24344
|
this.quorumClient = new QuorumPublicClient(target.chainId, target.rpcUrls, loggers);
|
|
24179
24345
|
if (this.quorumClient.size > 1) {
|
|
@@ -24216,8 +24382,9 @@ var ChainScanner = class {
|
|
|
24216
24382
|
if (this.timer || this.stopped) return;
|
|
24217
24383
|
this.logger.info({ chainId: this.target.chainId }, "Block scanner started");
|
|
24218
24384
|
this.timer = setInterval(() => {
|
|
24219
|
-
if (this.mutex.isLocked()) return;
|
|
24385
|
+
if (this.mutex.isLocked()) return this.reportStall();
|
|
24220
24386
|
void this.mutex.runExclusive(async () => {
|
|
24387
|
+
this.pass = { phase: "head", startedAt: Date.now(), reportedAt: 0 };
|
|
24221
24388
|
try {
|
|
24222
24389
|
await this.scan();
|
|
24223
24390
|
} catch (error) {
|
|
@@ -24231,10 +24398,66 @@ var ChainScanner = class {
|
|
|
24231
24398
|
"Error in block scanner"
|
|
24232
24399
|
);
|
|
24233
24400
|
this.errorHandler(error);
|
|
24401
|
+
} finally {
|
|
24402
|
+
this.notePassEnd();
|
|
24234
24403
|
}
|
|
24235
24404
|
});
|
|
24236
24405
|
}, this.scanIntervalMs);
|
|
24237
24406
|
}
|
|
24407
|
+
/**
|
|
24408
|
+
* Records which await the current pass is sitting on, for the watchdog.
|
|
24409
|
+
*
|
|
24410
|
+
* A no-op when no pass is registered — `scan()` is driven directly by tests
|
|
24411
|
+
* and by nothing else, so there is no watchdog to inform.
|
|
24412
|
+
*/
|
|
24413
|
+
enter(phase) {
|
|
24414
|
+
if (this.pass) this.pass.phase = phase;
|
|
24415
|
+
}
|
|
24416
|
+
/**
|
|
24417
|
+
* Says that a pass has outlived the quorum budgets and names the phase it is
|
|
24418
|
+
* stuck in, then repeats every {@link STALL_REPORT_INTERVAL_MS} until it ends.
|
|
24419
|
+
*
|
|
24420
|
+
* Observation only: the pass is left alone. Aborting it would need a cancel
|
|
24421
|
+
* path through the quorum client that does not exist, and the cursor is safe
|
|
24422
|
+
* either way — a pass that never completes never advances it.
|
|
24423
|
+
*/
|
|
24424
|
+
reportStall() {
|
|
24425
|
+
const pass = this.pass;
|
|
24426
|
+
if (!pass) return;
|
|
24427
|
+
const now = Date.now();
|
|
24428
|
+
const heldMs = now - pass.startedAt;
|
|
24429
|
+
if (heldMs < STALL_THRESHOLD_MS) return;
|
|
24430
|
+
if (pass.reportedAt !== 0 && now - pass.reportedAt < STALL_REPORT_INTERVAL_MS) return;
|
|
24431
|
+
pass.reportedAt = now;
|
|
24432
|
+
this.logger.warn(
|
|
24433
|
+
{
|
|
24434
|
+
chainId: this.target.chainId,
|
|
24435
|
+
phase: pass.phase,
|
|
24436
|
+
heldMs,
|
|
24437
|
+
cursor: this.cursor?.toString(),
|
|
24438
|
+
rpcUrls: this.target.rpcUrls
|
|
24439
|
+
},
|
|
24440
|
+
"Block scan pass has outlived its budget \u2014 this chain is not being scanned"
|
|
24441
|
+
);
|
|
24442
|
+
}
|
|
24443
|
+
/**
|
|
24444
|
+
* Closes out a pass, and says so if the watchdog complained about it — without
|
|
24445
|
+
* this the log shows a chain going quiet and never shows it coming back.
|
|
24446
|
+
*/
|
|
24447
|
+
notePassEnd() {
|
|
24448
|
+
const pass = this.pass;
|
|
24449
|
+
this.pass = void 0;
|
|
24450
|
+
if (!pass || pass.reportedAt === 0) return;
|
|
24451
|
+
this.logger.warn(
|
|
24452
|
+
{
|
|
24453
|
+
chainId: this.target.chainId,
|
|
24454
|
+
phase: pass.phase,
|
|
24455
|
+
heldMs: Date.now() - pass.startedAt,
|
|
24456
|
+
cursor: this.cursor?.toString()
|
|
24457
|
+
},
|
|
24458
|
+
"Block scan pass finished after outliving its budget \u2014 scanning has resumed"
|
|
24459
|
+
);
|
|
24460
|
+
}
|
|
24238
24461
|
/**
|
|
24239
24462
|
* Points this loop at different endpoints, keeping the cursor.
|
|
24240
24463
|
*
|
|
@@ -24276,11 +24499,12 @@ var ChainScanner = class {
|
|
|
24276
24499
|
}
|
|
24277
24500
|
if (currentBlock <= this.cursor) return;
|
|
24278
24501
|
const fromBlock = this.cursor + 1n;
|
|
24279
|
-
const toBlock = fromBlock +
|
|
24502
|
+
const toBlock = fromBlock + this.span > currentBlock ? currentBlock : fromBlock + this.span;
|
|
24280
24503
|
this.logger.debug(
|
|
24281
24504
|
{ chainId: this.target.chainId, fromBlock, toBlock, gap: Number(toBlock - fromBlock) },
|
|
24282
24505
|
"Scanning blocks"
|
|
24283
24506
|
);
|
|
24507
|
+
this.enter("logs");
|
|
24284
24508
|
let logs;
|
|
24285
24509
|
try {
|
|
24286
24510
|
logs = await retryPromise3(
|
|
@@ -24302,6 +24526,7 @@ var ChainScanner = class {
|
|
|
24302
24526
|
);
|
|
24303
24527
|
} catch (error) {
|
|
24304
24528
|
if (isBlockRangeError(error)) return;
|
|
24529
|
+
this.narrow(toBlock - fromBlock);
|
|
24305
24530
|
throw error;
|
|
24306
24531
|
}
|
|
24307
24532
|
if (this.stopped) return;
|
|
@@ -24312,6 +24537,7 @@ var ChainScanner = class {
|
|
|
24312
24537
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: placed.length },
|
|
24313
24538
|
"Found OrderPlaced events in block scan"
|
|
24314
24539
|
);
|
|
24540
|
+
this.enter("publish-orders");
|
|
24315
24541
|
await this.publishOrders(placed);
|
|
24316
24542
|
}
|
|
24317
24543
|
if (filled.length > 0) {
|
|
@@ -24319,10 +24545,36 @@ var ChainScanner = class {
|
|
|
24319
24545
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: filled.length },
|
|
24320
24546
|
"Found OrderFilled events in block scan"
|
|
24321
24547
|
);
|
|
24548
|
+
this.enter("publish-fills");
|
|
24322
24549
|
await this.publishFills(filled);
|
|
24323
24550
|
}
|
|
24324
24551
|
if (this.stopped) return;
|
|
24325
24552
|
this.cursor = toBlock;
|
|
24553
|
+
this.widen();
|
|
24554
|
+
}
|
|
24555
|
+
/**
|
|
24556
|
+
* Halves the span below the range that just failed, so the next pass asks
|
|
24557
|
+
* for something a range-capped endpoint will answer. Halving the attempted
|
|
24558
|
+
* range rather than the span matters near the head, where a pass reads fewer
|
|
24559
|
+
* blocks than the span allows.
|
|
24560
|
+
*/
|
|
24561
|
+
narrow(attempted) {
|
|
24562
|
+
const next = attempted / 2n;
|
|
24563
|
+
if (next >= this.span) return;
|
|
24564
|
+
this.span = next;
|
|
24565
|
+
this.logger.warn(
|
|
24566
|
+
{ chainId: this.target.chainId, span: Number(next) },
|
|
24567
|
+
"Narrowing the block scan range after a failed read"
|
|
24568
|
+
);
|
|
24569
|
+
}
|
|
24570
|
+
/** Doubles the span back toward {@link MAX_BLOCK_RANGE} after a good read. */
|
|
24571
|
+
widen() {
|
|
24572
|
+
if (this.span >= MAX_BLOCK_RANGE) return;
|
|
24573
|
+
const next = this.span === 0n ? 1n : this.span * 2n;
|
|
24574
|
+
this.span = next > MAX_BLOCK_RANGE ? MAX_BLOCK_RANGE : next;
|
|
24575
|
+
if (this.span === MAX_BLOCK_RANGE) {
|
|
24576
|
+
this.logger.info({ chainId: this.target.chainId }, "Block scan range back to full");
|
|
24577
|
+
}
|
|
24326
24578
|
}
|
|
24327
24579
|
async publishOrders(logs) {
|
|
24328
24580
|
const rebuilt = reconstructOrdersFromLogs(logs, {
|
|
@@ -24689,7 +24941,7 @@ var LimitOrderController = class {
|
|
|
24689
24941
|
]);
|
|
24690
24942
|
return { order, fills, bids };
|
|
24691
24943
|
}
|
|
24692
|
-
/** The pairs the orderbook lists,
|
|
24944
|
+
/** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
|
|
24693
24945
|
books() {
|
|
24694
24946
|
return this.service.books();
|
|
24695
24947
|
}
|
|
@@ -24737,10 +24989,7 @@ var PairController = class {
|
|
|
24737
24989
|
* the pair's symbols have to resolve before the market can be hydrated.
|
|
24738
24990
|
*/
|
|
24739
24991
|
async add(pair, options) {
|
|
24740
|
-
const { engine,
|
|
24741
|
-
if (!engine || !tradingPairs) {
|
|
24742
|
-
throw new Error("No trading engine is running \u2014 this filler was started without pairs");
|
|
24743
|
-
}
|
|
24992
|
+
const { engine, assetRegistry, adminStrategies, balanceTokens, config } = this.runtime;
|
|
24744
24993
|
const assets = options?.assets;
|
|
24745
24994
|
const nextAssets = { ...config.assets ?? {}, ...assets ?? {} };
|
|
24746
24995
|
const nextPairs = [...this.pairs, pair];
|
|
@@ -24779,7 +25028,6 @@ var PairController = class {
|
|
|
24779
25028
|
/** Closes a market. Orders already in flight against it still complete. */
|
|
24780
25029
|
async remove(index) {
|
|
24781
25030
|
const { engine, tradingPairs, adminStrategies, config } = this.runtime;
|
|
24782
|
-
if (!engine || !tradingPairs) throw new Error("No trading engine is running");
|
|
24783
25031
|
if (index < 0 || index >= this.pairs.length) throw new Error(`Unknown pair ${index}`);
|
|
24784
25032
|
engine.removePair(tradingPairs[index]);
|
|
24785
25033
|
config.pairs = this.pairs.filter((_, i) => i !== index);
|
|
@@ -24791,7 +25039,7 @@ var PairController = class {
|
|
|
24791
25039
|
await this.persist();
|
|
24792
25040
|
}
|
|
24793
25041
|
livePair(index) {
|
|
24794
|
-
const pair = this.runtime.tradingPairs
|
|
25042
|
+
const pair = this.runtime.tradingPairs[index];
|
|
24795
25043
|
if (!pair) throw new Error(`Unknown pair ${index}`);
|
|
24796
25044
|
return pair;
|
|
24797
25045
|
}
|
|
@@ -24883,9 +25131,9 @@ var ChainController = class {
|
|
|
24883
25131
|
throw error;
|
|
24884
25132
|
}
|
|
24885
25133
|
const curve = confirmationPolicies[String(chainId)];
|
|
24886
|
-
if (curve) this.runtime.confirmationPolicy
|
|
24887
|
-
else if (!this.runtime.confirmationPolicy
|
|
24888
|
-
this.runtime.confirmationPolicy
|
|
25134
|
+
if (curve) this.runtime.confirmationPolicy.add(chainId, curve);
|
|
25135
|
+
else if (!this.runtime.confirmationPolicy.has(chainId)) {
|
|
25136
|
+
this.runtime.confirmationPolicy.adopt(chainId, resolvedPolicy);
|
|
24889
25137
|
}
|
|
24890
25138
|
this.runtime.resolvedChains.push(resolved);
|
|
24891
25139
|
config.chains = [...config.chains, { rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl }];
|
|
@@ -24913,7 +25161,7 @@ var ChainController = class {
|
|
|
24913
25161
|
await intentFiller.removeChain(chainId);
|
|
24914
25162
|
if (this.ownsScanner) await this.scanner.removeChain(chainId);
|
|
24915
25163
|
configService.removeChain(chainId);
|
|
24916
|
-
this.runtime.confirmationPolicy
|
|
25164
|
+
this.runtime.confirmationPolicy.remove(chainId);
|
|
24917
25165
|
this.runtime.chainClientManager.invalidate(chainKey);
|
|
24918
25166
|
this.runtime.resolvedChains.splice(index, 1);
|
|
24919
25167
|
config.chains = config.chains.filter((_, i) => i !== index);
|