dexbot 1.1.13 → 1.2.0
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/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 +17 -1
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +84 -18
- package/dist/modules/constants.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 +16 -12
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +183 -78
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_credential_client.d.ts +1 -0
- package/dist/modules/dexbot_credential_client.d.ts.map +1 -1
- package/dist/modules/dexbot_credential_client.js +15 -6
- package/dist/modules/dexbot_credential_client.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 +78 -42
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/key_store.d.ts.map +1 -1
- package/dist/modules/key_store.js +2 -0
- package/dist/modules/key_store.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/node_manager.d.ts +9 -0
- package/dist/modules/node_manager.d.ts.map +1 -1
- package/dist/modules/node_manager.js +35 -17
- package/dist/modules/node_manager.js.map +1 -1
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +56 -13
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/async_lock.d.ts +33 -4
- package/dist/modules/order/async_lock.d.ts.map +1 -1
- package/dist/modules/order/async_lock.js +69 -7
- package/dist/modules/order/async_lock.js.map +1 -1
- package/dist/modules/order/grid.d.ts +435 -450
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +1621 -1548
- 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 +771 -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 +40 -3
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +97 -15
- 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 +1 -1
- package/dist/modules/order/sync_engine.d.ts.map +1 -1
- package/dist/modules/order/sync_engine.js +121 -29
- 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 +56 -19
- 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/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 +14 -4
|
@@ -1,838 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* (resume-from-persisted-grid) and from the maintenance runtime's targeted
|
|
7
|
-
* drift reconciliation path.
|
|
8
|
-
*
|
|
9
|
-
* Renamed from startup_reconcile.ts — the function is a general grid/chain
|
|
10
|
-
* reconciler, not a startup-only concern; maintenance depends on the same
|
|
11
|
-
* logic. See modules/dexbot_maintenance_runtime.ts for the runtime caller.
|
|
12
|
-
*
|
|
13
|
-
* Purpose:
|
|
14
|
-
* - Resume previously persisted grids from storage
|
|
15
|
-
* - Match persisted grids with current blockchain state
|
|
16
|
-
* - Reconcile VIRTUAL orders with actual open orders
|
|
17
|
-
* - Activate appropriate orders based on blockchain state
|
|
18
|
-
* - Detect and handle partial order dust
|
|
19
|
-
* - Trigger rebalancing when needed
|
|
20
|
-
*
|
|
21
|
-
* ===============================================================================
|
|
22
|
-
* EXPORTS (3 functions)
|
|
23
|
-
* ===============================================================================
|
|
24
|
-
*
|
|
25
|
-
* PUBLIC FUNCTIONS:
|
|
26
|
-
* 1. reconcileGridOrders(manager, logger) - Main reconciliation function (async)
|
|
27
|
-
* Reconciles blockchain orders with persisted grid, activates as needed
|
|
28
|
-
* Handles partial orders and triggers rebalancing if dust detected
|
|
29
|
-
* Returns null or rebalance result
|
|
30
|
-
*
|
|
31
|
-
* 2. attemptResumePersistedGridByPriceMatch(manager, persistedGrid) - Resume from storage (async)
|
|
32
|
-
* Matches persisted grid to current market state using price proximity
|
|
33
|
-
* Validates grid structure, locks/activates orders as appropriate
|
|
34
|
-
* Returns { success, reason, activatedIds } or { success: false, reason }
|
|
35
|
-
*
|
|
36
|
-
* 3. decideStartupGridAction(manager, chainOrders, persistedGrid) - Determine startup action
|
|
37
|
-
* Decides whether to resume persisted grid, create new grid, or abort
|
|
38
|
-
* Returns { action, reason } where action is 'resume', 'create_new', or 'abort'
|
|
39
|
-
*
|
|
40
|
-
* INTERNAL HELPERS:
|
|
41
|
-
* 4. _countActiveOnGrid(manager, type) - Count active/partial orders by type
|
|
42
|
-
* 5. _pickVirtualSlotsToActivate(manager, type, count) - Pick virtual slots for activation
|
|
43
|
-
* 6. _isGridEdgeFullyActive(manager, orderType, updateCount) - Check if grid edge is fully active on one side
|
|
44
|
-
* 7. _findLargestOrder(unmatchedOrders, updateCount) - Find the largest unmatched chain order
|
|
45
|
-
* 8. _cancelLargestOrder({...}) - Cancel the largest unmatched chain order (async)
|
|
46
|
-
* 9. _createOrderFromGrid({...}) - Create a new on-chain order from a grid slot (async)
|
|
47
|
-
* 10. _cancelChainOrder({...}) - Cancel a specific on-chain order (async)
|
|
48
|
-
* 11. _recoverStartupSyncFailure({...}) - Handle sync failure recovery at startup (async)
|
|
49
|
-
* 12. _executeStartupUpdateBatch({...}) - Execute startup updates in one batch (async)
|
|
50
|
-
* 13. _executeStartupSingleUpdate({...}) - Execute one startup update (async)
|
|
51
|
-
* 14. _executeStartupSequentialUpdateFallback({...}) - Sequential backup after batch retries (async)
|
|
52
|
-
* 15. _reconcileStartupSide({...}) - Reconcile one side (buy/sell) against chain at startup (async)
|
|
53
|
-
*
|
|
54
|
-
* ===============================================================================
|
|
55
|
-
*
|
|
56
|
-
* STARTUP FLOW:
|
|
57
|
-
* 1. Load persisted grid from storage (if exists)
|
|
58
|
-
* 2. Call decideStartupGridAction() to determine action
|
|
59
|
-
* 3. If resume: attemptResumePersistedGridByPriceMatch()
|
|
60
|
-
* 4. Call reconcileGridOrders() to sync with blockchain
|
|
61
|
-
* 5. Activate appropriate orders
|
|
62
|
-
* 6. Check for dust partials and trigger rebalancing if needed
|
|
63
|
-
*
|
|
64
|
-
* ===============================================================================
|
|
65
|
-
*/
|
|
66
|
-
const { ORDER_TYPES, ORDER_STATES, TIMING, BTS_PRECISION } = require('../constants');
|
|
67
|
-
const { getMinAbsoluteOrderSize, getAssetFees, blockchainToFloat, calculatePriceTolerance } = require('./utils/math');
|
|
68
|
-
const { isOrderPlaced, parseChainOrder, buildCreateOrderArgs, isOrderOnChain, buildOutsideInPairGroups, extractBatchOperationResults } = require('./utils/order');
|
|
2
|
+
const { _countActiveOnGrid, _pickVirtualSlotsToActivate, _getStartupSideComparators, _isGridEdgeFullyActive, _findLargestOrder, _cancelLargestOrder, _createOrderFromGrid, _cancelChainOrder, _recoverStartupSyncFailure, _refreshStartupUpdatePlans, _prepareStartupUpdatePlan, _finalizeStartupUpdate, _executeStartupUpdateBatch, _executeStartupSingleUpdate, _executeStartupSequentialUpdateFallback, _createStartupOrderWithHandling, _extractBatchOperationResults, _resolveGroupRecovery, _executeStartupCreateGroupBatch, _buildOutsideInCreateGroups, _executePlannedStartupCreates, _reconcileStartupSide, } = require('./grid_reconcile_internal');
|
|
3
|
+
const { ORDER_TYPES, ORDER_STATES } = require('../constants');
|
|
4
|
+
const { calculatePriceTolerance } = require('./utils/math');
|
|
5
|
+
const { isOrderPlaced, parseChainOrder, isOrderOnChain, buildCreateOrderArgs, buildOutsideInPairGroups, extractBatchOperationResults } = require('./utils/order');
|
|
69
6
|
const { resolveAccountRef } = require('./utils/system');
|
|
70
7
|
const Format = require('./format');
|
|
71
8
|
const SUSPECTED_DUPLICATE_TOLERANCE_MULTIPLIER = 5;
|
|
72
9
|
/**
|
|
73
|
-
* Count active orders on the grid for a given type.
|
|
74
|
-
* @param {Object} manager - OrderManager instance.
|
|
75
|
-
* @param {string} type - ORDER_TYPES value.
|
|
76
|
-
* @returns {number} Count of active and partial orders with orderId.
|
|
77
|
-
* @private
|
|
78
|
-
*/
|
|
79
|
-
function _countActiveOnGrid(manager, type) {
|
|
80
|
-
const active = manager.getOrdersByTypeAndState(type, ORDER_STATES.ACTIVE).filter((o) => o && o.orderId);
|
|
81
|
-
const partial = manager.getOrdersByTypeAndState(type, ORDER_STATES.PARTIAL).filter((o) => o && o.orderId);
|
|
82
|
-
return active.length + partial.length;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Pick virtual slots to activate based on type and count.
|
|
86
|
-
* @param {Object} manager - OrderManager instance.
|
|
87
|
-
* @param {string} type - ORDER_TYPES value.
|
|
88
|
-
* @param {number} count - Number of slots to pick.
|
|
89
|
-
* @returns {Array<Object>} Array of picked virtual slots.
|
|
90
|
-
* @private
|
|
91
|
-
*/
|
|
92
|
-
function _pickVirtualSlotsToActivate(manager, type, count) {
|
|
93
|
-
if (count <= 0)
|
|
94
|
-
return [];
|
|
95
|
-
// CRITICAL FIX: Filter by type BEFORE sorting
|
|
96
|
-
// Only get slots of the requested type (SELL or BUY), not a mix
|
|
97
|
-
const slotsOfType = Array.from(manager.orders.values())
|
|
98
|
-
.filter((slot) => slot && slot.type === type)
|
|
99
|
-
.sort((a, b) => type === ORDER_TYPES.BUY ? b.price - a.price : a.price - b.price);
|
|
100
|
-
let effectiveMin = 0;
|
|
101
|
-
try {
|
|
102
|
-
effectiveMin = getMinAbsoluteOrderSize(type, manager.assets);
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
effectiveMin = 0;
|
|
106
|
-
}
|
|
107
|
-
const valid = [];
|
|
108
|
-
for (const slot of slotsOfType) {
|
|
109
|
-
if (valid.length >= count)
|
|
110
|
-
break;
|
|
111
|
-
if (!slot.orderId && slot.state === ORDER_STATES.VIRTUAL) {
|
|
112
|
-
// Role invariant: Only pick slots that make sense for this type based on current market pivot
|
|
113
|
-
// (Strategy will enforce this strictly, but we filter here for cleaner activation)
|
|
114
|
-
if (slot.id && (Number(slot.size) || 0) >= effectiveMin) {
|
|
115
|
-
valid.push(slot);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return valid;
|
|
120
|
-
}
|
|
121
|
-
function _getStartupSideComparators(orderType, assets) {
|
|
122
|
-
const isSell = orderType === ORDER_TYPES.SELL;
|
|
123
|
-
const sortUpdateComparator = isSell
|
|
124
|
-
? (a, b) => (parseChainOrder(a, assets)?.price || 0) - (parseChainOrder(b, assets)?.price || 0)
|
|
125
|
-
: (a, b) => (parseChainOrder(b, assets)?.price || 0) - (parseChainOrder(a, assets)?.price || 0);
|
|
126
|
-
const sortExcessCancelComparator = isSell
|
|
127
|
-
? (a, b) => (b.parsed.price || 0) - (a.parsed.price || 0)
|
|
128
|
-
: (a, b) => (a.parsed.price || 0) - (b.parsed.price || 0);
|
|
129
|
-
const sortMatchedCancelComparator = isSell
|
|
130
|
-
? (a, b) => (b.price || 0) - (a.price || 0)
|
|
131
|
-
: (a, b) => (a.price || 0) - (b.price || 0);
|
|
132
|
-
return {
|
|
133
|
-
sortUpdateComparator,
|
|
134
|
-
sortExcessCancelComparator,
|
|
135
|
-
sortMatchedCancelComparator,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Detect if grid edge is fully occupied with active orders.
|
|
140
|
-
* When all outermost (furthest from market) orders are ACTIVE with orderId,
|
|
141
|
-
* we're at grid edge and all balance is committed to those orders.
|
|
142
|
-
*
|
|
143
|
-
* @param {Object} manager - OrderManager instance
|
|
144
|
-
* @param {string} orderType - ORDER_TYPES.BUY or ORDER_TYPES.SELL
|
|
145
|
-
* @param {number} updateCount - Number of orders being updated
|
|
146
|
-
* @returns {boolean} true if edge orders are all active
|
|
147
|
-
* @private
|
|
148
|
-
*/
|
|
149
|
-
function _isGridEdgeFullyActive(manager, orderType, updateCount) {
|
|
150
|
-
if (!manager || updateCount <= 0)
|
|
151
|
-
return false;
|
|
152
|
-
// Get all orders of this type
|
|
153
|
-
const allOrders = Array.from(manager.orders.values()).filter((o) => o.type === orderType);
|
|
154
|
-
if (allOrders.length === 0)
|
|
155
|
-
return false;
|
|
156
|
-
// Sort: for BUY (highest to lowest price), for SELL (lowest to highest)
|
|
157
|
-
// This puts market edge first, grid edge (furthest) last
|
|
158
|
-
const sorted = orderType === ORDER_TYPES.BUY
|
|
159
|
-
? allOrders.sort((a, b) => (b.price || 0) - (a.price || 0)) // Buy: high to low price
|
|
160
|
-
: allOrders.sort((a, b) => (a.price || 0) - (b.price || 0)); // Sell: low to high price
|
|
161
|
-
// Get the outermost orders (last N in sorted = furthest from market)
|
|
162
|
-
const outerEdgeCount = Math.min(updateCount, sorted.length);
|
|
163
|
-
const edgeOrders = sorted.slice(-outerEdgeCount);
|
|
164
|
-
// Check if ALL edge orders are ACTIVE (placed on blockchain)
|
|
165
|
-
// Empty array check prevents vacuous truth: every([]) returns true
|
|
166
|
-
if (edgeOrders.length === 0)
|
|
167
|
-
return false;
|
|
168
|
-
const allEdgeActive = edgeOrders.every((o) => isOrderPlaced(o));
|
|
169
|
-
return allEdgeActive;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Find the largest order among those being updated.
|
|
173
|
-
* Returns both the order and its index in unmatchedOrders for pairing with gridOrders.
|
|
174
|
-
* @param {Array<Object>} unmatchedOrders - Array of unmatched on-chain orders
|
|
175
|
-
* @param {number} updateCount - Number of orders being updated (first N)
|
|
176
|
-
* @returns {{order: Object, index: number}|null} Largest order and its index, or null if none found
|
|
177
|
-
* @private
|
|
178
|
-
*/
|
|
179
|
-
function _findLargestOrder(unmatchedOrders, updateCount) {
|
|
180
|
-
if (!Array.isArray(unmatchedOrders) || unmatchedOrders.length === 0)
|
|
181
|
-
return null;
|
|
182
|
-
const ordersToCheck = unmatchedOrders.slice(0, updateCount);
|
|
183
|
-
let largestOrder = null;
|
|
184
|
-
let largestIndex = -1;
|
|
185
|
-
let largestSize = 0;
|
|
186
|
-
for (let i = 0; i < ordersToCheck.length; i++) {
|
|
187
|
-
const order = ordersToCheck[i];
|
|
188
|
-
const size = Number(order.for_sale) || 0;
|
|
189
|
-
if (size > largestSize) {
|
|
190
|
-
largestSize = size;
|
|
191
|
-
largestOrder = order;
|
|
192
|
-
largestIndex = i;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return largestIndex >= 0 ? { order: largestOrder, index: largestIndex } : null;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Cancel the largest unmatched order to free up maximum funds.
|
|
199
|
-
* This is more efficient than reducing to size 1 and then updating twice.
|
|
200
|
-
* Returns the grid slot index and grid order that needs to be filled.
|
|
201
|
-
* @param {Object} params - Destructured parameters
|
|
202
|
-
* @param {Function} params.chainOrders - Chain order query function
|
|
203
|
-
* @param {string} params.account - Account ID or name
|
|
204
|
-
* @param {string} params.privateKey - Active/owner private key
|
|
205
|
-
* @param {Object} params.manager - OrderManager instance
|
|
206
|
-
* @param {Array<Object>} params.unmatchedOrders - Unmatched on-chain orders
|
|
207
|
-
* @param {number} params.updateCount - Number of orders being updated
|
|
208
|
-
* @param {string} params.orderType - ORDER_TYPES.BUY or ORDER_TYPES.SELL
|
|
209
|
-
* @param {boolean} params.dryRun - If true, skip actual cancellation
|
|
210
|
-
* @returns {Promise<{gridIndex: number, gridOrder: Object}|null>} Grid slot info or null
|
|
211
|
-
* @private
|
|
212
|
-
*/
|
|
213
|
-
async function _cancelLargestOrder({ chainOrders, account, privateKey, manager, unmatchedOrders, updateCount, orderType, dryRun, fillLockAlreadyHeld = false }) {
|
|
214
|
-
if (dryRun)
|
|
215
|
-
return null;
|
|
216
|
-
if (!Array.isArray(unmatchedOrders) || unmatchedOrders.length === 0)
|
|
217
|
-
return null;
|
|
218
|
-
const logger = manager && manager.logger;
|
|
219
|
-
// Find the largest order among those being updated
|
|
220
|
-
const largestInfo = _findLargestOrder(unmatchedOrders, updateCount);
|
|
221
|
-
if (!largestInfo)
|
|
222
|
-
return null;
|
|
223
|
-
const { order: largestOrder, index: largestIndex } = largestInfo;
|
|
224
|
-
const originalSize = Number(largestOrder.for_sale) || 0;
|
|
225
|
-
const orderId = largestOrder.id;
|
|
226
|
-
logger?.log?.(`Grid edge detected: cancelling largest order ${orderId} (size ${originalSize}) to free up funds`, 'info');
|
|
227
|
-
try {
|
|
228
|
-
// Cancel the largest order on blockchain and release untracked funds.
|
|
229
|
-
await _cancelChainOrder({
|
|
230
|
-
chainOrders,
|
|
231
|
-
account,
|
|
232
|
-
privateKey,
|
|
233
|
-
manager,
|
|
234
|
-
chainOrderId: orderId,
|
|
235
|
-
chainOrderObj: largestOrder,
|
|
236
|
-
releaseUntrackedFunds: true,
|
|
237
|
-
dryRun,
|
|
238
|
-
fillLockAlreadyHeld,
|
|
239
|
-
});
|
|
240
|
-
logger?.log?.(`Cancelled largest order ${orderId}`, 'info');
|
|
241
|
-
// Mark for removal from unmatched list (handled by caller)
|
|
242
|
-
// Return info needed to create this order fresh later
|
|
243
|
-
return { index: largestIndex, orderType };
|
|
244
|
-
}
|
|
245
|
-
catch (err) {
|
|
246
|
-
logger?.log?.(`Warning: Could not cancel largest order ${orderId}: ${err.message}`, 'warn');
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Create a new chain order from a grid slot.
|
|
252
|
-
* @param {Object} params - Creation parameters.
|
|
253
|
-
* @param {Object} params.chainOrders - Chain orders module.
|
|
254
|
-
* @param {string} params.account - Account name.
|
|
255
|
-
* @param {string} params.privateKey - Private key.
|
|
256
|
-
* @param {Object} params.manager - OrderManager instance.
|
|
257
|
-
* @param {Object} params.gridOrder - Grid order object.
|
|
258
|
-
* @param {boolean} params.dryRun - Whether to simulate.
|
|
259
|
-
* @returns {Promise<void>}
|
|
260
|
-
* @private
|
|
261
|
-
*/
|
|
262
|
-
async function _createOrderFromGrid({ chainOrders, account, privateKey, manager, gridOrder, dryRun, fillLockAlreadyHeld = false }) {
|
|
263
|
-
if (dryRun)
|
|
264
|
-
return;
|
|
265
|
-
// ATOMIC RE-VERIFICATION: Ensure slot is still virtual and hasn't been filled by recovery sync
|
|
266
|
-
const currentSlot = manager.orders.get(gridOrder.id);
|
|
267
|
-
if (currentSlot && currentSlot.orderId) {
|
|
268
|
-
manager.logger?.log?.(`[_createOrderFromGrid] SKIP: Slot ${gridOrder.id} already has orderId ${currentSlot.orderId}`, 'warn');
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
const { amountToSell, sellAssetId, minToReceive, receiveAssetId } = buildCreateOrderArgs(gridOrder, manager.assets.assetA, manager.assets.assetB);
|
|
272
|
-
const result = await chainOrders.createOrder(account, privateKey, amountToSell, sellAssetId, minToReceive, receiveAssetId, null, false);
|
|
273
|
-
if (result && result.skipped) {
|
|
274
|
-
const logger = manager && manager.logger;
|
|
275
|
-
logger?.log?.(`[_createOrderFromGrid] Skipped slot ${gridOrder.id}: order amounts too small to place on-chain`, 'warn');
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const operationResults = extractBatchOperationResults(result) || [];
|
|
279
|
-
const chainOrderId = operationResults[0] && operationResults[0][1];
|
|
280
|
-
if (chainOrderId) {
|
|
281
|
-
const btsFeeData = getAssetFees('BTS');
|
|
282
|
-
// Centralized Fund Tracking: Use manager's sync core to handle state transition and fund deduction
|
|
283
|
-
// CRITICAL: Use _applySync (lock-free) since caller holds _gridLock
|
|
284
|
-
await manager._applySync({
|
|
285
|
-
gridOrderId: gridOrder.id,
|
|
286
|
-
chainOrderId,
|
|
287
|
-
isPartialPlacement: false,
|
|
288
|
-
fee: btsFeeData.createFee
|
|
289
|
-
}, 'createOrder', { gridLockAlreadyHeld: true });
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
// CRITICAL FIX: Recovery sync if order extraction fails
|
|
293
|
-
const logger = manager && manager.logger;
|
|
294
|
-
logger?.log?.(`[_createOrderFromGrid] CRITICAL: createOrder succeeded but chainOrderId extraction failed`, 'error');
|
|
295
|
-
try {
|
|
296
|
-
const freshChainOrders = await chainOrders.readOpenOrders(resolveAccountRef(manager, account), TIMING.CONNECTION_TIMEOUT_MS);
|
|
297
|
-
// CRITICAL FIX: Use skipAccounting: false - order discovery must update accounting
|
|
298
|
-
// Orphan order requires fund deduction to prevent phantom capital
|
|
299
|
-
await manager.syncFromOpenOrders(freshChainOrders, {
|
|
300
|
-
skipAccounting: false,
|
|
301
|
-
source: 'chainOrderIdExtractionFailure',
|
|
302
|
-
gridLockAlreadyHeld: true,
|
|
303
|
-
fillLockAlreadyHeld,
|
|
304
|
-
protectCommittedOrders: true
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
catch (syncErr) {
|
|
308
|
-
logger?.log?.(`[_createOrderFromGrid] Recovery sync failed: ${syncErr.message}`, 'error');
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Cancel a chain order and sync with manager.
|
|
314
|
-
* @param {Object} params - Cancellation parameters.
|
|
315
|
-
* @param {Object} params.chainOrders - Chain orders module.
|
|
316
|
-
* @param {string} params.account - Account name.
|
|
317
|
-
* @param {string} params.privateKey - Private key.
|
|
318
|
-
* @param {Object} params.manager - OrderManager instance.
|
|
319
|
-
* @param {string} params.chainOrderId - ID of the chain order to cancel.
|
|
320
|
-
* @param {boolean} params.dryRun - Whether to simulate.
|
|
321
|
-
* @param {Object} [params.chainOrderObj] - Raw chain order object (needed for fund release).
|
|
322
|
-
* @param {boolean} [params.releaseUntrackedFunds=false] - If true, release the cancelled order's
|
|
323
|
-
* committed funds via addToChainFree. Use only for unmatched chain orders that have no
|
|
324
|
-
* corresponding ACTIVE/PARTIAL grid slot (where synchronizeWithChain cannot release them).
|
|
325
|
-
* @returns {Promise<void>}
|
|
326
|
-
* @private
|
|
327
|
-
*/
|
|
328
|
-
async function _cancelChainOrder({ chainOrders, account, privateKey, manager, chainOrderId, dryRun, chainOrderObj, releaseUntrackedFunds = false, fillLockAlreadyHeld = false }) {
|
|
329
|
-
if (dryRun)
|
|
330
|
-
return;
|
|
331
|
-
const cancelResult = await chainOrders.cancelOrder(account, privateKey, chainOrderId);
|
|
332
|
-
if (cancelResult?.verifiedAfterFailure) {
|
|
333
|
-
const freshChainOrders = await chainOrders.readOpenOrders(resolveAccountRef(manager, account), TIMING.CONNECTION_TIMEOUT_MS);
|
|
334
|
-
await manager.syncFromOpenOrders(freshChainOrders, {
|
|
335
|
-
skipAccounting: false,
|
|
336
|
-
source: 'cancelOrder',
|
|
337
|
-
gridLockAlreadyHeld: true,
|
|
338
|
-
fillLockAlreadyHeld,
|
|
339
|
-
protectCommittedOrders: true
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
// CRITICAL: Use _applySync (lock-free) since caller holds _gridLock
|
|
344
|
-
await manager._applySync(chainOrderId, 'cancelOrder', { gridLockAlreadyHeld: true });
|
|
345
|
-
}
|
|
346
|
-
// Unmatched chain orders are not represented as ACTIVE/PARTIAL grid slots, so
|
|
347
|
-
// synchronizeWithChain('cancelOrder') cannot release their commitment.
|
|
348
|
-
if (releaseUntrackedFunds && manager.accountant && chainOrderObj) {
|
|
349
|
-
const parsed = parseChainOrder(chainOrderObj, manager.assets);
|
|
350
|
-
if (parsed && parsed.size > 0) {
|
|
351
|
-
await manager.accountant.addToChainFree(parsed.type, parsed.size, 'startup-cancel-unmatched');
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
async function _recoverStartupSyncFailure({ chainOrders, manager, account, logger, triggerMessage, source, fillLockAlreadyHeld = false }) {
|
|
356
|
-
try {
|
|
357
|
-
logger?.log?.(triggerMessage, 'warn');
|
|
358
|
-
const freshChainOrders = await chainOrders.readOpenOrders(resolveAccountRef(manager, account), TIMING.CONNECTION_TIMEOUT_MS);
|
|
359
|
-
await manager.syncFromOpenOrders(freshChainOrders, {
|
|
360
|
-
skipAccounting: false,
|
|
361
|
-
source,
|
|
362
|
-
gridLockAlreadyHeld: true,
|
|
363
|
-
fillLockAlreadyHeld,
|
|
364
|
-
protectCommittedOrders: true
|
|
365
|
-
});
|
|
366
|
-
return freshChainOrders;
|
|
367
|
-
}
|
|
368
|
-
catch (syncErr) {
|
|
369
|
-
logger?.log?.(`Startup: Recovery sync failed: ${syncErr.message}`, 'error');
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
function _refreshStartupUpdatePlans(updatePlans, chainOpenOrders) {
|
|
374
|
-
if (!Array.isArray(updatePlans) || updatePlans.length === 0)
|
|
375
|
-
return [];
|
|
376
|
-
const chainById = new Map((Array.isArray(chainOpenOrders) ? chainOpenOrders : [])
|
|
377
|
-
.filter(o => o && o.id)
|
|
378
|
-
.map(o => [o.id, o]));
|
|
379
|
-
const refreshed = [];
|
|
380
|
-
for (const plan of updatePlans) {
|
|
381
|
-
if (!plan?.chainOrderId || !plan?.gridOrder?.id)
|
|
382
|
-
continue;
|
|
383
|
-
const freshChainOrder = chainById.get(plan.chainOrderId);
|
|
384
|
-
if (!freshChainOrder)
|
|
385
|
-
continue;
|
|
386
|
-
refreshed.push({
|
|
387
|
-
...plan,
|
|
388
|
-
chainOrderObj: freshChainOrder,
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
return refreshed;
|
|
392
|
-
}
|
|
393
|
-
function _prepareStartupUpdatePlan(plan, manager, logger) {
|
|
394
|
-
const chainOrderId = plan?.chainOrderId;
|
|
395
|
-
const gridOrder = plan?.gridOrder;
|
|
396
|
-
if (!chainOrderId || !gridOrder?.id)
|
|
397
|
-
return null;
|
|
398
|
-
const currentSlot = manager.orders.get(gridOrder.id);
|
|
399
|
-
if (!currentSlot || (currentSlot.orderId && currentSlot.orderId !== chainOrderId)) {
|
|
400
|
-
logger?.log?.(`Startup: Skip update ${chainOrderId} -> ${gridOrder.id}; slot already mapped (${currentSlot?.orderId || 'none'})`, 'warn');
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
if (currentSlot.orderId === chainOrderId) {
|
|
404
|
-
logger?.log?.(`Startup: Skip update ${chainOrderId} -> ${gridOrder.id}; slot already matched`, 'debug');
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
const { amountToSell, minToReceive } = buildCreateOrderArgs(gridOrder, manager.assets.assetA, manager.assets.assetB);
|
|
408
|
-
return {
|
|
409
|
-
plan,
|
|
410
|
-
chainOrderId,
|
|
411
|
-
gridOrder,
|
|
412
|
-
parsedChain: parseChainOrder(plan.chainOrderObj, manager.assets),
|
|
413
|
-
updateParams: {
|
|
414
|
-
newPrice: gridOrder.price,
|
|
415
|
-
amountToSell,
|
|
416
|
-
minToReceive,
|
|
417
|
-
orderType: gridOrder.type,
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
async function _finalizeStartupUpdate({ manager, preparedUpdate }) {
|
|
422
|
-
const { plan, parsedChain } = preparedUpdate;
|
|
423
|
-
if (parsedChain && parsedChain.size > 0 && manager.accountant) {
|
|
424
|
-
await manager.accountant.addToChainFree(plan.gridOrder.type, parsedChain.size, 'startup-align');
|
|
425
|
-
}
|
|
426
|
-
// Extract deferred_fee from the raw chain order object so the fee
|
|
427
|
-
// lifecycle (cancel refunds, fill maker discounts) reconstructs
|
|
428
|
-
// the correct btsFeeState after a grid reset.
|
|
429
|
-
// deferred_fee from chain is in raw satoshis (BTS precision 5).
|
|
430
|
-
// The fee lifecycle operates in float BTS units, so convert here.
|
|
431
|
-
const rawDeferredFee = Format.toFiniteNumber(plan.chainOrderObj?.deferred_fee, null);
|
|
432
|
-
const deferredFeeFloat = rawDeferredFee !== null ? blockchainToFloat(rawDeferredFee, BTS_PRECISION) : null;
|
|
433
|
-
const btsFeeData = getAssetFees('BTS');
|
|
434
|
-
await manager._applySync({
|
|
435
|
-
gridOrderId: plan.gridOrder.id,
|
|
436
|
-
chainOrderId: plan.chainOrderId,
|
|
437
|
-
isPartialPlacement: false,
|
|
438
|
-
fee: btsFeeData.updateFee,
|
|
439
|
-
skipAccounting: false,
|
|
440
|
-
deferredFee: deferredFeeFloat,
|
|
441
|
-
}, 'createOrder', { gridLockAlreadyHeld: true });
|
|
442
|
-
}
|
|
443
|
-
async function _executeStartupUpdateBatch({ updatePlans, chainOrders, account, privateKey, manager, dryRun, }) {
|
|
444
|
-
if (!Array.isArray(updatePlans) || updatePlans.length === 0 || dryRun) {
|
|
445
|
-
return { executed: false, prepared: 0, skipped: true };
|
|
446
|
-
}
|
|
447
|
-
if (typeof chainOrders?.buildUpdateOrderOp !== 'function' || typeof chainOrders?.executeBatch !== 'function') {
|
|
448
|
-
throw new Error('chainOrders does not support batch update operations');
|
|
449
|
-
}
|
|
450
|
-
const logger = manager?.logger;
|
|
451
|
-
const prepared = [];
|
|
452
|
-
for (const plan of updatePlans) {
|
|
453
|
-
const preparedPlan = _prepareStartupUpdatePlan(plan, manager, logger);
|
|
454
|
-
if (!preparedPlan)
|
|
455
|
-
continue;
|
|
456
|
-
const buildResult = await chainOrders.buildUpdateOrderOp(account, preparedPlan.chainOrderId, preparedPlan.updateParams, plan.chainOrderObj || null);
|
|
457
|
-
if (!buildResult) {
|
|
458
|
-
logger?.log?.(`Startup: Skip update ${preparedPlan.chainOrderId} -> ${preparedPlan.gridOrder.id}; no blockchain delta`, 'debug');
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
prepared.push({
|
|
462
|
-
...preparedPlan,
|
|
463
|
-
op: buildResult.op,
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
if (prepared.length === 0) {
|
|
467
|
-
return { executed: false, prepared: 0, skipped: true };
|
|
468
|
-
}
|
|
469
|
-
logger?.log?.(`Startup: Broadcasting update batch (${prepared.length} op${prepared.length > 1 ? 's' : ''})`, 'info');
|
|
470
|
-
await chainOrders.executeBatch(account, privateKey, prepared.map(p => p.op));
|
|
471
|
-
for (const entry of prepared) {
|
|
472
|
-
await _finalizeStartupUpdate({ manager, preparedUpdate: entry });
|
|
473
|
-
}
|
|
474
|
-
return { executed: true, prepared: prepared.length, skipped: false };
|
|
475
|
-
}
|
|
476
|
-
async function _executeStartupSingleUpdate({ plan, chainOrders, account, privateKey, manager, dryRun, }) {
|
|
477
|
-
if (dryRun)
|
|
478
|
-
return { executed: false, skipped: true };
|
|
479
|
-
if (typeof chainOrders?.updateOrder !== 'function') {
|
|
480
|
-
throw new Error('chainOrders.updateOrder is required for sequential update fallback');
|
|
481
|
-
}
|
|
482
|
-
const logger = manager?.logger;
|
|
483
|
-
const prepared = _prepareStartupUpdatePlan(plan, manager, logger);
|
|
484
|
-
if (!prepared)
|
|
485
|
-
return { executed: false, skipped: true };
|
|
486
|
-
const result = await chainOrders.updateOrder(account, privateKey, prepared.chainOrderId, prepared.updateParams);
|
|
487
|
-
if (!result) {
|
|
488
|
-
logger?.log?.(`Startup: Skip update ${prepared.chainOrderId} -> ${prepared.gridOrder.id}; no blockchain delta`, 'debug');
|
|
489
|
-
return { executed: false, skipped: true };
|
|
490
|
-
}
|
|
491
|
-
await _finalizeStartupUpdate({ manager, preparedUpdate: prepared });
|
|
492
|
-
return { executed: true, skipped: false };
|
|
493
|
-
}
|
|
494
|
-
async function _executeStartupSequentialUpdateFallback({ updatePlans, chainOrders, account, privateKey, manager, dryRun, fillLockAlreadyHeld = false, }) {
|
|
495
|
-
if (!Array.isArray(updatePlans) || updatePlans.length === 0 || dryRun) {
|
|
496
|
-
return { executed: 0, skipped: 0, failed: 0 };
|
|
497
|
-
}
|
|
498
|
-
const logger = manager?.logger;
|
|
499
|
-
let queue = updatePlans.slice(0);
|
|
500
|
-
let executed = 0;
|
|
501
|
-
let skipped = 0;
|
|
502
|
-
let failed = 0;
|
|
503
|
-
logger?.log?.(`Startup: Falling back to sequential updates for ${queue.length} pending order(s)`, 'warn');
|
|
504
|
-
while (queue.length > 0) {
|
|
505
|
-
const plan = queue.shift();
|
|
506
|
-
if (!plan)
|
|
507
|
-
continue;
|
|
508
|
-
try {
|
|
509
|
-
const result = await _executeStartupSingleUpdate({
|
|
510
|
-
plan,
|
|
511
|
-
chainOrders,
|
|
512
|
-
account,
|
|
513
|
-
privateKey,
|
|
514
|
-
manager,
|
|
515
|
-
dryRun,
|
|
516
|
-
});
|
|
517
|
-
if (result.executed)
|
|
518
|
-
executed++;
|
|
519
|
-
else
|
|
520
|
-
skipped++;
|
|
521
|
-
}
|
|
522
|
-
catch (err) {
|
|
523
|
-
failed++;
|
|
524
|
-
logger?.log?.(`Startup: Sequential update failed for ${plan.chainOrderId} -> ${plan.gridOrderId || plan.gridOrder?.id}: ${err.message}`, 'error');
|
|
525
|
-
const refreshedChainOrders = await _recoverStartupSyncFailure({
|
|
526
|
-
chainOrders,
|
|
527
|
-
manager,
|
|
528
|
-
account,
|
|
529
|
-
logger,
|
|
530
|
-
triggerMessage: `Startup: Triggering recovery sync after sequential update failure for ${plan.chainOrderId}`,
|
|
531
|
-
source: 'startupReconcileSequentialUpdateFailure',
|
|
532
|
-
fillLockAlreadyHeld,
|
|
533
|
-
});
|
|
534
|
-
queue = _refreshStartupUpdatePlans(queue, refreshedChainOrders);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
return { executed, skipped, failed };
|
|
538
|
-
}
|
|
539
|
-
async function _createStartupOrderWithHandling({ chainOrders, account, privateKey, manager, gridOrder, orderLabel, dryRun, recovery, fillLockAlreadyHeld = false }) {
|
|
540
|
-
try {
|
|
541
|
-
await _createOrderFromGrid({ chainOrders, account, privateKey, manager, gridOrder, dryRun, fillLockAlreadyHeld });
|
|
542
|
-
}
|
|
543
|
-
catch (err) {
|
|
544
|
-
manager?.logger?.log?.(`Startup: Failed to create ${orderLabel}: ${err.message}`, 'error');
|
|
545
|
-
if (recovery && recovery.triggerMessage && recovery.source) {
|
|
546
|
-
await _recoverStartupSyncFailure({
|
|
547
|
-
chainOrders,
|
|
548
|
-
manager,
|
|
549
|
-
account,
|
|
550
|
-
logger: manager?.logger,
|
|
551
|
-
triggerMessage: recovery.triggerMessage,
|
|
552
|
-
source: recovery.source,
|
|
553
|
-
fillLockAlreadyHeld,
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
// _extractBatchOperationResults — thin wrapper over shared utility,
|
|
559
|
-
// returns empty array (not null) for callers that iterate directly.
|
|
560
|
-
function _extractBatchOperationResults(result) {
|
|
561
|
-
return extractBatchOperationResults(result) || [];
|
|
562
|
-
}
|
|
563
|
-
function _resolveGroupRecovery(group, fallbackMessage, fallbackSource) {
|
|
564
|
-
for (const plan of group || []) {
|
|
565
|
-
if (plan?.recovery?.triggerMessage && plan?.recovery?.source) {
|
|
566
|
-
return { triggerMessage: plan.recovery.triggerMessage, source: plan.recovery.source };
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return { triggerMessage: fallbackMessage, source: fallbackSource };
|
|
570
|
-
}
|
|
571
|
-
async function _executeStartupCreateGroupBatch({ group, chainOrders, account, privateKey, manager, dryRun, groupIndex, totalGroups, fillLockAlreadyHeld = false, }) {
|
|
572
|
-
if (!Array.isArray(group) || group.length === 0 || dryRun)
|
|
573
|
-
return;
|
|
574
|
-
if (typeof chainOrders?.buildCreateOrderOp !== 'function' || typeof chainOrders?.executeBatch !== 'function') {
|
|
575
|
-
throw new Error('chainOrders does not support batch create operations');
|
|
576
|
-
}
|
|
577
|
-
const logger = manager?.logger;
|
|
578
|
-
const prepared = [];
|
|
579
|
-
for (const plan of group) {
|
|
580
|
-
const gridOrder = plan?.gridOrder;
|
|
581
|
-
if (!gridOrder || !gridOrder.id)
|
|
582
|
-
continue;
|
|
583
|
-
const currentSlot = manager.orders.get(gridOrder.id);
|
|
584
|
-
if (currentSlot?.orderId) {
|
|
585
|
-
logger?.log?.(`Startup: Skip create ${plan.orderLabel} - slot ${gridOrder.id} already has orderId ${currentSlot.orderId}`, 'warn');
|
|
586
|
-
continue;
|
|
587
|
-
}
|
|
588
|
-
const { amountToSell, sellAssetId, minToReceive, receiveAssetId } = buildCreateOrderArgs(gridOrder, manager.assets.assetA, manager.assets.assetB);
|
|
589
|
-
const buildResult = await chainOrders.buildCreateOrderOp(account, amountToSell, sellAssetId, minToReceive, receiveAssetId, null);
|
|
590
|
-
if (!buildResult) {
|
|
591
|
-
logger?.log?.(`Startup: Skip create ${plan.orderLabel} - order amounts too small to place on-chain`, 'warn');
|
|
592
|
-
continue;
|
|
593
|
-
}
|
|
594
|
-
prepared.push({ plan, op: buildResult.op });
|
|
595
|
-
}
|
|
596
|
-
if (prepared.length === 0)
|
|
597
|
-
return;
|
|
598
|
-
const recovery = _resolveGroupRecovery(group, `Startup: Triggering recovery sync after create group ${groupIndex + 1}/${totalGroups} failure`, 'startupCreateGroupFailure');
|
|
599
|
-
try {
|
|
600
|
-
logger?.log?.(`Startup: Broadcasting create group ${groupIndex + 1}/${totalGroups} in single batch (${prepared.length} op${prepared.length > 1 ? 's' : ''})`, 'info');
|
|
601
|
-
const batchResult = await chainOrders.executeBatch(account, privateKey, prepared.map(p => p.op));
|
|
602
|
-
const opResults = _extractBatchOperationResults(batchResult);
|
|
603
|
-
const btsFeeData = getAssetFees('BTS');
|
|
604
|
-
let missingChainOrderId = false;
|
|
605
|
-
for (let i = 0; i < prepared.length; i++) {
|
|
606
|
-
const chainOrderId = opResults[i] && opResults[i][1];
|
|
607
|
-
const plan = prepared[i].plan;
|
|
608
|
-
if (!chainOrderId) {
|
|
609
|
-
logger?.log?.(`Startup: create result missing chainOrderId for ${plan.orderLabel}`, 'error');
|
|
610
|
-
missingChainOrderId = true;
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
await manager._applySync({
|
|
614
|
-
gridOrderId: plan.gridOrder.id,
|
|
615
|
-
chainOrderId,
|
|
616
|
-
isPartialPlacement: false,
|
|
617
|
-
fee: btsFeeData.createFee
|
|
618
|
-
}, 'createOrder', { gridLockAlreadyHeld: true });
|
|
619
|
-
}
|
|
620
|
-
if (missingChainOrderId) {
|
|
621
|
-
await _recoverStartupSyncFailure({
|
|
622
|
-
chainOrders,
|
|
623
|
-
manager,
|
|
624
|
-
account,
|
|
625
|
-
logger,
|
|
626
|
-
triggerMessage: recovery.triggerMessage,
|
|
627
|
-
source: recovery.source,
|
|
628
|
-
fillLockAlreadyHeld,
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
catch (err) {
|
|
633
|
-
logger?.log?.(`Startup: Failed to create group ${groupIndex + 1}/${totalGroups}: ${err.message}`, 'error');
|
|
634
|
-
await _recoverStartupSyncFailure({
|
|
635
|
-
chainOrders,
|
|
636
|
-
manager,
|
|
637
|
-
account,
|
|
638
|
-
logger,
|
|
639
|
-
triggerMessage: recovery.triggerMessage,
|
|
640
|
-
source: recovery.source,
|
|
641
|
-
fillLockAlreadyHeld,
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
function _buildOutsideInCreateGroups(createPlans) {
|
|
646
|
-
return buildOutsideInPairGroups(createPlans, {
|
|
647
|
-
isValid: p => Boolean(p?.gridOrder),
|
|
648
|
-
getType: p => p.orderType,
|
|
649
|
-
getPrice: p => p.gridOrder?.price,
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
async function _executePlannedStartupCreates({ createPlans, chainOrders, account, privateKey, manager, dryRun, fillLockAlreadyHeld = false, }) {
|
|
653
|
-
const logger = manager?.logger;
|
|
654
|
-
const groups = _buildOutsideInCreateGroups(createPlans);
|
|
655
|
-
if (groups.length === 0)
|
|
656
|
-
return;
|
|
657
|
-
logger?.log?.(`Startup: Executing ${createPlans.length} planned create(s) in ${groups.length} outside->center group(s)`, 'info');
|
|
658
|
-
for (let i = 0; i < groups.length; i++) {
|
|
659
|
-
const group = groups[i];
|
|
660
|
-
const labels = group.map(p => `${p.orderType.toUpperCase()}:${p.gridOrder?.id}`).join(', ');
|
|
661
|
-
logger?.log?.(`Startup: Create group ${i + 1}/${groups.length} (${labels})`, 'info');
|
|
662
|
-
const canBatchCreate = typeof chainOrders?.buildCreateOrderOp === 'function' && typeof chainOrders?.executeBatch === 'function';
|
|
663
|
-
if (group.length > 1 && canBatchCreate) {
|
|
664
|
-
await _executeStartupCreateGroupBatch({
|
|
665
|
-
group,
|
|
666
|
-
chainOrders,
|
|
667
|
-
account,
|
|
668
|
-
privateKey,
|
|
669
|
-
manager,
|
|
670
|
-
dryRun,
|
|
671
|
-
groupIndex: i,
|
|
672
|
-
totalGroups: groups.length,
|
|
673
|
-
fillLockAlreadyHeld,
|
|
674
|
-
});
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
if (group.length > 1 && !canBatchCreate) {
|
|
678
|
-
logger?.log?.('Startup: Batch create helpers unavailable; falling back to sequential creates for this group', 'warn');
|
|
679
|
-
}
|
|
680
|
-
for (const plan of group) {
|
|
681
|
-
await _createStartupOrderWithHandling({
|
|
682
|
-
chainOrders,
|
|
683
|
-
account,
|
|
684
|
-
privateKey,
|
|
685
|
-
manager,
|
|
686
|
-
gridOrder: plan.gridOrder,
|
|
687
|
-
orderLabel: plan.orderLabel,
|
|
688
|
-
dryRun,
|
|
689
|
-
recovery: plan.recovery,
|
|
690
|
-
fillLockAlreadyHeld,
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
async function _reconcileStartupSide({ orderType, targetCount, chainSideOrders, unmatchedSideOrders, manager, chainOrders, account, privateKey, dryRun, plannedCreates, plannedUpdates, fillLockAlreadyHeld = false, }) {
|
|
696
|
-
const logger = manager?.logger;
|
|
697
|
-
const sideUpper = orderType === ORDER_TYPES.SELL ? 'SELL' : 'BUY';
|
|
698
|
-
const balanceKey = orderType === ORDER_TYPES.SELL ? 'sellFree' : 'buyFree';
|
|
699
|
-
const balanceSymbol = orderType === ORDER_TYPES.SELL ? manager.assets.assetA.symbol : manager.assets.assetB.symbol;
|
|
700
|
-
const { sortUpdateComparator, sortExcessCancelComparator, sortMatchedCancelComparator, } = _getStartupSideComparators(orderType, manager.assets);
|
|
701
|
-
const matchedOnGrid = _countActiveOnGrid(manager, orderType);
|
|
702
|
-
const neededSlots = Math.max(0, targetCount - matchedOnGrid);
|
|
703
|
-
const desiredSlots = _pickVirtualSlotsToActivate(manager, orderType, neededSlots);
|
|
704
|
-
const sortedUnmatched = unmatchedSideOrders.slice(0).sort(sortUpdateComparator);
|
|
705
|
-
const updateCount = Math.min(sortedUnmatched.length, desiredSlots.length);
|
|
706
|
-
let cancelledIndex = null;
|
|
707
|
-
let projectedSideBalance = Number(manager.accountTotals?.[balanceKey] || 0);
|
|
708
|
-
logger?.log?.(`Startup ${sideUpper}: matchedOnGrid=${matchedOnGrid}, needSlots=${neededSlots}, unmatched=${sortedUnmatched.length}, updates=${updateCount}`, 'info');
|
|
709
|
-
if (updateCount > 0 && _isGridEdgeFullyActive(manager, orderType, updateCount)) {
|
|
710
|
-
logger?.log?.(`Startup: ${sideUpper} grid edge is fully active, cancelling largest order to free funds`, 'info');
|
|
711
|
-
const cancelInfo = await _cancelLargestOrder({
|
|
712
|
-
chainOrders,
|
|
713
|
-
account,
|
|
714
|
-
privateKey,
|
|
715
|
-
manager,
|
|
716
|
-
unmatchedOrders: sortedUnmatched,
|
|
717
|
-
updateCount,
|
|
718
|
-
orderType,
|
|
719
|
-
dryRun,
|
|
720
|
-
fillLockAlreadyHeld,
|
|
721
|
-
});
|
|
722
|
-
if (cancelInfo)
|
|
723
|
-
cancelledIndex = cancelInfo.index;
|
|
724
|
-
}
|
|
725
|
-
for (let i = 0; i < updateCount; i++) {
|
|
726
|
-
if (cancelledIndex !== null && i === cancelledIndex)
|
|
727
|
-
continue;
|
|
728
|
-
const chainOrder = sortedUnmatched[i];
|
|
729
|
-
const gridOrder = desiredSlots[i];
|
|
730
|
-
const gridSize = Number(gridOrder.size) || 0;
|
|
731
|
-
const parsedChain = parseChainOrder(chainOrder, manager.assets);
|
|
732
|
-
const currentSize = parsedChain ? parsedChain.size : 0;
|
|
733
|
-
const sizeIncrease = Math.max(0, gridSize - currentSize);
|
|
734
|
-
const currentAssetBalance = projectedSideBalance;
|
|
735
|
-
if (sizeIncrease > currentAssetBalance) {
|
|
736
|
-
logger?.log?.(`Startup: Skipping ${sideUpper} update ${chainOrder.id} - insufficient balance for increase (need +${Format.formatSizeByOrderType(sizeIncrease, orderType, manager.assets)} ${balanceSymbol}, have ${Format.formatSizeByOrderType(currentAssetBalance, orderType, manager.assets)} ${balanceSymbol})`, 'warn');
|
|
737
|
-
continue;
|
|
738
|
-
}
|
|
739
|
-
logger?.log?.(`Startup: Updating chain ${sideUpper} ${chainOrder.id} -> grid ${gridOrder.id} (price=${Format.formatPrice6(gridOrder.price)}, size=${Format.formatSizeByOrderType(gridOrder.size, orderType, manager.assets)})`, 'info');
|
|
740
|
-
plannedUpdates.push({
|
|
741
|
-
orderType,
|
|
742
|
-
chainOrderId: chainOrder.id,
|
|
743
|
-
chainOrderObj: chainOrder,
|
|
744
|
-
gridOrderId: gridOrder.id,
|
|
745
|
-
gridOrder: { ...gridOrder },
|
|
746
|
-
});
|
|
747
|
-
projectedSideBalance += (currentSize - gridSize);
|
|
748
|
-
}
|
|
749
|
-
if (cancelledIndex !== null && !dryRun) {
|
|
750
|
-
const targetGridOrder = desiredSlots[cancelledIndex];
|
|
751
|
-
if (targetGridOrder) {
|
|
752
|
-
logger?.log?.(`Startup: Creating new ${sideUpper} for cancelled slot at grid ${targetGridOrder.id} (price=${Format.formatPrice6(targetGridOrder.price)}, size=${Format.formatSizeByOrderType(targetGridOrder.size, orderType, manager.assets)})`, 'info');
|
|
753
|
-
plannedCreates.push({
|
|
754
|
-
orderType,
|
|
755
|
-
gridOrder: targetGridOrder,
|
|
756
|
-
orderLabel: `${sideUpper} for cancelled slot`,
|
|
757
|
-
recovery: {
|
|
758
|
-
triggerMessage: `Startup: Triggering recovery sync after ${sideUpper} creation failure`,
|
|
759
|
-
source: 'phase3CreationFailure',
|
|
760
|
-
},
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
const processedUnmatched = sortedUnmatched.slice(updateCount);
|
|
765
|
-
const chainCount = chainSideOrders.length;
|
|
766
|
-
const createCount = Math.max(0, targetCount - chainCount);
|
|
767
|
-
const remainingSlots = desiredSlots.slice(updateCount);
|
|
768
|
-
for (let i = 0; i < Math.min(createCount, remainingSlots.length); i++) {
|
|
769
|
-
const gridOrder = remainingSlots[i];
|
|
770
|
-
logger?.log?.(`Startup: Creating ${sideUpper} for grid ${gridOrder.id} (price=${Format.formatPrice6(gridOrder.price)}, size=${Format.formatSizeByOrderType(gridOrder.size, orderType, manager.assets)})`, 'info');
|
|
771
|
-
plannedCreates.push({
|
|
772
|
-
orderType,
|
|
773
|
-
gridOrder,
|
|
774
|
-
orderLabel: sideUpper,
|
|
775
|
-
// Intentionally no recovery metadata here.
|
|
776
|
-
// This preserves legacy behavior: regular virtual-slot creates only log failures,
|
|
777
|
-
// while cancelled-slot replacement creates trigger a startup recovery sync.
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
let cancelCount = Math.max(0, chainCount - targetCount);
|
|
781
|
-
if (cancelCount > 0) {
|
|
782
|
-
const parsedUnmatched = processedUnmatched
|
|
783
|
-
.map(co => ({ chain: co, parsed: parseChainOrder(co, manager.assets) }))
|
|
784
|
-
.filter(x => x.parsed)
|
|
785
|
-
.sort(sortExcessCancelComparator);
|
|
786
|
-
for (const x of parsedUnmatched) {
|
|
787
|
-
if (cancelCount <= 0)
|
|
788
|
-
break;
|
|
789
|
-
logger?.log?.(`Startup: Cancelling excess ${sideUpper} chain order ${x.chain.id}`, 'info');
|
|
790
|
-
try {
|
|
791
|
-
await _cancelChainOrder({
|
|
792
|
-
chainOrders,
|
|
793
|
-
account,
|
|
794
|
-
privateKey,
|
|
795
|
-
manager,
|
|
796
|
-
chainOrderId: x.chain.id,
|
|
797
|
-
chainOrderObj: x.chain,
|
|
798
|
-
releaseUntrackedFunds: true,
|
|
799
|
-
dryRun,
|
|
800
|
-
fillLockAlreadyHeld,
|
|
801
|
-
});
|
|
802
|
-
logger?.log?.(`Startup: Successfully cancelled excess ${sideUpper} order ${x.chain.id}`, 'info');
|
|
803
|
-
cancelCount--;
|
|
804
|
-
}
|
|
805
|
-
catch (err) {
|
|
806
|
-
logger?.log?.(`Startup: Failed to cancel ${sideUpper} ${x.chain.id}: ${err.message}`, 'error');
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
if (cancelCount > 0) {
|
|
810
|
-
const activeOrders = manager.getOrdersByTypeAndState(orderType, ORDER_STATES.ACTIVE)
|
|
811
|
-
.filter(o => o && o.orderId)
|
|
812
|
-
.sort(sortMatchedCancelComparator);
|
|
813
|
-
for (const o of activeOrders) {
|
|
814
|
-
if (cancelCount <= 0)
|
|
815
|
-
break;
|
|
816
|
-
logger?.log?.(`Startup: Cancelling excess matched ${sideUpper} ${o.orderId} (grid ${o.id})`, 'warn');
|
|
817
|
-
try {
|
|
818
|
-
await _cancelChainOrder({ chainOrders, account, privateKey, manager, chainOrderId: o.orderId, dryRun, chainOrderObj: o, fillLockAlreadyHeld });
|
|
819
|
-
logger?.log?.(`Startup: Successfully cancelled excess matched ${sideUpper} order ${o.orderId} (grid ${o.id})`, 'info');
|
|
820
|
-
cancelCount--;
|
|
821
|
-
}
|
|
822
|
-
catch (err) {
|
|
823
|
-
logger?.log?.(`Startup: Failed to cancel matched ${sideUpper} ${o.orderId}: ${err.message}`, 'error');
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
return {
|
|
829
|
-
chainCount,
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* Attempt to resume a persisted grid when orderIds don't match (e.g. orders.json out of sync),
|
|
834
|
-
* by matching existing on-chain open orders to grid orders using price+size matching.
|
|
835
|
-
*
|
|
836
10
|
* Returns { resumed: boolean, matchedCount: number }.
|
|
837
11
|
* @param {Object} params - Destructured parameters
|
|
838
12
|
* @param {Object} params.manager - OrderManager instance
|
|
@@ -842,7 +16,7 @@ async function _reconcileStartupSide({ orderType, targetCount, chainSideOrders,
|
|
|
842
16
|
* @param {Function} params.storeGrid - Grid persistence callback
|
|
843
17
|
* @returns {Promise<{resumed: boolean, matchedCount: number}>}
|
|
844
18
|
*/
|
|
845
|
-
async function attemptResumePersistedGridByPriceMatch({ manager, persistedGrid, chainOpenOrders, logger, storeGrid,
|
|
19
|
+
async function attemptResumePersistedGridByPriceMatch({ manager, persistedGrid, chainOpenOrders, logger, storeGrid, }) {
|
|
846
20
|
if (!Array.isArray(persistedGrid) || persistedGrid.length === 0)
|
|
847
21
|
return { resumed: false, matchedCount: 0 };
|
|
848
22
|
if (!Array.isArray(chainOpenOrders) || chainOpenOrders.length === 0)
|
|
@@ -851,9 +25,9 @@ async function attemptResumePersistedGridByPriceMatch({ manager, persistedGrid,
|
|
|
851
25
|
return { resumed: false, matchedCount: 0 };
|
|
852
26
|
try {
|
|
853
27
|
logger && logger.log && logger.log('No matching active order IDs found. Attempting to match by price...', 'info');
|
|
854
|
-
const
|
|
855
|
-
await
|
|
856
|
-
await manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders', {
|
|
28
|
+
const { loadGrid } = require('./grid');
|
|
29
|
+
await loadGrid(manager, persistedGrid);
|
|
30
|
+
await manager.synchronizeWithChain(chainOpenOrders, 'readOpenOrders', {});
|
|
857
31
|
const matchedOrderIds = new Set(Array.from(manager.orders.values())
|
|
858
32
|
.filter((o) => o && isOrderOnChain(o))
|
|
859
33
|
.map((o) => o.orderId)
|
|
@@ -890,7 +64,7 @@ async function attemptResumePersistedGridByPriceMatch({ manager, persistedGrid,
|
|
|
890
64
|
* @param {Function} [params.attemptResumeFn=attemptResumePersistedGridByPriceMatch] - Resume function
|
|
891
65
|
* @returns {Promise<Object>} { shouldRegenerate, hasActiveMatch, resumedByPrice, matchedCount }
|
|
892
66
|
*/
|
|
893
|
-
async function decideStartupGridAction({ persistedGrid, chainOpenOrders, manager, logger, storeGrid, attemptResumeFn = attemptResumePersistedGridByPriceMatch,
|
|
67
|
+
async function decideStartupGridAction({ persistedGrid, chainOpenOrders, manager, logger, storeGrid, attemptResumeFn = attemptResumePersistedGridByPriceMatch, }) {
|
|
894
68
|
const persisted = Array.isArray(persistedGrid) ? persistedGrid : [];
|
|
895
69
|
const chain = Array.isArray(chainOpenOrders) ? chainOpenOrders : [];
|
|
896
70
|
if (persisted.length === 0) {
|
|
@@ -902,7 +76,7 @@ async function decideStartupGridAction({ persistedGrid, chainOpenOrders, manager
|
|
|
902
76
|
return { shouldRegenerate: false, hasActiveMatch: true, resumedByPrice: false, matchedCount: 0 };
|
|
903
77
|
}
|
|
904
78
|
if (chain.length > 0) {
|
|
905
|
-
const resume = await attemptResumeFn({ manager, persistedGrid: persisted, chainOpenOrders: chain, logger, storeGrid
|
|
79
|
+
const resume = await attemptResumeFn({ manager, persistedGrid: persisted, chainOpenOrders: chain, logger, storeGrid });
|
|
906
80
|
return { shouldRegenerate: !resume.resumed, hasActiveMatch: false, resumedByPrice: !!resume.resumed, matchedCount: resume.matchedCount || 0 };
|
|
907
81
|
}
|
|
908
82
|
return { shouldRegenerate: true, hasActiveMatch: false, resumedByPrice: false, matchedCount: 0 };
|
|
@@ -926,7 +100,7 @@ async function decideStartupGridAction({ persistedGrid, chainOpenOrders, manager
|
|
|
926
100
|
* @param {Array<Object>} params.chainOpenOrders - On-chain open orders
|
|
927
101
|
* @returns {Promise<Object>} Reconciliation result
|
|
928
102
|
*/
|
|
929
|
-
async function reconcileGridOrders({ manager, config, account, privateKey, chainOrders, chainOpenOrders,
|
|
103
|
+
async function reconcileGridOrders({ manager, config, account, privateKey, chainOrders, chainOpenOrders, }) {
|
|
930
104
|
// Parameter validation
|
|
931
105
|
if (!manager || typeof manager.synchronizeWithChain !== 'function') {
|
|
932
106
|
throw new Error('reconcileGridOrders: manager must be provided with synchronizeWithChain method');
|
|
@@ -1026,7 +200,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1026
200
|
dryRun,
|
|
1027
201
|
chainOrderObj: u.chain,
|
|
1028
202
|
releaseUntrackedFunds: true,
|
|
1029
|
-
fillLockAlreadyHeld,
|
|
1030
203
|
});
|
|
1031
204
|
cancelledDuplicateIds.add(p.orderId);
|
|
1032
205
|
logger?.log?.(`Cancelled unmatched duplicate chain order ${p.orderId} at price ${Format.formatPrice6(p.price)} — ` +
|
|
@@ -1067,7 +240,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1067
240
|
dryRun,
|
|
1068
241
|
plannedCreates,
|
|
1069
242
|
plannedUpdates,
|
|
1070
|
-
fillLockAlreadyHeld,
|
|
1071
243
|
});
|
|
1072
244
|
const buyResult = await _reconcileStartupSide({
|
|
1073
245
|
orderType: ORDER_TYPES.BUY,
|
|
@@ -1081,7 +253,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1081
253
|
dryRun,
|
|
1082
254
|
plannedCreates,
|
|
1083
255
|
plannedUpdates,
|
|
1084
|
-
fillLockAlreadyHeld,
|
|
1085
256
|
});
|
|
1086
257
|
return { plannedCreates, plannedUpdates, chainSellCount: sellResult.chainCount, chainBuyCount: buyResult.chainCount };
|
|
1087
258
|
});
|
|
@@ -1119,7 +290,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1119
290
|
logger,
|
|
1120
291
|
triggerMessage: `Startup: Triggering recovery sync after update batch failure (attempt ${attempt}/${maxBatchAttempts})`,
|
|
1121
292
|
source: 'startupReconcileUpdateBatchFailure',
|
|
1122
|
-
fillLockAlreadyHeld,
|
|
1123
293
|
});
|
|
1124
294
|
updatePlans = _refreshStartupUpdatePlans(updatePlans, refreshedChainOrders);
|
|
1125
295
|
if (updatePlans.length === 0) {
|
|
@@ -1146,7 +316,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1146
316
|
privateKey,
|
|
1147
317
|
manager,
|
|
1148
318
|
dryRun,
|
|
1149
|
-
fillLockAlreadyHeld,
|
|
1150
319
|
});
|
|
1151
320
|
if (fallbackResult.executed > 0 || fallbackResult.skipped > 0 || fallbackResult.failed > 0) {
|
|
1152
321
|
logger?.log?.(`Startup: Sequential fallback complete (updated=${fallbackResult.executed}, skipped=${fallbackResult.skipped}, failed=${fallbackResult.failed})`, fallbackResult.failed > 0 ? 'warn' : 'info');
|
|
@@ -1165,7 +334,6 @@ async function reconcileGridOrders({ manager, config, account, privateKey, chain
|
|
|
1165
334
|
privateKey,
|
|
1166
335
|
manager,
|
|
1167
336
|
dryRun,
|
|
1168
|
-
fillLockAlreadyHeld,
|
|
1169
337
|
});
|
|
1170
338
|
}
|
|
1171
339
|
let finalChainSellCount = chainSellCount;
|