dexbot 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/credential-daemon.d.ts.map +1 -1
  2. package/dist/credential-daemon.js +6 -3
  3. package/dist/credential-daemon.js.map +1 -1
  4. package/dist/modules/credential_policy.d.ts.map +1 -1
  5. package/dist/modules/credential_policy.js +7 -5
  6. package/dist/modules/credential_policy.js.map +1 -1
  7. package/dist/modules/dexbot_class.d.ts.map +1 -1
  8. package/dist/modules/dexbot_class.js +52 -20
  9. package/dist/modules/dexbot_class.js.map +1 -1
  10. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  11. package/dist/modules/dexbot_maintenance_runtime.js +4 -0
  12. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  13. package/dist/modules/order/accounting.d.ts +8 -2
  14. package/dist/modules/order/accounting.d.ts.map +1 -1
  15. package/dist/modules/order/accounting.js +25 -15
  16. package/dist/modules/order/accounting.js.map +1 -1
  17. package/dist/modules/order/grid.js +1 -1
  18. package/dist/modules/order/grid.js.map +1 -1
  19. package/dist/modules/order/grid_reconcile_internal.js +13 -2
  20. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  21. package/dist/modules/order/manager.d.ts +82 -16
  22. package/dist/modules/order/manager.d.ts.map +1 -1
  23. package/dist/modules/order/manager.js +126 -235
  24. package/dist/modules/order/manager.js.map +1 -1
  25. package/dist/modules/order/sync_engine.d.ts +2 -3
  26. package/dist/modules/order/sync_engine.d.ts.map +1 -1
  27. package/dist/modules/order/sync_engine.js +13 -3
  28. package/dist/modules/order/sync_engine.js.map +1 -1
  29. package/dist/modules/order/utils/order.d.ts.map +1 -1
  30. package/dist/modules/order/utils/order.js +42 -6
  31. package/dist/modules/order/utils/order.js.map +1 -1
  32. package/dist/modules/types.d.ts +7 -12
  33. package/dist/modules/types.d.ts.map +1 -1
  34. package/package.json +1 -1
@@ -349,7 +349,7 @@ class DEXBot {
349
349
  shadowLocks: this.manager?.shadowOrderIds?.size || 0,
350
350
  batchInFlight: this._batchInFlight,
351
351
  recoveryInFlight: this._recoverySyncInFlight,
352
- broadcasting: this.manager?._state?.isBroadcastingActive() || false
352
+ broadcasting: this.manager?.isBroadcastingActive?.() || false
353
353
  };
354
354
  }
