dexbot 1.5.1 → 1.5.2
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/CHANGELOG.md +18 -0
- package/analysis/ama_fitting/package.json +1 -1
- package/analysis/trend_detection/package.json +1 -1
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/dexbot.js +5 -5
- package/dist/dexbot.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +1 -0
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +113 -47
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +7 -1
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_startup_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_startup_runtime.js +42 -3
- package/dist/modules/dexbot_startup_runtime.js.map +1 -1
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +19 -2
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/export.d.ts +122 -15
- package/dist/modules/order/export.d.ts.map +1 -1
- package/dist/modules/order/export.js +310 -46
- package/dist/modules/order/export.js.map +1 -1
- package/dist/modules/order/grid.js +1 -1
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +7 -3
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +31 -33
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/manager.d.ts +11 -5
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +12 -6
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +157 -11
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/math.d.ts +29 -4
- package/dist/modules/order/utils/math.d.ts.map +1 -1
- package/dist/modules/order/utils/math.js +46 -5
- package/dist/modules/order/utils/math.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +66 -5
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +138 -7
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +44 -27
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/scripts/analyze-credit.d.ts.map +1 -1
- package/dist/scripts/analyze-credit.js +53 -33
- package/dist/scripts/analyze-credit.js.map +1 -1
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +6 -5
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/README.md +2 -2
- package/docs/WORKFLOW.md +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ const { buildCreateOrderArgs, buildCreateOpFingerprint, extractBatchOperationRes
|
|
|
18
18
|
import * as validate from './order/utils/validate.js';
|
|
19
19
|
const { validateCreateTargetSlots, evaluateCommit, hasExecutableActions, stampGapEvacuationRotation } = validate;
|
|
20
20
|
import * as math from './order/utils/math.js';
|
|
21
|
-
const { validateOrderSize, findCrossedOrder, priceSlotEqual, isEvacuationRotationAllowed, getSellStartIdx, getPrecisionByOrderType, isSlotIndexInGapBand } = math;
|
|
21
|
+
const { validateOrderSize, findCrossedOrder, priceSlotEqual, isEvacuationRotationAllowed, isEvacuationSizeStillValid, getSellStartIdx, getPrecisionByOrderType, isSlotIndexInGapBand } = math;
|
|
22
22
|
/**
|
|
23
23
|
* Re-verify a B-stamped gap-evacuation rotation against the LIVE committed
|
|
24
24
|
* geometry (boundaryIdx/_gapSlots at execution time). The stamp freezes
|
|
@@ -280,28 +280,18 @@ function crossedOrderLabel(crossed) {
|
|
|
280
280
|
* an UPDATE-only rotation batch can re-price across an un-adopted chain
|
|
281
281
|
* order that master-grid-only checks cannot see (the pending/unmatched
|
|
282
282
|
* batch guards fire only for CREATE batches).
|
|
283
|
+
*
|
|
284
|
+
* Delegates to the shared order-utils builder so every crossing guard (COW
|
|
285
|
+
* create/rotation/fallback, startup reconcile placement, price-correction)
|
|
286
|
+
* sees the same set. Pending entries are pushed as wrappers (slotId +
|
|
287
|
+
* order): their inner order has no chain id yet, so orderId-only
|
|
288
|
+
* predicates would blind the guard to them — the shared
|
|
289
|
+
* isCrossingCheckCandidate predicate accepts slot-id-only wrappers.
|
|
283
290
|
* @param {Object} bot
|
|
284
291
|
* @returns {any[]}
|
|
285
292
|
*/
|
|
286
293
|
function buildCrossingCandidates(bot) {
|
|
287
|
-
|
|
288
|
-
if (!mgr)
|
|
289
|
-
return [];
|
|
290
|
-
const candidates = mgr.orders instanceof Map ? [...mgr.orders.values()] : [];
|
|
291
|
-
if (mgr._pendingBroadcasts instanceof Map) {
|
|
292
|
-
for (const entry of mgr._pendingBroadcasts.values()) {
|
|
293
|
-
const o = entry?.order;
|
|
294
|
-
if (o && o.type != null && o.price != null)
|
|
295
|
-
candidates.push(o);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (Array.isArray(mgr._lastUnmatchedChainOrders)) {
|
|
299
|
-
for (const o of mgr._lastUnmatchedChainOrders) {
|
|
300
|
-
if (o && o.type != null && o.price != null)
|
|
301
|
-
candidates.push(o);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return candidates;
|
|
294
|
+
return orderUtils.buildCrossingCheckCandidates(bot?.manager);
|
|
305
295
|
}
|
|
306
296
|
/**
|
|
307
297
|
* Drop only the pending-broadcast entries for the given CREATE slots.
|
|
@@ -1935,10 +1925,14 @@ async function pollChainForConfirmation(bot, opContexts, options = {}) {
|
|
|
1935
1925
|
// Only CREATE operations can be confirmed by polling (they appear as new orders on chain)
|
|
1936
1926
|
const createContexts = opContexts.filter((ctx) => ctx && ctx.kind === 'create' && ctx.finalInts && ctx.order);
|
|
1937
1927
|
if (createContexts.length === 0) {
|
|
1938
|
-
return { allConfirmed: false, confirmed: [], unconfirmed: [...opContexts] };
|
|
1928
|
+
return { allConfirmed: false, confirmed: [], unconfirmed: [...opContexts], confirmedChainIds: [] };
|
|
1939
1929
|
}
|
|
1940
1930
|
const accountRef = bot.accountId || bot.account?.id || bot.account;
|
|
1941
1931
|
let remaining = [...createContexts];
|
|
1932
|
+
// Chain ids of the poll-matched fresh creates. Retained (not just
|
|
1933
|
+
// logged) so the poll-confirmed adoption path can re-read them BY ID —
|
|
1934
|
+
// the window fallback cannot prove a lagging node has them yet.
|
|
1935
|
+
const matchedChainIds = [];
|
|
1942
1936
|
for (let attempt = 1; attempt <= maxPollRetries; attempt++) {
|
|
1943
1937
|
try {
|
|
1944
1938
|
const chainRead = await readOpenOrdersWithMetaSafe(chainOrders, accountRef);
|
|
@@ -1978,6 +1972,8 @@ async function pollChainForConfirmation(bot, opContexts, options = {}) {
|
|
|
1978
1972
|
});
|
|
1979
1973
|
if (match) {
|
|
1980
1974
|
bot.manager.logger.log(`[COW][POLL] Confirmed CREATE for slot ${ctx.order.id} on chain as ${match.id}`, 'debug');
|
|
1975
|
+
if (match.id && /^1\.7\.\d+$/.test(String(match.id)))
|
|
1976
|
+
matchedChainIds.push(String(match.id));
|
|
1981
1977
|
}
|
|
1982
1978
|
else {
|
|
1983
1979
|
stillUnconfirmed.push(ctx);
|
|
@@ -1986,7 +1982,7 @@ async function pollChainForConfirmation(bot, opContexts, options = {}) {
|
|
|
1986
1982
|
if (stillUnconfirmed.length === 0) {
|
|
1987
1983
|
const confirmed = createContexts;
|
|
1988
1984
|
bot.manager.logger.log(`[COW][POLL] All ${confirmed.length} CREATE(s) confirmed on chain after ${attempt} poll(s)`, 'info');
|
|
1989
|
-
return { allConfirmed: true, confirmed, unconfirmed: [] };
|
|
1985
|
+
return { allConfirmed: true, confirmed, unconfirmed: [], confirmedChainIds: [...matchedChainIds] };
|
|
1990
1986
|
}
|
|
1991
1987
|
remaining = stillUnconfirmed;
|
|
1992
1988
|
if (attempt < maxPollRetries) {
|
|
@@ -2003,7 +1999,7 @@ async function pollChainForConfirmation(bot, opContexts, options = {}) {
|
|
|
2003
1999
|
const confirmed = createContexts.filter((ctx) => !remaining.includes(ctx));
|
|
2004
2000
|
bot.manager.logger.log(`[COW][POLL] ${confirmed.length}/${createContexts.length} CREATE(s) confirmed after ${maxPollRetries} polls; ` +
|
|
2005
2001
|
`${remaining.length} unconfirmed. Falling back to reconciliation.`, 'warn');
|
|
2006
|
-
return { allConfirmed: false, confirmed, unconfirmed: remaining };
|
|
2002
|
+
return { allConfirmed: false, confirmed, unconfirmed: remaining, confirmedChainIds: [...matchedChainIds] };
|
|
2007
2003
|
}
|
|
2008
2004
|
/**
|
|
2009
2005
|
* Restore skipped update slots in the working grid to master state.
|
|
@@ -2378,6 +2374,12 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2378
2374
|
case 'chain_orphan_collision':
|
|
2379
2375
|
reason = `unmatched on-chain order ${violation.currentOrderId} at same price`;
|
|
2380
2376
|
break;
|
|
2377
|
+
case 'same_batch_price_duplicate':
|
|
2378
|
+
reason = `another CREATE in the same batch at same broadcast price (duplicate of ${violation.duplicateOf})`;
|
|
2379
|
+
break;
|
|
2380
|
+
case 'create_price_invalid':
|
|
2381
|
+
reason = `broadcast price is not a finite number`;
|
|
2382
|
+
break;
|
|
2381
2383
|
default:
|
|
2382
2384
|
reason = `existing orderId=${violation.currentOrderId}`;
|
|
2383
2385
|
}
|
|
@@ -2678,10 +2680,7 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2678
2680
|
// an opposite-side order cancelled in a later chunk would
|
|
2679
2681
|
// otherwise coexist with this create mid-broadcast and
|
|
2680
2682
|
// self-trade (production incident class).
|
|
2681
|
-
const createCrossed = findCrossedOrder(crossingCandidates, createPrice, order.type, bot.manager.assets, (o) =>
|
|
2682
|
-
const oid = o?.orderId || o?.chainOrderId;
|
|
2683
|
-
return o && oid && !cancelOpIndexByOrderId.has(oid);
|
|
2684
|
-
});
|
|
2683
|
+
const createCrossed = findCrossedOrder(crossingCandidates, createPrice, order.type, bot.manager.assets, (o) => orderUtils.isCrossingCheckCandidate(o, null, cancelOpIndexByOrderId));
|
|
2685
2684
|
const intraBatchCrossed = createCrossed ? null : findCrossedOrder(intraBatchCandidates, createPrice, order.type, bot.manager.assets);
|
|
2686
2685
|
const effectiveCrossed = createCrossed || intraBatchCrossed;
|
|
2687
2686
|
if (effectiveCrossed) {
|
|
@@ -2795,13 +2794,7 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2795
2794
|
// fatal fund assert). Skipping is safe: the slot keeps its
|
|
2796
2795
|
// old commitment and the next plan re-evaluates once
|
|
2797
2796
|
// the crossed order's cancel confirms.
|
|
2798
|
-
const crossedOrder = findCrossedOrder([...crossingCandidates, ...intraBatchCandidates], newPrice, orderType, bot.manager.assets, (o) =>
|
|
2799
|
-
const oid = o?.orderId || o?.chainOrderId;
|
|
2800
|
-
return o
|
|
2801
|
-
&& oid
|
|
2802
|
-
&& oid !== action.orderId
|
|
2803
|
-
&& !cancelOpIndexByOrderId.has(oid);
|
|
2804
|
-
});
|
|
2797
|
+
const crossedOrder = findCrossedOrder([...crossingCandidates, ...intraBatchCandidates], newPrice, orderType, bot.manager.assets, (o) => orderUtils.isCrossingCheckCandidate(o, action.orderId, cancelOpIndexByOrderId));
|
|
2805
2798
|
if (crossedOrder) {
|
|
2806
2799
|
skippedUpdateCount++;
|
|
2807
2800
|
if (action.id)
|
|
@@ -2855,6 +2848,32 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
2855
2848
|
`boundary ${bot.manager?.boundaryIdx}/gap ${bot.manager?._gapSlots} — re-proving live`, 'warn');
|
|
2856
2849
|
}
|
|
2857
2850
|
}
|
|
2851
|
+
if (stampUsable) {
|
|
2852
|
+
// Stamped-size re-proof: the stamp proved the
|
|
2853
|
+
// plan-time size, but an unprocessed fill can
|
|
2854
|
+
// land between plan-build and execution and
|
|
2855
|
+
// shrink the booked remaining below the
|
|
2856
|
+
// planned size (the PARTIAL-only growth guard
|
|
2857
|
+
// above misses it when the slot is not yet
|
|
2858
|
+
// PARTIAL). Re-prove against the LIVE master
|
|
2859
|
+
// size; invalidating the stamp routes into
|
|
2860
|
+
// the unstamped probe below, whose
|
|
2861
|
+
// isEvacuationRotationAllowed also rejects
|
|
2862
|
+
// growth and then falls through to the
|
|
2863
|
+
// normal last-fill guard.
|
|
2864
|
+
let stampPrecision = null;
|
|
2865
|
+
try {
|
|
2866
|
+
stampPrecision = getPrecisionByOrderType(bot.manager.assets, orderType);
|
|
2867
|
+
}
|
|
2868
|
+
catch {
|
|
2869
|
+
stampPrecision = null;
|
|
2870
|
+
}
|
|
2871
|
+
if (!isEvacuationSizeStillValid(newSize, Number(masterOrder?.size), stampPrecision)) {
|
|
2872
|
+
stampUsable = false;
|
|
2873
|
+
bot.manager.logger.log(`[LAST-FILL-GUARD] Stamped evacuation for ${action.id} -> ${action.newGridId} lost its size cover: ` +
|
|
2874
|
+
`planned ${Format.formatAmount(newSize)} exceeds booked remaining ${Format.formatAmount(Number(masterOrder?.size))} — re-proving live`, 'warn');
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2858
2877
|
if (stampUsable) {
|
|
2859
2878
|
bypassedEvacuation = true;
|
|
2860
2879
|
bot.manager.logger.log(`[LAST-FILL-GUARD] Allowing ${orderType} evacuation UPDATE for ${action.id} -> ${action.newGridId} at ${Format.formatPrice6(newPrice)}: ` +
|
|
@@ -3034,10 +3053,7 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
3034
3053
|
// replaces — no crossing of an opposite-side
|
|
3035
3054
|
// live order whose cancel is not already
|
|
3036
3055
|
// queued at an earlier op position.
|
|
3037
|
-
const fbCrossed = findCrossedOrder(crossingCandidates, fbPrice, fbType, bot.manager.assets, (o) =>
|
|
3038
|
-
const oid = o?.orderId || o?.chainOrderId;
|
|
3039
|
-
return o && oid && !cancelOpIndexByOrderId.has(oid);
|
|
3040
|
-
});
|
|
3056
|
+
const fbCrossed = findCrossedOrder(crossingCandidates, fbPrice, fbType, bot.manager.assets, (o) => orderUtils.isCrossingCheckCandidate(o, null, cancelOpIndexByOrderId));
|
|
3041
3057
|
if (fbCrossed) {
|
|
3042
3058
|
bot.manager.logger.log(`[COW-CROSS-GUARD] Skipping CREATE fallback for ${targetSlotId} at ` +
|
|
3043
3059
|
`${Format.formatPrice6(fbPrice)}: new ${fbType} crosses live ` +
|
|
@@ -3400,7 +3416,7 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
3400
3416
|
// refused-commit path: adopt from chain, keep pending
|
|
3401
3417
|
// protection if adoption is unavailable.
|
|
3402
3418
|
bot.manager.logger.log(`[COW][UNCERTAIN] Poll-confirmed commit refused; adopting placed orders from chain`, 'warn');
|
|
3403
|
-
const pollAdopted = await adoptPlacedBatchFromChain(bot, chainOrders, '[COW][UNCERTAIN]');
|
|
3419
|
+
const pollAdopted = await adoptPlacedBatchFromChain(bot, chainOrders, '[COW][UNCERTAIN]', { placedContexts: opContexts, polledCreateIds: confirmation.confirmedChainIds });
|
|
3404
3420
|
if (!pollAdopted) {
|
|
3405
3421
|
bot.manager.logger.log('[COW][UNCERTAIN] Poll-refused commit with unavailable chain adoption; keeping pending protection pending structural resync', 'error');
|
|
3406
3422
|
await requestStructuralResync(bot, 'poll-confirmed commit refused (chain adoption unavailable)', { reason: 'chain-adoption-unavailable' });
|
|
@@ -3420,7 +3436,7 @@ async function updateOrdersOnChainBatchCOW(bot, cowResult, options = {}) {
|
|
|
3420
3436
|
// on a failed adoption would let the next cycle re-create the
|
|
3421
3437
|
// VIRTUAL slots as duplicate on-chain orders. Keep the
|
|
3422
3438
|
// protection and defer to a structural resync instead.
|
|
3423
|
-
const pollAdoptedOk = await adoptPlacedBatchFromChain(bot, chainOrders, '[COW][UNCERTAIN]');
|
|
3439
|
+
const pollAdoptedOk = await adoptPlacedBatchFromChain(bot, chainOrders, '[COW][UNCERTAIN]', { placedContexts: opContexts, polledCreateIds: confirmation.confirmedChainIds });
|
|
3424
3440
|
if (!pollAdoptedOk) {
|
|
3425
3441
|
bot.manager.logger.log('[COW][UNCERTAIN] Poll-confirmed commit with unavailable chain adoption; keeping pending protection pending structural resync', 'error');
|
|
3426
3442
|
await requestStructuralResync(bot, 'poll-confirmed commit (chain adoption unavailable)', { reason: 'chain-adoption-unavailable' });
|
|
@@ -3520,9 +3536,13 @@ async function requestStructuralResync(bot, reason, details = {}) {
|
|
|
3520
3536
|
* @param {any} mgr - bot.manager
|
|
3521
3537
|
* @param {any} placedResults - broadcast result (has operation_results); null when unavailable
|
|
3522
3538
|
* @param {any[]} placedContexts - opContexts (aligned with operation_results); null when unavailable
|
|
3539
|
+
* @param {string[]|null} [extraCreateIds=null] - fresh CREATE chain ids from another
|
|
3540
|
+
* authoritative source (e.g. the uncertain-broadcast poll confirmation) when
|
|
3541
|
+
* no broadcast result exists; merged into createIds so the lagging-create
|
|
3542
|
+
* retry guards them
|
|
3523
3543
|
* @returns {string[]} Unique, well-formed 1.7.x order ids
|
|
3524
3544
|
*/
|
|
3525
|
-
function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts) {
|
|
3545
|
+
function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts, extraCreateIds = null) {
|
|
3526
3546
|
const masterIds = new Set();
|
|
3527
3547
|
const grid = mgr && mgr.grid;
|
|
3528
3548
|
if (Array.isArray(grid)) {
|
|
@@ -3532,6 +3552,17 @@ function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts) {
|
|
|
3532
3552
|
}
|
|
3533
3553
|
}
|
|
3534
3554
|
}
|
|
3555
|
+
// Master tracked ids live in the orders Map (mgr.grid is legacy and
|
|
3556
|
+
// unset on OrderManager — without this the by-id set omits every
|
|
3557
|
+
// pre-existing ACTIVE order and pass-1 phantom cleanup would virtualize
|
|
3558
|
+
// them as fills on a partial snapshot).
|
|
3559
|
+
if (mgr && mgr.orders instanceof Map) {
|
|
3560
|
+
for (const slot of mgr.orders.values()) {
|
|
3561
|
+
if (slot && slot.orderId && /^1\.7\.\d+$/.test(String(slot.orderId))) {
|
|
3562
|
+
masterIds.add(String(slot.orderId));
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3535
3566
|
const createIds = new Set();
|
|
3536
3567
|
if (placedResults && Array.isArray(placedContexts)) {
|
|
3537
3568
|
const opResults = extractBatchOperationResults(placedResults);
|
|
@@ -3547,6 +3578,33 @@ function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts) {
|
|
|
3547
3578
|
}
|
|
3548
3579
|
}
|
|
3549
3580
|
}
|
|
3581
|
+
if (Array.isArray(extraCreateIds)) {
|
|
3582
|
+
for (const id of extraCreateIds) {
|
|
3583
|
+
if (id && /^1\.7\.\d+$/.test(String(id)))
|
|
3584
|
+
createIds.add(String(id));
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
// Existing chain ids referenced by non-create op contexts (cancel /
|
|
3588
|
+
// rotation / size-update). Pre-existing orders whose absence is expected
|
|
3589
|
+
// (cancels/fills in this batch), so they join the by-id set but never
|
|
3590
|
+
// the lagging-create guard.
|
|
3591
|
+
if (Array.isArray(placedContexts)) {
|
|
3592
|
+
for (const ctx of placedContexts) {
|
|
3593
|
+
if (!ctx || ctx.kind === 'create')
|
|
3594
|
+
continue;
|
|
3595
|
+
const refs = [];
|
|
3596
|
+
if (ctx.kind === 'cancel' && ctx.order)
|
|
3597
|
+
refs.push(ctx.order.orderId);
|
|
3598
|
+
else if (ctx.kind === 'rotation' && ctx.rotation?.oldOrder)
|
|
3599
|
+
refs.push(ctx.rotation.oldOrder.orderId);
|
|
3600
|
+
else if (ctx.kind === 'size-update' && ctx.updateInfo?.partialOrder)
|
|
3601
|
+
refs.push(ctx.updateInfo.partialOrder.orderId);
|
|
3602
|
+
for (const id of refs) {
|
|
3603
|
+
if (id && /^1\.7\.\d+$/.test(String(id)))
|
|
3604
|
+
masterIds.add(String(id));
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3550
3608
|
const all = new Set([...masterIds, ...createIds]);
|
|
3551
3609
|
return { masterIds: [...masterIds], createIds: [...createIds], all: [...all] };
|
|
3552
3610
|
}
|
|
@@ -3573,6 +3631,10 @@ function collectKnownOnChainOrderIds(mgr, placedResults, placedContexts) {
|
|
|
3573
3631
|
* @param {Object} [opts]
|
|
3574
3632
|
* @param {any} [opts.placedResults] - broadcast result carrying operation_results
|
|
3575
3633
|
* @param {any[]} [opts.placedContexts] - opContexts aligned with operation_results
|
|
3634
|
+
* @param {string[]} [opts.polledCreateIds] - fresh CREATE chain ids confirmed by
|
|
3635
|
+
* the uncertain-broadcast poll (no broadcast result exists on that path);
|
|
3636
|
+
* routes the poll call sites through the by-id path with its lagging-create
|
|
3637
|
+
* retry instead of the unguarded window fallback
|
|
3576
3638
|
* @returns {Promise<boolean>} true if master was adopted from the chain
|
|
3577
3639
|
*/
|
|
3578
3640
|
/**
|
|
@@ -3605,16 +3667,20 @@ async function restoreBoundaryAfterAdoption(bot, workingBoundary) {
|
|
|
3605
3667
|
}
|
|
3606
3668
|
}
|
|
3607
3669
|
async function adoptPlacedBatchFromChain(bot, chainOrders, logPrefix, opts = {}) {
|
|
3608
|
-
const { placedResults = null, placedContexts = null } = opts || {};
|
|
3670
|
+
const { placedResults = null, placedContexts = null, polledCreateIds = null } = opts || {};
|
|
3609
3671
|
try {
|
|
3610
3672
|
const mgr = bot.manager;
|
|
3611
3673
|
const accountRef = bot.accountId || bot.account?.id || bot.account;
|
|
3612
|
-
// PREFERRED: re-read the exact placed/existing orders by id.
|
|
3613
|
-
//
|
|
3614
|
-
//
|
|
3615
|
-
//
|
|
3616
|
-
|
|
3617
|
-
|
|
3674
|
+
// PREFERRED: re-read the exact placed/existing orders by id. The by-id
|
|
3675
|
+
// set needs the broadcast result (freshest CREATE ids), the
|
|
3676
|
+
// poll-confirmed CREATE ids, or at minimum the contexts referencing
|
|
3677
|
+
// existing chain orders — without any id hints the set would be
|
|
3678
|
+
// incomplete and would wrongly sync master against a partial picture.
|
|
3679
|
+
const haveIdHints = Boolean(placedResults)
|
|
3680
|
+
|| (Array.isArray(placedContexts) && placedContexts.length > 0)
|
|
3681
|
+
|| (Array.isArray(polledCreateIds) && polledCreateIds.length > 0);
|
|
3682
|
+
const { all: knownIds, createIds } = haveIdHints
|
|
3683
|
+
? collectKnownOnChainOrderIds(mgr, placedResults, placedContexts, polledCreateIds)
|
|
3618
3684
|
: { all: [], createIds: [] };
|
|
3619
3685
|
if (knownIds.length > 0 && typeof chainOrders.batchReadOrders === 'function') {
|
|
3620
3686
|
// Retry/backoff (fix #6): a fresh CREATE absent from the first read
|