dexbot 1.1.14 → 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.
- package/README.md +1 -0
- package/dist/credential-daemon.d.ts.map +1 -1
- package/dist/credential-daemon.js +6 -3
- package/dist/credential-daemon.js.map +1 -1
- package/dist/market_adapter/market_adapter.js +1 -1
- package/dist/market_adapter/market_adapter.js.map +1 -1
- package/dist/modules/bitshares-native/lru_cache.d.ts +20 -0
- package/dist/modules/bitshares-native/lru_cache.d.ts.map +1 -0
- package/dist/modules/bitshares-native/lru_cache.js +45 -0
- package/dist/modules/bitshares-native/lru_cache.js.map +1 -0
- package/dist/modules/bitshares-native/resolvers.d.ts +2 -18
- package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -1
- package/dist/modules/bitshares-native/resolvers.js +2 -41
- package/dist/modules/bitshares-native/resolvers.js.map +1 -1
- package/dist/modules/bitshares-native/serial/chain_constants.d.ts +1 -0
- package/dist/modules/bitshares-native/serial/chain_constants.d.ts.map +1 -1
- package/dist/modules/bitshares-native/serial/chain_constants.js +3 -0
- package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -1
- package/dist/modules/bitshares-native/signing_client.d.ts.map +1 -1
- package/dist/modules/bitshares-native/signing_client.js +12 -1
- package/dist/modules/bitshares-native/signing_client.js.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.js +42 -3
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
- package/dist/modules/bitshares-native/transport.js +77 -27
- package/dist/modules/bitshares-native/transport.js.map +1 -1
- package/dist/modules/bitshares-native/tx/builder.d.ts.map +1 -1
- package/dist/modules/bitshares-native/tx/builder.js +26 -16
- package/dist/modules/bitshares-native/tx/builder.js.map +1 -1
- package/dist/modules/bitshares-native/tx/tx_cache.d.ts +17 -0
- package/dist/modules/bitshares-native/tx/tx_cache.d.ts.map +1 -0
- package/dist/modules/bitshares-native/tx/tx_cache.js +62 -0
- package/dist/modules/bitshares-native/tx/tx_cache.js.map +1 -0
- package/dist/modules/bot_settings.d.ts.map +1 -1
- package/dist/modules/bot_settings.js +2 -1
- package/dist/modules/bot_settings.js.map +1 -1
- package/dist/modules/chain_orders.d.ts +4 -1
- package/dist/modules/chain_orders.d.ts.map +1 -1
- package/dist/modules/chain_orders.js +30 -1
- package/dist/modules/chain_orders.js.map +1 -1
- package/dist/modules/config.d.ts +6 -0
- package/dist/modules/config.d.ts.map +1 -1
- package/dist/modules/config.js +11 -0
- package/dist/modules/config.js.map +1 -1
- package/dist/modules/constants.d.ts +14 -1
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +64 -6
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credential_policy.d.ts.map +1 -1
- package/dist/modules/credential_policy.js +7 -5
- package/dist/modules/credential_policy.js.map +1 -1
- package/dist/modules/credit_runtime.d.ts.map +1 -1
- package/dist/modules/credit_runtime.js +40 -29
- package/dist/modules/credit_runtime.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +6 -12
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +155 -81
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +1 -5
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +23 -48
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/launcher/child_env.d.ts.map +1 -1
- package/dist/modules/launcher/child_env.js +1 -0
- package/dist/modules/launcher/child_env.js.map +1 -1
- package/dist/modules/launcher/launch_modes.d.ts +2 -0
- package/dist/modules/launcher/launch_modes.d.ts.map +1 -1
- package/dist/modules/launcher/launch_modes.js +7 -6
- package/dist/modules/launcher/launch_modes.js.map +1 -1
- package/dist/modules/order/accounting.d.ts +8 -2
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +64 -26
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/async_lock.d.ts +12 -4
- package/dist/modules/order/async_lock.d.ts.map +1 -1
- package/dist/modules/order/async_lock.js +27 -5
- package/dist/modules/order/async_lock.js.map +1 -1
- package/dist/modules/order/grid.d.ts +434 -475
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +1612 -1592
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts +3 -73
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +11 -843
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts +8 -0
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -0
- package/dist/modules/order/grid_reconcile_internal.js +782 -0
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -0
- package/dist/modules/order/logger.d.ts +1 -1
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +26 -2
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts +118 -15
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +206 -236
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/runner.js +2 -2
- package/dist/modules/order/runner.js.map +1 -1
- package/dist/modules/order/strategy.js +3 -3
- package/dist/modules/order/strategy.js.map +1 -1
- package/dist/modules/order/sync_engine.d.ts +3 -4
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +85 -32
- package/dist/modules/order/sync_engine.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +17 -0
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +81 -24
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.js +4 -4
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/validate.d.ts.map +1 -1
- package/dist/modules/order/utils/validate.js +6 -8
- package/dist/modules/order/utils/validate.js.map +1 -1
- package/dist/modules/types.d.ts +7 -12
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/scripts/print_grid.js +3 -3
- package/dist/scripts/print_grid.js.map +1 -1
- package/dist/scripts/test-credit-renewal.js +1 -1
- package/dist/scripts/test-credit-renewal.js.map +1 -1
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +26 -12
- package/dist/unlock.js.map +1 -1
- package/package.json +1 -1
|
@@ -248,25 +248,28 @@ class DEXBot {
|
|
|
248
248
|
*/
|
|
249
249
|
_validateStartupConfig(config) {
|
|
250
250
|
const errors = [];
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
251
|
+
// Skip trading field validation in credit-only mode
|
|
252
|
+
if (!config.creditOnly) {
|
|
253
|
+
// Validate startPrice is numeric or valid string mode
|
|
254
|
+
const startPrice = config.startPrice;
|
|
255
|
+
const validPriceModes = ['pool', 'book'];
|
|
256
|
+
const isPriceNumeric = typeof startPrice === 'number' && Number.isFinite(startPrice) && startPrice > 0;
|
|
257
|
+
const isPriceMode = typeof startPrice === 'string' && validPriceModes.includes(startPrice.toLowerCase());
|
|
258
|
+
if (!isPriceNumeric && !isPriceMode) {
|
|
259
|
+
errors.push(`startPrice must be a positive number or valid mode (${validPriceModes.join('/')}), got: ${startPrice}`);
|
|
260
|
+
}
|
|
261
|
+
// Validate assetA and assetB are present
|
|
262
|
+
if (!config.assetA || typeof config.assetA !== 'string') {
|
|
263
|
+
errors.push(`assetA must be a non-empty string, got: ${config.assetA}`);
|
|
264
|
+
}
|
|
265
|
+
if (!config.assetB || typeof config.assetB !== 'string') {
|
|
266
|
+
errors.push(`assetB must be a non-empty string, got: ${config.assetB}`);
|
|
267
|
+
}
|
|
268
|
+
// Validate incrementPercent
|
|
269
|
+
const increment = config.incrementPercent;
|
|
270
|
+
if (!Number.isFinite(increment) || increment <= 0 || increment > 100) {
|
|
271
|
+
errors.push(`incrementPercent must be between 0 and 100, got: ${increment}`);
|
|
272
|
+
}
|
|
270
273
|
}
|
|
271
274
|
// Throw all validation errors at once
|
|
272
275
|
if (errors.length > 0) {
|
|
@@ -346,7 +349,7 @@ class DEXBot {
|
|
|
346
349
|
shadowLocks: this.manager?.shadowOrderIds?.size || 0,
|
|
347
350
|
batchInFlight: this._batchInFlight,
|
|
348
351
|
recoveryInFlight: this._recoverySyncInFlight,
|
|
349
|
-
broadcasting: this.manager?.
|
|
352
|
+
broadcasting: this.manager?.isBroadcastingActive?.() || false
|
|
350
353
|
};
|
|
351
354
|
}
|
|
352
355
|
/**
|
|
@@ -375,7 +378,7 @@ class DEXBot {
|
|
|
375
378
|
this.manager.logger.log(`Triggering state recovery sync (${reason})...`, 'info');
|
|
376
379
|
await this.manager.fetchAccountTotals(this.accountId);
|
|
377
380
|
const openOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
378
|
-
await this.manager.syncFromOpenOrders(openOrders, { skipAccounting: true
|
|
381
|
+
await this.manager.syncFromOpenOrders(openOrders, { skipAccounting: true });
|
|
379
382
|
if (typeof this.manager.persistGrid === 'function') {
|
|
380
383
|
await this.manager.persistGrid();
|
|
381
384
|
}
|
|
@@ -587,8 +590,6 @@ class DEXBot {
|
|
|
587
590
|
if (chainOpenOrders.length > 0 && this.manager?.syncFromOpenOrders) {
|
|
588
591
|
await this.manager.syncFromOpenOrders(chainOpenOrders, {
|
|
589
592
|
skipAccounting: true,
|
|
590
|
-
fillLockAlreadyHeld: true,
|
|
591
|
-
protectCommittedOrders: true
|
|
592
593
|
});
|
|
593
594
|
}
|
|
594
595
|
// 5. Persist the reconciled state
|
|
@@ -819,13 +820,14 @@ class DEXBot {
|
|
|
819
820
|
_buildOrphanFillFallbackKey(fill) {
|
|
820
821
|
return DexbotFillRuntime.buildOrphanFillFallbackKey(this, fill);
|
|
821
822
|
}
|
|
822
|
-
_isNewFillKey(fillKey, processedFillKeys, label = '') {
|
|
823
|
+
_isNewFillKey(fillKey, processedFillKeys, label = '', orderId = '') {
|
|
823
824
|
const now = Date.now();
|
|
824
825
|
if (this._recentlyQueuedFills.has(fillKey)) {
|
|
825
826
|
const lastProcessed = this._recentlyQueuedFills.get(fillKey);
|
|
826
827
|
if (now - lastProcessed < this._fillDedupeWindowMs) {
|
|
827
828
|
if (label) {
|
|
828
|
-
|
|
829
|
+
const idSuffix = orderId ? ` for ${orderId}` : '';
|
|
830
|
+
this.manager?.logger?.log?.(`${label} Skipping duplicate fill${idSuffix} (processed ${now - lastProcessed}ms ago)`, 'debug');
|
|
829
831
|
}
|
|
830
832
|
return false;
|
|
831
833
|
}
|
|
@@ -953,7 +955,7 @@ class DEXBot {
|
|
|
953
955
|
const chainOpenOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
954
956
|
if (this._shuttingDown)
|
|
955
957
|
return;
|
|
956
|
-
const syncResult = await this.manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders'
|
|
958
|
+
const syncResult = await this.manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders');
|
|
957
959
|
if (this._shuttingDown)
|
|
958
960
|
return;
|
|
959
961
|
if (syncResult?.filledOrders?.length > 0) {
|
|
@@ -970,7 +972,6 @@ class DEXBot {
|
|
|
970
972
|
await this._cancelDustOrders({
|
|
971
973
|
buy: reconnectHealth.buyDustOrders,
|
|
972
974
|
sell: reconnectHealth.sellDustOrders,
|
|
973
|
-
fillLockAlreadyHeld: true,
|
|
974
975
|
});
|
|
975
976
|
}
|
|
976
977
|
catch (_dustErr) {
|
|
@@ -1050,7 +1051,7 @@ class DEXBot {
|
|
|
1050
1051
|
if (!orphanFillKey) {
|
|
1051
1052
|
orphanFillKey = this._buildOrphanFillFallbackKey(fill);
|
|
1052
1053
|
}
|
|
1053
|
-
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[POST-RESET]')) {
|
|
1054
|
+
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[POST-RESET]', fillOp.order_id)) {
|
|
1054
1055
|
continue;
|
|
1055
1056
|
}
|
|
1056
1057
|
this._log(`[POST-RESET] Processing funds for unknown order ${fillOp.order_id} (not in grid but crediting proceeds)`, 'warn');
|
|
@@ -1066,7 +1067,7 @@ class DEXBot {
|
|
|
1066
1067
|
}
|
|
1067
1068
|
this._log(`[POST-RESET] Processing fill for ${gridOrder.type} order ${gridOrder.id} at price ${gridOrder.price}`);
|
|
1068
1069
|
const trackedFillKey = buildFillKey(fill);
|
|
1069
|
-
if (trackedFillKey && !this._isNewFillKey(trackedFillKey, processedFillKeys, '[POST-RESET]')) {
|
|
1070
|
+
if (trackedFillKey && !this._isNewFillKey(trackedFillKey, processedFillKeys, '[POST-RESET]', fillOp.order_id)) {
|
|
1070
1071
|
continue;
|
|
1071
1072
|
}
|
|
1072
1073
|
const accountingResult = await this._applyReplaySafeTrackedFillAccounting(fill, fillOp, {
|
|
@@ -1092,7 +1093,7 @@ class DEXBot {
|
|
|
1092
1093
|
if (requiresOpenOrdersSync) {
|
|
1093
1094
|
this._log('[POST-RESET] Falling back to open-orders sync for fill(s) missing replay-safe history identifiers', 'warn');
|
|
1094
1095
|
const postResetChainOpenOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
1095
|
-
const syncResult = await this.manager.syncFromOpenOrders(postResetChainOpenOrders
|
|
1096
|
+
const syncResult = await this.manager.syncFromOpenOrders(postResetChainOpenOrders);
|
|
1096
1097
|
if (syncResult.filledOrders?.length > 0) {
|
|
1097
1098
|
await this._processFillsWithBatching(syncResult.filledOrders, new Set(), '[POST-RESET] open-orders fallback');
|
|
1098
1099
|
}
|
|
@@ -1123,7 +1124,6 @@ class DEXBot {
|
|
|
1123
1124
|
await this._cancelDustOrders({
|
|
1124
1125
|
buy: postResetHealth.buyDustOrders,
|
|
1125
1126
|
sell: postResetHealth.sellDustOrders,
|
|
1126
|
-
fillLockAlreadyHeld: true,
|
|
1127
1127
|
});
|
|
1128
1128
|
}
|
|
1129
1129
|
catch (_dustErr) {
|
|
@@ -1219,7 +1219,7 @@ class DEXBot {
|
|
|
1219
1219
|
if (Array.isArray(chainOpenOrders) && chainOpenOrders.length > 0) {
|
|
1220
1220
|
this._log('Generating new grid and syncing with existing on-chain orders...');
|
|
1221
1221
|
await Grid.initializeGrid(this.manager);
|
|
1222
|
-
await this.manager.syncFromOpenOrders(chainOpenOrders, { skipAccounting: true
|
|
1222
|
+
await this.manager.syncFromOpenOrders(chainOpenOrders, { skipAccounting: true });
|
|
1223
1223
|
const rebalanceResult = await reconcileGridOrders({
|
|
1224
1224
|
manager: this.manager,
|
|
1225
1225
|
config: this.config,
|
|
@@ -1227,7 +1227,6 @@ class DEXBot {
|
|
|
1227
1227
|
privateKey: this.privateKey,
|
|
1228
1228
|
chainOrders,
|
|
1229
1229
|
chainOpenOrders,
|
|
1230
|
-
fillLockAlreadyHeld: true,
|
|
1231
1230
|
});
|
|
1232
1231
|
await this._executeBatchIfNeeded(rebalanceResult, 'startup reconcile (regenerated grid)');
|
|
1233
1232
|
}
|
|
@@ -1241,7 +1240,7 @@ class DEXBot {
|
|
|
1241
1240
|
this._log('Found active session. Loading and syncing existing grid.');
|
|
1242
1241
|
await Grid.loadGrid(this.manager, persistedGrid, persistedBoundaryIdx);
|
|
1243
1242
|
let startupChainOpenOrders = chainOpenOrders;
|
|
1244
|
-
const syncResult = await this.manager.syncFromOpenOrders(startupChainOpenOrders, { skipAccounting: true
|
|
1243
|
+
const syncResult = await this.manager.syncFromOpenOrders(startupChainOpenOrders, { skipAccounting: true });
|
|
1245
1244
|
// Process price corrections queued during startup sync.
|
|
1246
1245
|
// These are not picked up by _consumeFillQueue until a fill
|
|
1247
1246
|
// arrives, which may never come for an idle market.
|
|
@@ -1255,7 +1254,7 @@ class DEXBot {
|
|
|
1255
1254
|
// Refresh open orders so startup reconcile works with post-batch chain reality
|
|
1256
1255
|
// and avoids reconciling against a stale pre-batch snapshot.
|
|
1257
1256
|
startupChainOpenOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
1258
|
-
await this.manager.synchronizeWithChain(startupChainOpenOrders, 'readOpenOrders'
|
|
1257
|
+
await this.manager.synchronizeWithChain(startupChainOpenOrders, 'readOpenOrders');
|
|
1259
1258
|
}
|
|
1260
1259
|
}
|
|
1261
1260
|
const rebalanceResult = await reconcileGridOrders({
|
|
@@ -1265,7 +1264,6 @@ class DEXBot {
|
|
|
1265
1264
|
privateKey: this.privateKey,
|
|
1266
1265
|
chainOrders,
|
|
1267
1266
|
chainOpenOrders: startupChainOpenOrders,
|
|
1268
|
-
fillLockAlreadyHeld: true,
|
|
1269
1267
|
});
|
|
1270
1268
|
await this._executeBatchIfNeeded(rebalanceResult, 'startup reconcile (loaded grid)');
|
|
1271
1269
|
// Dust state is no longer persisted — cancelled immediately on detection.
|
|
@@ -1281,16 +1279,39 @@ class DEXBot {
|
|
|
1281
1279
|
await this._processFillsWithBootstrapMode(chainOrders);
|
|
1282
1280
|
}
|
|
1283
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
|
+
}
|
|
1284
1306
|
// Perform initial grid maintenance (thresholds, divergence, spread, health)
|
|
1285
1307
|
// Consolidated into shared logic to ensure consistent behavior at boot and runtime.
|
|
1286
1308
|
// CRITICAL: Pass lockAlreadyHeld since we're inside _fillProcessingLock.acquire()
|
|
1287
|
-
await this._runGridMaintenance('startup'
|
|
1309
|
+
await this._runGridMaintenance('startup');
|
|
1288
1310
|
// Cancel any dust from a previous bot lifetime immediately.
|
|
1289
1311
|
const startupHealth = await this.manager.checkGridHealth(this.updateOrdersOnChainPlan.bind(this));
|
|
1290
1312
|
await this._cancelDustOrders({
|
|
1291
1313
|
buy: startupHealth.buyDustOrders,
|
|
1292
1314
|
sell: startupHealth.sellDustOrders,
|
|
1293
|
-
fillLockAlreadyHeld: true,
|
|
1294
1315
|
});
|
|
1295
1316
|
this._log('Bootstrap phase complete - fill processing resumed', 'info');
|
|
1296
1317
|
}
|
|
@@ -1302,7 +1323,7 @@ class DEXBot {
|
|
|
1302
1323
|
await this._setupTriggerFileDetection();
|
|
1303
1324
|
await this._setupCreditRuntime();
|
|
1304
1325
|
await this._refreshAndSyncCreditRuntime();
|
|
1305
|
-
await this._runCreditRuntimeMaintenance('startup'
|
|
1326
|
+
await this._runCreditRuntimeMaintenance('startup');
|
|
1306
1327
|
this._setupBlockchainFetchInterval();
|
|
1307
1328
|
this._setupCreditWatchdogInterval();
|
|
1308
1329
|
this._setupCredentialDaemonWatchdogInterval();
|
|
@@ -1346,14 +1367,14 @@ class DEXBot {
|
|
|
1346
1367
|
}
|
|
1347
1368
|
try {
|
|
1348
1369
|
let openOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
1349
|
-
const syncResult = await this.manager.synchronizeWithChain(openOrders, 'readOpenOrders'
|
|
1370
|
+
const syncResult = await this.manager.synchronizeWithChain(openOrders, 'readOpenOrders');
|
|
1350
1371
|
let aborted = false;
|
|
1351
1372
|
if (syncResult?.filledOrders?.length > 0) {
|
|
1352
1373
|
this._log(`[SYNC-CHAIN] ${syncResult.filledOrders.length} filled order(s) found during ${tag}`, 'info');
|
|
1353
1374
|
const batchResult = await this._processFillsWithBatching(syncResult.filledOrders, new Set(), `${tag} sync-fill`);
|
|
1354
1375
|
if (!batchResult?.aborted) {
|
|
1355
1376
|
openOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
1356
|
-
await this.manager.synchronizeWithChain(openOrders, 'readOpenOrders'
|
|
1377
|
+
await this.manager.synchronizeWithChain(openOrders, 'readOpenOrders');
|
|
1357
1378
|
}
|
|
1358
1379
|
else {
|
|
1359
1380
|
aborted = true;
|
|
@@ -1507,11 +1528,11 @@ class DEXBot {
|
|
|
1507
1528
|
try {
|
|
1508
1529
|
// BOOTSTRAP OPTIMIZATION: During bootstrap, prioritize fill processing over grid-wide checks
|
|
1509
1530
|
// Process fills immediately with side-only rebalancing (no expensive full grid recalculations)
|
|
1510
|
-
if (this.manager.
|
|
1531
|
+
if (this.manager.isBootstrapping()) {
|
|
1511
1532
|
// During bootstrap: skip lock contention checks, process fills directly
|
|
1512
1533
|
let bootstrapSkipped = false;
|
|
1513
1534
|
await this.manager._fillProcessingLock.acquire(async () => {
|
|
1514
|
-
if (!this.manager.
|
|
1535
|
+
if (!this.manager.isBootstrapping()) {
|
|
1515
1536
|
// Bootstrap finished while waiting for the lock — no
|
|
1516
1537
|
// work to do, but the iteration is still healthy.
|
|
1517
1538
|
bootstrapSkipped = true;
|
|
@@ -1601,7 +1622,7 @@ class DEXBot {
|
|
|
1601
1622
|
if (!orphanFillKey) {
|
|
1602
1623
|
orphanFillKey = this._buildOrphanFillFallbackKey(fill);
|
|
1603
1624
|
}
|
|
1604
|
-
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[ORPHAN-FILL]')) {
|
|
1625
|
+
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[ORPHAN-FILL]', fillOp.order_id)) {
|
|
1605
1626
|
continue;
|
|
1606
1627
|
}
|
|
1607
1628
|
this.manager.logger.log(`[ORPHAN-FILL] Processing funds for unknown order ${fillOp.order_id} (not in grid but crediting proceeds)`, 'warn');
|
|
@@ -1631,7 +1652,7 @@ class DEXBot {
|
|
|
1631
1652
|
requiresOpenOrdersSync = true;
|
|
1632
1653
|
continue;
|
|
1633
1654
|
}
|
|
1634
|
-
if (!this._isNewFillKey(fillKey, processedFillKeys, '[FILL]')) {
|
|
1655
|
+
if (!this._isNewFillKey(fillKey, processedFillKeys, '[FILL]', fillOp.order_id)) {
|
|
1635
1656
|
continue;
|
|
1636
1657
|
}
|
|
1637
1658
|
validFills.push(fill);
|
|
@@ -1694,7 +1715,7 @@ class DEXBot {
|
|
|
1694
1715
|
}
|
|
1695
1716
|
this.manager.logger.log(`Syncing ${fillsToSync.length} fill(s) (open orders mode)`, 'info');
|
|
1696
1717
|
const chainOpenOrders = await chainOrders.readOpenOrders(this.account);
|
|
1697
|
-
const resultOpenOrders = await this.manager.syncFromOpenOrders(chainOpenOrders
|
|
1718
|
+
const resultOpenOrders = await this.manager.syncFromOpenOrders(chainOpenOrders);
|
|
1698
1719
|
// Dust is handled by post-fill detection below.
|
|
1699
1720
|
if (resultOpenOrders.filledOrders)
|
|
1700
1721
|
resolvedOrders.push(...resultOpenOrders.filledOrders);
|
|
@@ -1827,7 +1848,6 @@ class DEXBot {
|
|
|
1827
1848
|
const dustCancelResult = await this._cancelDustOrders({
|
|
1828
1849
|
buy: healthResult.buyDustOrders,
|
|
1829
1850
|
sell: healthResult.sellDustOrders,
|
|
1830
|
-
fillLockAlreadyHeld: true,
|
|
1831
1851
|
});
|
|
1832
1852
|
if (dustCancelResult?.batchResult?.aborted) {
|
|
1833
1853
|
abortedFillCycle = true;
|
|
@@ -1841,7 +1861,7 @@ class DEXBot {
|
|
|
1841
1861
|
// After: Grid maintenance waits for isPipelineEmpty() before structural changes
|
|
1842
1862
|
// Run only when the cycle contains at least one full fill.
|
|
1843
1863
|
if (shouldRunPostFillChecks && !abortedFillCycle) {
|
|
1844
|
-
await this._runGridMaintenance('post-fill'
|
|
1864
|
+
await this._runGridMaintenance('post-fill');
|
|
1845
1865
|
}
|
|
1846
1866
|
}
|
|
1847
1867
|
else if (pendingFillKeysForCurrentCycle.size > 0) {
|
|
@@ -1966,7 +1986,7 @@ class DEXBot {
|
|
|
1966
1986
|
if (!orphanFillKey) {
|
|
1967
1987
|
orphanFillKey = this._buildOrphanFillFallbackKey(fill);
|
|
1968
1988
|
}
|
|
1969
|
-
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[BOOTSTRAP]')) {
|
|
1989
|
+
if (orphanFillKey && !this._isNewFillKey(orphanFillKey, processedFillKeys, '[BOOTSTRAP]', fillOp.order_id)) {
|
|
1970
1990
|
continue;
|
|
1971
1991
|
}
|
|
1972
1992
|
this.manager.logger.log(`[BOOTSTRAP] Processing funds for unknown order ${fillOp.order_id} (not in grid but crediting proceeds)`, 'warn');
|
|
@@ -1979,7 +1999,7 @@ class DEXBot {
|
|
|
1979
1999
|
continue;
|
|
1980
2000
|
}
|
|
1981
2001
|
const trackedFillKey = buildFillKey(fill);
|
|
1982
|
-
if (trackedFillKey && !this._isNewFillKey(trackedFillKey, processedFillKeys, '[BOOTSTRAP]')) {
|
|
2002
|
+
if (trackedFillKey && !this._isNewFillKey(trackedFillKey, processedFillKeys, '[BOOTSTRAP]', fillOp.order_id)) {
|
|
1983
2003
|
continue;
|
|
1984
2004
|
}
|
|
1985
2005
|
const accountingResult = await this._applyReplaySafeTrackedFillAccounting(fill, fillOp, {
|
|
@@ -2000,7 +2020,7 @@ class DEXBot {
|
|
|
2000
2020
|
if (requiresOpenOrdersSync) {
|
|
2001
2021
|
this._log('[BOOTSTRAP] Falling back to open-orders sync for fill(s) missing replay-safe history identifiers', 'warn');
|
|
2002
2022
|
const bootstrapChainOpenOrders = await chainOrders.readOpenOrders(this.accountId);
|
|
2003
|
-
const syncResult = await this.manager.syncFromOpenOrders(bootstrapChainOpenOrders
|
|
2023
|
+
const syncResult = await this.manager.syncFromOpenOrders(bootstrapChainOpenOrders);
|
|
2004
2024
|
if (syncResult.filledOrders?.length > 0) {
|
|
2005
2025
|
const queuedOrderIds = new Set(validFills.map(fill => fill?.gridOrder?.orderId).filter(Boolean));
|
|
2006
2026
|
for (const filledOrder of syncResult.filledOrders) {
|
|
@@ -2238,8 +2258,6 @@ class DEXBot {
|
|
|
2238
2258
|
const openOrders = await chainOrders.readOpenOrders(accountRef);
|
|
2239
2259
|
const recoveryResult = await this.manager.syncFromOpenOrders(openOrders, {
|
|
2240
2260
|
skipAccounting: false,
|
|
2241
|
-
fillLockAlreadyHeld: true,
|
|
2242
|
-
protectCommittedOrders: true
|
|
2243
2261
|
});
|
|
2244
2262
|
this._preserveMissingCreateBlockersAfterRecovery(preRecoveryMissingCreateBlockers, recoveryResult);
|
|
2245
2263
|
// Persist any master grid mutations from the recovery sync. The
|
|
@@ -2583,13 +2601,10 @@ class DEXBot {
|
|
|
2583
2601
|
* @returns {Promise<Object>} Result object compatible with batch return shape
|
|
2584
2602
|
*/
|
|
2585
2603
|
async _reconcileAfterUncertainBroadcast(err, opContexts, options = {}) {
|
|
2586
|
-
if (
|
|
2587
|
-
this.manager
|
|
2588
|
-
|
|
2589
|
-
return this.manager._fillProcessingLock.acquire(
|
|
2590
|
-
...options,
|
|
2591
|
-
fillLockAlreadyHeld: true
|
|
2592
|
-
})));
|
|
2604
|
+
if (this.manager?._fillProcessingLock &&
|
|
2605
|
+
typeof this.manager._fillProcessingLock.acquire === 'function' &&
|
|
2606
|
+
!this.manager._fillProcessingLock.isReentrant()) {
|
|
2607
|
+
return this.manager._fillProcessingLock.acquire(() => this._reconcileAfterUncertainBroadcast(err, opContexts, options));
|
|
2593
2608
|
}
|
|
2594
2609
|
return this._reconcileAfterUncertainBroadcastImpl(err, opContexts, options);
|
|
2595
2610
|
}
|
|
@@ -2637,7 +2652,11 @@ class DEXBot {
|
|
|
2637
2652
|
fingerprint: entry.fingerprint
|
|
2638
2653
|
});
|
|
2639
2654
|
if (match) {
|
|
2640
|
-
adopted.push({
|
|
2655
|
+
adopted.push({
|
|
2656
|
+
slotId: entry.slotId,
|
|
2657
|
+
chainOrderId: match.id,
|
|
2658
|
+
orderType: entry.orderType || entry.order?.type,
|
|
2659
|
+
});
|
|
2641
2660
|
this.manager._pendingBroadcasts.delete(entry.fingerprint);
|
|
2642
2661
|
}
|
|
2643
2662
|
else {
|
|
@@ -2674,7 +2693,11 @@ class DEXBot {
|
|
|
2674
2693
|
fingerprint: entry.fingerprint
|
|
2675
2694
|
});
|
|
2676
2695
|
if (match) {
|
|
2677
|
-
adopted.push({
|
|
2696
|
+
adopted.push({
|
|
2697
|
+
slotId: entry.slotId,
|
|
2698
|
+
chainOrderId: match.id,
|
|
2699
|
+
orderType: entry.orderType || entry.order?.type,
|
|
2700
|
+
});
|
|
2678
2701
|
this.manager.logger.log(`[COW][UNCERTAIN] CREATE re-adopted after ${recheckRound} block(s): ` +
|
|
2679
2702
|
`${entry.slotId}->${match.id}`, 'info');
|
|
2680
2703
|
}
|
|
@@ -2702,8 +2725,6 @@ class DEXBot {
|
|
|
2702
2725
|
try {
|
|
2703
2726
|
await this.manager.syncFromOpenOrders(latestSnapshot, {
|
|
2704
2727
|
skipAccounting: true,
|
|
2705
|
-
fillLockAlreadyHeld: true,
|
|
2706
|
-
protectCommittedOrders: true
|
|
2707
2728
|
});
|
|
2708
2729
|
hadRotation = true;
|
|
2709
2730
|
}
|
|
@@ -2758,9 +2779,52 @@ class DEXBot {
|
|
|
2758
2779
|
catch (orphanErr) {
|
|
2759
2780
|
this.manager.logger.log(`[COW][UNCERTAIN] Auto-cancel pass failed: ${orphanErr?.message || orphanErr}`, 'warn');
|
|
2760
2781
|
}
|
|
2782
|
+
// Boundary shift recovery: the COW batch that should have committed the
|
|
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.
|
|
2789
|
+
//
|
|
2790
|
+
// NOTE: Direct mutation of manager.boundaryIdx outside a COW commit
|
|
2791
|
+
// violates the invariant stated in grid.ts:1217-1220 (boundary must only
|
|
2792
|
+
// be updated atomically inside _commitWorkingGrid). This is intentional
|
|
2793
|
+
// here because the COW commit already failed — the invariant was already
|
|
2794
|
+
// broken by the broadcast uncertainty, and the adjustment only restores
|
|
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) {
|
|
2798
|
+
let boundaryShift = 0;
|
|
2799
|
+
for (const entry of adopted) {
|
|
2800
|
+
if (entry.orderType === ORDER_TYPES.SELL) {
|
|
2801
|
+
boundaryShift--; // SELL CREATE → fill was BUY → boundary LEFT
|
|
2802
|
+
}
|
|
2803
|
+
else if (entry.orderType === ORDER_TYPES.BUY) {
|
|
2804
|
+
boundaryShift++; // BUY CREATE → fill was SELL → boundary RIGHT
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
if (boundaryShift !== 0) {
|
|
2808
|
+
const oldIdx = this.manager.boundaryIdx;
|
|
2809
|
+
if (typeof oldIdx === 'number') {
|
|
2810
|
+
const maxIdx = Math.max(0, (this.manager.orders?.size || 1) - 1);
|
|
2811
|
+
const newIdx = Math.max(0, Math.min(maxIdx, oldIdx + boundaryShift));
|
|
2812
|
+
if (newIdx !== oldIdx) {
|
|
2813
|
+
this.manager.boundaryIdx = newIdx;
|
|
2814
|
+
this.manager.logger.log(`[COW][UNCERTAIN] Boundary adjusted by ${boundaryShift} ` +
|
|
2815
|
+
`(${adopted.length} adopted CREATE(s), ${discarded.length} discarded): ` +
|
|
2816
|
+
`${oldIdx} → ${newIdx}. Next COW cycle will generate replacement opposite-side orders.`, 'warn');
|
|
2817
|
+
if (typeof this.manager._markGridDirty === 'function') {
|
|
2818
|
+
this.manager._markGridDirty();
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2761
2824
|
// Persist master grid mutations from the reconciliation sync and any
|
|
2762
2825
|
// orphan auto-cancellation that ran above. These apply outside the COW
|
|
2763
|
-
// broadcast path and would otherwise be in-memory only.
|
|
2826
|
+
// broadcast path and would otherwise be in-memory only. The boundary
|
|
2827
|
+
// adjustment above is also persisted here.
|
|
2764
2828
|
if (typeof this.manager?.persistGrid === 'function') {
|
|
2765
2829
|
await this.manager.persistGrid();
|
|
2766
2830
|
}
|
|
@@ -2898,8 +2962,6 @@ class DEXBot {
|
|
|
2898
2962
|
if (freshChain.length > 0 && this.manager?.syncFromOpenOrders) {
|
|
2899
2963
|
await this.manager.syncFromOpenOrders(freshChain, {
|
|
2900
2964
|
skipAccounting: true,
|
|
2901
|
-
fillLockAlreadyHeld: true,
|
|
2902
|
-
protectCommittedOrders: true
|
|
2903
2965
|
});
|
|
2904
2966
|
}
|
|
2905
2967
|
}
|
|
@@ -3294,7 +3356,7 @@ class DEXBot {
|
|
|
3294
3356
|
if (this._credentialRecoveryInFlight || !this._credentialRecoveryNeeded || this._shuttingDown) {
|
|
3295
3357
|
return;
|
|
3296
3358
|
}
|
|
3297
|
-
if (this.manager?.
|
|
3359
|
+
if (this.manager?.isBootstrapping?.() || this.manager?.isBroadcastingActive?.()) {
|
|
3298
3360
|
if (!this._credentialRecoveryDeferredTimer) {
|
|
3299
3361
|
this.manager?.logger?.log?.('[CREDENTIAL] Deferring credential recovery until startup/broadcast activity is idle.', 'info');
|
|
3300
3362
|
this._credentialRecoveryDeferredTimer = setTimeout(() => {
|
|
@@ -3316,7 +3378,7 @@ class DEXBot {
|
|
|
3316
3378
|
this._resumeGridPersistenceAfterCredentialRecovery('credential recovery started');
|
|
3317
3379
|
const runRecovery = async () => {
|
|
3318
3380
|
await this._triggerStateRecoverySync('credential daemon restored');
|
|
3319
|
-
await this._runGridMaintenance('credential-recovery'
|
|
3381
|
+
await this._runGridMaintenance('credential-recovery');
|
|
3320
3382
|
};
|
|
3321
3383
|
if (this.manager?._fillProcessingLock) {
|
|
3322
3384
|
await this.manager._fillProcessingLock.acquire(runRecovery);
|
|
@@ -3773,7 +3835,7 @@ class DEXBot {
|
|
|
3773
3835
|
batchId: this._currentBatchId || null,
|
|
3774
3836
|
payload: null,
|
|
3775
3837
|
timeoutMs: null
|
|
3776
|
-
}), []
|
|
3838
|
+
}), []);
|
|
3777
3839
|
}
|
|
3778
3840
|
catch (recoverErr) {
|
|
3779
3841
|
this.manager.logger.log(`[COW] Recovery from pending broadcasts failed: ${recoverErr?.message || recoverErr}`, 'error');
|
|
@@ -4133,7 +4195,7 @@ class DEXBot {
|
|
|
4133
4195
|
// throwing would re-enter the catch loop on the next attempt and
|
|
4134
4196
|
// potentially double-publish.
|
|
4135
4197
|
if (err instanceof BroadcastUncertainError) {
|
|
4136
|
-
return await this._reconcileAfterUncertainBroadcast(err, opContexts
|
|
4198
|
+
return await this._reconcileAfterUncertainBroadcast(err, opContexts);
|
|
4137
4199
|
}
|
|
4138
4200
|
// Handle hard abort
|
|
4139
4201
|
const hardAbortResult = await this._handleBatchHardAbort(err, 'COW batch processing', operations.length);
|
|
@@ -4439,6 +4501,10 @@ class DEXBot {
|
|
|
4439
4501
|
*/
|
|
4440
4502
|
async _runStartupSequence() {
|
|
4441
4503
|
try {
|
|
4504
|
+
if (this.config.creditOnly) {
|
|
4505
|
+
await this._runCreditOnlyStartup();
|
|
4506
|
+
return;
|
|
4507
|
+
}
|
|
4442
4508
|
const startupState = await this._initializeStartupState();
|
|
4443
4509
|
await this._finishStartupSequence(startupState);
|
|
4444
4510
|
}
|
|
@@ -4448,13 +4514,20 @@ class DEXBot {
|
|
|
4448
4514
|
throw err;
|
|
4449
4515
|
}
|
|
4450
4516
|
}
|
|
4517
|
+
async _runCreditOnlyStartup() {
|
|
4518
|
+
await this._setupCreditRuntime();
|
|
4519
|
+
await this._refreshAndSyncCreditRuntime();
|
|
4520
|
+
await this._runCreditRuntimeMaintenance('startup');
|
|
4521
|
+
this._setupCreditWatchdogInterval();
|
|
4522
|
+
this._setupCredentialDaemonWatchdogInterval();
|
|
4523
|
+
this._log('DEXBot started (credit-only mode)');
|
|
4524
|
+
}
|
|
4451
4525
|
/**
|
|
4452
4526
|
* Perform periodic grid checks: fund thresholds, spread condition, grid health.
|
|
4453
4527
|
* Called by the periodic blockchain fetch interval to check if grid needs updates.
|
|
4454
4528
|
*
|
|
4455
4529
|
* IMPORTANT: This method MUST only be called from within _fillProcessingLock.acquire()
|
|
4456
|
-
* (specifically from _setupBlockchainFetchInterval).
|
|
4457
|
-
* to avoid deadlock with _consumeFillQueue which uses the same lock ordering.
|
|
4530
|
+
* (specifically from _setupBlockchainFetchInterval).
|
|
4458
4531
|
*
|
|
4459
4532
|
* @private
|
|
4460
4533
|
*/
|
|
@@ -4601,14 +4674,17 @@ class DEXBot {
|
|
|
4601
4674
|
return { skipped: true, reason: 'grid resync unavailable' };
|
|
4602
4675
|
}
|
|
4603
4676
|
const message = reason ? `[CR-RESET] ${reason}` : '[CR-RESET] grid reset requested';
|
|
4604
|
-
// Manual/programmatic resets should advance the persisted center baseline
|
|
4605
|
-
// before rebuilding the grid, unless a caller explicitly disables it.
|
|
4606
4677
|
this._log(`${message}; rebuilding grid from fresh on-chain state`, 'info');
|
|
4607
4678
|
const resetOptions = {
|
|
4608
4679
|
...options,
|
|
4609
4680
|
refreshCenterPrice: options.refreshCenterPrice !== false,
|
|
4610
4681
|
};
|
|
4611
|
-
if (
|
|
4682
|
+
// Skip acquire if no lock exists (no fill processing to serialize against)
|
|
4683
|
+
// or if the caller already holds the lock (re-entrant). In the latter case
|
|
4684
|
+
// the lock's isReentrant() check prevents queueing and runs inline.
|
|
4685
|
+
// Otherwise, wait on the queue — this is the intended path for callers
|
|
4686
|
+
// outside the fill-processing context that need exclusive access.
|
|
4687
|
+
if (!this.manager._fillProcessingLock || this.manager._fillProcessingLock.isReentrant()) {
|
|
4612
4688
|
return this._performGridResync(resetOptions);
|
|
4613
4689
|
}
|
|
4614
4690
|
return this.manager._fillProcessingLock.acquire(async () => this._performGridResync(resetOptions));
|
|
@@ -4720,12 +4796,12 @@ class DEXBot {
|
|
|
4720
4796
|
* Cancel dust partial orders immediately — no maps, no timers, no delay.
|
|
4721
4797
|
* Each dust order is cancelled on chain and its slot rotated through the
|
|
4722
4798
|
* synthetic-fill pipeline.
|
|
4723
|
-
* @param {{ buy: Array, sell: Array
|
|
4799
|
+
* @param {{ buy: Array, sell: Array }} options
|
|
4724
4800
|
* @returns {Promise<{cancelledCount: number, batchResult: {aborted: boolean}|null}>}
|
|
4725
4801
|
* @private
|
|
4726
4802
|
*/
|
|
4727
|
-
async _cancelDustOrders({ buy: buyDust = [], sell: sellDust = []
|
|
4728
|
-
return DexbotMaintenanceRuntime.cancelDustOrders(this, { buy: buyDust, sell: sellDust
|
|
4803
|
+
async _cancelDustOrders({ buy: buyDust = [], sell: sellDust = [] } = {}) {
|
|
4804
|
+
return DexbotMaintenanceRuntime.cancelDustOrders(this, { buy: buyDust, sell: sellDust });
|
|
4729
4805
|
}
|
|
4730
4806
|
/**
|
|
4731
4807
|
* Set up the periodic dust health check interval.
|
|
@@ -4750,7 +4826,6 @@ class DEXBot {
|
|
|
4750
4826
|
await this._cancelDustOrders({
|
|
4751
4827
|
buy: health.buyDustOrders,
|
|
4752
4828
|
sell: health.sellDustOrders,
|
|
4753
|
-
fillLockAlreadyHeld: true,
|
|
4754
4829
|
});
|
|
4755
4830
|
});
|
|
4756
4831
|
}
|
|
@@ -4776,7 +4851,6 @@ class DEXBot {
|
|
|
4776
4851
|
*
|
|
4777
4852
|
* LOCK ORDERING:
|
|
4778
4853
|
* - Canonical order: _fillProcessingLock → _divergenceLock
|
|
4779
|
-
* - This function handles lock acquisition based on fillLockAlreadyHeld parameter
|
|
4780
4854
|
* - When called from post-fill context, fill lock is already held
|
|
4781
4855
|
* - When called from periodic context, both locks must be acquired
|
|
4782
4856
|
* - Matches the order used in _consumeFillQueue to prevent deadlocks
|