355
355
  /**
@@ -1279,6 +1279,30 @@ class DEXBot {
1279
1279
  await this._processFillsWithBootstrapMode(chainOrders);
1280
1280
  }
1281
1281
  this.manager.finishBootstrap();
1282
+ // Refresh account totals after bootstrap to eliminate the timing
1283
+ // gap between the initial balance fetch and grid operations (sync,
1284
+ // reconcile, fills). Without this, the first maintenance cycle sees
1285
+ // a fund drift (expected at bootstrap) and triggers an unnecessary
1286
+ // invariant violation + full recovery cycle.
1287
+ // Bound by a timeout to avoid blocking _fillProcessingLock on a
1288
+ // flaky node. If the fetch times out, continue with cached values;
1289
+ // the next periodic maintenance cycle will retry.
1290
+ const FETCH_TIMEOUT_MS = 30000;
1291
+ let _fetchTimeoutHandle;
1292
+ try {
1293
+ await Promise.race([
1294
+ this.manager.fetchAccountTotals(),
1295
+ new Promise((_, reject) => {
1296
+ _fetchTimeoutHandle = setTimeout(() => reject(new Error('timeout')), FETCH_TIMEOUT_MS);
1297
+ })
1298
+ ]);
1299
+ }
1300
+ catch (fetchErr) {
1301
+ this._log(`[STARTUP] [${this.config?.botKey || 'unknown'}] fetchAccountTotals ${fetchErr.message === 'timeout' ? 'timed out' : 'failed'} (${fetchErr.message}). Continuing with cached account totals.`, 'warn');
1302
+ }
1303
+ finally {
1304
+ clearTimeout(_fetchTimeoutHandle);
1305
+ }
1282
1306
  // Perform initial grid maintenance (thresholds, divergence, spread, health)
1283
1307
  // Consolidated into shared logic to ensure consistent behavior at boot and runtime.
1284
1308
  // CRITICAL: Pass lockAlreadyHeld since we're inside _fillProcessingLock.acquire()
@@ -1504,11 +1528,11 @@ class DEXBot {
1504
1528
  try {
1505
1529
  // BOOTSTRAP OPTIMIZATION: During bootstrap, prioritize fill processing over grid-wide checks
1506
1530
  // Process fills immediately with side-only rebalancing (no expensive full grid recalculations)
1507
- if (this.manager._state.isBootstrapping()) {
1531
+ if (this.manager.isBootstrapping()) {
1508
1532
  // During bootstrap: skip lock contention checks, process fills directly
1509
1533
  let bootstrapSkipped = false;
1510
1534
  await this.manager._fillProcessingLock.acquire(async () => {
1511
- if (!this.manager._state.isBootstrapping()) {
1535
+ if (!this.manager.isBootstrapping()) {
1512
1536
  // Bootstrap finished while waiting for the lock — no
1513
1537
  // work to do, but the iteration is still healthy.
1514
1538
  bootstrapSkipped = true;
@@ -2628,7 +2652,11 @@ class DEXBot {
2628
2652
  fingerprint: entry.fingerprint
2629
2653
  });
2630
2654
  if (match) {
2631
- adopted.push({ slotId: entry.slotId, chainOrderId: match.id });
2655
+ adopted.push({
2656
+ slotId: entry.slotId,
2657
+ chainOrderId: match.id,
2658
+ orderType: entry.orderType || entry.order?.type,
2659
+ });
2632
2660
  this.manager._pendingBroadcasts.delete(entry.fingerprint);
2633
2661
  }
2634
2662
  else {
@@ -2665,7 +2693,11 @@ class DEXBot {
2665
2693
  fingerprint: entry.fingerprint
2666
2694
  });
2667
2695
  if (match) {
2668
- adopted.push({ slotId: entry.slotId, chainOrderId: match.id });
2696
+ adopted.push({
2697
+ slotId: entry.slotId,
2698
+ chainOrderId: match.id,
2699
+ orderType: entry.orderType || entry.order?.type,
2700
+ });
2669
2701
  this.manager.logger.log(`[COW][UNCERTAIN] CREATE re-adopted after ${recheckRound} block(s): ` +
2670
2702
  `${entry.slotId}->${match.id}`, 'info');
2671
2703
  }
@@ -2748,27 +2780,27 @@ class DEXBot {
2748
2780
  this.manager.logger.log(`[COW][UNCERTAIN] Auto-cancel pass failed: ${orphanErr?.message || orphanErr}`, 'warn');
2749
2781
  }
2750
2782
  // Boundary shift recovery: the COW batch that should have committed the
2751
- // boundary shift from this fill cycle failed before commit. Every planned
2752
- // CREATE in the batch represents a fill whose opposite-side boundary step
2753
- // was lost — regardless of whether the individual CREATE was later adopted
2754
- // on chain (adopted) or not (discarded). Manually adjust manager.boundaryIdx
2755
- // so the next COW cycle (triggered by the next fill or periodic maintenance)
2756
- // generates replacement opposite-side orders using the correct boundary.
2783
+ // boundary shift from this fill cycle failed before commit. Only COUNT
2784
+ // ADOPTED CREATEs — orders that actually landed on-chain. Discarded
2785
+ // CREATEs represent orders that never existed, so the grid must NOT
2786
+ // shift the boundary for them. The next fill cycle's
2787
+ // calculateTargetGrid → deriveTargetBoundary will recompute the correct
2788
+ // boundary from scratch for any discarded slots.
2757
2789
  //
2758
2790
  // NOTE: Direct mutation of manager.boundaryIdx outside a COW commit
2759
2791
  // violates the invariant stated in grid.ts:1217-1220 (boundary must only
2760
2792
  // be updated atomically inside _commitWorkingGrid). This is intentional
2761
2793
  // here because the COW commit already failed — the invariant was already
2762
2794
  // broken by the broadcast uncertainty, and the adjustment only restores
2763
- // the state to what the successful commit would have produced.
2764
- if (hadRotation && this.manager && pending.length > 0) {
2795
+ // the state to what the successful commit would have produced for
2796
+ // orders that are confirmed on-chain.
2797
+ if (hadRotation && this.manager && adopted.length > 0) {
2765
2798
  let boundaryShift = 0;
2766
- for (const entry of pending) {
2767
- const orderType = entry.orderType || entry.order?.type;
2768
- if (orderType === ORDER_TYPES.SELL) {
2799
+ for (const entry of adopted) {
2800
+ if (entry.orderType === ORDER_TYPES.SELL) {
2769
2801
  boundaryShift--; // SELL CREATE → fill was BUY → boundary LEFT
2770
2802
  }
2771
- else if (orderType === ORDER_TYPES.BUY) {
2803
+ else if (entry.orderType === ORDER_TYPES.BUY) {
2772
2804
  boundaryShift++; // BUY CREATE → fill was SELL → boundary RIGHT
2773
2805
  }
2774
2806
  }
@@ -2780,10 +2812,10 @@ class DEXBot {
2780
2812
  if (newIdx !== oldIdx) {
2781
2813
  this.manager.boundaryIdx = newIdx;
2782
2814
  this.manager.logger.log(`[COW][UNCERTAIN] Boundary adjusted by ${boundaryShift} ` +
2783
- `(${adopted.length} adopted / ${discarded.length} discarded CREATE(s)): ` +
2815
+ `(${adopted.length} adopted CREATE(s), ${discarded.length} discarded): ` +
2784
2816
  `${oldIdx} → ${newIdx}. Next COW cycle will generate replacement opposite-side orders.`, 'warn');
2785
2817
  if (typeof this.manager._markGridDirty === 'function') {
2786
- this.manager._markGridDirty(`boundary adjustment after ${pending.length} planned CREATE(s)`);
2818
+ this.manager._markGridDirty();
2787
2819
  }
2788
2820
  }
2789
2821
  }
@@ -3324,7 +3356,7 @@ class DEXBot {
3324
3356
  if (this._credentialRecoveryInFlight || !this._credentialRecoveryNeeded || this._shuttingDown) {
3325
3357
  return;
3326
3358
  }
3327
- if (this.manager?._state?.isBootstrapping?.() || this.manager?._state?.isBroadcastingActive?.()) {
3359
+ if (this.manager?.isBootstrapping?.() || this.manager?.isBroadcastingActive?.()) {
3328
3360
  if (!this._credentialRecoveryDeferredTimer) {
3329
3361
  this.manager?.logger?.log?.('[CREDENTIAL] Deferring credential recovery until startup/broadcast activity is idle.', 'info');
3330
3362
  this._credentialRecoveryDeferredTimer = setTimeout(() => {