dexbot 1.6.6 → 1.6.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/README.md +5 -2
  3. package/analysis/trend_detection/package.json +1 -1
  4. package/claw/package.json +1 -1
  5. package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
  6. package/claw/runtimes/openclaw-plugin/package.json +1 -1
  7. package/claw/tests/test_claw_mcp_transport.ts +2 -2
  8. package/dist/dexbot.d.ts.map +1 -1
  9. package/dist/dexbot.js +22 -9
  10. package/dist/dexbot.js.map +1 -1
  11. package/dist/modules/account_bots.d.ts.map +1 -1
  12. package/dist/modules/account_bots.js +10 -9
  13. package/dist/modules/account_bots.js.map +1 -1
  14. package/dist/modules/account_orders.d.ts +20 -2
  15. package/dist/modules/account_orders.d.ts.map +1 -1
  16. package/dist/modules/account_orders.js +63 -3
  17. package/dist/modules/account_orders.js.map +1 -1
  18. package/dist/modules/bitshares-native/chain_client.d.ts +10 -0
  19. package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -1
  20. package/dist/modules/bitshares-native/chain_client.js +197 -28
  21. package/dist/modules/bitshares-native/chain_client.js.map +1 -1
  22. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
  23. package/dist/modules/bitshares-native/subscriptions.js +312 -10
  24. package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
  25. package/dist/modules/bitshares-native/transport.d.ts +1 -0
  26. package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
  27. package/dist/modules/bitshares-native/transport.js +97 -3
  28. package/dist/modules/bitshares-native/transport.js.map +1 -1
  29. package/dist/modules/bitshares_client.d.ts +20 -0
  30. package/dist/modules/bitshares_client.d.ts.map +1 -1
  31. package/dist/modules/bitshares_client.js +29 -0
  32. package/dist/modules/bitshares_client.js.map +1 -1
  33. package/dist/modules/chain_keys.d.ts +37 -22
  34. package/dist/modules/chain_keys.d.ts.map +1 -1
  35. package/dist/modules/chain_keys.js +145 -44
  36. package/dist/modules/chain_keys.js.map +1 -1
  37. package/dist/modules/cli_start_onboarding.d.ts +10 -0
  38. package/dist/modules/cli_start_onboarding.d.ts.map +1 -0
  39. package/dist/modules/cli_start_onboarding.js +15 -0
  40. package/dist/modules/cli_start_onboarding.js.map +1 -0
  41. package/dist/modules/cli_start_output.d.ts +6 -0
  42. package/dist/modules/cli_start_output.d.ts.map +1 -0
  43. package/dist/modules/cli_start_output.js +8 -0
  44. package/dist/modules/cli_start_output.js.map +1 -0
  45. package/dist/modules/constants.d.ts +13 -0
  46. package/dist/modules/constants.d.ts.map +1 -1
  47. package/dist/modules/constants.js +103 -1
  48. package/dist/modules/constants.js.map +1 -1
  49. package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
  50. package/dist/modules/dexbot_cow_runtime.js +49 -9
  51. package/dist/modules/dexbot_cow_runtime.js.map +1 -1
  52. package/dist/modules/dexbot_fill_runtime.d.ts +6 -1
  53. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
  54. package/dist/modules/dexbot_fill_runtime.js +22 -3
  55. package/dist/modules/dexbot_fill_runtime.js.map +1 -1
  56. package/dist/modules/dexbot_maintenance_runtime.d.ts +24 -1
  57. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
  58. package/dist/modules/dexbot_maintenance_runtime.js +45 -1
  59. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
  60. package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
  61. package/dist/modules/dexbot_state_recovery.js +13 -1
  62. package/dist/modules/dexbot_state_recovery.js.map +1 -1
  63. package/dist/modules/order/grid.d.ts.map +1 -1
  64. package/dist/modules/order/grid.js +33 -1
  65. package/dist/modules/order/grid.js.map +1 -1
  66. package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
  67. package/dist/modules/order/grid_reconcile_internal.js +11 -0
  68. package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
  69. package/dist/modules/order/logger.d.ts +12 -2
  70. package/dist/modules/order/logger.d.ts.map +1 -1
  71. package/dist/modules/order/logger.js +29 -7
  72. package/dist/modules/order/logger.js.map +1 -1
  73. package/dist/modules/order/manager.d.ts +49 -0
  74. package/dist/modules/order/manager.d.ts.map +1 -1
  75. package/dist/modules/order/manager.js +72 -24
  76. package/dist/modules/order/manager.js.map +1 -1
  77. package/dist/modules/order/utils/order.d.ts.map +1 -1
  78. package/dist/modules/order/utils/order.js +97 -12
  79. package/dist/modules/order/utils/order.js.map +1 -1
  80. package/dist/modules/order/utils/system.d.ts +97 -0
  81. package/dist/modules/order/utils/system.d.ts.map +1 -1
  82. package/dist/modules/order/utils/system.js +274 -2
  83. package/dist/modules/order/utils/system.js.map +1 -1
  84. package/dist/modules/paths.d.ts +7 -1
  85. package/dist/modules/paths.d.ts.map +1 -1
  86. package/dist/modules/paths.js +10 -3
  87. package/dist/modules/paths.js.map +1 -1
  88. package/dist/modules/utils/text_width.d.ts +19 -0
  89. package/dist/modules/utils/text_width.d.ts.map +1 -0
  90. package/dist/modules/utils/text_width.js +97 -0
  91. package/dist/modules/utils/text_width.js.map +1 -0
  92. package/dist/pm2.d.ts.map +1 -1
  93. package/dist/pm2.js +95 -18
  94. package/dist/pm2.js.map +1 -1
  95. package/dist/scripts/update.js +62 -13
  96. package/dist/scripts/update.js.map +1 -1
  97. package/dist/unlock.d.ts +14 -2
  98. package/dist/unlock.d.ts.map +1 -1
  99. package/dist/unlock.js +73 -4
  100. package/dist/unlock.js.map +1 -1
  101. package/docs/COW_INVARIANTS.md +8 -0
  102. package/docs/CREDENTIAL_SECURITY.md +26 -0
  103. package/docs/DEXBOT_COMPARISON.md +3 -3
  104. package/docs/EVOLUTION.md +6 -5
  105. package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
  106. package/docs/GRID_PRICE_INVARIANT.md +56 -1
  107. package/docs/LOGGING.md +17 -2
  108. package/docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md +6 -7
  109. package/docs/README.md +1 -1
  110. package/package.json +2 -2
