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.
- package/CHANGELOG.md +41 -1
- package/README.md +5 -2
- 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.d.ts.map +1 -1
- package/dist/dexbot.js +22 -9
- package/dist/dexbot.js.map +1 -1
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +10 -9
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/account_orders.d.ts +20 -2
- package/dist/modules/account_orders.d.ts.map +1 -1
- package/dist/modules/account_orders.js +63 -3
- package/dist/modules/account_orders.js.map +1 -1
- package/dist/modules/bitshares-native/chain_client.d.ts +10 -0
- package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -1
- package/dist/modules/bitshares-native/chain_client.js +197 -28
- package/dist/modules/bitshares-native/chain_client.js.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.js +312 -10
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
- package/dist/modules/bitshares-native/transport.d.ts +1 -0
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
- package/dist/modules/bitshares-native/transport.js +97 -3
- package/dist/modules/bitshares-native/transport.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +20 -0
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +29 -0
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/chain_keys.d.ts +37 -22
- package/dist/modules/chain_keys.d.ts.map +1 -1
- package/dist/modules/chain_keys.js +145 -44
- package/dist/modules/chain_keys.js.map +1 -1
- package/dist/modules/cli_start_onboarding.d.ts +10 -0
- package/dist/modules/cli_start_onboarding.d.ts.map +1 -0
- package/dist/modules/cli_start_onboarding.js +15 -0
- package/dist/modules/cli_start_onboarding.js.map +1 -0
- package/dist/modules/cli_start_output.d.ts +6 -0
- package/dist/modules/cli_start_output.d.ts.map +1 -0
- package/dist/modules/cli_start_output.js +8 -0
- package/dist/modules/cli_start_output.js.map +1 -0
- package/dist/modules/constants.d.ts +13 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +103 -1
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +49 -9
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts +6 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +22 -3
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +24 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +45 -1
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +13 -1
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +33 -1
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +11 -0
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/logger.d.ts +12 -2
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +29 -7
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts +49 -0
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +72 -24
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +97 -12
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +97 -0
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +274 -2
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/paths.d.ts +7 -1
- package/dist/modules/paths.d.ts.map +1 -1
- package/dist/modules/paths.js +10 -3
- package/dist/modules/paths.js.map +1 -1
- package/dist/modules/utils/text_width.d.ts +19 -0
- package/dist/modules/utils/text_width.d.ts.map +1 -0
- package/dist/modules/utils/text_width.js +97 -0
- package/dist/modules/utils/text_width.js.map +1 -0
- package/dist/pm2.d.ts.map +1 -1
- package/dist/pm2.js +95 -18
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/update.js +62 -13
- package/dist/scripts/update.js.map +1 -1
- package/dist/unlock.d.ts +14 -2
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +73 -4
- package/dist/unlock.js.map +1 -1
- package/docs/COW_INVARIANTS.md +8 -0
- package/docs/CREDENTIAL_SECURITY.md +26 -0
- package/docs/DEXBOT_COMPARISON.md +3 -3
- package/docs/EVOLUTION.md +6 -5
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/GRID_PRICE_INVARIANT.md +56 -1
- package/docs/LOGGING.md +17 -2
- package/docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md +6 -7
- package/docs/README.md +1 -1
- 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAiBH,QAAA,MAAQ,SAAS;QAzCoC,CAAC;UAyCf,CAAC;
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../../modules/order/utils/system.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAiBH,QAAA,MAAQ,SAAS;QAzCoC,CAAC;UAyCf,CAAC;AAiDxC;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAU,WAAW,GAAG,EAAE,GAAG,MAAM,KAAG,OAAO,CAAC,GAAG,CAiCxE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAU,WAAW,GAAG,EAAE,KAAK,GAAG,KAAG,OAAO,CAAC,GAAG,CAsB7E,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAAU,WAAW,GAAG,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CA0CzG,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAU,WAAW,GAAG,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CA2IvG,CAAC;AAcD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,GAAG,IAAI,KAAG,IAE7E,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,WAAW,GAAG,EAAE,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,OAAM,MAAe,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CA4B3H,CAAC;AAaF;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAA0B,EAAE,IAAI,GAAE,MAAe,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqBxM;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAA0B,EAAE,IAAI,GAAE,MAAe,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CASzM;AAgFD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,6BAA6B,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,IAAI,GAAE,MAAe,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwErM;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAmCzD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAsCD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAmFxG;AAMD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAgFzN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,GAAG,MAAM,CAc1E;AAiCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW7H;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAazH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,GAAE,MAAsB,GAAG,OAAO,CAexF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAgE1G;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAW7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,8BAA8B,CAAC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,QAAQ,EAAE,kCAAkC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CA+UxP;AAOD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAGpE;AAED;;;GAGG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAiHpN;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAkE;AAErI;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAalN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,mBAAmB,CAAC,CAAC,EACvC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IACN,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB,GACF,OAAO,CAAC,CAAC,CAAC,CA4DZ;AAMD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,CAYxC;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAGtD;AAED,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,2BAA2B,CAC7C,GAAG,EAAE,GAAG,EACR,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACtF,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+B5F"}
|
|
@@ -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.
|