@hyperbridge/simplex 0.16.5 → 0.16.7
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 +603 -186
- package/dist/bin/simplex.js.map +1 -1
- package/dist/index.cjs +387 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +110 -50
- package/dist/index.d.ts +110 -50
- package/dist/index.js +387 -79
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/index-Be446eYK.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-Bv9S1oj4.js +0 -106
- package/dist/ui/assets/index-DwJCXJ6a.css +0 -1
package/dist/bin/simplex.js
CHANGED
|
@@ -70714,13 +70714,13 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
70714
70714
|
}
|
|
70715
70715
|
function convertReadableWritablePair(pair, context) {
|
|
70716
70716
|
assertDictionary(pair, context);
|
|
70717
|
-
const
|
|
70718
|
-
assertRequiredField(
|
|
70719
|
-
assertReadableStream(
|
|
70717
|
+
const readable2 = pair === null || pair === void 0 ? void 0 : pair.readable;
|
|
70718
|
+
assertRequiredField(readable2, "readable", "ReadableWritablePair");
|
|
70719
|
+
assertReadableStream(readable2, `${context} has member 'readable' that`);
|
|
70720
70720
|
const writable = pair === null || pair === void 0 ? void 0 : pair.writable;
|
|
70721
70721
|
assertRequiredField(writable, "writable", "ReadableWritablePair");
|
|
70722
70722
|
assertWritableStream(writable, `${context} has member 'writable' that`);
|
|
70723
|
-
return { readable, writable };
|
|
70723
|
+
return { readable: readable2, writable };
|
|
70724
70724
|
}
|
|
70725
70725
|
class ReadableStream2 {
|
|
70726
70726
|
constructor(rawUnderlyingSource = {}, rawStrategy = {}) {
|
|
@@ -71399,7 +71399,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
71399
71399
|
if (controller._finishPromise !== void 0) {
|
|
71400
71400
|
return controller._finishPromise;
|
|
71401
71401
|
}
|
|
71402
|
-
const
|
|
71402
|
+
const readable2 = stream4._readable;
|
|
71403
71403
|
controller._finishPromise = newPromise((resolve11, reject) => {
|
|
71404
71404
|
controller._finishPromise_resolve = resolve11;
|
|
71405
71405
|
controller._finishPromise_reject = reject;
|
|
@@ -71407,15 +71407,15 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
71407
71407
|
const cancelPromise = controller._cancelAlgorithm(reason);
|
|
71408
71408
|
TransformStreamDefaultControllerClearAlgorithms(controller);
|
|
71409
71409
|
uponPromise(cancelPromise, () => {
|
|
71410
|
-
if (
|
|
71411
|
-
defaultControllerFinishPromiseReject(controller,
|
|
71410
|
+
if (readable2._state === "errored") {
|
|
71411
|
+
defaultControllerFinishPromiseReject(controller, readable2._storedError);
|
|
71412
71412
|
} else {
|
|
71413
|
-
ReadableStreamDefaultControllerError(
|
|
71413
|
+
ReadableStreamDefaultControllerError(readable2._readableStreamController, reason);
|
|
71414
71414
|
defaultControllerFinishPromiseResolve(controller);
|
|
71415
71415
|
}
|
|
71416
71416
|
return null;
|
|
71417
71417
|
}, (r7) => {
|
|
71418
|
-
ReadableStreamDefaultControllerError(
|
|
71418
|
+
ReadableStreamDefaultControllerError(readable2._readableStreamController, r7);
|
|
71419
71419
|
defaultControllerFinishPromiseReject(controller, r7);
|
|
71420
71420
|
return null;
|
|
71421
71421
|
});
|
|
@@ -71426,7 +71426,7 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
71426
71426
|
if (controller._finishPromise !== void 0) {
|
|
71427
71427
|
return controller._finishPromise;
|
|
71428
71428
|
}
|
|
71429
|
-
const
|
|
71429
|
+
const readable2 = stream4._readable;
|
|
71430
71430
|
controller._finishPromise = newPromise((resolve11, reject) => {
|
|
71431
71431
|
controller._finishPromise_resolve = resolve11;
|
|
71432
71432
|
controller._finishPromise_reject = reject;
|
|
@@ -71434,15 +71434,15 @@ var require_ponyfill_es2018 = __commonJS({
|
|
|
71434
71434
|
const flushPromise = controller._flushAlgorithm();
|
|
71435
71435
|
TransformStreamDefaultControllerClearAlgorithms(controller);
|
|
71436
71436
|
uponPromise(flushPromise, () => {
|
|
71437
|
-
if (
|
|
71438
|
-
defaultControllerFinishPromiseReject(controller,
|
|
71437
|
+
if (readable2._state === "errored") {
|
|
71438
|
+
defaultControllerFinishPromiseReject(controller, readable2._storedError);
|
|
71439
71439
|
} else {
|
|
71440
|
-
ReadableStreamDefaultControllerClose(
|
|
71440
|
+
ReadableStreamDefaultControllerClose(readable2._readableStreamController);
|
|
71441
71441
|
defaultControllerFinishPromiseResolve(controller);
|
|
71442
71442
|
}
|
|
71443
71443
|
return null;
|
|
71444
71444
|
}, (r7) => {
|
|
71445
|
-
ReadableStreamDefaultControllerError(
|
|
71445
|
+
ReadableStreamDefaultControllerError(readable2._readableStreamController, r7);
|
|
71446
71446
|
defaultControllerFinishPromiseReject(controller, r7);
|
|
71447
71447
|
return null;
|
|
71448
71448
|
});
|
|
@@ -214231,13 +214231,19 @@ function describeError(error) {
|
|
|
214231
214231
|
if (graphqlErrors?.length) return graphqlErrors.map((e4) => e4.message).join("; ");
|
|
214232
214232
|
return error instanceof Error ? error.message : String(error);
|
|
214233
214233
|
}
|
|
214234
|
+
function sameSymbol(a6, b5) {
|
|
214235
|
+
return a6.toLowerCase() === b5.toLowerCase();
|
|
214236
|
+
}
|
|
214234
214237
|
function findBook(books, tokenA, tokenB) {
|
|
214235
214238
|
const book = books.find(
|
|
214236
|
-
(b5) => b5.base
|
|
214239
|
+
(b5) => sameSymbol(b5.base, tokenA) && sameSymbol(b5.quote, tokenB) || sameSymbol(b5.base, tokenB) && sameSymbol(b5.quote, tokenA)
|
|
214237
214240
|
);
|
|
214238
214241
|
if (!book) throw new OrderbookRequestError(`no book trades ${tokenA} for ${tokenB}`);
|
|
214239
214242
|
return book;
|
|
214240
214243
|
}
|
|
214244
|
+
function optionalBigInt(value) {
|
|
214245
|
+
return value === null ? null : BigInt(value);
|
|
214246
|
+
}
|
|
214241
214247
|
function parseRate(raw) {
|
|
214242
214248
|
return {
|
|
214243
214249
|
side: raw.side,
|
|
@@ -214282,24 +214288,32 @@ function toRoute(tokenIn, tokenOut) {
|
|
|
214282
214288
|
function requiredInput(side, amountOut, rate) {
|
|
214283
214289
|
return side === "BID" ? divCeil(amountOut * ORDERBOOK_SCALE, rate) : divCeil(amountOut * rate, ORDERBOOK_SCALE);
|
|
214284
214290
|
}
|
|
214285
|
-
function
|
|
214286
|
-
|
|
214287
|
-
|
|
214291
|
+
function swapQuoteTotals(quote) {
|
|
214292
|
+
const worstFill = quote.fills.at(-1);
|
|
214293
|
+
if (quote.fillable && !worstFill)
|
|
214294
|
+
throw new OrderbookRequestError("the orderbook served a fillable quote with no fills");
|
|
214288
214295
|
return {
|
|
214289
|
-
|
|
214290
|
-
amountIn,
|
|
214291
|
-
amountOut,
|
|
214292
|
-
|
|
214293
|
-
|
|
214294
|
-
|
|
214295
|
-
|
|
214296
|
-
|
|
214297
|
-
|
|
214298
|
-
|
|
214299
|
-
|
|
214300
|
-
|
|
214301
|
-
|
|
214302
|
-
|
|
214296
|
+
side: quote.side,
|
|
214297
|
+
amountIn: quote.amountIn,
|
|
214298
|
+
amountOut: quote.fills.reduce((total, fill) => total + fill.amountOut, 0n),
|
|
214299
|
+
fillable: quote.fillable,
|
|
214300
|
+
maxFillableIn: quote.maxFillableIn,
|
|
214301
|
+
worstIn: worstFill?.amountIn ?? 0n,
|
|
214302
|
+
worstOut: worstFill?.amountOut ?? 0n
|
|
214303
|
+
};
|
|
214304
|
+
}
|
|
214305
|
+
function levelQuoteTotals(quote) {
|
|
214306
|
+
if (quote.fillable && quote.rate === null) {
|
|
214307
|
+
throw new OrderbookRequestError("the orderbook served a fillable quote with no rate");
|
|
214308
|
+
}
|
|
214309
|
+
return {
|
|
214310
|
+
side: quote.side,
|
|
214311
|
+
amountIn: quote.amountIn,
|
|
214312
|
+
amountOut: quote.amountOut,
|
|
214313
|
+
fillable: quote.fillable,
|
|
214314
|
+
maxFillableIn: quote.maxFillableIn,
|
|
214315
|
+
worstIn: quote.amountIn,
|
|
214316
|
+
worstOut: quote.amountOut
|
|
214303
214317
|
};
|
|
214304
214318
|
}
|
|
214305
214319
|
function toOrderbookAmount(amount, decimals) {
|
|
@@ -214385,7 +214399,7 @@ function encodePhantomBidDeclaration(declaration) {
|
|
|
214385
214399
|
}
|
|
214386
214400
|
return u8aToHex(new Uint8Array(bytes4));
|
|
214387
214401
|
}
|
|
214388
|
-
var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError,
|
|
214402
|
+
var import_merge_race, __defProp3, __getOwnPropNames2, __esm2, __export2, node_exports, wasm, full_path, __dirname$1, cachedTextDecoder, cachedUint8ArrayMemory0, WASM_VECTOR_LEN, cachedTextEncoder, encodeString5, cachedDataViewMemory0, KeccakMergeFinalization, KeccakMerge, wasmPath, wasmBytes2, bindings, wasmModule, wasmInstance, __wasm, init_node11, RequestStatus, TimeoutStatus, IntentOrderStatus, ABI, evmHost_default, ABI2, handlerV2_default, polkadotAssetHubPaseo, polkadotHubMainnet, pharosMainnet, pharosAtlantic, tronNile, chainConfigs, configsByStateMachineId, getConfigByStateMachineId, getChainId3, getViemChain, hyperbridgeAddress, ABI3, IntentGatewayV2_default, ChainConfigService, ABI4, uniswapRouterV2_default, chains, DEFAULT_ADDRESS, EvmChain, REQUEST_COMMITMENTS_SLOT, RESPONSE_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, STATE_COMMITMENTS_SLOT, MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, MissingConsensusUpdateTimeError, TokenBucket, BATCHES_NOT_SUPPORTED_CODE, TOO_BIG_BATCH_REQUEST_CODE, DEFAULT_MAX_BATCH_SIZE, _endpoint2, _headers3, _limiter, _maxBatchSize, _batchingSupported, _pending2, _flushTimer, _flushing, _nextId, __BatchingHttpProvider_instances, scheduleFlush_fn, flushNow_fn, flush_fn, post_fn, settleBatch_fn, settle_fn, handleBatchRefusal_fn, requeue_fn, _a5, BatchingHttpProvider, OFFCHAIN_BID_PREFIX, HYPERBRIDGE_TYPES_BUNDLE, BASE_TIP, HTTP_CONNECT_TIMEOUT_MS, INCLUSION_TIMEOUT_MS, DEFAULT_RPC_MAX_RPS, rpcLimiters, BidCodec, PackedUserOperationCodec, IntentsCoprocessor, ReviveContractInfo, ReviveAccountType, ReviveAccountInfo, H256, EvmStateProof, SubstrateHashing, SubstrateStateMachineProof, BasicProof, LeafIndexAndPos, MmrProof, ConsensusStateId, ConsensusMessage, FraudProofMessage, StateMachine, StateMachineId, StateMachineHeight, Proof, PostRequest, PostResponse, GetRequest, StorageValue, GetResponse, Request3, Response2, RequestMessage, RequestResponse, ResponseMessage, TimeoutMessage, GetRequestsWithProof, Message, DEFAULT_CHILD_STORAGE_PREFIX, PharosProofNodeCodec, SiblingLeftmostLeafProofCodec, NonExistenceProofCodec, AccountProofDataCodec, PharosStateProof, DEFAULT_POLL_INTERVAL, ADDRESS_ZERO2, DEFAULT_GRAFFITI, DEFAULT_LOGGER, REQUEST_STATUS_WEIGHTS, TIMEOUT_STATUS_WEIGHTS, COMBINED_STATUS_WEIGHTS, CHAIN_ID_TO_COINGECKO_NATIVE, TESTNET_CHAINS, ORDER_STATUS, loadDriver, detectEnvironment, STORAGE_KEYS, LEGACY_STORAGE_KEYS, ABI5, uniswapV3Quoter_default, UNISWAP_V4_QUOTER_ABI, ABI6, universalRouter_default, PERMIT2_ABI, UniswapQuoteEngine, COMMON_FEE_TIERS, Swap, ABI7, erc7281_default, DEFAULT_GRAFFITI2, ERC7821_BATCH_MODE, BundlerMethod, SELECT_SOLVER_TYPEHASH, PACKED_USEROP_TYPEHASH, DOMAIN_TYPEHASH, CryptoUtils, FEE_TOKEN_CACHE_TTL_MS, OrderPlacer, TOKEN_KEYED_GATEWAY_ABI, keyingByClient, PARTIAL_FILLS_SLOT, USED_USEROPS_STORAGE_KEY, OrderExecutor, _a6, OrderCanceller, FILL_ORDER_SELECTOR, SUPPORTED_INTENTS_VERSION, CONTRACT_VERSION_ABI, CANONICAL_EVM_TOKEN, ENTRY_POINT_EVENT_ABI, BidImpl, BidManager, RELAYER_MESSAGE_GAS, NO_BUNDLER_FILL_GAS_BASE, NO_BUNDLER_FILL_GAS_PER_OUTPUT, NO_BUNDLER_PAYMASTER_VERIFICATION_GAS, NO_BUNDLER_PAYMASTER_POST_OP_GAS, GasEstimator, OrderStatusChecker, ORDERBOOK_URLS, ORDERBOOK_DECIMALS, ORDERBOOK_SCALE, OrderbookRequestError, RATE_FIELDS, TOP_OF_BOOK_QUERY, ROUTE_LIQUIDITY_QUERY, QUOTE_QUERY, QUOTE_PESSIMISTIC_QUERY, HyperFxOrderbook, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, OrderbookQuoteNotConvergedError, MAX_EXACT_OUTPUT_ROUNDS, OrderbookMarket, ORDER_FEE_GAS_PRICE_BUMP_POLICY, IntentGateway, DECLARATION_V1, DECLARATION_V2, PAYMASTER_ADDRESS_BYTES, PAYMASTER_GAS_LIMIT_BYTES, PAYMASTER_DATA_OFFSET, PERMIT2_DATA_BYTES, PERMIT2_SPONSORSHIP_BYTES, MAX_DECLARED_ENTRIES, MAX_TOKEN_ID_BYTES, TeleportParams, MultiAccount;
|
|
214389
214403
|
var init_node10 = __esm({
|
|
214390
214404
|
"../sdk/dist/node/index.js"() {
|
|
214391
214405
|
"use strict";
|
|
@@ -228328,14 +228342,20 @@ query TopOfBook($tokenA: String!, $tokenB: String!, $fillChain: String!, $source
|
|
|
228328
228342
|
query RouteLiquidity($route: RouteInput!) {
|
|
228329
228343
|
books { id base quote }
|
|
228330
228344
|
routeLiquidity(route: $route) {
|
|
228331
|
-
route bestRate depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
|
|
228345
|
+
route bestRate slippageBps depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
|
|
228332
228346
|
}
|
|
228333
228347
|
}`;
|
|
228334
228348
|
QUOTE_QUERY = `
|
|
228335
228349
|
query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
228336
228350
|
quote(route: $route, amountIn: $amountIn) {
|
|
228337
|
-
route side amountIn
|
|
228338
|
-
fills { orderRate amountOut
|
|
228351
|
+
route side amountIn slippageBps fillable maxFillableIn
|
|
228352
|
+
fills { advertisedSize orderRate amountIn amountOut }
|
|
228353
|
+
}
|
|
228354
|
+
}`;
|
|
228355
|
+
QUOTE_PESSIMISTIC_QUERY = `
|
|
228356
|
+
query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
|
|
228357
|
+
quotePessimistic(route: $route, amountIn: $amountIn) {
|
|
228358
|
+
route side amountIn amountOut rate priceBucket slippageBps fillable maxFillableIn
|
|
228339
228359
|
}
|
|
228340
228360
|
}`;
|
|
228341
228361
|
HyperFxOrderbook = class {
|
|
@@ -228365,8 +228385,9 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228365
228385
|
return {
|
|
228366
228386
|
route: raw.route,
|
|
228367
228387
|
book,
|
|
228368
|
-
side: book.base
|
|
228369
|
-
bestRate:
|
|
228388
|
+
side: sameSymbol(book.base, route.tokenIn) ? "BID" : "ASK",
|
|
228389
|
+
bestRate: optionalBigInt(raw.bestRate),
|
|
228390
|
+
slippageBps: raw.slippageBps,
|
|
228370
228391
|
depthIn: BigInt(raw.depthIn),
|
|
228371
228392
|
depthOut: BigInt(raw.depthOut),
|
|
228372
228393
|
availableLiquidity: BigInt(raw.availableLiquidity),
|
|
@@ -228375,7 +228396,10 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228375
228396
|
solverCount: raw.solverCount
|
|
228376
228397
|
};
|
|
228377
228398
|
}
|
|
228378
|
-
/**
|
|
228399
|
+
/**
|
|
228400
|
+
* The optimistic quote for `amountIn` (1e18 tokenIn) on a route: the route's orders, best price
|
|
228401
|
+
* first, each taking what it can at its own price. The fills' `amountOut`s sum to the output.
|
|
228402
|
+
*/
|
|
228379
228403
|
async quote(route, amountIn) {
|
|
228380
228404
|
const { quote: raw } = await this.request(QUOTE_QUERY, {
|
|
228381
228405
|
route,
|
|
@@ -228385,18 +228409,39 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228385
228409
|
route: raw.route,
|
|
228386
228410
|
side: raw.side,
|
|
228387
228411
|
amountIn: BigInt(raw.amountIn),
|
|
228388
|
-
|
|
228389
|
-
rate: raw.rate === null ? null : BigInt(raw.rate),
|
|
228412
|
+
slippageBps: raw.slippageBps,
|
|
228390
228413
|
fillable: raw.fillable,
|
|
228391
|
-
depth: BigInt(raw.depth),
|
|
228392
228414
|
maxFillableIn: BigInt(raw.maxFillableIn),
|
|
228393
228415
|
fills: raw.fills.map((fill) => ({
|
|
228416
|
+
advertisedSize: BigInt(fill.advertisedSize),
|
|
228394
228417
|
orderRate: BigInt(fill.orderRate),
|
|
228395
|
-
|
|
228396
|
-
|
|
228418
|
+
amountIn: BigInt(fill.amountIn),
|
|
228419
|
+
amountOut: BigInt(fill.amountOut)
|
|
228397
228420
|
}))
|
|
228398
228421
|
};
|
|
228399
228422
|
}
|
|
228423
|
+
/**
|
|
228424
|
+
* The pessimistic quote for `amountIn` (1e18 tokenIn) on a route: one price, from the first level
|
|
228425
|
+
* deep enough to fill the whole trade by itself, at which every order in it fills; the route's
|
|
228426
|
+
* worst price when no one level can but its levels together can.
|
|
228427
|
+
*/
|
|
228428
|
+
async quotePessimistic(route, amountIn) {
|
|
228429
|
+
const { quotePessimistic: raw } = await this.request(
|
|
228430
|
+
QUOTE_PESSIMISTIC_QUERY,
|
|
228431
|
+
{ route, amountIn: amountIn.toString() }
|
|
228432
|
+
);
|
|
228433
|
+
return {
|
|
228434
|
+
route: raw.route,
|
|
228435
|
+
side: raw.side,
|
|
228436
|
+
amountIn: BigInt(raw.amountIn),
|
|
228437
|
+
amountOut: BigInt(raw.amountOut),
|
|
228438
|
+
rate: optionalBigInt(raw.rate),
|
|
228439
|
+
priceBucket: optionalBigInt(raw.priceBucket),
|
|
228440
|
+
slippageBps: raw.slippageBps,
|
|
228441
|
+
fillable: raw.fillable,
|
|
228442
|
+
maxFillableIn: BigInt(raw.maxFillableIn)
|
|
228443
|
+
};
|
|
228444
|
+
}
|
|
228400
228445
|
async request(query3, variables) {
|
|
228401
228446
|
try {
|
|
228402
228447
|
return await this.client.request(query3, variables);
|
|
@@ -228431,18 +228476,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228431
228476
|
this.name = "OrderbookQuoteNotConvergedError";
|
|
228432
228477
|
}
|
|
228433
228478
|
};
|
|
228434
|
-
InsufficientOrderbookLiquidityError = class extends Error {
|
|
228435
|
-
constructor(route, maxFillableIn) {
|
|
228436
|
-
super(
|
|
228437
|
-
`The HyperFX orderbook cannot fill ${route.tokenIn} -> ${route.tokenOut} on ${route.sourceChain} -> ${route.destinationChain}; max fillable input is ${maxFillableIn} raw units`
|
|
228438
|
-
);
|
|
228439
|
-
__publicField(this, "route");
|
|
228440
|
-
__publicField(this, "maxFillableIn");
|
|
228441
|
-
this.route = route;
|
|
228442
|
-
this.maxFillableIn = maxFillableIn;
|
|
228443
|
-
this.name = "InsufficientOrderbookLiquidityError";
|
|
228444
|
-
}
|
|
228445
|
-
};
|
|
228446
228479
|
MAX_EXACT_OUTPUT_ROUNDS = 16;
|
|
228447
228480
|
OrderbookMarket = class {
|
|
228448
228481
|
constructor(configService, orderbook) {
|
|
@@ -228452,26 +228485,67 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228452
228485
|
this.orderbook = orderbook;
|
|
228453
228486
|
}
|
|
228454
228487
|
async quoteIntent(params, sourceChain, destinationChain) {
|
|
228488
|
+
const orderbook = this.orderbook();
|
|
228489
|
+
if (!params.optimistic) {
|
|
228490
|
+
const { quote: quote2, tokenIn: tokenIn2, tokenOut: tokenOut2 } = await this.priceIntent(
|
|
228491
|
+
params,
|
|
228492
|
+
sourceChain,
|
|
228493
|
+
destinationChain,
|
|
228494
|
+
(route, amountIn) => orderbook.quotePessimistic(route, amountIn),
|
|
228495
|
+
levelQuoteTotals
|
|
228496
|
+
);
|
|
228497
|
+
return {
|
|
228498
|
+
route: quote2.route,
|
|
228499
|
+
side: quote2.side,
|
|
228500
|
+
amountIn: fromOrderbookAmount(quote2.amountIn, tokenIn2.decimals),
|
|
228501
|
+
amountOut: fromOrderbookAmount(quote2.amountOut, tokenOut2.decimals),
|
|
228502
|
+
rate: quote2.rate,
|
|
228503
|
+
priceBucket: quote2.priceBucket,
|
|
228504
|
+
slippageBps: quote2.slippageBps,
|
|
228505
|
+
fillable: quote2.fillable,
|
|
228506
|
+
maxFillableIn: fromOrderbookAmount(quote2.maxFillableIn, tokenIn2.decimals)
|
|
228507
|
+
};
|
|
228508
|
+
}
|
|
228509
|
+
const { quote, tokenIn, tokenOut } = await this.priceIntent(
|
|
228510
|
+
params,
|
|
228511
|
+
sourceChain,
|
|
228512
|
+
destinationChain,
|
|
228513
|
+
(route, amountIn) => orderbook.quote(route, amountIn),
|
|
228514
|
+
swapQuoteTotals
|
|
228515
|
+
);
|
|
228516
|
+
return {
|
|
228517
|
+
route: quote.route,
|
|
228518
|
+
side: quote.side,
|
|
228519
|
+
amountIn: fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
|
|
228520
|
+
slippageBps: quote.slippageBps,
|
|
228521
|
+
fillable: quote.fillable,
|
|
228522
|
+
maxFillableIn: fromOrderbookAmount(quote.maxFillableIn, tokenIn.decimals),
|
|
228523
|
+
legs: quote.fills.map((fill) => ({
|
|
228524
|
+
advertisedSize: fromOrderbookAmount(fill.advertisedSize, tokenOut.decimals),
|
|
228525
|
+
orderRate: fill.orderRate,
|
|
228526
|
+
amountIn: fromOrderbookAmount(fill.amountIn, tokenIn.decimals),
|
|
228527
|
+
amountOut: fromOrderbookAmount(fill.amountOut, tokenOut.decimals)
|
|
228528
|
+
}))
|
|
228529
|
+
};
|
|
228530
|
+
}
|
|
228531
|
+
/**
|
|
228532
|
+
* The orderbook's quote for an exact input, or for the input found to deliver
|
|
228533
|
+
* an exact output, with the assets it trades. A route that cannot fill the
|
|
228534
|
+
* trade answers with an unfillable quote rather than an error.
|
|
228535
|
+
*/
|
|
228536
|
+
async priceIntent(params, sourceChain, destinationChain, price, totals) {
|
|
228455
228537
|
validateQuoteParams(params);
|
|
228456
228538
|
const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
|
|
228457
228539
|
const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
|
|
228458
228540
|
const route = toRoute(tokenIn, tokenOut);
|
|
228459
228541
|
if (params.amountIn !== void 0) {
|
|
228460
|
-
const quote2 = await
|
|
228461
|
-
|
|
228462
|
-
|
|
228463
|
-
return buildQuote("EXACT_INPUT", params.amountIn, amountOut2, quote2, tokenIn, tokenOut);
|
|
228542
|
+
const quote2 = await price(route, toOrderbookAmount(params.amountIn, tokenIn.decimals));
|
|
228543
|
+
totals(quote2);
|
|
228544
|
+
return { quote: quote2, tokenIn, tokenOut };
|
|
228464
228545
|
}
|
|
228465
|
-
const
|
|
228466
|
-
const quote = await this.quoteExactOutput(route,
|
|
228467
|
-
return
|
|
228468
|
-
"EXACT_OUTPUT",
|
|
228469
|
-
fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
|
|
228470
|
-
amountOut,
|
|
228471
|
-
quote,
|
|
228472
|
-
tokenIn,
|
|
228473
|
-
tokenOut
|
|
228474
|
-
);
|
|
228546
|
+
const targetOut = toOrderbookAmount(params.amountOut, tokenOut.decimals);
|
|
228547
|
+
const quote = await this.quoteExactOutput(route, targetOut, tokenIn, price, totals);
|
|
228548
|
+
return { quote, tokenIn, tokenOut };
|
|
228475
228549
|
}
|
|
228476
228550
|
async availableLiquidity(params, sourceChain, destinationChain) {
|
|
228477
228551
|
const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
|
|
@@ -228524,22 +228598,29 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228524
228598
|
}
|
|
228525
228599
|
/**
|
|
228526
228600
|
* The orderbook only quotes an input amount, so an output is priced by
|
|
228527
|
-
* guessing the input from the best rate
|
|
228528
|
-
*
|
|
228601
|
+
* guessing the input from the best rate, then keeping the part of each quote
|
|
228602
|
+
* filled better than its worst rate and re-pricing the rest of `targetOut` at
|
|
228603
|
+
* the input-to-output ratio that part filled at, until a quote delivers it.
|
|
228604
|
+
* That ratio is the orderbook's own, with the protocol fee already taken off.
|
|
228605
|
+
*
|
|
228606
|
+
* An output the route cannot deliver is answered by quoting an input past its
|
|
228607
|
+
* `maxFillableIn`, which the orderbook serves as unfillable: the smallest such
|
|
228608
|
+
* input guessed from the best rate, or one raw unit when no order serves it.
|
|
228529
228609
|
*/
|
|
228530
|
-
async quoteExactOutput(route, targetOut, tokenIn) {
|
|
228531
|
-
const
|
|
228532
|
-
const liquidity = await orderbook.routeLiquidity(route);
|
|
228533
|
-
if (liquidity.bestRate === null) throw this.insufficient(route, 0n, tokenIn);
|
|
228610
|
+
async quoteExactOutput(route, targetOut, tokenIn, price, totals) {
|
|
228611
|
+
const liquidity = await this.orderbook().routeLiquidity(route);
|
|
228534
228612
|
const inputUnit = toOrderbookAmount(1n, tokenIn.decimals);
|
|
228535
|
-
let amountIn = roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
|
|
228613
|
+
let amountIn = liquidity.bestRate === null ? inputUnit : roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
|
|
228536
228614
|
let lastAmountIn = amountIn;
|
|
228537
228615
|
for (let round2 = 0; round2 < MAX_EXACT_OUTPUT_ROUNDS; round2++) {
|
|
228538
|
-
|
|
228539
|
-
const quote =
|
|
228540
|
-
if (quote.fillable && quote.amountOut >= targetOut) return
|
|
228616
|
+
const served = await price(route, amountIn);
|
|
228617
|
+
const quote = totals(served);
|
|
228618
|
+
if (quote.fillable && quote.amountOut >= targetOut) return served;
|
|
228619
|
+
if (!quote.fillable && amountIn > liquidity.maxFillableIn) return served;
|
|
228541
228620
|
lastAmountIn = amountIn;
|
|
228542
|
-
const
|
|
228621
|
+
const betterIn = quote.amountIn - quote.worstIn;
|
|
228622
|
+
const betterOut = quote.amountOut - quote.worstOut;
|
|
228623
|
+
const next = quote.fillable && quote.worstOut > 0n ? betterIn + roundUpTo(divCeil((targetOut - betterOut) * quote.worstIn, quote.worstOut), inputUnit) : amountIn;
|
|
228543
228624
|
amountIn = next > amountIn ? next : amountIn + inputUnit;
|
|
228544
228625
|
}
|
|
228545
228626
|
throw new OrderbookQuoteNotConvergedError(
|
|
@@ -228548,9 +228629,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228548
228629
|
fromOrderbookAmount(lastAmountIn, tokenIn.decimals)
|
|
228549
228630
|
);
|
|
228550
228631
|
}
|
|
228551
|
-
insufficient(route, maxFillableIn, tokenIn) {
|
|
228552
|
-
return new InsufficientOrderbookLiquidityError(route, fromOrderbookAmount(maxFillableIn, tokenIn.decimals));
|
|
228553
|
-
}
|
|
228554
228632
|
assetByAddress(chain2, address) {
|
|
228555
228633
|
const resolvedChain = resolveChain(chain2);
|
|
228556
228634
|
return withDecimals(
|
|
@@ -228709,22 +228787,6 @@ query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
|
228709
228787
|
this.orderbook = typeof orderbook === "string" ? new HyperFxOrderbook(orderbook) : orderbook;
|
|
228710
228788
|
return this;
|
|
228711
228789
|
}
|
|
228712
|
-
/**
|
|
228713
|
-
* Quotes an intent between this gateway's source and destination chains from
|
|
228714
|
-
* the HyperFX orderbook. Provide exactly one of `amountIn` or `amountOut`, in
|
|
228715
|
-
* raw token units.
|
|
228716
|
-
*
|
|
228717
|
-
* An exact-input quote is the orderbook's clearing price for `amountIn`,
|
|
228718
|
-
* which may combine several solvers' orders. An exact-output quote finds the
|
|
228719
|
-
* smallest input whose clearing price delivers `amountOut`. A cross-chain
|
|
228720
|
-
* route only counts orders whose solvers accept the source chain.
|
|
228721
|
-
*
|
|
228722
|
-
* The orderbook's rates already carry the gateway protocol fee, so the
|
|
228723
|
-
* returned amounts can be placed as the order's inputs and outputs directly.
|
|
228724
|
-
*
|
|
228725
|
-
* @throws {InsufficientOrderbookLiquidityError} When the route cannot fill the amount.
|
|
228726
|
-
* @throws {OrderbookRequestError} When the orderbook is unreachable or trades no book for the pair.
|
|
228727
|
-
*/
|
|
228728
228790
|
async quoteIntent(params) {
|
|
228729
228791
|
return this.market.quoteIntent(params, this.source.config.stateMachineId, this.dest.config.stateMachineId);
|
|
228730
228792
|
}
|
|
@@ -229954,6 +230016,7 @@ var init_external_links = __esm({
|
|
|
229954
230016
|
"src/config/external-links.json"() {
|
|
229955
230017
|
external_links_default = {
|
|
229956
230018
|
hyperfxApp: "https://app.hyperfx.finance",
|
|
230019
|
+
alchemyDashboard: "https://dashboard.alchemy.com/",
|
|
229957
230020
|
hyperbridgeExplorers: {
|
|
229958
230021
|
mainnet: "https://nexus.statescan.io",
|
|
229959
230022
|
testnet: "https://gargantua.statescan.io"
|
|
@@ -229987,12 +230050,58 @@ function chainByChainId(chainId) {
|
|
|
229987
230050
|
function chainByAlchemySubdomain(subdomain) {
|
|
229988
230051
|
return INIT_CHAINS.find((chain2) => chain2.alchemySubdomain === subdomain);
|
|
229989
230052
|
}
|
|
229990
|
-
var CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
|
|
230053
|
+
var FREE_RPC_URLS, CHAIN_NATIVE_SYMBOLS, INIT_CHAINS, HYPERBRIDGE_WS_DEFAULTS;
|
|
229991
230054
|
var init_chains2 = __esm({
|
|
229992
230055
|
"src/cli/init/chains.ts"() {
|
|
229993
230056
|
"use strict";
|
|
229994
230057
|
init_esm_shims();
|
|
229995
230058
|
init_external_links();
|
|
230059
|
+
FREE_RPC_URLS = {
|
|
230060
|
+
1: [
|
|
230061
|
+
"https://mainnet.gateway.tenderly.co",
|
|
230062
|
+
"https://rpc.mevblocker.io",
|
|
230063
|
+
"https://ethereum-rpc.publicnode.com",
|
|
230064
|
+
"https://ethereum.public.blockpi.network/v1/rpc/public",
|
|
230065
|
+
"https://eth.blockrazor.xyz",
|
|
230066
|
+
"https://eth.api.pocket.network",
|
|
230067
|
+
"https://ethereum-public.nodies.app",
|
|
230068
|
+
"https://eth-mainnet.public.blastapi.io",
|
|
230069
|
+
"https://public-eth.nownodes.io"
|
|
230070
|
+
],
|
|
230071
|
+
56: [
|
|
230072
|
+
"https://bsc-rpc.publicnode.com",
|
|
230073
|
+
"https://public-bsc-mainnet.fastnode.io",
|
|
230074
|
+
"https://public-bsc.nownodes.io",
|
|
230075
|
+
"https://bsc.blockrazor.xyz",
|
|
230076
|
+
"https://rpc-bsc.48.club",
|
|
230077
|
+
"https://bsc.api.pocket.network"
|
|
230078
|
+
],
|
|
230079
|
+
137: [
|
|
230080
|
+
"https://polygon-bor-rpc.publicnode.com",
|
|
230081
|
+
"https://polygon.drpc.org",
|
|
230082
|
+
"https://polygon.gateway.tenderly.co",
|
|
230083
|
+
"https://rpc.private.mev-x.com/polygon",
|
|
230084
|
+
"https://poly.api.pocket.network"
|
|
230085
|
+
],
|
|
230086
|
+
8453: [
|
|
230087
|
+
"https://base-rpc.publicnode.com",
|
|
230088
|
+
"https://base.gateway.tenderly.co",
|
|
230089
|
+
"https://base.public.blockpi.network/v1/rpc/public",
|
|
230090
|
+
"https://base.rpc.blxrbdn.com",
|
|
230091
|
+
"https://rpc.baseazul.dev",
|
|
230092
|
+
"https://mainnet.base.org",
|
|
230093
|
+
"https://base-public.nodies.app"
|
|
230094
|
+
],
|
|
230095
|
+
42161: [
|
|
230096
|
+
"https://arbitrum-one-rpc.publicnode.com",
|
|
230097
|
+
"https://arbitrum.gateway.tenderly.co",
|
|
230098
|
+
"https://arbitrum.drpc.org",
|
|
230099
|
+
"https://public-arb-mainnet.fastnode.io",
|
|
230100
|
+
"https://arb-one.api.pocket.network",
|
|
230101
|
+
"https://arbitrum-one-public.nodies.app",
|
|
230102
|
+
"https://arb1.arbitrum.io/rpc"
|
|
230103
|
+
]
|
|
230104
|
+
};
|
|
229996
230105
|
CHAIN_NATIVE_SYMBOLS = {
|
|
229997
230106
|
1: "ETH",
|
|
229998
230107
|
56: "BNB",
|
|
@@ -230017,7 +230126,8 @@ var init_chains2 = __esm({
|
|
|
230017
230126
|
label: "Ethereum",
|
|
230018
230127
|
network: "mainnet",
|
|
230019
230128
|
alchemySubdomain: "eth-mainnet",
|
|
230020
|
-
explorerUrl: external_links_default.chainExplorers.ethereum
|
|
230129
|
+
explorerUrl: external_links_default.chainExplorers.ethereum,
|
|
230130
|
+
defaultRpcUrls: FREE_RPC_URLS[1]
|
|
230021
230131
|
},
|
|
230022
230132
|
{
|
|
230023
230133
|
chainId: 42161,
|
|
@@ -230025,7 +230135,8 @@ var init_chains2 = __esm({
|
|
|
230025
230135
|
label: "Arbitrum",
|
|
230026
230136
|
network: "mainnet",
|
|
230027
230137
|
alchemySubdomain: "arb-mainnet",
|
|
230028
|
-
explorerUrl: external_links_default.chainExplorers.arbitrum
|
|
230138
|
+
explorerUrl: external_links_default.chainExplorers.arbitrum,
|
|
230139
|
+
defaultRpcUrls: FREE_RPC_URLS[42161]
|
|
230029
230140
|
},
|
|
230030
230141
|
{
|
|
230031
230142
|
chainId: 8453,
|
|
@@ -230033,7 +230144,8 @@ var init_chains2 = __esm({
|
|
|
230033
230144
|
label: "Base",
|
|
230034
230145
|
network: "mainnet",
|
|
230035
230146
|
alchemySubdomain: "base-mainnet",
|
|
230036
|
-
explorerUrl: external_links_default.chainExplorers.base
|
|
230147
|
+
explorerUrl: external_links_default.chainExplorers.base,
|
|
230148
|
+
defaultRpcUrls: FREE_RPC_URLS[8453]
|
|
230037
230149
|
},
|
|
230038
230150
|
{
|
|
230039
230151
|
chainId: 137,
|
|
@@ -230041,7 +230153,8 @@ var init_chains2 = __esm({
|
|
|
230041
230153
|
label: "Polygon",
|
|
230042
230154
|
network: "mainnet",
|
|
230043
230155
|
alchemySubdomain: "polygon-mainnet",
|
|
230044
|
-
explorerUrl: external_links_default.chainExplorers.polygon
|
|
230156
|
+
explorerUrl: external_links_default.chainExplorers.polygon,
|
|
230157
|
+
defaultRpcUrls: FREE_RPC_URLS[137]
|
|
230045
230158
|
},
|
|
230046
230159
|
{
|
|
230047
230160
|
chainId: 56,
|
|
@@ -230049,7 +230162,8 @@ var init_chains2 = __esm({
|
|
|
230049
230162
|
label: "BNB Chain",
|
|
230050
230163
|
network: "mainnet",
|
|
230051
230164
|
alchemySubdomain: "bnb-mainnet",
|
|
230052
|
-
explorerUrl: external_links_default.chainExplorers.bnb
|
|
230165
|
+
explorerUrl: external_links_default.chainExplorers.bnb,
|
|
230166
|
+
defaultRpcUrls: FREE_RPC_URLS[56]
|
|
230053
230167
|
},
|
|
230054
230168
|
{
|
|
230055
230169
|
chainId: 11155111,
|
|
@@ -295054,12 +295168,12 @@ var init_lib3 = __esm({
|
|
|
295054
295168
|
return Promise.resolve(ab);
|
|
295055
295169
|
}
|
|
295056
295170
|
stream() {
|
|
295057
|
-
const
|
|
295058
|
-
|
|
295171
|
+
const readable2 = new Readable3();
|
|
295172
|
+
readable2._read = function() {
|
|
295059
295173
|
};
|
|
295060
|
-
|
|
295061
|
-
|
|
295062
|
-
return
|
|
295174
|
+
readable2.push(this[BUFFER]);
|
|
295175
|
+
readable2.push(null);
|
|
295176
|
+
return readable2;
|
|
295063
295177
|
}
|
|
295064
295178
|
toString() {
|
|
295065
295179
|
return "[object Blob]";
|
|
@@ -316113,6 +316227,19 @@ async function collectRpcUrls(state, prefill) {
|
|
|
316113
316227
|
for (const chain2 of state.chains) {
|
|
316114
316228
|
const existing = prefillRpcFor(chain2.meta.chainId, prefill);
|
|
316115
316229
|
const derived2 = alchemy.candidate(chain2.meta.chainId);
|
|
316230
|
+
const bundled = chain2.meta.defaultRpcUrls;
|
|
316231
|
+
if (!existing?.length && !derived2 && bundled?.length) {
|
|
316232
|
+
const usePublic = guard(
|
|
316233
|
+
await confirm({
|
|
316234
|
+
message: `Use ${bundled.length} public RPC endpoints for ${chain2.meta.label}?`,
|
|
316235
|
+
initialValue: true
|
|
316236
|
+
})
|
|
316237
|
+
);
|
|
316238
|
+
if (usePublic) {
|
|
316239
|
+
chain2.rpcUrls = [...bundled];
|
|
316240
|
+
continue;
|
|
316241
|
+
}
|
|
316242
|
+
}
|
|
316116
316243
|
let url2;
|
|
316117
316244
|
if (existing?.length) {
|
|
316118
316245
|
url2 = await askRpcUrl(chain2.meta, existing[0]);
|
|
@@ -316290,6 +316417,22 @@ async function stepBundlers(state, prefill) {
|
|
|
316290
316417
|
for (const chain2 of alchemyChains) chain2.bundlerUrl = chain2.rpcUrls[0];
|
|
316291
316418
|
}
|
|
316292
316419
|
}
|
|
316420
|
+
const alchemyCapable = state.chains.filter((chain2) => !chain2.bundlerUrl && chain2.meta.alchemySubdomain);
|
|
316421
|
+
if (alchemyCapable.length > 0) {
|
|
316422
|
+
const useKey = guard(
|
|
316423
|
+
await confirm({
|
|
316424
|
+
message: `Configure the bundler for ${alchemyCapable.map((c6) => c6.meta.label).join(", ")} from one Alchemy API key?`,
|
|
316425
|
+
initialValue: true
|
|
316426
|
+
})
|
|
316427
|
+
);
|
|
316428
|
+
if (useKey) {
|
|
316429
|
+
const apiKey = await askSecret("Alchemy API key");
|
|
316430
|
+
for (const chain2 of alchemyCapable) {
|
|
316431
|
+
const url2 = deriveAlchemyRpc(apiKey, chain2.meta.chainId);
|
|
316432
|
+
if (url2) chain2.bundlerUrl = url2;
|
|
316433
|
+
}
|
|
316434
|
+
}
|
|
316435
|
+
}
|
|
316293
316436
|
const pimlico = new ProviderDerivation({
|
|
316294
316437
|
detect: (url2) => parsePimlicoUrl(url2)?.apiKey ?? null,
|
|
316295
316438
|
derive: derivePimlicoBundler,
|
|
@@ -318650,10 +318793,16 @@ var CANCEL_ORDER_MUTATION = `
|
|
|
318650
318793
|
`;
|
|
318651
318794
|
var OrderbookRequestError2 = class extends Error {
|
|
318652
318795
|
};
|
|
318796
|
+
function graphqlEndpoint(url2) {
|
|
318797
|
+
const trimmed = url2.trim();
|
|
318798
|
+
if (trimmed.includes("?")) return trimmed;
|
|
318799
|
+
const withoutTrailingSlash = trimmed.replace(/\/+$/, "");
|
|
318800
|
+
return withoutTrailingSlash.endsWith("/graphql") ? withoutTrailingSlash : `${withoutTrailingSlash}/graphql`;
|
|
318801
|
+
}
|
|
318653
318802
|
var OrderbookClient = class {
|
|
318654
318803
|
constructor(url2, requestTimeoutMs, loggers = defaultLoggerContext()) {
|
|
318655
|
-
this.url = url2;
|
|
318656
318804
|
this.requestTimeoutMs = requestTimeoutMs;
|
|
318805
|
+
this.url = graphqlEndpoint(url2);
|
|
318657
318806
|
this.logger = loggers.get("orderbook");
|
|
318658
318807
|
}
|
|
318659
318808
|
/** The limits to validate against before posting, plus the books on offer. */
|
|
@@ -318751,7 +318900,9 @@ var OrderbookClient = class {
|
|
|
318751
318900
|
clearTimeout(timer);
|
|
318752
318901
|
}
|
|
318753
318902
|
if (!response.ok) {
|
|
318754
|
-
throw new OrderbookRequestError2(
|
|
318903
|
+
throw new OrderbookRequestError2(
|
|
318904
|
+
`Orderbook at ${this.url} returned HTTP ${response.status} ${response.statusText}`
|
|
318905
|
+
);
|
|
318755
318906
|
}
|
|
318756
318907
|
const body = await response.json().catch((err) => {
|
|
318757
318908
|
throw new OrderbookRequestError2(`Orderbook returned a body that is not JSON: ${describe(err)}`);
|
|
@@ -318870,7 +319021,7 @@ var HEARTBEAT_TYPES = {
|
|
|
318870
319021
|
var LimitOrderValidationError = class extends Error {
|
|
318871
319022
|
};
|
|
318872
319023
|
var LimitOrderService = class {
|
|
318873
|
-
constructor(store, client, contractService, configService, assetRegistry, signer, defaultTtlSecs, delegationService, loggers = defaultLoggerContext(), startingNonce = initialOrderNonce) {
|
|
319024
|
+
constructor(store, client, contractService, configService, assetRegistry, signer, defaultTtlSecs, delegationService, loggers = defaultLoggerContext(), startingNonce = initialOrderNonce, vaultBalances) {
|
|
318874
319025
|
this.store = store;
|
|
318875
319026
|
this.client = client;
|
|
318876
319027
|
this.contractService = contractService;
|
|
@@ -318880,6 +319031,7 @@ var LimitOrderService = class {
|
|
|
318880
319031
|
this.defaultTtlSecs = defaultTtlSecs;
|
|
318881
319032
|
this.delegationService = delegationService;
|
|
318882
319033
|
this.startingNonce = startingNonce;
|
|
319034
|
+
this.vaultBalances = vaultBalances;
|
|
318883
319035
|
this.logger = loggers.get("limit-orders");
|
|
318884
319036
|
}
|
|
318885
319037
|
/**
|
|
@@ -319015,6 +319167,18 @@ var LimitOrderService = class {
|
|
|
319015
319167
|
throw err;
|
|
319016
319168
|
}
|
|
319017
319169
|
}
|
|
319170
|
+
/**
|
|
319171
|
+
* The pairs an order can be written against, the smallest payout each token may carry, and
|
|
319172
|
+
* the tokens the orderbook registers on each chain.
|
|
319173
|
+
*
|
|
319174
|
+
* The orderbook lists the books; `resolveBook` refuses a request naming anything else, so the
|
|
319175
|
+
* operator is offered exactly these and no combination of symbols they could not post. The
|
|
319176
|
+
* chains do the same for the source chains an order can accept.
|
|
319177
|
+
*/
|
|
319178
|
+
async books() {
|
|
319179
|
+
const limits = await this.limits();
|
|
319180
|
+
return { books: limits.books, minOrderSizes: limits.serverInfo.minOrderSizes, chains: limits.chains ?? [] };
|
|
319181
|
+
}
|
|
319018
319182
|
/**
|
|
319019
319183
|
* Reads `serverInfo` and `books`, reusing the last read for {@link LIMITS_TTL_MS}.
|
|
319020
319184
|
*
|
|
@@ -319095,6 +319259,18 @@ var LimitOrderService = class {
|
|
|
319095
319259
|
);
|
|
319096
319260
|
}
|
|
319097
319261
|
}
|
|
319262
|
+
if (book.base !== book.quote) {
|
|
319263
|
+
const input = normalizeSymbol(request3.tokenIn);
|
|
319264
|
+
const lacking = sources.filter((source) => {
|
|
319265
|
+
const listed = limits.chains?.find((chain2) => chain2.id === source);
|
|
319266
|
+
return listed && !listed.tokens.some((token) => normalizeSymbol(token.symbol) === input);
|
|
319267
|
+
});
|
|
319268
|
+
if (lacking.length > 0) {
|
|
319269
|
+
throw new LimitOrderValidationError(
|
|
319270
|
+
`The orderbook has no ${request3.tokenIn} on ${lacking.join(", ")}, so swaps from there cannot pay this order`
|
|
319271
|
+
);
|
|
319272
|
+
}
|
|
319273
|
+
}
|
|
319098
319274
|
if (ttlSecs < limits.serverInfo.minOrderTtlSecs) {
|
|
319099
319275
|
throw new LimitOrderValidationError(
|
|
319100
319276
|
`ttlSecs must be at least the orderbook's minimum of ${limits.serverInfo.minOrderTtlSecs}; got ${ttlSecs}`
|
|
@@ -319122,28 +319298,61 @@ var LimitOrderService = class {
|
|
|
319122
319298
|
* balance on the fill chain, so the operator hears it while they are creating
|
|
319123
319299
|
* the order.
|
|
319124
319300
|
*
|
|
319301
|
+
* A fill pays out of the wallet and then withdraws any shortfall from the
|
|
319302
|
+
* configured ERC-4626 vaults in the same batch, so what those vaults hold of
|
|
319303
|
+
* the payout token backs the order too and is counted here. Without it an
|
|
319304
|
+
* operator who sweeps inventory into a vault is refused orders their filler
|
|
319305
|
+
* would have filled.
|
|
319306
|
+
*
|
|
319125
319307
|
* Each order is checked against the whole balance, not against what is left of
|
|
319126
319308
|
* it after the others. One balance backs every order resting on it — that is
|
|
319127
319309
|
* what quoting both sides of a book is — and the orderbook says as much: it
|
|
319128
319310
|
* advertises each entry at `min(quoted, balance)` rather than dividing the
|
|
319129
319311
|
* balance between them. Whichever order fills first draws the inventory down,
|
|
319130
319312
|
* and the rest are cut to what is left. Netting them here would refuse the
|
|
319131
|
-
* second side of every book.
|
|
319313
|
+
* second side of every book. Vault positions are counted the same way: the
|
|
319314
|
+
* whole position, not what is left after reservations for fills in flight.
|
|
319132
319315
|
*/
|
|
319133
319316
|
async assertWalletCanPay(symbol3, chain2, payout) {
|
|
319134
319317
|
const token = this.assetRegistry.getAddress(symbol3, chain2);
|
|
319135
319318
|
if (!token) return;
|
|
319136
319319
|
const decimals = await this.decimalsFor(symbol3, token, chain2);
|
|
319137
|
-
const
|
|
319320
|
+
const wallet = toScaled(
|
|
319138
319321
|
await this.contractService.getTokenBalance(chain2, token, this.signer.address),
|
|
319139
319322
|
decimals
|
|
319140
319323
|
);
|
|
319141
|
-
|
|
319324
|
+
const vaults = await this.vaultHoldings(token, chain2);
|
|
319325
|
+
if (wallet + vaults < payout) {
|
|
319326
|
+
const held = vaults > 0n ? `${toHuman(wallet)} in the wallet and ${toHuman(vaults)} in vaults` : toHuman(wallet);
|
|
319142
319327
|
throw new LimitOrderValidationError(
|
|
319143
|
-
`The wallet holds ${
|
|
319328
|
+
`The wallet holds ${held} ${symbol3} on ${chain2}, which cannot pay out ${toHuman(payout)}`
|
|
319144
319329
|
);
|
|
319145
319330
|
}
|
|
319146
319331
|
}
|
|
319332
|
+
/**
|
|
319333
|
+
* What the configured vaults hold of `token` on `chain`, in scaled units.
|
|
319334
|
+
*
|
|
319335
|
+
* Zero when no vault is configured. A snapshot that cannot be read is also
|
|
319336
|
+
* zero: the wallet alone then has to cover the order, which refuses one the
|
|
319337
|
+
* filler might have managed rather than accepting one it could not.
|
|
319338
|
+
*/
|
|
319339
|
+
async vaultHoldings(token, chain2) {
|
|
319340
|
+
if (!this.vaultBalances) return 0n;
|
|
319341
|
+
let positions;
|
|
319342
|
+
try {
|
|
319343
|
+
positions = await this.vaultBalances.getBalanceSnapshot(chain2);
|
|
319344
|
+
} catch (err) {
|
|
319345
|
+
this.logger.warn({ chain: chain2, token, err }, "Could not read vault balances; backing the order on the wallet alone");
|
|
319346
|
+
return 0n;
|
|
319347
|
+
}
|
|
319348
|
+
const wanted = token.toLowerCase();
|
|
319349
|
+
let total = 0n;
|
|
319350
|
+
for (const position of positions) {
|
|
319351
|
+
if (position.chain !== chain2 || position.asset.toLowerCase() !== wanted) continue;
|
|
319352
|
+
total += toScaled(position.positionAssets, position.decimals);
|
|
319353
|
+
}
|
|
319354
|
+
return total;
|
|
319355
|
+
}
|
|
319147
319356
|
/**
|
|
319148
319357
|
* Works a limit order down by what a fill delivered, and puts the rest back on
|
|
319149
319358
|
* the orderbook.
|
|
@@ -320724,7 +320933,7 @@ var NotificationService = class extends EventEmitter6 {
|
|
|
320724
320933
|
title: "Swap filled",
|
|
320725
320934
|
body: swapBody(event),
|
|
320726
320935
|
tag: `simplex-swap-${id2}`,
|
|
320727
|
-
url: "./
|
|
320936
|
+
url: "./history"
|
|
320728
320937
|
}).catch((error) => this.logger.error({ err: error }, "Could not publish swap notification"));
|
|
320729
320938
|
}
|
|
320730
320939
|
};
|
|
@@ -320917,6 +321126,7 @@ init_chains2();
|
|
|
320917
321126
|
init_alchemy();
|
|
320918
321127
|
init_prompt_utils();
|
|
320919
321128
|
init_state();
|
|
321129
|
+
init_defaults8();
|
|
320920
321130
|
init_Logger();
|
|
320921
321131
|
var logger2 = getLogger("setup");
|
|
320922
321132
|
async function defaultRpcRequest(url2, method, params) {
|
|
@@ -320963,6 +321173,18 @@ async function handleSetupRequest(server, setup, req, res, path2, method) {
|
|
|
320963
321173
|
};
|
|
320964
321174
|
return sendJson(res, 200, defaults3);
|
|
320965
321175
|
}
|
|
321176
|
+
if (endpoint === "orderbook") {
|
|
321177
|
+
if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
|
|
321178
|
+
const url2 = DEFAULT_ORDERBOOK_URLS.mainnet;
|
|
321179
|
+
try {
|
|
321180
|
+
const { books } = await new OrderbookClient(url2, DEFAULT_ORDERBOOK_TIMEOUT_MS).limits();
|
|
321181
|
+
const orderbook = { url: url2, books };
|
|
321182
|
+
return sendJson(res, 200, orderbook);
|
|
321183
|
+
} catch (err) {
|
|
321184
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
321185
|
+
return sendJson(res, 502, { error: `Could not read the orderbook's markets: ${reason}` });
|
|
321186
|
+
}
|
|
321187
|
+
}
|
|
320966
321188
|
if (method !== "POST") return sendJson(res, 405, { error: "Method not allowed" });
|
|
320967
321189
|
let body;
|
|
320968
321190
|
try {
|
|
@@ -321732,6 +321954,11 @@ var UiServer = class {
|
|
|
321732
321954
|
if (method === "POST") return this.handleLimitOrderCreate(req, res);
|
|
321733
321955
|
return sendJson(res, 405, { error: "Method not allowed" });
|
|
321734
321956
|
}
|
|
321957
|
+
if (path2 === "/api/orderbook/books") {
|
|
321958
|
+
if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
|
|
321959
|
+
if (method !== "GET") return sendJson(res, 405, { error: "Method not allowed" });
|
|
321960
|
+
return this.handleLimitOrders(res, () => this.operator.limitOrders.books());
|
|
321961
|
+
}
|
|
321735
321962
|
const limitOrderMatch = path2.match(/^\/api\/limit-orders\/([\w-]+)$/);
|
|
321736
321963
|
if (limitOrderMatch) {
|
|
321737
321964
|
if (this.mode !== "operator") return sendJson(res, 409, { error: "Filler is not running" });
|
|
@@ -323269,14 +323496,10 @@ var RATE_LIMIT_RPC_CODES = /* @__PURE__ */ new Set([-32005, -32097, -32016, 429]
|
|
|
323269
323496
|
var RATE_LIMIT_SUSPENSION_MS = 5 * 6e4;
|
|
323270
323497
|
var SCAN_BUDGET = {
|
|
323271
323498
|
timeoutMs: 5e3,
|
|
323272
|
-
retryCount: 1,
|
|
323273
|
-
retryDelayMs: 500,
|
|
323274
323499
|
deadlineMs: 12e3
|
|
323275
323500
|
};
|
|
323276
323501
|
var CONFIRMATION_BUDGET = {
|
|
323277
323502
|
timeoutMs: 1e4,
|
|
323278
|
-
retryCount: 1,
|
|
323279
|
-
retryDelayMs: 500,
|
|
323280
323503
|
deadlineMs: 3e4
|
|
323281
323504
|
};
|
|
323282
323505
|
var MALFORMED_RESPONSE_SUSPENSION_MS = 3e4;
|
|
@@ -323368,8 +323591,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323368
323591
|
chain: chain2,
|
|
323369
323592
|
transport: http(url2, {
|
|
323370
323593
|
timeout: budget.timeoutMs,
|
|
323371
|
-
|
|
323372
|
-
|
|
323594
|
+
// No transport-level retry. viem's retry sleeps for the provider's
|
|
323595
|
+
// `Retry-After` verbatim and uncapped, which reaches the socket
|
|
323596
|
+
// before {@link suspensionMsFor} can apply its clamp — and even
|
|
323597
|
+
// without that header, retrying inside the task is what makes a
|
|
323598
|
+
// task's worst case (timeout, backoff, timeout) approach the
|
|
323599
|
+
// deadline that is supposed to bound it. One attempt per endpoint
|
|
323600
|
+
// per call; `retryPromise` above the quorum owns the retry policy.
|
|
323601
|
+
retryCount: 0
|
|
323373
323602
|
})
|
|
323374
323603
|
})
|
|
323375
323604
|
);
|
|
@@ -323383,11 +323612,10 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323383
323612
|
return this.rpcUrls.filter((url2) => (_QuorumPublicClient.benchedUntil.get(url2) ?? 0) > now);
|
|
323384
323613
|
}
|
|
323385
323614
|
/**
|
|
323386
|
-
* The endpoints this call queries
|
|
323387
|
-
*
|
|
323388
|
-
*
|
|
323389
|
-
*
|
|
323390
|
-
* queried again; there is nobody left to prefer.
|
|
323615
|
+
* The endpoints this call queries: everyone not benched. A benched endpoint is
|
|
323616
|
+
* dropped outright: it answers nothing while throttled, so asking it could only
|
|
323617
|
+
* cost a request. With every endpoint benched, everyone is queried again; there
|
|
323618
|
+
* is nobody left to prefer.
|
|
323391
323619
|
*/
|
|
323392
323620
|
participants() {
|
|
323393
323621
|
const now = Date.now();
|
|
@@ -323395,12 +323623,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323395
323623
|
const active = all7.filter(({ idx }) => (_QuorumPublicClient.benchedUntil.get(this.rpcUrls[idx]) ?? 0) <= now);
|
|
323396
323624
|
const allBenched = active.length === 0;
|
|
323397
323625
|
const queried = allBenched ? all7 : active;
|
|
323398
|
-
return {
|
|
323399
|
-
queried,
|
|
323400
|
-
skipped: all7.length - queried.length,
|
|
323401
|
-
threshold: quorumThreshold(queried.length),
|
|
323402
|
-
allBenched
|
|
323403
|
-
};
|
|
323626
|
+
return { queried, skipped: all7.length - queried.length, allBenched };
|
|
323404
323627
|
}
|
|
323405
323628
|
/**
|
|
323406
323629
|
* Records a task failure against its endpoint. Only the failures
|
|
@@ -323445,17 +323668,21 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323445
323668
|
* retries would otherwise stall every confirmation poll). Stragglers settle
|
|
323446
323669
|
* out of band and are ignored. `tryDecide` returns undefined to keep
|
|
323447
323670
|
* waiting; once every task has settled, `finalize` must decide (it throws
|
|
323448
|
-
* the QuorumError when the full response set still has no quorum).
|
|
323449
|
-
*
|
|
323450
|
-
*
|
|
323671
|
+
* the QuorumError when the full response set still has no quorum).
|
|
323672
|
+
*
|
|
323673
|
+
* Both are handed the bar: the BFT threshold over the endpoints that have not
|
|
323674
|
+
* failed, answered or still out. A failure lowers it rather than counting
|
|
323675
|
+
* against it. Counting the pending endpoints keeps an early decision to a bar
|
|
323676
|
+
* no lower than the full response set would get, so a vote it did not wait for
|
|
323677
|
+
* could never have reversed it.
|
|
323451
323678
|
*
|
|
323452
323679
|
* `deadlineMs` bounds the other direction: when the bar is NOT reachable
|
|
323453
323680
|
* from the fast responders, the call used to wait out every straggler, and
|
|
323454
323681
|
* the caller's own retry loop multiplied that into minutes of a chain not
|
|
323455
323682
|
* being scanned at all, silently. At the deadline the outstanding endpoints
|
|
323456
323683
|
* are treated as failures — named in the QuorumError, so the operator sees
|
|
323457
|
-
* which ones held the call up — and `finalize` decides
|
|
323458
|
-
*
|
|
323684
|
+
* which ones held the call up — and `finalize` decides over the endpoints
|
|
323685
|
+
* that answered.
|
|
323459
323686
|
*/
|
|
323460
323687
|
settleUntilQuorum(tasks, tryDecide, finalize) {
|
|
323461
323688
|
return new Promise((resolve11, reject) => {
|
|
@@ -323473,13 +323700,14 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323473
323700
|
const evaluate = () => {
|
|
323474
323701
|
if (done) return;
|
|
323475
323702
|
try {
|
|
323476
|
-
const
|
|
323703
|
+
const threshold = quorumThreshold(tasks.length - failures.length);
|
|
323704
|
+
const early = tryDecide(fulfilled, threshold);
|
|
323477
323705
|
if (early !== void 0) {
|
|
323478
323706
|
finish(() => resolve11(early));
|
|
323479
323707
|
return;
|
|
323480
323708
|
}
|
|
323481
323709
|
if (outstanding === 0) {
|
|
323482
|
-
const value = finalize(fulfilled, failures);
|
|
323710
|
+
const value = finalize(fulfilled, failures, threshold);
|
|
323483
323711
|
finish(() => resolve11(value));
|
|
323484
323712
|
}
|
|
323485
323713
|
} catch (error) {
|
|
@@ -323497,7 +323725,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323497
323725
|
error: new Error(`no answer within the ${this.deadlineMs}ms quorum deadline`)
|
|
323498
323726
|
}));
|
|
323499
323727
|
try {
|
|
323500
|
-
const value = finalize(fulfilled, [...failures, ...stragglers]);
|
|
323728
|
+
const value = finalize(fulfilled, [...failures, ...stragglers], quorumThreshold(fulfilled.length));
|
|
323501
323729
|
finish(() => resolve11(value));
|
|
323502
323730
|
} catch (error) {
|
|
323503
323731
|
finish(() => reject(error));
|
|
@@ -323524,22 +323752,22 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323524
323752
|
});
|
|
323525
323753
|
}
|
|
323526
323754
|
/**
|
|
323527
|
-
* Highest block head backed by a
|
|
323528
|
-
*
|
|
323755
|
+
* Highest block head backed by a quorum of the endpoints that answer. Throws
|
|
323756
|
+
* {@link QuorumError} when none do.
|
|
323529
323757
|
*/
|
|
323530
323758
|
async getBlockNumber() {
|
|
323531
|
-
const { queried, skipped,
|
|
323759
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
323532
323760
|
return this.settleUntilQuorum(
|
|
323533
323761
|
queried.map(({ idx, client }) => ({ idx, task: client.getBlockNumber() })),
|
|
323534
323762
|
// Early exit as soon as a quorum is satisfiable. Late voters could
|
|
323535
323763
|
// only raise the reported head; the earlier (lower) head is
|
|
323536
323764
|
// conservative for every consumer (fewer confirmations counted,
|
|
323537
323765
|
// smaller scan windows).
|
|
323538
|
-
(fulfilled) => this.quorumHead(
|
|
323766
|
+
(fulfilled, threshold) => this.quorumHead(
|
|
323539
323767
|
fulfilled.map((f7) => f7.value),
|
|
323540
323768
|
threshold
|
|
323541
323769
|
) ?? void 0,
|
|
323542
|
-
(fulfilled, failures) => {
|
|
323770
|
+
(fulfilled, failures, threshold) => {
|
|
323543
323771
|
const head = this.quorumHead(
|
|
323544
323772
|
fulfilled.map((f7) => f7.value),
|
|
323545
323773
|
threshold
|
|
@@ -323580,7 +323808,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323580
323808
|
}
|
|
323581
323809
|
return views;
|
|
323582
323810
|
};
|
|
323583
|
-
const { queried, skipped,
|
|
323811
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
323584
323812
|
return this.settleUntilQuorum(
|
|
323585
323813
|
queried.map(({ idx, client }) => ({
|
|
323586
323814
|
idx,
|
|
@@ -323599,8 +323827,8 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323599
323827
|
// Early exit only on a POSITIVE quorum: an agreeing inclusion group can
|
|
323600
323828
|
// only grow, so the first satisfiable quorum is final. Not-found votes
|
|
323601
323829
|
// never decide early — "not confirmed" needs the full response set.
|
|
323602
|
-
(fulfilled) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
323603
|
-
(fulfilled, failures) => {
|
|
323830
|
+
(fulfilled, threshold) => aggregateConfirmations(toViews(fulfilled), threshold) ?? void 0,
|
|
323831
|
+
(fulfilled, failures, threshold) => {
|
|
323604
323832
|
const confirmations = aggregateConfirmations(toViews(fulfilled), threshold);
|
|
323605
323833
|
if (confirmations === null) {
|
|
323606
323834
|
throw new QuorumError(
|
|
@@ -323629,7 +323857,7 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323629
323857
|
}
|
|
323630
323858
|
return groups;
|
|
323631
323859
|
};
|
|
323632
|
-
const { queried, skipped,
|
|
323860
|
+
const { queried, skipped, allBenched } = this.participants();
|
|
323633
323861
|
return this.settleUntilQuorum(
|
|
323634
323862
|
queried.map(({ idx, client }) => ({
|
|
323635
323863
|
idx,
|
|
@@ -323641,13 +323869,13 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
323641
323869
|
// two disjoint groups can't both hold one), so the first satisfiable
|
|
323642
323870
|
// agreement is final — no need to wait out stragglers that could
|
|
323643
323871
|
// only join or lose.
|
|
323644
|
-
(fulfilled) => {
|
|
323872
|
+
(fulfilled, threshold) => {
|
|
323645
323873
|
for (const group of groupOf(fulfilled).values()) {
|
|
323646
323874
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
323647
323875
|
}
|
|
323648
323876
|
return void 0;
|
|
323649
323877
|
},
|
|
323650
|
-
(fulfilled, failures) => {
|
|
323878
|
+
(fulfilled, failures, threshold) => {
|
|
323651
323879
|
const groups = groupOf(fulfilled);
|
|
323652
323880
|
for (const group of groups.values()) {
|
|
323653
323881
|
if (group.providerIdxs.length >= threshold) return group.result;
|
|
@@ -362247,8 +362475,15 @@ var IntentFiller = class {
|
|
|
362247
362475
|
const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
|
|
362248
362476
|
this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
|
|
362249
362477
|
if (validStrategies.length === 0) {
|
|
362250
|
-
|
|
362251
|
-
this.
|
|
362478
|
+
const priced = eligibleStrategies.some((s4) => s4 !== null);
|
|
362479
|
+
this.logger.warn(
|
|
362480
|
+
{ orderId: order.id },
|
|
362481
|
+
priced ? "No profitable strategy found for order" : "No strategy can fill this order"
|
|
362482
|
+
);
|
|
362483
|
+
this.monitor.emit("orderSkipped", {
|
|
362484
|
+
orderId: order.id,
|
|
362485
|
+
reason: priced ? "No profitable strategy" : "No strategy can fill this order"
|
|
362486
|
+
});
|
|
362252
362487
|
return null;
|
|
362253
362488
|
}
|
|
362254
362489
|
this.logger.info(
|
|
@@ -362697,16 +362932,63 @@ function byBestOfferFirst(a6, b5) {
|
|
|
362697
362932
|
if (a6.available !== b5.available) return a6.available > b5.available ? -1 : 1;
|
|
362698
362933
|
return a6.order.id < b5.order.id ? -1 : 1;
|
|
362699
362934
|
}
|
|
362935
|
+
function whyUnmatched(orders, incoming, resolve11, now = /* @__PURE__ */ new Date()) {
|
|
362936
|
+
let furthest = -1;
|
|
362937
|
+
for (const order of orders) {
|
|
362938
|
+
const failed = firstFailed(order, incoming, resolve11, now);
|
|
362939
|
+
furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
|
|
362940
|
+
}
|
|
362941
|
+
const { source, destination, inputSymbol } = incoming;
|
|
362942
|
+
if (furthest < CHECKS.length) {
|
|
362943
|
+
switch (CHECKS[Math.max(furthest, 0)]) {
|
|
362944
|
+
case "open":
|
|
362945
|
+
return "no limit order is open";
|
|
362946
|
+
case "chain":
|
|
362947
|
+
return `no open limit order fills on ${destination}`;
|
|
362948
|
+
case "input":
|
|
362949
|
+
return `no limit order on ${destination} takes ${inputSymbol} in`;
|
|
362950
|
+
case "output":
|
|
362951
|
+
return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
|
|
362952
|
+
case "source":
|
|
362953
|
+
return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
|
|
362954
|
+
}
|
|
362955
|
+
}
|
|
362956
|
+
const priced = orders.filter((order) => firstFailed(order, incoming, resolve11, now) === null).map((order) => ({
|
|
362957
|
+
order,
|
|
362958
|
+
offer: offerFor({
|
|
362959
|
+
side: order.side,
|
|
362960
|
+
inputAmount: incoming.inputNet,
|
|
362961
|
+
price: BigInt(order.price),
|
|
362962
|
+
outputDecimals: incoming.outputDecimals
|
|
362963
|
+
})
|
|
362964
|
+
}));
|
|
362965
|
+
if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
|
|
362966
|
+
return "the limit orders that meet its rate have nothing left to pay out";
|
|
362967
|
+
}
|
|
362968
|
+
const best = priced.reduce((a6, b5) => b5.offer > a6.offer ? b5 : a6);
|
|
362969
|
+
const symbol3 = limitOrderLegs(best.order).output;
|
|
362970
|
+
return `it asks for ${readable(incoming.requestedOutput)} ${symbol3}; the best limit order offers ${readable(best.offer)} ${symbol3}`;
|
|
362971
|
+
}
|
|
362972
|
+
var CHECKS = ["open", "chain", "input", "output", "source"];
|
|
362700
362973
|
function serves(order, incoming, resolve11, now) {
|
|
362701
|
-
|
|
362702
|
-
|
|
362703
|
-
|
|
362974
|
+
return firstFailed(order, incoming, resolve11, now) === null;
|
|
362975
|
+
}
|
|
362976
|
+
function firstFailed(order, incoming, resolve11, now) {
|
|
362977
|
+
if (order.status !== "open") return "open";
|
|
362978
|
+
if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
|
|
362979
|
+
if (order.fillChain !== incoming.destination) return "chain";
|
|
362704
362980
|
const legs = limitOrderLegs(order);
|
|
362705
|
-
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return
|
|
362981
|
+
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
|
|
362706
362982
|
const outputToken = resolve11(legs.output, order.fillChain);
|
|
362707
|
-
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return
|
|
362708
|
-
if (incoming.source === incoming.destination) return
|
|
362709
|
-
return order.acceptedSources.includes(incoming.source);
|
|
362983
|
+
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
|
|
362984
|
+
if (incoming.source === incoming.destination) return null;
|
|
362985
|
+
return order.acceptedSources.includes(incoming.source) ? null : "source";
|
|
362986
|
+
}
|
|
362987
|
+
function readable(amount) {
|
|
362988
|
+
const [whole, fraction] = toHuman(amount).split(".");
|
|
362989
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
362990
|
+
const cut = fraction?.slice(0, 6).replace(/0+$/, "");
|
|
362991
|
+
return cut ? `${grouped}.${cut}` : grouped;
|
|
362710
362992
|
}
|
|
362711
362993
|
|
|
362712
362994
|
// src/orderbook/usd.ts
|
|
@@ -362850,15 +363132,20 @@ var FXFiller = class _FXFiller {
|
|
|
362850
363132
|
}
|
|
362851
363133
|
try {
|
|
362852
363134
|
if (order.inputs.length !== order.output.assets.length) {
|
|
362853
|
-
this.logger.
|
|
362854
|
-
{ inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
363135
|
+
this.logger.info(
|
|
363136
|
+
{ orderId: order.id, inputs: order.inputs.length, outputs: order.output.assets.length },
|
|
362855
363137
|
"Order input/output length mismatch or empty"
|
|
362856
363138
|
);
|
|
362857
363139
|
return false;
|
|
362858
363140
|
}
|
|
362859
363141
|
if ((await this.matchOrder(order)).length === 0) {
|
|
362860
|
-
this.logger.
|
|
362861
|
-
{
|
|
363142
|
+
this.logger.info(
|
|
363143
|
+
{
|
|
363144
|
+
orderId: order.id,
|
|
363145
|
+
sourceChain: order.source,
|
|
363146
|
+
destChain: order.destination,
|
|
363147
|
+
reason: await this.explainUnmatched(order)
|
|
363148
|
+
},
|
|
362862
363149
|
"No limit order matches this order"
|
|
362863
363150
|
);
|
|
362864
363151
|
return false;
|
|
@@ -363400,30 +363687,53 @@ var FXFiller = class _FXFiller {
|
|
|
363400
363687
|
*/
|
|
363401
363688
|
async matchLeg(order, leg) {
|
|
363402
363689
|
if (!this.limitOrders) return [];
|
|
363690
|
+
const incoming = await this.incomingFor(order, leg);
|
|
363691
|
+
if (!incoming) return [];
|
|
363692
|
+
return matchLimitOrders(
|
|
363693
|
+
await this.limitOrders.open(),
|
|
363694
|
+
incoming,
|
|
363695
|
+
(symbol3, chain2) => this.registry.getAddress(symbol3, chain2)
|
|
363696
|
+
);
|
|
363697
|
+
}
|
|
363698
|
+
/** Why no limit order serves the order, leg by leg, for the line that says it was passed over. */
|
|
363699
|
+
async explainUnmatched(order) {
|
|
363700
|
+
if (!this.limitOrders) return "limit orders are not loaded";
|
|
363701
|
+
const open2 = await this.limitOrders.open();
|
|
363702
|
+
const reasons = [];
|
|
363703
|
+
for (let leg = 0; leg < order.inputs.length; leg++) {
|
|
363704
|
+
const incoming = await this.incomingFor(order, leg);
|
|
363705
|
+
const reason = incoming ? whyUnmatched(open2, incoming, (symbol3, chain2) => this.registry.getAddress(symbol3, chain2)) : `its input token is not in the asset registry on ${order.source}`;
|
|
363706
|
+
reasons.push(order.inputs.length > 1 ? `leg ${leg}: ${reason}` : reason);
|
|
363707
|
+
}
|
|
363708
|
+
return reasons.join("; ");
|
|
363709
|
+
}
|
|
363710
|
+
/**
|
|
363711
|
+
* One leg as the matcher sees it, or null when its input token is not one the asset
|
|
363712
|
+
* registry knows on the source chain.
|
|
363713
|
+
*
|
|
363714
|
+
* Amounts cross into the matcher at 1e18, the unit limit orders are kept in.
|
|
363715
|
+
*/
|
|
363716
|
+
async incomingFor(order, leg) {
|
|
363403
363717
|
const input = order.inputs[leg];
|
|
363404
363718
|
const output3 = order.output.assets[leg];
|
|
363405
|
-
if (!input || !output3) return
|
|
363719
|
+
if (!input || !output3) return null;
|
|
363406
363720
|
const inputToken = bytes32ToBytes20(input.token);
|
|
363407
363721
|
const outputToken = bytes32ToBytes20(output3.token);
|
|
363408
363722
|
const inputSymbol = this.registry.symbolFor(inputToken, order.source);
|
|
363409
|
-
if (!inputSymbol) return
|
|
363723
|
+
if (!inputSymbol) return null;
|
|
363410
363724
|
const [inputDecimals, outputDecimals] = await Promise.all([
|
|
363411
363725
|
this.contractService.getTokenDecimals(inputToken, order.source),
|
|
363412
363726
|
this.contractService.getTokenDecimals(outputToken, order.destination)
|
|
363413
363727
|
]);
|
|
363414
|
-
return
|
|
363415
|
-
|
|
363416
|
-
|
|
363417
|
-
|
|
363418
|
-
|
|
363419
|
-
|
|
363420
|
-
|
|
363421
|
-
|
|
363422
|
-
|
|
363423
|
-
outputDecimals
|
|
363424
|
-
},
|
|
363425
|
-
(symbol3, chain2) => this.registry.getAddress(symbol3, chain2)
|
|
363426
|
-
);
|
|
363728
|
+
return {
|
|
363729
|
+
source: order.source,
|
|
363730
|
+
destination: order.destination,
|
|
363731
|
+
inputSymbol,
|
|
363732
|
+
outputToken,
|
|
363733
|
+
inputNet: toScaled(input.amount, inputDecimals),
|
|
363734
|
+
requestedOutput: toScaled(output3.amount, outputDecimals),
|
|
363735
|
+
outputDecimals
|
|
363736
|
+
};
|
|
363427
363737
|
}
|
|
363428
363738
|
/**
|
|
363429
363739
|
* The order's input in **USD**, or null when no limit order connects its input
|
|
@@ -364615,7 +364925,10 @@ async function bootFiller(config5, options) {
|
|
|
364615
364925
|
runtimeSigner,
|
|
364616
364926
|
config5.orderbook.defaultTtlSecs ?? DEFAULT_LIMIT_ORDER_TTL_SECONDS,
|
|
364617
364927
|
new DelegationService(chainClientManager, configService, runtimeSigner),
|
|
364618
|
-
options.loggers
|
|
364928
|
+
options.loggers,
|
|
364929
|
+
initialOrderNonce,
|
|
364930
|
+
// A fill withdraws a payout shortfall from the vaults, so what they hold backs an order too.
|
|
364931
|
+
vaultVenue
|
|
364619
364932
|
);
|
|
364620
364933
|
intentFiller.setLimitOrderService(limitOrderService);
|
|
364621
364934
|
const lifecycle = new LimitOrderLifecycle(
|
|
@@ -365172,6 +365485,8 @@ var MAX_BLOCK_RANGE = 1000n;
|
|
|
365172
365485
|
var PUBLISH_CHUNK = 250;
|
|
365173
365486
|
var yieldToConsumers = () => new Promise((resolve11) => setImmediate(resolve11));
|
|
365174
365487
|
var STOP_DRAIN_TIMEOUT_MS = 5e3;
|
|
365488
|
+
var STALL_THRESHOLD_MS = 45e3;
|
|
365489
|
+
var STALL_REPORT_INTERVAL_MS = 6e4;
|
|
365175
365490
|
var ChainScanner = class {
|
|
365176
365491
|
constructor(target, loggers = defaultLoggerContext(), scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS) {
|
|
365177
365492
|
this.target = target;
|
|
@@ -365185,6 +365500,18 @@ var ChainScanner = class {
|
|
|
365185
365500
|
this.errorHandler = () => {
|
|
365186
365501
|
};
|
|
365187
365502
|
this.stopped = false;
|
|
365503
|
+
/**
|
|
365504
|
+
* How far past the cursor the next pass reads, as `toBlock - fromBlock`:
|
|
365505
|
+
* halved after a failed read, doubled after a good one, between 0 (one block)
|
|
365506
|
+
* and {@link MAX_BLOCK_RANGE}.
|
|
365507
|
+
*
|
|
365508
|
+
* Free endpoints cap `eth_getLogs` ranges far below the full span — on
|
|
365509
|
+
* Arbitrum three of seven refused 100 blocks — and the cursor never moves
|
|
365510
|
+
* past a range it failed to read. At a fixed span a catch-up that outgrew
|
|
365511
|
+
* those caps was stuck for good: the range only grew, so the capped endpoints
|
|
365512
|
+
* never rejoined and the quorum never formed.
|
|
365513
|
+
*/
|
|
365514
|
+
this.span = MAX_BLOCK_RANGE;
|
|
365188
365515
|
this.logger = loggers.get("chain-scanner");
|
|
365189
365516
|
this.quorumClient = new QuorumPublicClient(target.chainId, target.rpcUrls, loggers);
|
|
365190
365517
|
if (this.quorumClient.size > 1) {
|
|
@@ -365227,8 +365554,9 @@ var ChainScanner = class {
|
|
|
365227
365554
|
if (this.timer || this.stopped) return;
|
|
365228
365555
|
this.logger.info({ chainId: this.target.chainId }, "Block scanner started");
|
|
365229
365556
|
this.timer = setInterval(() => {
|
|
365230
|
-
if (this.mutex.isLocked()) return;
|
|
365557
|
+
if (this.mutex.isLocked()) return this.reportStall();
|
|
365231
365558
|
void this.mutex.runExclusive(async () => {
|
|
365559
|
+
this.pass = { phase: "head", startedAt: Date.now(), reportedAt: 0 };
|
|
365232
365560
|
try {
|
|
365233
365561
|
await this.scan();
|
|
365234
365562
|
} catch (error) {
|
|
@@ -365242,10 +365570,66 @@ var ChainScanner = class {
|
|
|
365242
365570
|
"Error in block scanner"
|
|
365243
365571
|
);
|
|
365244
365572
|
this.errorHandler(error);
|
|
365573
|
+
} finally {
|
|
365574
|
+
this.notePassEnd();
|
|
365245
365575
|
}
|
|
365246
365576
|
});
|
|
365247
365577
|
}, this.scanIntervalMs);
|
|
365248
365578
|
}
|
|
365579
|
+
/**
|
|
365580
|
+
* Records which await the current pass is sitting on, for the watchdog.
|
|
365581
|
+
*
|
|
365582
|
+
* A no-op when no pass is registered — `scan()` is driven directly by tests
|
|
365583
|
+
* and by nothing else, so there is no watchdog to inform.
|
|
365584
|
+
*/
|
|
365585
|
+
enter(phase) {
|
|
365586
|
+
if (this.pass) this.pass.phase = phase;
|
|
365587
|
+
}
|
|
365588
|
+
/**
|
|
365589
|
+
* Says that a pass has outlived the quorum budgets and names the phase it is
|
|
365590
|
+
* stuck in, then repeats every {@link STALL_REPORT_INTERVAL_MS} until it ends.
|
|
365591
|
+
*
|
|
365592
|
+
* Observation only: the pass is left alone. Aborting it would need a cancel
|
|
365593
|
+
* path through the quorum client that does not exist, and the cursor is safe
|
|
365594
|
+
* either way — a pass that never completes never advances it.
|
|
365595
|
+
*/
|
|
365596
|
+
reportStall() {
|
|
365597
|
+
const pass = this.pass;
|
|
365598
|
+
if (!pass) return;
|
|
365599
|
+
const now = Date.now();
|
|
365600
|
+
const heldMs = now - pass.startedAt;
|
|
365601
|
+
if (heldMs < STALL_THRESHOLD_MS) return;
|
|
365602
|
+
if (pass.reportedAt !== 0 && now - pass.reportedAt < STALL_REPORT_INTERVAL_MS) return;
|
|
365603
|
+
pass.reportedAt = now;
|
|
365604
|
+
this.logger.warn(
|
|
365605
|
+
{
|
|
365606
|
+
chainId: this.target.chainId,
|
|
365607
|
+
phase: pass.phase,
|
|
365608
|
+
heldMs,
|
|
365609
|
+
cursor: this.cursor?.toString(),
|
|
365610
|
+
rpcUrls: this.target.rpcUrls
|
|
365611
|
+
},
|
|
365612
|
+
"Block scan pass has outlived its budget \u2014 this chain is not being scanned"
|
|
365613
|
+
);
|
|
365614
|
+
}
|
|
365615
|
+
/**
|
|
365616
|
+
* Closes out a pass, and says so if the watchdog complained about it — without
|
|
365617
|
+
* this the log shows a chain going quiet and never shows it coming back.
|
|
365618
|
+
*/
|
|
365619
|
+
notePassEnd() {
|
|
365620
|
+
const pass = this.pass;
|
|
365621
|
+
this.pass = void 0;
|
|
365622
|
+
if (!pass || pass.reportedAt === 0) return;
|
|
365623
|
+
this.logger.warn(
|
|
365624
|
+
{
|
|
365625
|
+
chainId: this.target.chainId,
|
|
365626
|
+
phase: pass.phase,
|
|
365627
|
+
heldMs: Date.now() - pass.startedAt,
|
|
365628
|
+
cursor: this.cursor?.toString()
|
|
365629
|
+
},
|
|
365630
|
+
"Block scan pass finished after outliving its budget \u2014 scanning has resumed"
|
|
365631
|
+
);
|
|
365632
|
+
}
|
|
365249
365633
|
/**
|
|
365250
365634
|
* Points this loop at different endpoints, keeping the cursor.
|
|
365251
365635
|
*
|
|
@@ -365287,11 +365671,12 @@ var ChainScanner = class {
|
|
|
365287
365671
|
}
|
|
365288
365672
|
if (currentBlock <= this.cursor) return;
|
|
365289
365673
|
const fromBlock = this.cursor + 1n;
|
|
365290
|
-
const toBlock = fromBlock +
|
|
365674
|
+
const toBlock = fromBlock + this.span > currentBlock ? currentBlock : fromBlock + this.span;
|
|
365291
365675
|
this.logger.debug(
|
|
365292
365676
|
{ chainId: this.target.chainId, fromBlock, toBlock, gap: Number(toBlock - fromBlock) },
|
|
365293
365677
|
"Scanning blocks"
|
|
365294
365678
|
);
|
|
365679
|
+
this.enter("logs");
|
|
365295
365680
|
let logs;
|
|
365296
365681
|
try {
|
|
365297
365682
|
logs = await retryPromise(
|
|
@@ -365313,6 +365698,7 @@ var ChainScanner = class {
|
|
|
365313
365698
|
);
|
|
365314
365699
|
} catch (error) {
|
|
365315
365700
|
if (isBlockRangeError(error)) return;
|
|
365701
|
+
this.narrow(toBlock - fromBlock);
|
|
365316
365702
|
throw error;
|
|
365317
365703
|
}
|
|
365318
365704
|
if (this.stopped) return;
|
|
@@ -365323,6 +365709,7 @@ var ChainScanner = class {
|
|
|
365323
365709
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: placed.length },
|
|
365324
365710
|
"Found OrderPlaced events in block scan"
|
|
365325
365711
|
);
|
|
365712
|
+
this.enter("publish-orders");
|
|
365326
365713
|
await this.publishOrders(placed);
|
|
365327
365714
|
}
|
|
365328
365715
|
if (filled.length > 0) {
|
|
@@ -365330,10 +365717,36 @@ var ChainScanner = class {
|
|
|
365330
365717
|
{ chainId: this.target.chainId, fromBlock, toBlock, eventCount: filled.length },
|
|
365331
365718
|
"Found OrderFilled events in block scan"
|
|
365332
365719
|
);
|
|
365720
|
+
this.enter("publish-fills");
|
|
365333
365721
|
await this.publishFills(filled);
|
|
365334
365722
|
}
|
|
365335
365723
|
if (this.stopped) return;
|
|
365336
365724
|
this.cursor = toBlock;
|
|
365725
|
+
this.widen();
|
|
365726
|
+
}
|
|
365727
|
+
/**
|
|
365728
|
+
* Halves the span below the range that just failed, so the next pass asks
|
|
365729
|
+
* for something a range-capped endpoint will answer. Halving the attempted
|
|
365730
|
+
* range rather than the span matters near the head, where a pass reads fewer
|
|
365731
|
+
* blocks than the span allows.
|
|
365732
|
+
*/
|
|
365733
|
+
narrow(attempted) {
|
|
365734
|
+
const next = attempted / 2n;
|
|
365735
|
+
if (next >= this.span) return;
|
|
365736
|
+
this.span = next;
|
|
365737
|
+
this.logger.warn(
|
|
365738
|
+
{ chainId: this.target.chainId, span: Number(next) },
|
|
365739
|
+
"Narrowing the block scan range after a failed read"
|
|
365740
|
+
);
|
|
365741
|
+
}
|
|
365742
|
+
/** Doubles the span back toward {@link MAX_BLOCK_RANGE} after a good read. */
|
|
365743
|
+
widen() {
|
|
365744
|
+
if (this.span >= MAX_BLOCK_RANGE) return;
|
|
365745
|
+
const next = this.span === 0n ? 1n : this.span * 2n;
|
|
365746
|
+
this.span = next > MAX_BLOCK_RANGE ? MAX_BLOCK_RANGE : next;
|
|
365747
|
+
if (this.span === MAX_BLOCK_RANGE) {
|
|
365748
|
+
this.logger.info({ chainId: this.target.chainId }, "Block scan range back to full");
|
|
365749
|
+
}
|
|
365337
365750
|
}
|
|
365338
365751
|
async publishOrders(logs) {
|
|
365339
365752
|
const rebuilt = reconstructOrdersFromLogs(logs, {
|
|
@@ -365702,6 +366115,10 @@ var LimitOrderController = class {
|
|
|
365702
366115
|
]);
|
|
365703
366116
|
return { order, fills, bids };
|
|
365704
366117
|
}
|
|
366118
|
+
/** The pairs the orderbook lists, the smallest payout each token may carry, and its tokens per chain. */
|
|
366119
|
+
books() {
|
|
366120
|
+
return this.service.books();
|
|
366121
|
+
}
|
|
365705
366122
|
/** Creates the order, posts it, and reports what the orderbook made of it. */
|
|
365706
366123
|
async create(request3) {
|
|
365707
366124
|
const posted = await this.service.create(request3);
|