@@ -209,6 +209,103 @@ export declare function persistGridSnapshot(manager: any, accountOrders: any, sn
209
209
  * @returns {number} Number of streak entries restored
210
210
  */
211
211
  export declare function restoreGapEvacStreaks(manager: any, persisted: any): number;
212
+ /**
213
+ * Normalize a persisted LAST-FILL-GUARD pivot row (shared by the
214
+ * AccountOrders sanitizer/loader and the payload builder's row contract).
215
+ *
216
+ * Returns the row normalized to {price:number, type, fillsAt:number,
217
+ * genesisHash} when every field passes its gate, null otherwise:
218
+ * - finite price > 0
219
+ * - type is ORDER_TYPES.BUY or ORDER_TYPES.SELL
220
+ * - finite fillsAt > 0
221
+ * - non-empty string genesisHash
222
+ *
223
+ * @param {any} row - Raw candidate row
224
+ * @returns {Object|null} Normalized row or null
225
+ */
226
+ export declare function normalizeLastFillPivot(row: any): {
227
+ price: number;
228
+ type: string;
229
+ fillsAt: number;
230
+ genesisHash: string;
231
+ } | null;
232
+ /**
233
+ * Write the LAST-FILL-GUARD pivot scalar family in one place (free
234
+ * implementation shared by the manager method, cow_runtime's queued-fill
235
+ * refresh, and restoreLastFillPivot).
236
+ *
237
+ * Writes _lastFilledPrice, _lastFilledType, _lastFilledAt,
238
+ * lastFillPivotSource and the per-side mirror (_lastFilledBuyPrice or
239
+ * _lastFilledSellPrice). `atMs` lets restoreLastFillPivot preserve the
240
+ * persisted fill timestamp (otherwise every restart would re-stamp "now"
241
+ * and the TTL would degrade to "time since last restart"); ordinary fill
242
+ * calls omit it and take Date.now().
243
+ *
244
+ * @param {Object} manager - OrderManager instance (or legacy stub)
245
+ * @param {string} type - ORDER_TYPES.BUY or ORDER_TYPES.SELL
246
+ * @param {number} price - Pivot price (finite, > 0)
247
+ * @param {string} provenance - 'fill' (persist-eligible) or 'book' (seed heuristic)
248
+ * @param {number} [atMs] - Fill timestamp to preserve (defaults to now)
249
+ * @returns {boolean} True when the pivot was written
250
+ */
251
+ export declare function setLastFillPivot(manager: any, type: any, price: any, provenance: 'fill' | 'book', atMs?: number): boolean;
252
+ /**
253
+ * Clear the manager's LAST-FILL-GUARD pivot to cold state — the FULL scalar
254
+ * family, per-side mirrors included. Clearing only the primary fields would
255
+ * leave seedLastFilledPricesFromBook's early-return (both mirrors set)
256
+ * silently suppressing the startup book seed after every grid rebuild — the
257
+ * exact cold window the persist/restore feature exists to close.
258
+ *
259
+ * Used whenever the pivot's generation is invalidated: grid rebuild via
260
+ * initializeGrid, rejected snapshot reset, and any path that re-anchors the
261
+ * boundary outside the fill flow. The guard re-arms on the next real fill
262
+ * (or via the startup book seed), never against a boundary that no longer
263
+ * exists. Must not touch disk: the pivot is a live-grid invariant, and the
264
+ * persist pipeline clears the stored row on the next flush by passing null.
265
+ *
266
+ * @param {Object} manager - OrderManager instance
267
+ * @param {string} reason - Log/debug label
268
+ * @returns {boolean} True when an armed pivot was cleared
269
+ */
270
+ export declare function resetLastFillPivot(manager: any, reason?: string): boolean;
271
+ /**
272
+ * Restore the persisted LAST-FILL-GUARD pivot into the manager.
273
+ *
274
+ * MUST run with the boundary (after loadGrid has applied the persisted
275
+ * genesis + re-typed the grid, before the first reconcile/broadcast) so the
276
+ * guard is never armed against a geometry the snapshot does not describe.
277
+ *
278
+ * Validation chain (fail → no-op, never arm on a poisoned value):
279
+ * 1. Shape: normalizeLastFillPivot re-validates the row (shared gate).
280
+ * 2. TTL: a pivot older than GRID_LIMITS.LAST_FILL_PIVOT_TTL_MS expires
281
+ * instead of vetoing legitimate placements after long downtime (the
282
+ * pivot is a "latest fill" fact, not a permanent ratchet). Expired rows
283
+ * are dead rows: the shared drop path (clearPersistedLastFillPivot
284
+ * under the bot's persistence lock, best-effort) erases them so a later
285
+ * validation failure cannot leave a half-invalid row armed.
286
+ * 3. Genesis binding: a row whose genesisHash differs from the manager's
287
+ * current genesis belongs to a dead generation — dropped through the
288
+ * same shared drop path (erased from disk so it cannot re-arm on the
289
+ * next restart).
290
+ * 4. On-grid check reuses the runtime's own ladder validator
291
+ * (resolveOnGridPivot in dexbot_cow_runtime, safe to import from here —
292
+ * utils/system never imports it otherwise, so no cycle). One
293
+ * implementation for both the live guard and the restore path, same
294
+ * increment fallback chain. On success the snapped ladder level is
295
+ * restored — the guard's own convention — never the raw persisted
296
+ * float, and the ORIGINAL fillsAt is preserved through
297
+ * setLastFillPivot's atMs so the TTL keeps meaning "age of the last
298
+ * fill", not "time since this restart".
299
+ *
300
+ * @param {Object} manager - OrderManager instance
301
+ * @param {Object|null} persisted - {price, type, fillsAt, genesisHash} from loadLastFillPivot
302
+ * @param {Object} [options]
303
+ * @param {number} [options.now] - Injectable clock (tests)
304
+ * @returns {boolean} True when the guard was re-armed from the snapshot
305
+ */
306
+ export declare function restoreLastFillPivot(manager: any, persisted: any, options?: {
307
+ now?: number;
308
+ }): boolean;
212
309
  /**
213
310
  * Retry grid persistence if previous attempt failed.
214
311
  * Clears persistence warning flag if successful.
@@ -1 +1 @@
1
- {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../../modules/order/utils/system.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../../modules/order/utils/system.ts"],"names":[],"mappings":"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"}
@@ -50,7 +50,7 @@ const require = createRequire(import.meta.url);
50
50
  import { path } from '../../path_api.js';
51
51
  import { getStorage } from '../../storage/index.js';
52
52
  const storage = getStorage();
53
- import { API_LIMITS, ORDER_TYPES, COW_ACTIONS, FEE_PARAMETERS, BTS_PRECISION, PIPELINE_TIMING, NATIVE_CLIENT } from '../../constants.js';
53
+ import { API_LIMITS, ORDER_TYPES, COW_ACTIONS, FEE_PARAMETERS, BTS_PRECISION, PIPELINE_TIMING, NATIVE_CLIENT, GRID_LIMITS } from '../../constants.js';
54
54
  import { PATHS } from '../../paths.js';
55
55
  import { toFiniteNumber, isValidNumber } from '../format.js';
56
56
  import * as MathUtils from './math.js';
@@ -61,6 +61,39 @@ import { getErrorMessage } from '../../utils/errors.js';
61
61
  import { withTimeout } from './timeout.js';
62
62
  const { ensureDir, readJSON } = storage;
63
63
  const systemLogger = new Logger('System');
64
+ /**
65
+ * Lazily-bound ladder validator: resolveOnGridPivot from the COW runtime,
66
+ * imported on first use (not at module top level). utils/system.ts Must not
67
+ * statically import dexbot_cow_runtime (the runtime is a startup-graph
68
+ * heavyweight and the static edge would pull the whole COW engine into
69
+ * everything that touches persistence helpers); the runtime CAN statically
70
+ * import utils/system (it already does), so this edge stays one-directional
71
+ * and cycle-free in practice.
72
+ *
73
+ * Shared on purpose (centralization): one ladder-validation implementation
74
+ * — the increment fallback chain (manager.config → DEFAULT_CONFIG) and the
75
+ * one-increment drift refusal — serves both the live guard's per-probe
76
+ * validation and the persisted-pivot restore. The restorer must never
77
+ * accept a pivot value the runtime would itself refuse per probe.
78
+ *
79
+ * @param {Object} manager - OrderManager instance
80
+ * @param {number} price - Candidate pivot price
81
+ * @returns {Object|null} {price, slotIdx, snapped, nearestDrift} or null
82
+ */
83
+ function cowRuntimeLadderValidator(manager, price) {
84
+ try {
85
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
86
+ const { resolveOnGridPivot } = require('../../dexbot_cow_runtime');
87
+ const validated = resolveOnGridPivot(manager, price);
88
+ if (!validated || validated.price == null || validated.idx == null)
89
+ return null;
90
+ return { price: validated.price, slotIdx: validated.idx, snapped: !!validated.snapped, nearestDrift: validated.nearestDrift };
91
+ }
92
+ catch (e) {
93
+ systemLogger.warn(`restoreLastFillPivot: ladder validator unavailable (${getErrorMessage(e)})`);
94
+ return null;
95
+ }
96
+ }
64
97
  function _debugLogAndNull(method, symA, symB) {
65
98
  return (err) => {
66
99
  // debug level: underlying derivePoolPrice/deriveMarketPrice already log at warn
@@ -856,12 +889,19 @@ export async function persistGridSnapshot(manager, accountOrders, snapshotOrders
856
889
  .slice(-500)
857
890
  .map((e) => ({ slotId: e.slotId, side: e.side, ts: Number(e.ts) }))
858
891
  : undefined;
892
+ // LAST-FILL-GUARD pivot (restart resilience): only a fill-provenanced
893
+ // pivot is eligible — a book-seeded heuristic is never persisted as if
894
+ // it were market truth. Every other case (book seed, cold manager,
895
+ // legacy stub without the flags) passes null, which storeMasterGrid
896
+ // treats as an explicit clear; the row carries the CURRENT live
897
+ // genesis hash so a restore that sees a re-derived genesis refuses it.
898
+ const lastFillPivot = buildLastFillPivotPayload(manager);
859
899
  await accountOrders.storeMasterGrid(orders, btsFeesOwed, manager.boundaryIdx, manager.assets || null, {
860
900
  persistedAt: nowIso(),
861
901
  config: debugConfig,
862
902
  accountTotals,
863
903
  btsBalance
864
- }, fillKeys, genesis, gapEvacStreaks, pendingFillCrawls);
904
+ }, fillKeys, genesis, gapEvacStreaks, pendingFillCrawls, lastFillPivot);
865
905
  return true;
866
906
  }
867
907
  catch (e) {
@@ -897,6 +937,238 @@ export function restoreGapEvacStreaks(manager, persisted) {
897
937
  manager._gapEvacStreaks = streaks;
898
938
  return streaks.size;
899
939
  }
940
+ /**
941
+ * Build the persist payload for the LAST-FILL-GUARD pivot, or null when there
942
+ * is nothing durable to write.
943
+ *
944
+ * Source-of-truth contract (single ledger): the manager's in-memory pivot is
945
+ * authoritative during runtime; the disk row is a mirror that follows the
946
+ * grid snapshot, so it invalidates whenever the snapshot does:
947
+ * - Only a 'fill'-provenance pivot is eligible — a book-seeded pivot is a
948
+ * heuristic, not market truth, and must not fossilize into the snapshot.
949
+ * - The row carries the CURRENT live genesis hash, so a restore that sees a
950
+ * different (re-derived/regenerated) genesis refuses it (a pivot from an
951
+ * old generation is not valid after a grid regeneration).
952
+ * - A pivot without a live genesis is not persistable: null always clears,
953
+ * so an expired generation never leaves a stale row behind on disk.
954
+ *
955
+ * @param {Object} manager - OrderManager instance
956
+ * @returns {Object|null} {price, type, fillsAt, genesisHash} or null to clear
957
+ */
958
+ function buildLastFillPivotPayload(manager) {
959
+ const price = Number(manager?._lastFilledPrice);
960
+ const type = manager?._lastFilledType;
961
+ if (manager?.lastFillPivotSource !== 'fill')
962
+ return null;
963
+ if (!Number.isFinite(price) || price <= 0)
964
+ return null;
965
+ if (type !== ORDER_TYPES.BUY && type !== ORDER_TYPES.SELL)
966
+ return null;
967
+ const fillsAt = Number(manager._lastFilledAt);
968
+ if (!Number.isFinite(fillsAt))
969
+ return null;
970
+ const genesisHash = manager?._genesis?.priceLevelsHash;
971
+ if (typeof genesisHash !== 'string' || genesisHash.length === 0)
972
+ return null;
973
+ return { price, type, fillsAt, genesisHash };
974
+ }
975
+ /**
976
+ * Normalize a persisted LAST-FILL-GUARD pivot row (shared by the
977
+ * AccountOrders sanitizer/loader and the payload builder's row contract).
978
+ *
979
+ * Returns the row normalized to {price:number, type, fillsAt:number,
980
+ * genesisHash} when every field passes its gate, null otherwise:
981
+ * - finite price > 0
982
+ * - type is ORDER_TYPES.BUY or ORDER_TYPES.SELL
983
+ * - finite fillsAt > 0
984
+ * - non-empty string genesisHash
985
+ *
986
+ * @param {any} row - Raw candidate row
987
+ * @returns {Object|null} Normalized row or null
988
+ */
989
+ export function normalizeLastFillPivot(row) {
990
+ if (!row || typeof row !== 'object')
991
+ return null;
992
+ const price = Number(row.price);
993
+ const type = row.type;
994
+ const fillsAt = Number(row.fillsAt);
995
+ const genesisHash = row.genesisHash;
996
+ if (!Number.isFinite(price) || price <= 0)
997
+ return null;
998
+ if (type !== ORDER_TYPES.BUY && type !== ORDER_TYPES.SELL)
999
+ return null;
1000
+ if (!Number.isFinite(fillsAt) || fillsAt <= 0)
1001
+ return null;
1002
+ if (typeof genesisHash !== 'string' || genesisHash.length === 0)
1003
+ return null;
1004
+ return { price, type, fillsAt, genesisHash };
1005
+ }
1006
+ /**
1007
+ * Write the LAST-FILL-GUARD pivot scalar family in one place (free
1008
+ * implementation shared by the manager method, cow_runtime's queued-fill
1009
+ * refresh, and restoreLastFillPivot).
1010
+ *
1011
+ * Writes _lastFilledPrice, _lastFilledType, _lastFilledAt,
1012
+ * lastFillPivotSource and the per-side mirror (_lastFilledBuyPrice or
1013
+ * _lastFilledSellPrice). `atMs` lets restoreLastFillPivot preserve the
1014
+ * persisted fill timestamp (otherwise every restart would re-stamp "now"
1015
+ * and the TTL would degrade to "time since last restart"); ordinary fill
1016
+ * calls omit it and take Date.now().
1017
+ *
1018
+ * @param {Object} manager - OrderManager instance (or legacy stub)
1019
+ * @param {string} type - ORDER_TYPES.BUY or ORDER_TYPES.SELL
1020
+ * @param {number} price - Pivot price (finite, > 0)
1021
+ * @param {string} provenance - 'fill' (persist-eligible) or 'book' (seed heuristic)
1022
+ * @param {number} [atMs] - Fill timestamp to preserve (defaults to now)
1023
+ * @returns {boolean} True when the pivot was written
1024
+ */
1025
+ export function setLastFillPivot(manager, type, price, provenance, atMs) {
1026
+ if (!manager)
1027
+ return false;
1028
+ if (type !== ORDER_TYPES.BUY && type !== ORDER_TYPES.SELL)
1029
+ return false;
1030
+ const p = Number(price);
1031
+ if (!Number.isFinite(p) || p <= 0)
1032
+ return false;
1033
+ const now = Number.isFinite(Number(atMs)) && Number(atMs) > 0 ? Number(atMs) : Date.now();
1034
+ manager._lastFilledPrice = p;
1035
+ manager._lastFilledType = type;
1036
+ manager._lastFilledAt = now;
1037
+ manager.lastFillPivotSource = provenance;
1038
+ if (type === ORDER_TYPES.BUY)
1039
+ manager._lastFilledBuyPrice = p;
1040
+ else
1041
+ manager._lastFilledSellPrice = p;
1042
+ return true;
1043
+ }
1044
+ /**
1045
+ * Clear the manager's LAST-FILL-GUARD pivot to cold state — the FULL scalar
1046
+ * family, per-side mirrors included. Clearing only the primary fields would
1047
+ * leave seedLastFilledPricesFromBook's early-return (both mirrors set)
1048
+ * silently suppressing the startup book seed after every grid rebuild — the
1049
+ * exact cold window the persist/restore feature exists to close.
1050
+ *
1051
+ * Used whenever the pivot's generation is invalidated: grid rebuild via
1052
+ * initializeGrid, rejected snapshot reset, and any path that re-anchors the
1053
+ * boundary outside the fill flow. The guard re-arms on the next real fill
1054
+ * (or via the startup book seed), never against a boundary that no longer
1055
+ * exists. Must not touch disk: the pivot is a live-grid invariant, and the
1056
+ * persist pipeline clears the stored row on the next flush by passing null.
1057
+ *
1058
+ * @param {Object} manager - OrderManager instance
1059
+ * @param {string} reason - Log/debug label
1060
+ * @returns {boolean} True when an armed pivot was cleared
1061
+ */
1062
+ export function resetLastFillPivot(manager, reason = 'unspecified') {
1063
+ if (!manager)
1064
+ return false;
1065
+ const wasArmed = manager._lastFilledPrice != null && manager._lastFilledType != null;
1066
+ manager._lastFilledPrice = null;
1067
+ manager._lastFilledType = null;
1068
+ manager._lastFilledAt = 0;
1069
+ manager.lastFillPivotSource = null;
1070
+ manager._lastFilledBuyPrice = null;
1071
+ manager._lastFilledSellPrice = null;
1072
+ if (wasArmed) {
1073
+ try {
1074
+ manager.logger?.log?.(`[LAST-FILL-GUARD] Pivot cleared (${reason}); guard re-arms on the next fill`, 'info');
1075
+ }
1076
+ catch { /* logging is best-effort */ }
1077
+ }
1078
+ return wasArmed;
1079
+ }
1080
+ /**
1081
+ * Restore the persisted LAST-FILL-GUARD pivot into the manager.
1082
+ *
1083
+ * MUST run with the boundary (after loadGrid has applied the persisted
1084
+ * genesis + re-typed the grid, before the first reconcile/broadcast) so the
1085
+ * guard is never armed against a geometry the snapshot does not describe.
1086
+ *
1087
+ * Validation chain (fail → no-op, never arm on a poisoned value):
1088
+ * 1. Shape: normalizeLastFillPivot re-validates the row (shared gate).
1089
+ * 2. TTL: a pivot older than GRID_LIMITS.LAST_FILL_PIVOT_TTL_MS expires
1090
+ * instead of vetoing legitimate placements after long downtime (the
1091
+ * pivot is a "latest fill" fact, not a permanent ratchet). Expired rows
1092
+ * are dead rows: the shared drop path (clearPersistedLastFillPivot
1093
+ * under the bot's persistence lock, best-effort) erases them so a later
1094
+ * validation failure cannot leave a half-invalid row armed.
1095
+ * 3. Genesis binding: a row whose genesisHash differs from the manager's
1096
+ * current genesis belongs to a dead generation — dropped through the
1097
+ * same shared drop path (erased from disk so it cannot re-arm on the
1098
+ * next restart).
1099
+ * 4. On-grid check reuses the runtime's own ladder validator
1100
+ * (resolveOnGridPivot in dexbot_cow_runtime, safe to import from here —
1101
+ * utils/system never imports it otherwise, so no cycle). One
1102
+ * implementation for both the live guard and the restore path, same
1103
+ * increment fallback chain. On success the snapped ladder level is
1104
+ * restored — the guard's own convention — never the raw persisted
1105
+ * float, and the ORIGINAL fillsAt is preserved through
1106
+ * setLastFillPivot's atMs so the TTL keeps meaning "age of the last
1107
+ * fill", not "time since this restart".
1108
+ *
1109
+ * @param {Object} manager - OrderManager instance
1110
+ * @param {Object|null} persisted - {price, type, fillsAt, genesisHash} from loadLastFillPivot
1111
+ * @param {Object} [options]
1112
+ * @param {number} [options.now] - Injectable clock (tests)
1113
+ * @returns {boolean} True when the guard was re-armed from the snapshot
1114
+ */
1115
+ export function restoreLastFillPivot(manager, persisted, options = {}) {
1116
+ if (!manager || !persisted || typeof persisted !== 'object')
1117
+ return false;
1118
+ const row = normalizeLastFillPivot(persisted);
1119
+ if (!row)
1120
+ return false;
1121
+ // Shared drop path for every "this row is dead" verdict: erase the
1122
+ // persisted row best-effort so a rejected value can never re-arm the
1123
+ // same rejection on the next restart (storeMasterGrid deliberately
1124
+ // keeps untouched rows for legacy `undefined` callers, so without the
1125
+ // erase the verdict has no TTL guarantee across restarts either).
1126
+ const dropPersistedRow = () => {
1127
+ try {
1128
+ const acct = manager.accountOrders;
1129
+ if (acct && typeof acct.clearPersistedLastFillPivot === 'function') {
1130
+ void acct.clearPersistedLastFillPivot();
1131
+ }
1132
+ }
1133
+ catch { /* best-effort */ }
1134
+ };
1135
+ // TTL first: an expired pivot is dropped, not re-armed.
1136
+ const now = Number.isFinite(options?.now) ? Number(options.now) : Date.now();
1137
+ if (now - row.fillsAt > Number(GRID_LIMITS.LAST_FILL_PIVOT_TTL_MS)) {
1138
+ manager.logger?.log?.(`[LAST-FILL-GUARD] Persisted pivot expired (age ${Math.round((now - row.fillsAt) / 1000)}s > TTL ${Math.round(Number(GRID_LIMITS.LAST_FILL_PIVOT_TTL_MS) / 1000)}s) — guard re-arms on the next fill`, 'info');
1139
+ dropPersistedRow();
1140
+ return false;
1141
+ }
1142
+ // Genesis binding: a persisted pivot from another genesis is not valid
1143
+ // after a regeneration/re-derive. Drop the row so it cannot resurrect,
1144
+ // and let the startup book seed take over.
1145
+ const liveHash = manager._genesis?.priceLevelsHash;
1146
+ if (typeof liveHash !== 'string' || liveHash.length === 0 || liveHash !== row.genesisHash) {
1147
+ manager.logger?.log?.(`[LAST-FILL-GUARD] Persisted pivot genesis mismatch (stored ${row.genesisHash} vs live ${liveHash ?? 'none'}) — dropping (grid regenerated); book seed will arm instead`, 'warn');
1148
+ dropPersistedRow();
1149
+ return false;
1150
+ }
1151
+ // On-grid validation through the runtime's own ladder validator — the
1152
+ // exact same one-increment drift rule, snap, and off-grid refusal the
1153
+ // live guard applies per probe. Import is safe: utils/system.ts does not
1154
+ // otherwise import the cow runtime, and the runtime's own module top
1155
+ // level already imports utils/system (sleep, then the pivot helpers),
1156
+ // so this adds no new edge to the graph.
1157
+ try {
1158
+ const validated = cowRuntimeLadderValidator(manager, row.price);
1159
+ if (!validated)
1160
+ return false;
1161
+ // Arm through the shared writer, preserving the persisted timestamp.
1162
+ setLastFillPivot(manager, row.type, validated.price, 'fill', row.fillsAt);
1163
+ manager.logger?.log?.(`[LAST-FILL-GUARD] Restored persisted pivot ${validated.price}(${row.type})` +
1164
+ `${validated.slotIdx != null ? ` slot=${validated.slotIdx}` : ''}` +
1165
+ `${validated.snapped ? ' (snapped)' : ''} age=${Math.round((now - row.fillsAt) / 1000)}s from snapshot`, 'info');
1166
+ return true;
1167
+ }
1168
+ catch {
1169
+ return false;
1170
+ }
1171
+ }
900
1172
  /**
901
1173
  * Retry grid persistence if previous attempt failed.
902
1174
  * Clears persistence warning flag if successful.