dexbot 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (807) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/claw/README.md +513 -0
  4. package/claw/scripts/memu_runner.py +385 -0
  5. package/claw/skills/bitshares-guide/SKILL.md +14 -0
  6. package/claw/skills/bitshares-guide/references/example-skill-shells.md +18 -0
  7. package/claw/skills/bitshares-guide/references/presentation-patterns.md +19 -0
  8. package/claw/skills/bitshares-guide/references/scope-guardrails.md +9 -0
  9. package/claw/skills/launcher-ops/SKILL.md +18 -0
  10. package/claw/skills/launcher-ops/references/launcher-workflow.md +23 -0
  11. package/claw/skills/margin-trading/SKILL.md +23 -0
  12. package/claw/skills/margin-trading/references/honest-asset-list.md +74 -0
  13. package/claw/skills/margin-trading/references/honest-assets.md +44 -0
  14. package/claw/skills/margin-trading/references/position-management.md +203 -0
  15. package/claw/skills/margin-trading/references/trading-concepts.md +80 -0
  16. package/claw/skills/memu-memory/SKILL.md +218 -0
  17. package/claw/skills/shared/references/js-automation-overview.md +24 -0
  18. package/claw/skills/shared/references/safety-and-staleness.md +11 -0
  19. package/claw/skills/shared/references/skill-boundaries.md +17 -0
  20. package/claw/skills/trend-detection/SKILL.md +33 -0
  21. package/claw/skills/trend-detection/agents/openai.yaml +3 -0
  22. package/claw/skills/trend-detection/references/service.md +45 -0
  23. package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts +3 -0
  24. package/dist/analysis/ama_fitting/analyze_ama_price_changes.d.ts.map +1 -0
  25. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js +149 -0
  26. package/dist/analysis/ama_fitting/analyze_ama_price_changes.js.map +1 -0
  27. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts +3 -0
  28. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.d.ts.map +1 -0
  29. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js +387 -0
  30. package/dist/analysis/ama_fitting/analyze_lambda_vs_slow.js.map +1 -0
  31. package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts +3 -0
  32. package/dist/analysis/ama_fitting/calibrate_convergence_er.d.ts.map +1 -0
  33. package/dist/analysis/ama_fitting/calibrate_convergence_er.js +200 -0
  34. package/dist/analysis/ama_fitting/calibrate_convergence_er.js.map +1 -0
  35. package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts +3 -0
  36. package/dist/analysis/ama_fitting/fetch_lp_candles.d.ts.map +1 -0
  37. package/dist/analysis/ama_fitting/fetch_lp_candles.js +212 -0
  38. package/dist/analysis/ama_fitting/fetch_lp_candles.js.map +1 -0
  39. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts +31 -0
  40. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.d.ts.map +1 -0
  41. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js +249 -0
  42. package/dist/analysis/ama_fitting/generate_unified_comparison_chart.js.map +1 -0
  43. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts +2 -0
  44. package/dist/analysis/ama_fitting/optimizer_high_resolution.d.ts.map +1 -0
  45. package/dist/analysis/ama_fitting/optimizer_high_resolution.js +782 -0
  46. package/dist/analysis/ama_fitting/optimizer_high_resolution.js.map +1 -0
  47. package/dist/analysis/analyze_derivatives.d.ts +95 -0
  48. package/dist/analysis/analyze_derivatives.d.ts.map +1 -0
  49. package/dist/analysis/analyze_derivatives.js +295 -0
  50. package/dist/analysis/analyze_derivatives.js.map +1 -0
  51. package/dist/analysis/analyze_dynamic_weight.d.ts +14 -0
  52. package/dist/analysis/analyze_dynamic_weight.d.ts.map +1 -0
  53. package/dist/analysis/analyze_dynamic_weight.js +208 -0
  54. package/dist/analysis/analyze_dynamic_weight.js.map +1 -0
  55. package/dist/analysis/analyze_kalman.d.ts +13 -0
  56. package/dist/analysis/analyze_kalman.d.ts.map +1 -0
  57. package/dist/analysis/analyze_kalman.js +123 -0
  58. package/dist/analysis/analyze_kalman.js.map +1 -0
  59. package/dist/analysis/analyze_regime.d.ts +20 -0
  60. package/dist/analysis/analyze_regime.d.ts.map +1 -0
  61. package/dist/analysis/analyze_regime.js +139 -0
  62. package/dist/analysis/analyze_regime.js.map +1 -0
  63. package/dist/analysis/analyze_regime_windows.d.ts +19 -0
  64. package/dist/analysis/analyze_regime_windows.d.ts.map +1 -0
  65. package/dist/analysis/analyze_regime_windows.js +413 -0
  66. package/dist/analysis/analyze_regime_windows.js.map +1 -0
  67. package/dist/analysis/analyze_risk_profile.d.ts +3 -0
  68. package/dist/analysis/analyze_risk_profile.d.ts.map +1 -0
  69. package/dist/analysis/analyze_risk_profile.js +159 -0
  70. package/dist/analysis/analyze_risk_profile.js.map +1 -0
  71. package/dist/analysis/analyze_trade_heatmap.d.ts +3 -0
  72. package/dist/analysis/analyze_trade_heatmap.d.ts.map +1 -0
  73. package/dist/analysis/analyze_trade_heatmap.js +367 -0
  74. package/dist/analysis/analyze_trade_heatmap.js.map +1 -0
  75. package/dist/analysis/analyze_volatility.d.ts +22 -0
  76. package/dist/analysis/analyze_volatility.d.ts.map +1 -0
  77. package/dist/analysis/analyze_volatility.js +172 -0
  78. package/dist/analysis/analyze_volatility.js.map +1 -0
  79. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts +99 -0
  80. package/dist/analysis/bot_fitting/backtest_ama_sweep.d.ts.map +1 -0
  81. package/dist/analysis/bot_fitting/backtest_ama_sweep.js +804 -0
  82. package/dist/analysis/bot_fitting/backtest_ama_sweep.js.map +1 -0
  83. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts +2 -0
  84. package/dist/analysis/bot_fitting/backtest_bot_fitting.d.ts.map +1 -0
  85. package/dist/analysis/bot_fitting/backtest_bot_fitting.js +347 -0
  86. package/dist/analysis/bot_fitting/backtest_bot_fitting.js.map +1 -0
  87. package/dist/analysis/bot_fitting/shared_utils.d.ts +15 -0
  88. package/dist/analysis/bot_fitting/shared_utils.d.ts.map +1 -0
  89. package/dist/analysis/bot_fitting/shared_utils.js +48 -0
  90. package/dist/analysis/bot_fitting/shared_utils.js.map +1 -0
  91. package/dist/analysis/bot_key_utils.d.ts +16 -0
  92. package/dist/analysis/bot_key_utils.d.ts.map +1 -0
  93. package/dist/analysis/bot_key_utils.js +61 -0
  94. package/dist/analysis/bot_key_utils.js.map +1 -0
  95. package/dist/analysis/bot_usage/discover_bot_accounts.d.ts +3 -0
  96. package/dist/analysis/bot_usage/discover_bot_accounts.d.ts.map +1 -0
  97. package/dist/analysis/bot_usage/discover_bot_accounts.js +498 -0
  98. package/dist/analysis/bot_usage/discover_bot_accounts.js.map +1 -0
  99. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +546 -0
  100. package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -0
  101. package/dist/analysis/bot_usage/kibana_bot_queries.js +427 -0
  102. package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -0
  103. package/dist/analysis/chart_utils.d.ts +13 -0
  104. package/dist/analysis/chart_utils.d.ts.map +1 -0
  105. package/dist/analysis/chart_utils.js +142 -0
  106. package/dist/analysis/chart_utils.js.map +1 -0
  107. package/dist/analysis/derivative_chart_generator.d.ts +18 -0
  108. package/dist/analysis/derivative_chart_generator.d.ts.map +1 -0
  109. package/dist/analysis/derivative_chart_generator.js +896 -0
  110. package/dist/analysis/derivative_chart_generator.js.map +1 -0
  111. package/dist/analysis/math_utils.d.ts +35 -0
  112. package/dist/analysis/math_utils.d.ts.map +1 -0
  113. package/dist/analysis/math_utils.js +121 -0
  114. package/dist/analysis/math_utils.js.map +1 -0
  115. package/dist/analysis/price_sources.d.ts +34 -0
  116. package/dist/analysis/price_sources.d.ts.map +1 -0
  117. package/dist/analysis/price_sources.js +103 -0
  118. package/dist/analysis/price_sources.js.map +1 -0
  119. package/dist/analysis/trade_profitability.d.ts +115 -0
  120. package/dist/analysis/trade_profitability.d.ts.map +1 -0
  121. package/dist/analysis/trade_profitability.js +1247 -0
  122. package/dist/analysis/trade_profitability.js.map +1 -0
  123. package/dist/analysis/tradingview/analyze_tradingview.d.ts +38 -0
  124. package/dist/analysis/tradingview/analyze_tradingview.d.ts.map +1 -0
  125. package/dist/analysis/tradingview/analyze_tradingview.js +212 -0
  126. package/dist/analysis/tradingview/analyze_tradingview.js.map +1 -0
  127. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts +9 -0
  128. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.d.ts.map +1 -0
  129. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js +1717 -0
  130. package/dist/analysis/tradingview/tradingview_uplot_chart_generator.js.map +1 -0
  131. package/dist/analysis/trend_detection/derivative_analyzer.d.ts +250 -0
  132. package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +1 -0
  133. package/dist/analysis/trend_detection/derivative_analyzer.js +903 -0
  134. package/dist/analysis/trend_detection/derivative_analyzer.js.map +1 -0
  135. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts +6 -0
  136. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.d.ts.map +1 -0
  137. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js +1449 -0
  138. package/dist/analysis/trend_detection/dynamic_weight_chart_generator.js.map +1 -0
  139. package/dist/analysis/trend_detection/hurst_analyzer.d.ts +42 -0
  140. package/dist/analysis/trend_detection/hurst_analyzer.d.ts.map +1 -0
  141. package/dist/analysis/trend_detection/hurst_analyzer.js +159 -0
  142. package/dist/analysis/trend_detection/hurst_analyzer.js.map +1 -0
  143. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts +6 -0
  144. package/dist/analysis/trend_detection/kalman_chart_generator.d.ts.map +1 -0
  145. package/dist/analysis/trend_detection/kalman_chart_generator.js +408 -0
  146. package/dist/analysis/trend_detection/kalman_chart_generator.js.map +1 -0
  147. package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts +103 -0
  148. package/dist/analysis/trend_detection/kalman_trend_analyzer.d.ts.map +1 -0
  149. package/dist/analysis/trend_detection/kalman_trend_analyzer.js +229 -0
  150. package/dist/analysis/trend_detection/kalman_trend_analyzer.js.map +1 -0
  151. package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts +34 -0
  152. package/dist/analysis/trend_detection/kalman_velocity_smoothing.d.ts.map +1 -0
  153. package/dist/analysis/trend_detection/kalman_velocity_smoothing.js +89 -0
  154. package/dist/analysis/trend_detection/kalman_velocity_smoothing.js.map +1 -0
  155. package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts +39 -0
  156. package/dist/analysis/trend_detection/permutation_entropy_analyzer.d.ts.map +1 -0
  157. package/dist/analysis/trend_detection/permutation_entropy_analyzer.js +132 -0
  158. package/dist/analysis/trend_detection/permutation_entropy_analyzer.js.map +1 -0
  159. package/dist/analysis/trend_detection/regime_chart_generator.d.ts +6 -0
  160. package/dist/analysis/trend_detection/regime_chart_generator.d.ts.map +1 -0
  161. package/dist/analysis/trend_detection/regime_chart_generator.js +345 -0
  162. package/dist/analysis/trend_detection/regime_chart_generator.js.map +1 -0
  163. package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts +2 -0
  164. package/dist/analysis/trend_detection/tests/test_kalman_trend.d.ts.map +1 -0
  165. package/dist/analysis/trend_detection/tests/test_kalman_trend.js +130 -0
  166. package/dist/analysis/trend_detection/tests/test_kalman_trend.js.map +1 -0
  167. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts +2 -0
  168. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.d.ts.map +1 -0
  169. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js +38 -0
  170. package/dist/analysis/trend_detection/tests/test_kalman_velocity_smoothing.js.map +1 -0
  171. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts +6 -0
  172. package/dist/analysis/trend_detection/volatility_chart_generator.d.ts.map +1 -0
  173. package/dist/analysis/trend_detection/volatility_chart_generator.js +732 -0
  174. package/dist/analysis/trend_detection/volatility_chart_generator.js.map +1 -0
  175. package/dist/bot.d.ts +49 -0
  176. package/dist/bot.d.ts.map +1 -0
  177. package/dist/bot.js +221 -0
  178. package/dist/bot.js.map +1 -0
  179. package/dist/credential-daemon.d.ts +64 -0
  180. package/dist/credential-daemon.d.ts.map +1 -0
  181. package/dist/credential-daemon.js +987 -0
  182. package/dist/credential-daemon.js.map +1 -0
  183. package/dist/dexbot.d.ts +87 -0
  184. package/dist/dexbot.d.ts.map +1 -0
  185. package/dist/dexbot.js +1222 -0
  186. package/dist/dexbot.js.map +1 -0
  187. package/dist/market_adapter/ama_signal_runner.d.ts +3 -0
  188. package/dist/market_adapter/ama_signal_runner.d.ts.map +1 -0
  189. package/dist/market_adapter/ama_signal_runner.js +175 -0
  190. package/dist/market_adapter/ama_signal_runner.js.map +1 -0
  191. package/dist/market_adapter/candle_utils.d.ts +57 -0
  192. package/dist/market_adapter/candle_utils.d.ts.map +1 -0
  193. package/dist/market_adapter/candle_utils.js +252 -0
  194. package/dist/market_adapter/candle_utils.js.map +1 -0
  195. package/dist/market_adapter/core/asymmetric_bounds.d.ts +20 -0
  196. package/dist/market_adapter/core/asymmetric_bounds.d.ts.map +1 -0
  197. package/dist/market_adapter/core/asymmetric_bounds.js +88 -0
  198. package/dist/market_adapter/core/asymmetric_bounds.js.map +1 -0
  199. package/dist/market_adapter/core/config_normalizers.d.ts +10 -0
  200. package/dist/market_adapter/core/config_normalizers.d.ts.map +1 -0
  201. package/dist/market_adapter/core/config_normalizers.js +24 -0
  202. package/dist/market_adapter/core/config_normalizers.js.map +1 -0
  203. package/dist/market_adapter/core/kibana_candles.d.ts +74 -0
  204. package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -0
  205. package/dist/market_adapter/core/kibana_candles.js +249 -0
  206. package/dist/market_adapter/core/kibana_candles.js.map +1 -0
  207. package/dist/market_adapter/core/kibana_client.d.ts +27 -0
  208. package/dist/market_adapter/core/kibana_client.d.ts.map +1 -0
  209. package/dist/market_adapter/core/kibana_client.js +120 -0
  210. package/dist/market_adapter/core/kibana_client.js.map +1 -0
  211. package/dist/market_adapter/core/kibana_market_candles.d.ts +143 -0
  212. package/dist/market_adapter/core/kibana_market_candles.d.ts.map +1 -0
  213. package/dist/market_adapter/core/kibana_market_candles.js +170 -0
  214. package/dist/market_adapter/core/kibana_market_candles.js.map +1 -0
  215. package/dist/market_adapter/core/market_adapter_service.d.ts +210 -0
  216. package/dist/market_adapter/core/market_adapter_service.d.ts.map +1 -0
  217. package/dist/market_adapter/core/market_adapter_service.js +2328 -0
  218. package/dist/market_adapter/core/market_adapter_service.js.map +1 -0
  219. package/dist/market_adapter/core/strategies/ama.d.ts +2 -0
  220. package/dist/market_adapter/core/strategies/ama.d.ts.map +1 -0
  221. package/dist/market_adapter/core/strategies/ama.js +138 -0
  222. package/dist/market_adapter/core/strategies/ama.js.map +1 -0
  223. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts +50 -0
  224. package/dist/market_adapter/core/strategies/ama_slope_model.d.ts.map +1 -0
  225. package/dist/market_adapter/core/strategies/ama_slope_model.js +143 -0
  226. package/dist/market_adapter/core/strategies/ama_slope_model.js.map +1 -0
  227. package/dist/market_adapter/core/strategies/atr/calculator.d.ts +12 -0
  228. package/dist/market_adapter/core/strategies/atr/calculator.d.ts.map +1 -0
  229. package/dist/market_adapter/core/strategies/atr/calculator.js +47 -0
  230. package/dist/market_adapter/core/strategies/atr/calculator.js.map +1 -0
  231. package/dist/market_adapter/core/strategies/collateral_manager.d.ts +32 -0
  232. package/dist/market_adapter/core/strategies/collateral_manager.d.ts.map +1 -0
  233. package/dist/market_adapter/core/strategies/collateral_manager.js +68 -0
  234. package/dist/market_adapter/core/strategies/collateral_manager.js.map +1 -0
  235. package/dist/market_adapter/core/strategies/regime_gate.d.ts +66 -0
  236. package/dist/market_adapter/core/strategies/regime_gate.d.ts.map +1 -0
  237. package/dist/market_adapter/core/strategies/regime_gate.js +189 -0
  238. package/dist/market_adapter/core/strategies/regime_gate.js.map +1 -0
  239. package/dist/market_adapter/index.d.ts +10 -0
  240. package/dist/market_adapter/index.d.ts.map +1 -0
  241. package/dist/market_adapter/index.js +71 -0
  242. package/dist/market_adapter/index.js.map +1 -0
  243. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts +3 -0
  244. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.d.ts.map +1 -0
  245. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js +1165 -0
  246. package/dist/market_adapter/inputs/fetch_cex_synthetic_data.js.map +1 -0
  247. package/dist/market_adapter/inputs/fetch_lp_data.d.ts +54 -0
  248. package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -0
  249. package/dist/market_adapter/inputs/fetch_lp_data.js +743 -0
  250. package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -0
  251. package/dist/market_adapter/inputs/kibana_source.d.ts +68 -0
  252. package/dist/market_adapter/inputs/kibana_source.d.ts.map +1 -0
  253. package/dist/market_adapter/inputs/kibana_source.js +143 -0
  254. package/dist/market_adapter/inputs/kibana_source.js.map +1 -0
  255. package/dist/market_adapter/interval_utils.d.ts +6 -0
  256. package/dist/market_adapter/interval_utils.d.ts.map +1 -0
  257. package/dist/market_adapter/interval_utils.js +14 -0
  258. package/dist/market_adapter/interval_utils.js.map +1 -0
  259. package/dist/market_adapter/log_format.d.ts +16 -0
  260. package/dist/market_adapter/log_format.d.ts.map +1 -0
  261. package/dist/market_adapter/log_format.js +72 -0
  262. package/dist/market_adapter/log_format.js.map +1 -0
  263. package/dist/market_adapter/lp_chart_core.d.ts +6 -0
  264. package/dist/market_adapter/lp_chart_core.d.ts.map +1 -0
  265. package/dist/market_adapter/lp_chart_core.js +874 -0
  266. package/dist/market_adapter/lp_chart_core.js.map +1 -0
  267. package/dist/market_adapter/lp_chart_runner.d.ts +135 -0
  268. package/dist/market_adapter/lp_chart_runner.d.ts.map +1 -0
  269. package/dist/market_adapter/lp_chart_runner.js +337 -0
  270. package/dist/market_adapter/lp_chart_runner.js.map +1 -0
  271. package/dist/market_adapter/lp_chart_strategy_loader.d.ts +23 -0
  272. package/dist/market_adapter/lp_chart_strategy_loader.d.ts.map +1 -0
  273. package/dist/market_adapter/lp_chart_strategy_loader.js +169 -0
  274. package/dist/market_adapter/lp_chart_strategy_loader.js.map +1 -0
  275. package/dist/market_adapter/market_adapter.d.ts +133 -0
  276. package/dist/market_adapter/market_adapter.d.ts.map +1 -0
  277. package/dist/market_adapter/market_adapter.js +1331 -0
  278. package/dist/market_adapter/market_adapter.js.map +1 -0
  279. package/dist/market_adapter/merge_lp_data.d.ts +3 -0
  280. package/dist/market_adapter/merge_lp_data.d.ts.map +1 -0
  281. package/dist/market_adapter/merge_lp_data.js +125 -0
  282. package/dist/market_adapter/merge_lp_data.js.map +1 -0
  283. package/dist/market_adapter/test_helpers.d.ts +14 -0
  284. package/dist/market_adapter/test_helpers.d.ts.map +1 -0
  285. package/dist/market_adapter/test_helpers.js +22 -0
  286. package/dist/market_adapter/test_helpers.js.map +1 -0
  287. package/dist/market_adapter/utils/adapter_client.d.ts +20 -0
  288. package/dist/market_adapter/utils/adapter_client.d.ts.map +1 -0
  289. package/dist/market_adapter/utils/adapter_client.js +88 -0
  290. package/dist/market_adapter/utils/adapter_client.js.map +1 -0
  291. package/dist/market_adapter/utils/atomic_write.d.ts +9 -0
  292. package/dist/market_adapter/utils/atomic_write.d.ts.map +1 -0
  293. package/dist/market_adapter/utils/atomic_write.js +13 -0
  294. package/dist/market_adapter/utils/atomic_write.js.map +1 -0
  295. package/dist/market_adapter/utils/chain.d.ts +50 -0
  296. package/dist/market_adapter/utils/chain.d.ts.map +1 -0
  297. package/dist/market_adapter/utils/chain.js +196 -0
  298. package/dist/market_adapter/utils/chain.js.map +1 -0
  299. package/dist/market_adapter/utils/data_discovery.d.ts +6 -0
  300. package/dist/market_adapter/utils/data_discovery.d.ts.map +1 -0
  301. package/dist/market_adapter/utils/data_discovery.js +35 -0
  302. package/dist/market_adapter/utils/data_discovery.js.map +1 -0
  303. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts +15 -0
  304. package/dist/market_adapter/utils/dynamic_grid_snapshot.d.ts.map +1 -0
  305. package/dist/market_adapter/utils/dynamic_grid_snapshot.js +60 -0
  306. package/dist/market_adapter/utils/dynamic_grid_snapshot.js.map +1 -0
  307. package/dist/market_adapter/utils/file_lock.d.ts +19 -0
  308. package/dist/market_adapter/utils/file_lock.d.ts.map +1 -0
  309. package/dist/market_adapter/utils/file_lock.js +191 -0
  310. package/dist/market_adapter/utils/file_lock.js.map +1 -0
  311. package/dist/market_adapter/utils/native_history.d.ts +19 -0
  312. package/dist/market_adapter/utils/native_history.d.ts.map +1 -0
  313. package/dist/market_adapter/utils/native_history.js +172 -0
  314. package/dist/market_adapter/utils/native_history.js.map +1 -0
  315. package/dist/market_adapter/utils/paths.d.ts +5 -0
  316. package/dist/market_adapter/utils/paths.d.ts.map +1 -0
  317. package/dist/market_adapter/utils/paths.js +7 -0
  318. package/dist/market_adapter/utils/paths.js.map +1 -0
  319. package/dist/modules/account_bots.d.ts +112 -0
  320. package/dist/modules/account_bots.d.ts.map +1 -0
  321. package/dist/modules/account_bots.js +1259 -0
  322. package/dist/modules/account_bots.js.map +1 -0
  323. package/dist/modules/account_orders.d.ts +273 -0
  324. package/dist/modules/account_orders.d.ts.map +1 -0
  325. package/dist/modules/account_orders.js +662 -0
  326. package/dist/modules/account_orders.js.map +1 -0
  327. package/dist/modules/authority_resolver.d.ts +2 -0
  328. package/dist/modules/authority_resolver.d.ts.map +1 -0
  329. package/dist/modules/authority_resolver.js +231 -0
  330. package/dist/modules/authority_resolver.js.map +1 -0
  331. package/dist/modules/bitshares-native/chain_client.d.ts +38 -0
  332. package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -0
  333. package/dist/modules/bitshares-native/chain_client.js +322 -0
  334. package/dist/modules/bitshares-native/chain_client.js.map +1 -0
  335. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts +59 -0
  336. package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -0
  337. package/dist/modules/bitshares-native/crypto/ecc.browser.js +456 -0
  338. package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -0
  339. package/dist/modules/bitshares-native/crypto/ecc.d.ts +66 -0
  340. package/dist/modules/bitshares-native/crypto/ecc.d.ts.map +1 -0
  341. package/dist/modules/bitshares-native/crypto/ecc.js +557 -0
  342. package/dist/modules/bitshares-native/crypto/ecc.js.map +1 -0
  343. package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts +2 -0
  344. package/dist/modules/bitshares-native/crypto/ecc_selector.d.ts.map +1 -0
  345. package/dist/modules/bitshares-native/crypto/ecc_selector.js +20 -0
  346. package/dist/modules/bitshares-native/crypto/ecc_selector.js.map +1 -0
  347. package/dist/modules/bitshares-native/index.d.ts +3 -0
  348. package/dist/modules/bitshares-native/index.d.ts.map +1 -0
  349. package/dist/modules/bitshares-native/index.js +32 -0
  350. package/dist/modules/bitshares-native/index.js.map +1 -0
  351. package/dist/modules/bitshares-native/resolvers.d.ts +40 -0
  352. package/dist/modules/bitshares-native/resolvers.d.ts.map +1 -0
  353. package/dist/modules/bitshares-native/resolvers.js +161 -0
  354. package/dist/modules/bitshares-native/resolvers.js.map +1 -0
  355. package/dist/modules/bitshares-native/serial/chain_constants.d.ts +56 -0
  356. package/dist/modules/bitshares-native/serial/chain_constants.d.ts.map +1 -0
  357. package/dist/modules/bitshares-native/serial/chain_constants.js +92 -0
  358. package/dist/modules/bitshares-native/serial/chain_constants.js.map +1 -0
  359. package/dist/modules/bitshares-native/serial/index.d.ts +10 -0
  360. package/dist/modules/bitshares-native/serial/index.d.ts.map +1 -0
  361. package/dist/modules/bitshares-native/serial/index.js +14 -0
  362. package/dist/modules/bitshares-native/serial/index.js.map +1 -0
  363. package/dist/modules/bitshares-native/serial/operations.d.ts +102 -0
  364. package/dist/modules/bitshares-native/serial/operations.d.ts.map +1 -0
  365. package/dist/modules/bitshares-native/serial/operations.js +615 -0
  366. package/dist/modules/bitshares-native/serial/operations.js.map +1 -0
  367. package/dist/modules/bitshares-native/serial/serializer.d.ts +64 -0
  368. package/dist/modules/bitshares-native/serial/serializer.d.ts.map +1 -0
  369. package/dist/modules/bitshares-native/serial/serializer.js +252 -0
  370. package/dist/modules/bitshares-native/serial/serializer.js.map +1 -0
  371. package/dist/modules/bitshares-native/serial/types.d.ts +120 -0
  372. package/dist/modules/bitshares-native/serial/types.d.ts.map +1 -0
  373. package/dist/modules/bitshares-native/serial/types.js +837 -0
  374. package/dist/modules/bitshares-native/serial/types.js.map +1 -0
  375. package/dist/modules/bitshares-native/signing_client.d.ts +6 -0
  376. package/dist/modules/bitshares-native/signing_client.d.ts.map +1 -0
  377. package/dist/modules/bitshares-native/signing_client.js +141 -0
  378. package/dist/modules/bitshares-native/signing_client.js.map +1 -0
  379. package/dist/modules/bitshares-native/subscriptions.d.ts +6 -0
  380. package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -0
  381. package/dist/modules/bitshares-native/subscriptions.js +865 -0
  382. package/dist/modules/bitshares-native/subscriptions.js.map +1 -0
  383. package/dist/modules/bitshares-native/transport.d.ts +51 -0
  384. package/dist/modules/bitshares-native/transport.d.ts.map +1 -0
  385. package/dist/modules/bitshares-native/transport.js +447 -0
  386. package/dist/modules/bitshares-native/transport.js.map +1 -0
  387. package/dist/modules/bitshares-native/tx/builder.d.ts +30 -0
  388. package/dist/modules/bitshares-native/tx/builder.d.ts.map +1 -0
  389. package/dist/modules/bitshares-native/tx/builder.js +262 -0
  390. package/dist/modules/bitshares-native/tx/builder.js.map +1 -0
  391. package/dist/modules/bitshares_client.d.ts +129 -0
  392. package/dist/modules/bitshares_client.d.ts.map +1 -0
  393. package/dist/modules/bitshares_client.js +627 -0
  394. package/dist/modules/bitshares_client.js.map +1 -0
  395. package/dist/modules/bot_settings.d.ts +33 -0
  396. package/dist/modules/bot_settings.d.ts.map +1 -0
  397. package/dist/modules/bot_settings.js +328 -0
  398. package/dist/modules/bot_settings.js.map +1 -0
  399. package/dist/modules/bots_file_lock.d.ts +88 -0
  400. package/dist/modules/bots_file_lock.d.ts.map +1 -0
  401. package/dist/modules/bots_file_lock.js +161 -0
  402. package/dist/modules/bots_file_lock.js.map +1 -0
  403. package/dist/modules/chain_keys.d.ts +302 -0
  404. package/dist/modules/chain_keys.d.ts.map +1 -0
  405. package/dist/modules/chain_keys.js +1095 -0
  406. package/dist/modules/chain_keys.js.map +1 -0
  407. package/dist/modules/chain_orders.d.ts +423 -0
  408. package/dist/modules/chain_orders.d.ts.map +1 -0
  409. package/dist/modules/chain_orders.js +1201 -0
  410. package/dist/modules/chain_orders.js.map +1 -0
  411. package/dist/modules/cli_whitelist_args.d.ts +6 -0
  412. package/dist/modules/cli_whitelist_args.d.ts.map +1 -0
  413. package/dist/modules/cli_whitelist_args.js +13 -0
  414. package/dist/modules/cli_whitelist_args.js.map +1 -0
  415. package/dist/modules/config.d.ts +57 -0
  416. package/dist/modules/config.d.ts.map +1 -0
  417. package/dist/modules/config.js +107 -0
  418. package/dist/modules/config.js.map +1 -0
  419. package/dist/modules/constants.d.ts +635 -0
  420. package/dist/modules/constants.d.ts.map +1 -0
  421. package/dist/modules/constants.js +1464 -0
  422. package/dist/modules/constants.js.map +1 -0
  423. package/dist/modules/cr_planner.d.ts +51 -0
  424. package/dist/modules/cr_planner.d.ts.map +1 -0
  425. package/dist/modules/cr_planner.js +299 -0
  426. package/dist/modules/cr_planner.js.map +1 -0
  427. package/dist/modules/credential_policy.d.ts +188 -0
  428. package/dist/modules/credential_policy.d.ts.map +1 -0
  429. package/dist/modules/credential_policy.js +1452 -0
  430. package/dist/modules/credential_policy.js.map +1 -0
  431. package/dist/modules/credential_runtime.d.ts +42 -0
  432. package/dist/modules/credential_runtime.d.ts.map +1 -0
  433. package/dist/modules/credential_runtime.js +148 -0
  434. package/dist/modules/credential_runtime.js.map +1 -0
  435. package/dist/modules/credential_session_cache.d.ts +36 -0
  436. package/dist/modules/credential_session_cache.d.ts.map +1 -0
  437. package/dist/modules/credential_session_cache.js +106 -0
  438. package/dist/modules/credential_session_cache.js.map +1 -0
  439. package/dist/modules/credit_runtime.d.ts +412 -0
  440. package/dist/modules/credit_runtime.d.ts.map +1 -0
  441. package/dist/modules/credit_runtime.js +2866 -0
  442. package/dist/modules/credit_runtime.js.map +1 -0
  443. package/dist/modules/crypto/browser_provider.d.ts +15 -0
  444. package/dist/modules/crypto/browser_provider.d.ts.map +1 -0
  445. package/dist/modules/crypto/browser_provider.js +113 -0
  446. package/dist/modules/crypto/browser_provider.js.map +1 -0
  447. package/dist/modules/crypto/index.d.ts +9 -0
  448. package/dist/modules/crypto/index.d.ts.map +1 -0
  449. package/dist/modules/crypto/index.js +44 -0
  450. package/dist/modules/crypto/index.js.map +1 -0
  451. package/dist/modules/crypto/node_provider.d.ts +15 -0
  452. package/dist/modules/crypto/node_provider.d.ts.map +1 -0
  453. package/dist/modules/crypto/node_provider.js +74 -0
  454. package/dist/modules/crypto/node_provider.js.map +1 -0
  455. package/dist/modules/crypto/provider.d.ts +34 -0
  456. package/dist/modules/crypto/provider.d.ts.map +1 -0
  457. package/dist/modules/crypto/provider.js +3 -0
  458. package/dist/modules/crypto/provider.js.map +1 -0
  459. package/dist/modules/crypto/pure_ripemd160.d.ts +8 -0
  460. package/dist/modules/crypto/pure_ripemd160.d.ts.map +1 -0
  461. package/dist/modules/crypto/pure_ripemd160.js +142 -0
  462. package/dist/modules/crypto/pure_ripemd160.js.map +1 -0
  463. package/dist/modules/crypto/pure_scrypt.d.ts +23 -0
  464. package/dist/modules/crypto/pure_scrypt.d.ts.map +1 -0
  465. package/dist/modules/crypto/pure_scrypt.js +161 -0
  466. package/dist/modules/crypto/pure_scrypt.js.map +1 -0
  467. package/dist/modules/crypto/pure_secp256k1.d.ts +29 -0
  468. package/dist/modules/crypto/pure_secp256k1.d.ts.map +1 -0
  469. package/dist/modules/crypto/pure_secp256k1.js +195 -0
  470. package/dist/modules/crypto/pure_secp256k1.js.map +1 -0
  471. package/dist/modules/crypto/sync.d.ts +13 -0
  472. package/dist/modules/crypto/sync.d.ts.map +1 -0
  473. package/dist/modules/crypto/sync.js +37 -0
  474. package/dist/modules/crypto/sync.js.map +1 -0
  475. package/dist/modules/dexbot_class.d.ts +1074 -0
  476. package/dist/modules/dexbot_class.d.ts.map +1 -0
  477. package/dist/modules/dexbot_class.js +4613 -0
  478. package/dist/modules/dexbot_class.js.map +1 -0
  479. package/dist/modules/dexbot_credential_client.d.ts +71 -0
  480. package/dist/modules/dexbot_credential_client.d.ts.map +1 -0
  481. package/dist/modules/dexbot_credential_client.js +224 -0
  482. package/dist/modules/dexbot_credential_client.js.map +1 -0
  483. package/dist/modules/dexbot_fill_runtime.d.ts +173 -0
  484. package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -0
  485. package/dist/modules/dexbot_fill_runtime.js +217 -0
  486. package/dist/modules/dexbot_fill_runtime.js.map +1 -0
  487. package/dist/modules/dexbot_maintenance_runtime.d.ts +279 -0
  488. package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -0
  489. package/dist/modules/dexbot_maintenance_runtime.js +1613 -0
  490. package/dist/modules/dexbot_maintenance_runtime.js.map +1 -0
  491. package/dist/modules/env.d.ts +12 -0
  492. package/dist/modules/env.d.ts.map +1 -0
  493. package/dist/modules/env.js +57 -0
  494. package/dist/modules/env.js.map +1 -0
  495. package/dist/modules/fund_registry.d.ts +96 -0
  496. package/dist/modules/fund_registry.d.ts.map +1 -0
  497. package/dist/modules/fund_registry.js +261 -0
  498. package/dist/modules/fund_registry.js.map +1 -0
  499. package/dist/modules/general_settings.d.ts +36 -0
  500. package/dist/modules/general_settings.d.ts.map +1 -0
  501. package/dist/modules/general_settings.js +63 -0
  502. package/dist/modules/general_settings.js.map +1 -0
  503. package/dist/modules/graceful_shutdown.d.ts +86 -0
  504. package/dist/modules/graceful_shutdown.d.ts.map +1 -0
  505. package/dist/modules/graceful_shutdown.js +164 -0
  506. package/dist/modules/graceful_shutdown.js.map +1 -0
  507. package/dist/modules/key_store.d.ts +60 -0
  508. package/dist/modules/key_store.d.ts.map +1 -0
  509. package/dist/modules/key_store.js +215 -0
  510. package/dist/modules/key_store.js.map +1 -0
  511. package/dist/modules/launcher/bot_supervisor.d.ts +84 -0
  512. package/dist/modules/launcher/bot_supervisor.d.ts.map +1 -0
  513. package/dist/modules/launcher/bot_supervisor.js +1067 -0
  514. package/dist/modules/launcher/bot_supervisor.js.map +1 -0
  515. package/dist/modules/launcher/child_env.d.ts +9 -0
  516. package/dist/modules/launcher/child_env.d.ts.map +1 -0
  517. package/dist/modules/launcher/child_env.js +78 -0
  518. package/dist/modules/launcher/child_env.js.map +1 -0
  519. package/dist/modules/launcher/credential_bootstrap.d.ts +22 -0
  520. package/dist/modules/launcher/credential_bootstrap.d.ts.map +1 -0
  521. package/dist/modules/launcher/credential_bootstrap.js +303 -0
  522. package/dist/modules/launcher/credential_bootstrap.js.map +1 -0
  523. package/dist/modules/launcher/credential_daemon.d.ts +32 -0
  524. package/dist/modules/launcher/credential_daemon.d.ts.map +1 -0
  525. package/dist/modules/launcher/credential_daemon.js +173 -0
  526. package/dist/modules/launcher/credential_daemon.js.map +1 -0
  527. package/dist/modules/launcher/credential_secret.d.ts +6 -0
  528. package/dist/modules/launcher/credential_secret.d.ts.map +1 -0
  529. package/dist/modules/launcher/credential_secret.js +15 -0
  530. package/dist/modules/launcher/credential_secret.js.map +1 -0
  531. package/dist/modules/launcher/foreign_cred_daemon.d.ts +2 -0
  532. package/dist/modules/launcher/foreign_cred_daemon.d.ts.map +1 -0
  533. package/dist/modules/launcher/foreign_cred_daemon.js +166 -0
  534. package/dist/modules/launcher/foreign_cred_daemon.js.map +1 -0
  535. package/dist/modules/launcher/headless_password.d.ts +8 -0
  536. package/dist/modules/launcher/headless_password.d.ts.map +1 -0
  537. package/dist/modules/launcher/headless_password.js +33 -0
  538. package/dist/modules/launcher/headless_password.js.map +1 -0
  539. package/dist/modules/launcher/launch_modes.d.ts +36 -0
  540. package/dist/modules/launcher/launch_modes.d.ts.map +1 -0
  541. package/dist/modules/launcher/launch_modes.js +129 -0
  542. package/dist/modules/launcher/launch_modes.js.map +1 -0
  543. package/dist/modules/launcher/market_adapter_runtime.d.ts +43 -0
  544. package/dist/modules/launcher/market_adapter_runtime.d.ts.map +1 -0
  545. package/dist/modules/launcher/market_adapter_runtime.js +217 -0
  546. package/dist/modules/launcher/market_adapter_runtime.js.map +1 -0
  547. package/dist/modules/launcher/market_adapter_watchdog.d.ts +26 -0
  548. package/dist/modules/launcher/market_adapter_watchdog.d.ts.map +1 -0
  549. package/dist/modules/launcher/market_adapter_watchdog.js +214 -0
  550. package/dist/modules/launcher/market_adapter_watchdog.js.map +1 -0
  551. package/dist/modules/launcher/monolithic_runtime.d.ts +89 -0
  552. package/dist/modules/launcher/monolithic_runtime.d.ts.map +1 -0
  553. package/dist/modules/launcher/monolithic_runtime.js +425 -0
  554. package/dist/modules/launcher/monolithic_runtime.js.map +1 -0
  555. package/dist/modules/launcher/runtime_entry.d.ts +26 -0
  556. package/dist/modules/launcher/runtime_entry.d.ts.map +1 -0
  557. package/dist/modules/launcher/runtime_entry.js +49 -0
  558. package/dist/modules/launcher/runtime_entry.js.map +1 -0
  559. package/dist/modules/launcher/status_reporting.d.ts +48 -0
  560. package/dist/modules/launcher/status_reporting.d.ts.map +1 -0
  561. package/dist/modules/launcher/status_reporting.js +113 -0
  562. package/dist/modules/launcher/status_reporting.js.map +1 -0
  563. package/dist/modules/launcher/supervisor_control.d.ts +6 -0
  564. package/dist/modules/launcher/supervisor_control.d.ts.map +1 -0
  565. package/dist/modules/launcher/supervisor_control.js +59 -0
  566. package/dist/modules/launcher/supervisor_control.js.map +1 -0
  567. package/dist/modules/logger.d.ts +2 -0
  568. package/dist/modules/logger.d.ts.map +1 -0
  569. package/dist/modules/logger.js +71 -0
  570. package/dist/modules/logger.js.map +1 -0
  571. package/dist/modules/market_adapter_whitelist.d.ts +22 -0
  572. package/dist/modules/market_adapter_whitelist.d.ts.map +1 -0
  573. package/dist/modules/market_adapter_whitelist.js +82 -0
  574. package/dist/modules/market_adapter_whitelist.js.map +1 -0
  575. package/dist/modules/node_health_cache.d.ts +55 -0
  576. package/dist/modules/node_health_cache.d.ts.map +1 -0
  577. package/dist/modules/node_health_cache.js +134 -0
  578. package/dist/modules/node_health_cache.js.map +1 -0
  579. package/dist/modules/node_manager.d.ts +215 -0
  580. package/dist/modules/node_manager.d.ts.map +1 -0
  581. package/dist/modules/node_manager.js +628 -0
  582. package/dist/modules/node_manager.js.map +1 -0
  583. package/dist/modules/order/accounting.d.ts +349 -0
  584. package/dist/modules/order/accounting.d.ts.map +1 -0
  585. package/dist/modules/order/accounting.js +1191 -0
  586. package/dist/modules/order/accounting.js.map +1 -0
  587. package/dist/modules/order/async_lock.d.ts +126 -0
  588. package/dist/modules/order/async_lock.d.ts.map +1 -0
  589. package/dist/modules/order/async_lock.js +195 -0
  590. package/dist/modules/order/async_lock.js.map +1 -0
  591. package/dist/modules/order/export.d.ts +129 -0
  592. package/dist/modules/order/export.d.ts.map +1 -0
  593. package/dist/modules/order/export.js +316 -0
  594. package/dist/modules/order/export.js.map +1 -0
  595. package/dist/modules/order/format.d.ts +158 -0
  596. package/dist/modules/order/format.d.ts.map +1 -0
  597. package/dist/modules/order/format.js +232 -0
  598. package/dist/modules/order/format.js.map +1 -0
  599. package/dist/modules/order/grid.d.ts +549 -0
  600. package/dist/modules/order/grid.d.ts.map +1 -0
  601. package/dist/modules/order/grid.js +1746 -0
  602. package/dist/modules/order/grid.js.map +1 -0
  603. package/dist/modules/order/grid_reconcile.d.ts +154 -0
  604. package/dist/modules/order/grid_reconcile.d.ts.map +1 -0
  605. package/dist/modules/order/grid_reconcile.js +1192 -0
  606. package/dist/modules/order/grid_reconcile.js.map +1 -0
  607. package/dist/modules/order/index.d.ts +55 -0
  608. package/dist/modules/order/index.d.ts.map +1 -0
  609. package/dist/modules/order/index.js +79 -0
  610. package/dist/modules/order/index.js.map +1 -0
  611. package/dist/modules/order/logger.d.ts +136 -0
  612. package/dist/modules/order/logger.d.ts.map +1 -0
  613. package/dist/modules/order/logger.js +614 -0
  614. package/dist/modules/order/logger.js.map +1 -0
  615. package/dist/modules/order/logger_state.d.ts +133 -0
  616. package/dist/modules/order/logger_state.d.ts.map +1 -0
  617. package/dist/modules/order/logger_state.js +170 -0
  618. package/dist/modules/order/logger_state.js.map +1 -0
  619. package/dist/modules/order/manager.d.ts +413 -0
  620. package/dist/modules/order/manager.d.ts.map +1 -0
  621. package/dist/modules/order/manager.js +1605 -0
  622. package/dist/modules/order/manager.js.map +1 -0
  623. package/dist/modules/order/processed_fill_store.d.ts +135 -0
  624. package/dist/modules/order/processed_fill_store.d.ts.map +1 -0
  625. package/dist/modules/order/processed_fill_store.js +288 -0
  626. package/dist/modules/order/processed_fill_store.js.map +1 -0
  627. package/dist/modules/order/runner.d.ts +70 -0
  628. package/dist/modules/order/runner.d.ts.map +1 -0
  629. package/dist/modules/order/runner.js +140 -0
  630. package/dist/modules/order/runner.js.map +1 -0
  631. package/dist/modules/order/strategy.d.ts +196 -0
  632. package/dist/modules/order/strategy.d.ts.map +1 -0
  633. package/dist/modules/order/strategy.js +398 -0
  634. package/dist/modules/order/strategy.js.map +1 -0
  635. package/dist/modules/order/sync_engine.d.ts +386 -0
  636. package/dist/modules/order/sync_engine.d.ts.map +1 -0
  637. package/dist/modules/order/sync_engine.js +1459 -0
  638. package/dist/modules/order/sync_engine.js.map +1 -0
  639. package/dist/modules/order/utils/math.d.ts +677 -0
  640. package/dist/modules/order/utils/math.d.ts.map +1 -0
  641. package/dist/modules/order/utils/math.js +1172 -0
  642. package/dist/modules/order/utils/math.js.map +1 -0
  643. package/dist/modules/order/utils/order.d.ts +572 -0
  644. package/dist/modules/order/utils/order.d.ts.map +1 -0
  645. package/dist/modules/order/utils/order.js +1145 -0
  646. package/dist/modules/order/utils/order.js.map +1 -0
  647. package/dist/modules/order/utils/system.d.ts +268 -0
  648. package/dist/modules/order/utils/system.d.ts.map +1 -0
  649. package/dist/modules/order/utils/system.js +1261 -0
  650. package/dist/modules/order/utils/system.js.map +1 -0
  651. package/dist/modules/order/utils/validate.d.ts +280 -0
  652. package/dist/modules/order/utils/validate.d.ts.map +1 -0
  653. package/dist/modules/order/utils/validate.js +918 -0
  654. package/dist/modules/order/utils/validate.js.map +1 -0
  655. package/dist/modules/order/working_grid.d.ts +212 -0
  656. package/dist/modules/order/working_grid.d.ts.map +1 -0
  657. package/dist/modules/order/working_grid.js +279 -0
  658. package/dist/modules/order/working_grid.js.map +1 -0
  659. package/dist/modules/path_api.d.ts +32 -0
  660. package/dist/modules/path_api.d.ts.map +1 -0
  661. package/dist/modules/path_api.js +148 -0
  662. package/dist/modules/path_api.js.map +1 -0
  663. package/dist/modules/paths.d.ts +58 -0
  664. package/dist/modules/paths.d.ts.map +1 -0
  665. package/dist/modules/paths.js +82 -0
  666. package/dist/modules/paths.js.map +1 -0
  667. package/dist/modules/process_discovery.d.ts +58 -0
  668. package/dist/modules/process_discovery.d.ts.map +1 -0
  669. package/dist/modules/process_discovery.js +265 -0
  670. package/dist/modules/process_discovery.js.map +1 -0
  671. package/dist/modules/runtime.d.ts +34 -0
  672. package/dist/modules/runtime.d.ts.map +1 -0
  673. package/dist/modules/runtime.js +98 -0
  674. package/dist/modules/runtime.js.map +1 -0
  675. package/dist/modules/runtime_settings.d.ts +12 -0
  676. package/dist/modules/runtime_settings.d.ts.map +1 -0
  677. package/dist/modules/runtime_settings.js +122 -0
  678. package/dist/modules/runtime_settings.js.map +1 -0
  679. package/dist/modules/settings_merge.d.ts +38 -0
  680. package/dist/modules/settings_merge.d.ts.map +1 -0
  681. package/dist/modules/settings_merge.js +246 -0
  682. package/dist/modules/settings_merge.js.map +1 -0
  683. package/dist/modules/storage/browser_adapter.d.ts +56 -0
  684. package/dist/modules/storage/browser_adapter.d.ts.map +1 -0
  685. package/dist/modules/storage/browser_adapter.js +231 -0
  686. package/dist/modules/storage/browser_adapter.js.map +1 -0
  687. package/dist/modules/storage/index.d.ts +28 -0
  688. package/dist/modules/storage/index.d.ts.map +1 -0
  689. package/dist/modules/storage/index.js +52 -0
  690. package/dist/modules/storage/index.js.map +1 -0
  691. package/dist/modules/storage/node_adapter.d.ts +49 -0
  692. package/dist/modules/storage/node_adapter.d.ts.map +1 -0
  693. package/dist/modules/storage/node_adapter.js +167 -0
  694. package/dist/modules/storage/node_adapter.js.map +1 -0
  695. package/dist/modules/storage/types.d.ts +92 -0
  696. package/dist/modules/storage/types.d.ts.map +1 -0
  697. package/dist/modules/storage/types.js +3 -0
  698. package/dist/modules/storage/types.js.map +1 -0
  699. package/dist/modules/types.d.ts +1382 -0
  700. package/dist/modules/types.d.ts.map +1 -0
  701. package/dist/modules/types.js +11 -0
  702. package/dist/modules/types.js.map +1 -0
  703. package/dist/modules/utils/base58check.d.ts +12 -0
  704. package/dist/modules/utils/base58check.d.ts.map +1 -0
  705. package/dist/modules/utils/base58check.js +112 -0
  706. package/dist/modules/utils/base58check.js.map +1 -0
  707. package/dist/modules/utils/build_dir.d.ts +7 -0
  708. package/dist/modules/utils/build_dir.d.ts.map +1 -0
  709. package/dist/modules/utils/build_dir.js +8 -0
  710. package/dist/modules/utils/build_dir.js.map +1 -0
  711. package/dist/modules/utils/fs_utils.d.ts +25 -0
  712. package/dist/modules/utils/fs_utils.d.ts.map +1 -0
  713. package/dist/modules/utils/fs_utils.js +27 -0
  714. package/dist/modules/utils/fs_utils.js.map +1 -0
  715. package/dist/modules/utils/math_utils.d.ts +10 -0
  716. package/dist/modules/utils/math_utils.d.ts.map +1 -0
  717. package/dist/modules/utils/math_utils.js +17 -0
  718. package/dist/modules/utils/math_utils.js.map +1 -0
  719. package/dist/modules/validate_profiles.d.ts +37 -0
  720. package/dist/modules/validate_profiles.d.ts.map +1 -0
  721. package/dist/modules/validate_profiles.js +369 -0
  722. package/dist/modules/validate_profiles.js.map +1 -0
  723. package/dist/pm2.d.ts +167 -0
  724. package/dist/pm2.d.ts.map +1 -0
  725. package/dist/pm2.js +998 -0
  726. package/dist/pm2.js.map +1 -0
  727. package/dist/scripts/analyze-git.d.ts +11 -0
  728. package/dist/scripts/analyze-git.d.ts.map +1 -0
  729. package/dist/scripts/analyze-git.js +851 -0
  730. package/dist/scripts/analyze-git.js.map +1 -0
  731. package/dist/scripts/analyze-orders.d.ts +17 -0
  732. package/dist/scripts/analyze-orders.d.ts.map +1 -0
  733. package/dist/scripts/analyze-orders.js +1568 -0
  734. package/dist/scripts/analyze-orders.js.map +1 -0
  735. package/dist/scripts/diagnose-kibana-candles.d.ts +3 -0
  736. package/dist/scripts/diagnose-kibana-candles.d.ts.map +1 -0
  737. package/dist/scripts/diagnose-kibana-candles.js +82 -0
  738. package/dist/scripts/diagnose-kibana-candles.js.map +1 -0
  739. package/dist/scripts/diagnose-pool-history.d.ts +3 -0
  740. package/dist/scripts/diagnose-pool-history.d.ts.map +1 -0
  741. package/dist/scripts/diagnose-pool-history.js +296 -0
  742. package/dist/scripts/diagnose-pool-history.js.map +1 -0
  743. package/dist/scripts/divergence-calc.d.ts +26 -0
  744. package/dist/scripts/divergence-calc.d.ts.map +1 -0
  745. package/dist/scripts/divergence-calc.js +119 -0
  746. package/dist/scripts/divergence-calc.js.map +1 -0
  747. package/dist/scripts/generate_lp_chart.d.ts +31 -0
  748. package/dist/scripts/generate_lp_chart.d.ts.map +1 -0
  749. package/dist/scripts/generate_lp_chart.js +92 -0
  750. package/dist/scripts/generate_lp_chart.js.map +1 -0
  751. package/dist/scripts/generate_mainnet_corpus_report.d.ts +24 -0
  752. package/dist/scripts/generate_mainnet_corpus_report.d.ts.map +1 -0
  753. package/dist/scripts/generate_mainnet_corpus_report.js +237 -0
  754. package/dist/scripts/generate_mainnet_corpus_report.js.map +1 -0
  755. package/dist/scripts/generate_market_adapter_whitelist.d.ts +2 -0
  756. package/dist/scripts/generate_market_adapter_whitelist.d.ts.map +1 -0
  757. package/dist/scripts/generate_market_adapter_whitelist.js +111 -0
  758. package/dist/scripts/generate_market_adapter_whitelist.js.map +1 -0
  759. package/dist/scripts/migrate_bot_keys.d.ts +6 -0
  760. package/dist/scripts/migrate_bot_keys.d.ts.map +1 -0
  761. package/dist/scripts/migrate_bot_keys.js +301 -0
  762. package/dist/scripts/migrate_bot_keys.js.map +1 -0
  763. package/dist/scripts/native_release_gates.d.ts +3 -0
  764. package/dist/scripts/native_release_gates.d.ts.map +1 -0
  765. package/dist/scripts/native_release_gates.js +52 -0
  766. package/dist/scripts/native_release_gates.js.map +1 -0
  767. package/dist/scripts/print_grid.d.ts +16 -0
  768. package/dist/scripts/print_grid.d.ts.map +1 -0
  769. package/dist/scripts/print_grid.js +80 -0
  770. package/dist/scripts/print_grid.js.map +1 -0
  771. package/dist/scripts/run-tests.d.ts +2 -0
  772. package/dist/scripts/run-tests.d.ts.map +1 -0
  773. package/dist/scripts/run-tests.js +49 -0
  774. package/dist/scripts/run-tests.js.map +1 -0
  775. package/dist/scripts/sync-version.d.ts +14 -0
  776. package/dist/scripts/sync-version.d.ts.map +1 -0
  777. package/dist/scripts/sync-version.js +158 -0
  778. package/dist/scripts/sync-version.js.map +1 -0
  779. package/dist/scripts/test-credit-renewal.d.ts +2 -0
  780. package/dist/scripts/test-credit-renewal.d.ts.map +1 -0
  781. package/dist/scripts/test-credit-renewal.js +329 -0
  782. package/dist/scripts/test-credit-renewal.js.map +1 -0
  783. package/dist/scripts/update.d.ts +24 -0
  784. package/dist/scripts/update.d.ts.map +1 -0
  785. package/dist/scripts/update.js +650 -0
  786. package/dist/scripts/update.js.map +1 -0
  787. package/dist/scripts/validate_bots.d.ts +16 -0
  788. package/dist/scripts/validate_bots.d.ts.map +1 -0
  789. package/dist/scripts/validate_bots.js +106 -0
  790. package/dist/scripts/validate_bots.js.map +1 -0
  791. package/dist/scripts/verify-browser-bundle.d.ts +22 -0
  792. package/dist/scripts/verify-browser-bundle.d.ts.map +1 -0
  793. package/dist/scripts/verify-browser-bundle.js +339 -0
  794. package/dist/scripts/verify-browser-bundle.js.map +1 -0
  795. package/dist/unlock.d.ts +48 -0
  796. package/dist/unlock.d.ts.map +1 -0
  797. package/dist/unlock.js +925 -0
  798. package/dist/unlock.js.map +1 -0
  799. package/package.json +112 -0
  800. package/scripts/README.md +356 -0
  801. package/scripts/clear-all.sh +195 -0
  802. package/scripts/clear-logs.sh +95 -0
  803. package/scripts/clear-market-adapter.sh +196 -0
  804. package/scripts/clear-orders.sh +113 -0
  805. package/scripts/create-bot-symlinks.sh +51 -0
  806. package/scripts/postinstall.js +9 -0
  807. package/scripts/reset-settings.sh +118 -0
@@ -0,0 +1,1459 @@
1
+ "use strict";
2
+ /**
3
+ * modules/order/sync_engine.ts - SyncEngine
4
+ *
5
+ * Blockchain synchronization and reconciliation engine.
6
+ * Exports a single SyncEngine class handling all blockchain state matching.
7
+ *
8
+ * Responsibilities:
9
+ * - Match blockchain open orders to grid orders
10
+ * - Detect and handle partial fills
11
+ * - Process fill history events
12
+ * - Update fund state based on blockchain
13
+ * - Fetch and cache account balances
14
+ * - Initialize asset metadata
15
+ *
16
+ * Uses AsyncLock to prevent concurrent sync operations (defense-in-depth locking).
17
+ *
18
+ * ===============================================================================
19
+ * TABLE OF CONTENTS - SyncEngine Class (8 methods) + 1 module-level helper
20
+ * ===============================================================================
21
+ *
22
+ * MODULE-LEVEL HELPER (internal, not exported):
23
+ * hasEquivalentRawOnChainOrder(a, b) - Compare two chain orders for equivalence
24
+ * Used internally during sync to detect redundant/duplicate chain entries
25
+ *
26
+ * INITIALIZATION (1 method)
27
+ * 1. constructor(manager) - Create new SyncEngine with manager reference
28
+ *
29
+ * BLOCKCHAIN SYNCHRONIZATION (3 methods - async)
30
+ * 2. syncFromOpenOrders(chainOrders, options) - Main sync entry point (async)
31
+ * Reconciles grid against fresh blockchain snapshot
32
+ * Uses AsyncLock to ensure only one sync at a time (defense-in-depth)
33
+ * Performs two-pass reconciliation (grid→chain, then chain→grid)
34
+ *
35
+ * 3. _doSyncFromOpenOrders(chainOrders, options) - Execute sync with locking (async, internal)
36
+ * Acquires _syncLock, validates chain orders, calls _performSyncFromOpenOrders
37
+ *
38
+ * 4. _performSyncFromOpenOrders(mgr, precA, precB, parsedChain, rawChain, options) - Core sync logic (internal)
39
+ * Performs actual two-pass reconciliation without locking
40
+ * Pass 1: Match grid orders to chain (known grid → chain, includes orphan cleanup)
41
+ * Pass 2: Add missing chain orders (unknown chain → grid)
42
+ *
43
+ * FILL PROCESSING (1 method)
44
+ * 5. syncFromFillHistory(fill) - Process fill event synchronously
45
+ * Updates grid order state based on fill data
46
+ * Updates fund state and accounting
47
+ * Handles both maker and taker fills
48
+ *
49
+ * FULL SYNCHRONIZATION (1 method - async)
50
+ * 6. synchronizeWithChain(chainData, source) - Full sync (fetch + sync) (async)
51
+ * Fetches fresh account balances
52
+ * Calls syncFromOpenOrders() with chain data
53
+ * Source: event type that triggered sync (fill, poll, broadcast, etc.)
54
+ *
55
+ * ACCOUNT STATE (2 methods - async)
56
+ * 7. fetchAccountBalancesAndSetTotals(accountId) - Fetch account totals (async)
57
+ * Retrieves BUY/SELL totals and free balances from blockchain
58
+ * Sets manager.accountTotals
59
+ * Triggers fund recalculation
60
+ *
61
+ * 8. initializeAssets() - Initialize asset metadata (async)
62
+ * Fetches asset precision and other metadata
63
+ * Sets manager.assets
64
+ * Called once at bot startup
65
+ *
66
+ * ===============================================================================
67
+ *
68
+ * LOCK HIERARCHY:
69
+ * 1. _syncLock (AsyncLock): Ensures only one full-sync at a time
70
+ * 2. Per-order locks (shadowOrderIds): Protect specific orders during sync
71
+ * 3. Lock refresh mechanism: Prevents timeout during long reconciliation
72
+ *
73
+ * TWO-PASS RECONCILIATION:
74
+ * PASS 1: Grid → Chain
75
+ * - For each grid order with orderId, find matching chain order
76
+ * - Detect partial fills (chain size < grid size)
77
+ * - Update sizes and mark as filled if needed
78
+ * - Downgrade to VIRTUAL if not found on-chain
79
+ *
80
+ * PASS 2: Chain → Grid
81
+ * - For each chain order not matched to grid order
82
+ * - Create new grid order for unexpected chain order
83
+ * - Mark as ACTIVE with blockchain orderId
84
+ *
85
+ * ===============================================================================
86
+ */
87
+ const { ORDER_TYPES, ORDER_STATES, TIMING, BTS_PRECISION } = require('../constants');
88
+ const Format = require('./format');
89
+ const { toFiniteNumber } = Format;
90
+ const { blockchainToFloat, floatToBlockchainInt, hasValidAccountTotals, calculatePriceTolerance, getAssetFees } = require('./utils/math');
91
+ const { parseChainOrder, findMatchingGridOrderByOpenOrder, applyChainSizeToGridOrder, convertToSpreadPlaceholder, virtualizeOrder, buildFillKey, isOrderPlaced, isOrderOnChain, hasOnChainId, isOrderVirtual, isPhantomOrder } = require('./utils/order');
92
+ const { resolveProcessedFillPersistenceMode } = require('./processed_fill_store');
93
+ const { lookupAsset } = require('./utils/system');
94
+ /**
95
+ * Compare two raw chain order objects for on-chain equivalence.
96
+ * @param {Object|null} a - First chain order
97
+ * @param {Object|null} b - Second chain order
98
+ * @returns {boolean} True if orders are equivalent
99
+ */
100
+ function hasEquivalentRawOnChainOrder(a, b) {
101
+ if (!a && !b)
102
+ return true;
103
+ if (!a || !b)
104
+ return false;
105
+ const aBase = a.sell_price?.base || {};
106
+ const bBase = b.sell_price?.base || {};
107
+ const aQuote = a.sell_price?.quote || {};
108
+ const bQuote = b.sell_price?.quote || {};
109
+ return String(a.id ?? '') === String(b.id ?? '') &&
110
+ String(a.for_sale ?? '') === String(b.for_sale ?? '') &&
111
+ String(aBase.amount ?? '') === String(bBase.amount ?? '') &&
112
+ String(aBase.asset_id ?? '') === String(bBase.asset_id ?? '') &&
113
+ String(aQuote.amount ?? '') === String(bQuote.amount ?? '') &&
114
+ String(aQuote.asset_id ?? '') === String(bQuote.asset_id ?? '');
115
+ }
116
+ function describeNearestAdoptionCandidates(mgr, chainOrder, precision, calcTolerance, matchedGridOrderIds = null) {
117
+ if (!mgr?.orders || !chainOrder || typeof precision !== 'number')
118
+ return 'candidate diagnostics unavailable';
119
+ const chainPrice = toFiniteNumber(chainOrder.price);
120
+ const chainSize = toFiniteNumber(chainOrder.size);
121
+ const chainInt = floatToBlockchainInt(chainSize, precision);
122
+ const candidates = [];
123
+ for (const slot of mgr.orders.values()) {
124
+ if (!slot || ![ORDER_STATES.ACTIVE, ORDER_STATES.PARTIAL, ORDER_STATES.VIRTUAL].includes(slot.state))
125
+ continue;
126
+ const typeMatch = slot.type === chainOrder.type;
127
+ const spreadMatch = slot.type === ORDER_TYPES.SPREAD;
128
+ if (!typeMatch && !spreadMatch)
129
+ continue;
130
+ const price = toFiniteNumber(slot.price);
131
+ const priceDiff = Math.abs(price - chainPrice);
132
+ const effectiveSize = slot.size > 0 ? toFiniteNumber(slot.size) : chainSize;
133
+ const tolerance = calcTolerance(price, effectiveSize, chainOrder.type) || 0;
134
+ const gridInt = floatToBlockchainInt(toFiniteNumber(slot.size), precision);
135
+ const sizeDiffInt = chainInt - gridInt;
136
+ const hasOrderId = Boolean(slot.orderId);
137
+ const alreadyMatched = Boolean(matchedGridOrderIds?.has?.(slot.id));
138
+ const sizeOk = Math.abs(sizeDiffInt) <= 1;
139
+ const primaryEligible = typeMatch && priceDiff <= tolerance && sizeOk;
140
+ const fallbackEligible = (typeMatch || spreadMatch) && priceDiff <= tolerance && sizeOk && !hasOrderId && !alreadyMatched;
141
+ candidates.push({
142
+ slot,
143
+ typeMatch,
144
+ spreadMatch,
145
+ priceDiff,
146
+ tolerance,
147
+ sizeDiffInt,
148
+ hasOrderId,
149
+ alreadyMatched,
150
+ primaryEligible,
151
+ fallbackEligible,
152
+ });
153
+ }
154
+ candidates.sort((a, b) => a.priceDiff - b.priceDiff);
155
+ if (candidates.length === 0)
156
+ return 'no same-side or spread candidate slots exist';
157
+ return candidates.slice(0, 5).map((candidate) => {
158
+ const slot = candidate.slot;
159
+ const reasons = [];
160
+ if (!candidate.typeMatch && !candidate.spreadMatch)
161
+ reasons.push('type');
162
+ if (candidate.priceDiff > candidate.tolerance)
163
+ reasons.push('price');
164
+ if (Math.abs(candidate.sizeDiffInt) > 1)
165
+ reasons.push('size');
166
+ if (candidate.hasOrderId)
167
+ reasons.push(`occupied:${slot.orderId}`);
168
+ if (candidate.alreadyMatched)
169
+ reasons.push('alreadyMatched');
170
+ if (candidate.primaryEligible)
171
+ reasons.push('primary-matchable');
172
+ if (candidate.fallbackEligible)
173
+ reasons.push('fallback-adoptable');
174
+ return `${slot.id || 'unknown'} ${slot.type}/${slot.state}` +
175
+ ` orderId=${slot.orderId || 'none'}` +
176
+ ` price=${Format.formatPrice6(slot.price)}` +
177
+ ` diff=${Format.formatPrice6(candidate.priceDiff)}` +
178
+ ` tol=${Format.formatPrice6(candidate.tolerance)}` +
179
+ ` size=${Format.formatSizeByOrderType(slot.size || 0, chainOrder.type, mgr.assets)}` +
180
+ ` sizeDiffInt=${candidate.sizeDiffInt}` +
181
+ ` reason=${reasons.join('|') || 'unknown'}`;
182
+ }).join('; ');
183
+ }
184
+ function _getDriftToleranceMultiplier(mgr) {
185
+ return mgr?.config?.gridLimits?.PRICE_DRIFT_TOLERANCE_MULTIPLIER;
186
+ }
187
+ /**
188
+ * Find the closest same-side (or spread) candidate slot for a chain order and
189
+ * return a "price-drift-orphan" tag if the slot's price diff is larger than the
190
+ * strict tolerance but small enough to be considered a meaningful drift.
191
+ *
192
+ * Rationale: when the chain order has drifted from the planned slot price by
193
+ * more than the matcher's strict tolerance (so the matcher correctly rejects
194
+ * the adoption), the next planning cycle's pre-broadcast guard will refuse
195
+ * new CREATEs and the structural resync will cancel+recreate. Tagging the
196
+ * entry as a price-drift-orphan lets the auto-cancel path prioritize it and
197
+ * the diagnostics surface the exact slot the orphan drifted from.
198
+ *
199
+ * "Meaningful drift" = price diff > strict tolerance AND price diff
200
+ * <= strict tolerance * PRICE_DRIFT_TOLERANCE_MULTIPLIER. Larger diffs are
201
+ * considered wildly-out-of-tolerance orphans (e.g. legacy orders from a
202
+ * previous config) and are NOT tagged — the resync will discard them
203
+ * structurally instead.
204
+ *
205
+ * @param {Object} mgr - OrderManager instance
206
+ * @param {Object} chainOrder - Parsed chain order { price, size, type }
207
+ * @param {Function} calcToleranceFn - calculatePriceTolerance function
208
+ * @returns {{candidateSlotId: string, candidateSlotPrice: number, priceDiff: number, tolerance: number}|null}
209
+ */
210
+ function computeOutOfToleranceDriftTag(mgr, chainOrder, calcToleranceFn) {
211
+ if (!mgr?.orders || !chainOrder)
212
+ return null;
213
+ const chainPrice = toFiniteNumber(chainOrder.price);
214
+ if (!Number.isFinite(chainPrice))
215
+ return null;
216
+ const chainSize = toFiniteNumber(chainOrder.size);
217
+ const orderType = chainOrder.type;
218
+ if (orderType !== ORDER_TYPES.BUY && orderType !== ORDER_TYPES.SELL)
219
+ return null;
220
+ let bestDrift = null;
221
+ for (const slot of mgr.orders.values()) {
222
+ if (!slot)
223
+ continue;
224
+ if (slot.type !== orderType && slot.type !== ORDER_TYPES.SPREAD)
225
+ continue;
226
+ if (slot.orderId)
227
+ continue;
228
+ if (![ORDER_STATES.ACTIVE, ORDER_STATES.PARTIAL, ORDER_STATES.VIRTUAL].includes(slot.state))
229
+ continue;
230
+ const slotPrice = toFiniteNumber(slot.price);
231
+ if (!Number.isFinite(slotPrice))
232
+ continue;
233
+ const priceDiff = Math.abs(slotPrice - chainPrice);
234
+ const effectiveSize = slot.size > 0 ? toFiniteNumber(slot.size) : chainSize;
235
+ const tolerance = (calcToleranceFn ? (calcToleranceFn(slotPrice, effectiveSize, orderType) || 0) : 0);
236
+ if (priceDiff <= tolerance)
237
+ continue;
238
+ const driftBudget = tolerance * _getDriftToleranceMultiplier(mgr);
239
+ if (driftBudget > 0 && priceDiff > driftBudget)
240
+ continue;
241
+ if (!bestDrift || priceDiff < bestDrift.priceDiff) {
242
+ bestDrift = {
243
+ candidateSlotId: slot.id,
244
+ candidateSlotPrice: slotPrice,
245
+ priceDiff,
246
+ tolerance
247
+ };
248
+ }
249
+ }
250
+ return bestDrift;
251
+ }
252
+ class SyncEngine {
253
+ manager;
254
+ /**
255
+ * @param {Object} manager - OrderManager instance
256
+ */
257
+ constructor(manager) {
258
+ this.manager = manager;
259
+ }
260
+ /**
261
+ * Reconcile grid orders against fresh blockchain open orders snapshot.
262
+ * This is the MAIN SYNCHRONIZATION MECHANISM that corrects the grid state when
263
+ * the blockchain state diverges from our local expectations.
264
+ *
265
+ * CRITICAL: This method uses AsyncLock (defense-in-depth) to ensure only one
266
+ * full-sync operation runs at a time. WITHIN that lock, per-order locks prevent
267
+ * concurrent createOrder/cancelOrder races.
268
+ *
269
+ * LOCK HIERARCHY:
270
+ * 1. _syncLock (AsyncLock): Ensures only one full-sync at a time
271
+ * 2. Per-order locks (shadowOrderIds): Protect specific orders during sync
272
+ * 3. Lock refresh mechanism: Prevents timeout during long reconciliation
273
+ *
274
+ * RECONCILIATION FLOW:
275
+ * ========================================================================
276
+ * This method performs a two-pass reconciliation:
277
+ *
278
+ * PASS 1: Match grid orders to chain orders (known grid → chain lookup)
279
+ * - For each grid order with an orderId, find the matching chain order
280
+ * - Detect partial fills: if chain size < grid size, downgrade to PARTIAL state
281
+ * - Detect full fills: if order no longer exists on chain, convert to SPREAD
282
+ * - Detect price slippage: flag orders for price correction if needed
283
+ * - Update grid order sizes to match chain reality
284
+ *
285
+ * PASS 2: Orphan chain orders (chain → grid lookup)
286
+ * - For chain orders not matched in Pass 1, find best grid slot match
287
+ * - This handles cases where an order was placed but grid lost track (race condition)
288
+ * - Uses price tolerance and geometric proximity to find the best match
289
+ * - Once matched, retroactively assign orderId and synchronize state
290
+ *
291
+ * CRITICAL RULES:
292
+ * 1. ACTIVE orders can only stay ACTIVE if size matches chain exactly
293
+ * If chain size < grid size → must transition to PARTIAL
294
+ * 2. If an ACTIVE order is not found on chain → it filled → convert to SPREAD
295
+ * 3. Precision matters: Use blockchain integer arithmetic to compare sizes
296
+ * Floating point comparisons can give false positives for partial fills
297
+ * 4. Size updates are applied via applyChainSizeToGridOrder() which handles
298
+ * precision conversion and may adjust sizes slightly for blockchain granularity
299
+ *
300
+ * PRICE TOLERANCE CALCULATION:
301
+ * calculatePriceTolerance() can return null in these cases:
302
+ * 1. assets parameter is null/missing
303
+ * 2. gridPrice is 0 or null (invalid price)
304
+ * 3. orderSize is 0 or null (invalid size - orders should not sync with 0 size)
305
+ * 4. assetA or assetB precision is undefined (asset metadata not loaded)
306
+ * When tolerance is null, we treat it as 0 (strict: any price difference flagged).
307
+ * This is safe because null signals a configuration/data issue, not a real order.
308
+ *
309
+ * RETURNS: { filledOrders, updatedOrders, ordersNeedingCorrection }
310
+ * - filledOrders: Orders that completed (now SPREAD placeholders)
311
+ * - updatedOrders: All orders modified during sync (state changes, size updates)
312
+ * - ordersNeedingCorrection: Orders with price slippage requiring correction
313
+ *
314
+ * EDGE CASES HANDLED:
315
+ * - Orphan chain orders (placed but grid lost track due to race condition)
316
+ * - Partial fills (size reduced on chain)
317
+ * - Full fills (order removed from chain completely)
318
+ * - Price tolerance (small slippage acceptable, large slippage flagged)
319
+ * - Precision mismatches (blockchain integer precision vs float grid)
320
+ * - Double spending prevention (each chain order matched to at most one grid order)
321
+ */
322
+ /**
323
+ * Synchronize grid orders with blockchain open orders snapshot.
324
+ * @param {Array|null} chainOrders - Array of blockchain order objects
325
+ * @param {Object} [options={}] - Sync options (e.g., { skipAccounting: true })
326
+ * @returns {Promise<Object>} Result with filledOrders, updatedOrders, ordersNeedingCorrection
327
+ */
328
+ async syncFromOpenOrders(chainOrders, options = {}) {
329
+ const mgr = this.manager;
330
+ if (!mgr) {
331
+ throw new Error('manager required for syncFromOpenOrders');
332
+ }
333
+ if (!mgr._syncLock) {
334
+ mgr.logger?.log?.('Error: syncLock not initialized', 'error');
335
+ return { filledOrders: [], updatedOrders: [], ordersNeedingCorrection: [], unmatchedChainOrders: [] };
336
+ }
337
+ if (!options?.fillLockAlreadyHeld && mgr._fillProcessingLock) {
338
+ return mgr._fillProcessingLock.acquire(async () => {
339
+ return this.syncFromOpenOrders(chainOrders, {
340
+ ...options,
341
+ fillLockAlreadyHeld: true
342
+ });
343
+ });
344
+ }
345
+ mgr.logger?.log?.(`[SYNC] Starting synchronization from ${chainOrders?.length || 0} blockchain orders...`, 'info');
346
+ const timeoutMs = TIMING.SYNC_LOCK_TIMEOUT_MS;
347
+ let timedOut = false;
348
+ let timeoutHandle;
349
+ const syncStartedAt = Date.now();
350
+ try {
351
+ const result = await Promise.race([
352
+ mgr._syncLock.acquire(async () => {
353
+ const innerResult = await this._doSyncFromOpenOrders(chainOrders, options);
354
+ if (timedOut) {
355
+ mgr.logger?.log?.(`[SYNC] Sync completed after timeout (${Date.now() - syncStartedAt}ms) — ` +
356
+ `timeout won the race; next sync will reconcile chain state`, 'warn');
357
+ return innerResult;
358
+ }
359
+ const unmatchedCount = Array.isArray(innerResult.unmatchedChainOrders)
360
+ ? innerResult.unmatchedChainOrders.length
361
+ : 0;
362
+ mgr._lastUnmatchedChainOrders = unmatchedCount > 0
363
+ ? innerResult.unmatchedChainOrders.map(order => ({ ...order }))
364
+ : [];
365
+ mgr._lastUnmatchedChainOrdersAt = unmatchedCount > 0 ? Date.now() : 0;
366
+ mgr.logger?.log?.(`[SYNC] Synchronization complete: ${innerResult.filledOrders.length} filled, ` +
367
+ `${innerResult.updatedOrders.length} updated, ` +
368
+ `${innerResult.ordersNeedingCorrection.length} needing correction.`, 'info');
369
+ return innerResult;
370
+ }),
371
+ new Promise((_, reject) => {
372
+ timeoutHandle = setTimeout(() => {
373
+ timedOut = true;
374
+ reject(new Error(`Sync timed out after ${timeoutMs}ms`));
375
+ }, timeoutMs);
376
+ })
377
+ ]);
378
+ clearTimeout(timeoutHandle);
379
+ return result;
380
+ }
381
+ catch (err) {
382
+ clearTimeout(timeoutHandle);
383
+ if (err.message?.includes('timed out')) {
384
+ mgr.logger?.log?.('[SYNC] Lock may be held by orphan after timeout; will release when inner work completes', 'warn');
385
+ }
386
+ mgr.logger?.log?.(`Sync lock error: ${err.message}`, 'error');
387
+ throw err;
388
+ }
389
+ }
390
+ /**
391
+ * Internal method that performs the actual sync logic within lock context.
392
+ *
393
+ * This is the core synchronization implementation that runs inside _syncLock
394
+ * to ensure exclusive access. It validates inputs, parses chain orders, and
395
+ * delegates to _performSyncFromOpenOrders for the actual reconciliation.
396
+ *
397
+ * VALIDATION CHECKS:
398
+ * - Manager must be initialized
399
+ * - Chain orders must be a valid array
400
+ * - Manager.orders must be initialized as Map
401
+ * - Asset precisions must be available
402
+ *
403
+ * PARSING:
404
+ * Converts raw blockchain order objects to normalized format using parseChainOrder()
405
+ * with appropriate asset precision.
406
+ *
407
+ * @param {Array<Object>|null} chainOrders - Array of raw blockchain order objects
408
+ * Each object contains sell_price, for_sale, id, etc. from blockchain
409
+ * @param {Object} options - Sync options
410
+ * - validateOnly {boolean}: If true, only validate without applying changes
411
+ * - skipFundRecalc {boolean}: If true, skip fund recalculation after sync
412
+ * @returns {Promise<Object>} Sync result:
413
+ * - filledOrders {Array}: Orders that were detected as filled
414
+ * - updatedOrders {Array}: Orders that were updated during sync
415
+ * - ordersNeedingCorrection {Array}: Orders flagged for price correction
416
+ * @async
417
+ * @private
418
+ */
419
+ async _doSyncFromOpenOrders(chainOrders, options) {
420
+ const mgr = this.manager;
421
+ // Validate inputs
422
+ if (!mgr) {
423
+ throw new Error('manager required');
424
+ }
425
+ if (!chainOrders || !Array.isArray(chainOrders)) {
426
+ return { filledOrders: [], updatedOrders: [], ordersNeedingCorrection: [], unmatchedChainOrders: [] };
427
+ }
428
+ if (!mgr.orders || !(mgr.orders instanceof Map)) {
429
+ mgr.logger?.log?.('Error: manager.orders is not initialized as a Map', 'error');
430
+ return { filledOrders: [], updatedOrders: [], ordersNeedingCorrection: [], unmatchedChainOrders: [] };
431
+ }
432
+ if (mgr.assets?.assetA?.precision === undefined || mgr.assets?.assetB?.precision === undefined) {
433
+ mgr.logger?.log?.('Error: manager.assets precision missing', 'error');
434
+ return { filledOrders: [], updatedOrders: [], ordersNeedingCorrection: [], unmatchedChainOrders: [] };
435
+ }
436
+ const assetAPrecision = mgr.assets.assetA.precision;
437
+ const assetBPrecision = mgr.assets.assetB.precision;
438
+ const assetAId = mgr.assets.assetA.id;
439
+ const assetBId = mgr.assets.assetB.id;
440
+ if (!assetAId || !assetBId) {
441
+ mgr.logger?.log?.('Error: manager.assets asset IDs missing', 'error');
442
+ return { filledOrders: [], updatedOrders: [], ordersNeedingCorrection: [], unmatchedChainOrders: [] };
443
+ }
444
+ // Use separate maps: parsed (floats) and raw (blockchain integers)
445
+ // This eliminates type confusion - each map has a single, clear purpose
446
+ const parsedChainOrders = new Map();
447
+ const rawChainOrders = new Map();
448
+ for (const order of chainOrders) {
449
+ try {
450
+ const parsed = parseChainOrder(order, mgr.assets);
451
+ if (!parsed)
452
+ continue;
453
+ // Store parsed (converted) data in parsedChainOrders
454
+ parsedChainOrders.set(order.id, parsed);
455
+ // Store raw blockchain data in separate map - clean separation of concerns
456
+ rawChainOrders.set(order.id, order);
457
+ }
458
+ catch (e) {
459
+ mgr.logger?.log?.(`Warning: Error parsing chain order ${order.id}: ${e.message}`, 'warn');
460
+ continue;
461
+ }
462
+ }
463
+ // Collect all order IDs that might be modified during reconciliation
464
+ // Lock them to prevent concurrent modifications from createOrder/cancelOrder
465
+ const orderIdsToLock = new Set();
466
+ for (const gridOrder of mgr.orders.values()) {
467
+ // Lock any order with a chain orderId (already on-chain)
468
+ if (gridOrder.orderId) {
469
+ orderIdsToLock.add(gridOrder.id);
470
+ orderIdsToLock.add(gridOrder.orderId);
471
+ }
472
+ // Also lock ACTIVE/PARTIAL orders that might transition to/from SPREAD
473
+ if (gridOrder.state === ORDER_STATES.ACTIVE || gridOrder.state === ORDER_STATES.PARTIAL) {
474
+ orderIdsToLock.add(gridOrder.id);
475
+ }
476
+ }
477
+ const chainOrderIdsOnGrid = new Set();
478
+ const matchedGridOrderIds = new Set();
479
+ const filledOrders = [];
480
+ const updatedOrders = [];
481
+ const ordersNeedingCorrection = [];
482
+ const unmatchedChainOrders = [];
483
+ // Lock orders before reconciliation
484
+ mgr.lockOrders([...orderIdsToLock]);
485
+ // Keep lock leases alive during long reconciliation runs.
486
+ const refreshEveryMs = Math.max(TIMING.LOCK_REFRESH_MIN_MS, Math.floor(TIMING.LOCK_TIMEOUT_MS / 3));
487
+ const refreshLockLeases = () => {
488
+ const expiresAt = Date.now() + TIMING.LOCK_TIMEOUT_MS;
489
+ for (const id of orderIdsToLock) {
490
+ mgr.shadowOrderIds.set(id, expiresAt);
491
+ }
492
+ mgr.logger?.log?.(`Refreshed lock leases for ${orderIdsToLock.size} orders`, 'debug');
493
+ };
494
+ refreshLockLeases();
495
+ const lockRefreshTimer = setInterval(refreshLockLeases, refreshEveryMs);
496
+ try {
497
+ const runReconciliation = async () => {
498
+ mgr.pauseFundRecalc();
499
+ try {
500
+ await this._performSyncFromOpenOrders(mgr, assetAPrecision, assetBPrecision, parsedChainOrders, rawChainOrders, chainOrderIdsOnGrid, matchedGridOrderIds, filledOrders, updatedOrders, ordersNeedingCorrection, unmatchedChainOrders, options);
501
+ }
502
+ finally {
503
+ await mgr.resumeFundRecalc();
504
+ }
505
+ };
506
+ if (options?.gridLockAlreadyHeld) {
507
+ await runReconciliation();
508
+ }
509
+ else {
510
+ await mgr._gridLock.acquire(runReconciliation);
511
+ }
512
+ }
513
+ finally {
514
+ clearInterval(lockRefreshTimer);
515
+ // Unlock after reconciliation completes
516
+ mgr.unlockOrders([...orderIdsToLock]);
517
+ }
518
+ return { filledOrders, updatedOrders, ordersNeedingCorrection, unmatchedChainOrders };
519
+ }
520
+ /**
521
+ * Internal two-pass reconciliation with locks already held.
522
+ * @param {Object} mgr - OrderManager instance
523
+ * @param {number} assetAPrecision - Asset A precision
524
+ * @param {number} assetBPrecision - Asset B precision
525
+ * @param {Map<string, import('./types').ParsedChainOrder>} parsedChainOrders - Parsed chain orders by ID
526
+ * @param {Map<string, Object>} rawChainOrders - Raw blockchain order objects by ID
527
+ * @param {Set<string>} chainOrderIdsOnGrid - Output set: chain order IDs matched to grid
528
+ * @param {Set<string>} matchedGridOrderIds - Output set: grid order IDs matched to chain
529
+ * @param {Array<Object>} filledOrders - Output array: orders detected as filled
530
+ * @param {Array<Object>} updatedOrders - Output array: orders updated during sync
531
+ * @param {Array<Object>} ordersNeedingCorrection - Output array: orders flagged for price correction
532
+ * @param {Array<Object>} unmatchedChainOrders - Output array: open chain orders with no adoptable grid slot
533
+ * @param {Object} options - Sync options (skipAccounting, etc.)
534
+ * @returns {Promise<{filledOrders: Array, updatedOrders: Array, ordersNeedingCorrection: Array, unmatchedChainOrders: Array}>}
535
+ */
536
+ async _performSyncFromOpenOrders(mgr, assetAPrecision, assetBPrecision, parsedChainOrders, rawChainOrders, chainOrderIdsOnGrid, matchedGridOrderIds, filledOrders, updatedOrders, ordersNeedingCorrection, unmatchedChainOrders, options) {
537
+ const skipAccounting = Boolean(options?.skipAccounting);
538
+ const queueCorrection = (entry) => {
539
+ ordersNeedingCorrection.push(entry);
540
+ if (!Array.isArray(mgr.ordersNeedingPriceCorrection))
541
+ return;
542
+ const existingIndex = mgr.ordersNeedingPriceCorrection.findIndex((queued) => queued?.chainOrderId === entry.chainOrderId && Boolean(queued?.isSurplus) === Boolean(entry.isSurplus));
543
+ if (existingIndex >= 0) {
544
+ mgr.ordersNeedingPriceCorrection[existingIndex] = {
545
+ ...mgr.ordersNeedingPriceCorrection[existingIndex],
546
+ ...entry
547
+ };
548
+ }
549
+ else {
550
+ mgr.ordersNeedingPriceCorrection.push({ ...entry });
551
+ }
552
+ };
553
+ // ====================================================================
554
+ // PASS 1: GRID → CHAIN - Match grid orders to blockchain
555
+ // ====================================================================
556
+ for (const gridOrder of mgr.orders.values()) {
557
+ if (gridOrder.orderId && parsedChainOrders.has(gridOrder.orderId)) {
558
+ const chainOrder = parsedChainOrders.get(gridOrder.orderId);
559
+ let updatedOrder = { ...gridOrder };
560
+ chainOrderIdsOnGrid.add(gridOrder.orderId);
561
+ // Store raw blockchain data in grid slot for later update calculation.
562
+ // Tag the snapshot with `fetchedAt` so the drift check in
563
+ // syncFromFillHistory can distinguish a fresh chain read from a
564
+ // never-synced entry (the latter has no fetchedAt and is treated
565
+ // as if grid-equals-chain, since the cache is just a thin mirror
566
+ // of the grid baseline at this point).
567
+ const rawSnapshot = rawChainOrders.get(gridOrder.orderId);
568
+ updatedOrder.rawOnChain = rawSnapshot
569
+ ? { ...rawSnapshot, fetchedAt: Date.now() }
570
+ : rawSnapshot;
571
+ // Side mismatch: keep slot untouched and queue cancellation for stale chain order.
572
+ if (gridOrder.type !== chainOrder.type) {
573
+ mgr.logger?.log?.(`[SYNC] Type mismatch for ${gridOrder.id}: grid=${gridOrder.type}, chain=${chainOrder.type}. ` +
574
+ `Queuing stale chain order ${gridOrder.orderId} for cancellation to prevent fund tracking corruption.`, 'warn');
575
+ queueCorrection({
576
+ gridOrder: { ...gridOrder },
577
+ chainOrderId: gridOrder.orderId,
578
+ expectedPrice: gridOrder.price,
579
+ actualPrice: chainOrder.price,
580
+ size: chainOrder.size,
581
+ type: chainOrder.type,
582
+ typeMismatch: true,
583
+ isSurplus: true,
584
+ sideUpdated: chainOrder.type
585
+ });
586
+ continue;
587
+ }
588
+ else {
589
+ const priceTolerance = calculatePriceTolerance(gridOrder.price, gridOrder.size, gridOrder.type, mgr.assets);
590
+ const normalizedTolerance = (priceTolerance === null) ? 0 : priceTolerance;
591
+ // Null tolerance is strict mode (0).
592
+ if (Math.abs(chainOrder.price - gridOrder.price) > normalizedTolerance) {
593
+ queueCorrection({
594
+ gridOrder: { ...gridOrder },
595
+ chainOrderId: gridOrder.orderId,
596
+ expectedPrice: gridOrder.price,
597
+ actualPrice: chainOrder.price,
598
+ size: chainOrder.size,
599
+ type: gridOrder.type
600
+ });
601
+ }
602
+ }
603
+ // Chain side determines which precision applies to for_sale.
604
+ const precision = (chainOrder.type === ORDER_TYPES.SELL) ? assetAPrecision : assetBPrecision;
605
+ const currentSizeInt = floatToBlockchainInt(gridOrder.size, precision);
606
+ const chainSizeInt = floatToBlockchainInt(chainOrder.size, precision);
607
+ if (currentSizeInt !== chainSizeInt) {
608
+ const newSize = blockchainToFloat(chainSizeInt, precision, true);
609
+ const newInt = floatToBlockchainInt(newSize, precision);
610
+ if (newInt > 0) {
611
+ const nextOrder = await applyChainSizeToGridOrder(mgr, updatedOrder, newSize);
612
+ if (nextOrder) {
613
+ // Merge updated state if size actually changed
614
+ updatedOrder = { ...updatedOrder, ...nextOrder };
615
+ }
616
+ // Transition to PARTIAL when chain size < grid size (partial fill).
617
+ // Real-time fill events set this via syncFromFillHistory, but
618
+ // downtime fills detected during open-orders sync must also update
619
+ // state so that checkWindowDust can identify dust orders.
620
+ updatedOrder.state = (chainSizeInt < currentSizeInt)
621
+ ? ORDER_STATES.PARTIAL
622
+ : gridOrder.state;
623
+ await mgr._applyOrderUpdate(updatedOrder, 'sync-pass1-partial', { skipAccounting, fee: 0 });
624
+ }
625
+ else {
626
+ const spreadOrder = convertToSpreadPlaceholder(gridOrder);
627
+ await mgr._applyOrderUpdate(spreadOrder, 'sync-pass1-filled', { skipAccounting, fee: 0 });
628
+ filledOrders.push(spreadOrder);
629
+ updatedOrders.push(spreadOrder);
630
+ }
631
+ }
632
+ }
633
+ else if (gridOrder.state === ORDER_STATES.ACTIVE || gridOrder.state === ORDER_STATES.PARTIAL) {
634
+ const currentGridOrder = mgr.orders.get(gridOrder.id) || gridOrder;
635
+ const hadOrderId = Boolean(currentGridOrder?.orderId);
636
+ const spreadOrder = convertToSpreadPlaceholder(currentGridOrder);
637
+ await mgr._applyOrderUpdate(spreadOrder, 'sync-cleanup-phantom', { skipAccounting, fee: 0 });
638
+ // Only genuine disappearances (had orderId) count as fills.
639
+ if (hadOrderId) {
640
+ filledOrders.push({ ...currentGridOrder });
641
+ }
642
+ }
643
+ }
644
+ // ====================================================================
645
+ // PASS 2: CHAIN → GRID - Match unmatched chain orders to grid
646
+ // ====================================================================
647
+ for (const [chainOrderId, chainOrder] of parsedChainOrders) {
648
+ if (chainOrderIdsOnGrid.has(chainOrderId))
649
+ continue;
650
+ // The grid allows at most one order per price level. If a placed
651
+ // grid order already exists at the orphan's price (within tolerance),
652
+ // this orphan is a stale duplicate — skip adoption so the reconcile
653
+ // layer cancels it. Size is irrelevant; any duplicate violates the
654
+ // invariant. Check BEFORE any adoption attempt, including the first
655
+ // match (line 684), because a VIRTUAL slot at a nearby price can
656
+ // otherwise silently adopt the orphan before the duplicate guard runs.
657
+ const duplicatePriceOrder = Array.from(mgr.orders.values()).find((o) => o.type === chainOrder.type &&
658
+ isOrderPlaced(o) &&
659
+ Math.abs(o.price - chainOrder.price) <= calculatePriceTolerance(Math.min(o.price, chainOrder.price), Math.max(o.size, chainOrder.size), o.type, mgr.assets));
660
+ if (duplicatePriceOrder) {
661
+ unmatchedChainOrders.push({
662
+ chainOrderId,
663
+ type: chainOrder.type,
664
+ price: chainOrder.price,
665
+ size: chainOrder.size,
666
+ raw: rawChainOrders.get(chainOrderId),
667
+ reason: 'duplicate-price-level',
668
+ candidateSlotId: duplicatePriceOrder.id,
669
+ });
670
+ mgr.logger?.log?.(`[SYNC] Orphaned chain order ${chainOrderId} (${chainOrder.type}, price=${chainOrder.price}, ` +
671
+ `size=${chainOrder.size}) — NOT adopted: duplicates price level of active ` +
672
+ `${duplicatePriceOrder.id} (${duplicatePriceOrder.orderId} at ${duplicatePriceOrder.price})`, 'warn');
673
+ continue;
674
+ }
675
+ const match = findMatchingGridOrderByOpenOrder({ orderId: chainOrderId, type: chainOrder.type, price: chainOrder.price, size: chainOrder.size }, {
676
+ orders: mgr.orders,
677
+ assets: mgr.assets,
678
+ calcToleranceFn: (p, s, t) => calculatePriceTolerance(p, s, t, mgr.assets),
679
+ logger: mgr.logger,
680
+ allowSmallerChainSize: true,
681
+ requireAvailableSlot: true,
682
+ excludeGridOrderIds: matchedGridOrderIds
683
+ });
684
+ if (match && !matchedGridOrderIds.has(match.id)) {
685
+ let bestMatch = { ...match };
686
+ const wasVirtual = match.state === ORDER_STATES.VIRTUAL;
687
+ const wasPartial = match.state === ORDER_STATES.PARTIAL;
688
+ bestMatch.orderId = chainOrderId;
689
+ bestMatch.state = wasVirtual ? ORDER_STATES.ACTIVE : match.state;
690
+ const bestMatchRaw = rawChainOrders.get(chainOrderId);
691
+ bestMatch.rawOnChain = bestMatchRaw
692
+ ? { ...bestMatchRaw, fetchedAt: Date.now() }
693
+ : bestMatchRaw;
694
+ matchedGridOrderIds.add(bestMatch.id);
695
+ // Reconstruct btsFeeState from raw chain order's deferred_fee.
696
+ // After a grid reset, in-memory orders lose btsFeeState. The chain's
697
+ // limit_order_object stores the original deferred_fee (or 0 after a
698
+ // partial fill). Restoring it ensures correct fee lifecycle accounting
699
+ // (cancel refunds, fill maker discounts) on the reconciled order.
700
+ // Only set btsFeeState when deferred_fee > 0 — _resolveBtsFeeLifecycle
701
+ // deletes btsFeeState when nextDeferred is 0, so setting it to 0 here
702
+ // would be an unnecessary set-then-delete cycle.
703
+ if (bestMatch.rawOnChain) {
704
+ const rawDeferredFee = toFiniteNumber(bestMatch.rawOnChain.deferred_fee, null);
705
+ if (rawDeferredFee !== null && rawDeferredFee > 0) {
706
+ bestMatch.btsFeeState = { deferredFee: blockchainToFloat(rawDeferredFee, BTS_PRECISION) };
707
+ }
708
+ else if (wasVirtual && rawDeferredFee !== null && rawDeferredFee <= 0 && bestMatch.rawOnChain.for_sale > 0) {
709
+ // Core zeros deferred_fee on every fill (fill_limit_order db_market.cpp:1895).
710
+ // If deferred_fee is 0 while for_sale > 0, the order was partially filled.
711
+ bestMatch.state = ORDER_STATES.PARTIAL;
712
+ }
713
+ }
714
+ const precision = (bestMatch.type === ORDER_TYPES.SELL) ? assetAPrecision : assetBPrecision;
715
+ const targetInt = floatToBlockchainInt(match.size, precision);
716
+ const chainInt = floatToBlockchainInt(chainOrder.size, precision);
717
+ if (targetInt !== chainInt) {
718
+ const updated = await applyChainSizeToGridOrder(mgr, bestMatch, chainOrder.size);
719
+ if (updated)
720
+ bestMatch = { ...bestMatch, ...updated };
721
+ if (chainInt > 0) {
722
+ if (wasVirtual) {
723
+ // Only override to ACTIVE if partial-fill check above didn't already
724
+ // set PARTIAL via deferred_fee === 0. Skip override to preserve signal.
725
+ if (bestMatch.state !== ORDER_STATES.PARTIAL) {
726
+ bestMatch.state = ORDER_STATES.ACTIVE;
727
+ }
728
+ }
729
+ else if (chainInt < targetInt) {
730
+ bestMatch.state = ORDER_STATES.PARTIAL;
731
+ }
732
+ else if (wasPartial) {
733
+ bestMatch.state = ORDER_STATES.PARTIAL;
734
+ }
735
+ else {
736
+ bestMatch.state = ORDER_STATES.ACTIVE;
737
+ }
738
+ }
739
+ else {
740
+ const spreadOrder = convertToSpreadPlaceholder(bestMatch);
741
+ filledOrders.push({ ...bestMatch });
742
+ await mgr._applyOrderUpdate(spreadOrder, 'sync-pass2-filled', { skipAccounting, fee: 0 });
743
+ updatedOrders.push(spreadOrder);
744
+ chainOrderIdsOnGrid.add(chainOrderId);
745
+ continue;
746
+ }
747
+ }
748
+ else if (wasPartial) {
749
+ bestMatch.state = ORDER_STATES.PARTIAL;
750
+ }
751
+ await mgr._applyOrderUpdate(bestMatch, 'sync-pass2-orphan', { skipAccounting, fee: 0 });
752
+ updatedOrders.push(bestMatch);
753
+ chainOrderIdsOnGrid.add(chainOrderId);
754
+ }
755
+ else if (match) {
756
+ mgr.logger?.log?.(`Warning: Orphan chain order ${chainOrderId} matched grid order ${match.id}, ` +
757
+ `but grid order was already matched to another chain order. Skipping to prevent double-assignment.`, 'warn');
758
+ }
759
+ else {
760
+ // No grid slot matched by type+price+size in the first pass.
761
+ // Fallback: adopt into the nearest VIRTUAL/spread slot so the
762
+ // orphan becomes visible to checkWindowDust for dust auto-cancel.
763
+ // The duplicate-price-level guard already ran above so it is not
764
+ // repeated here.
765
+ const adoptedSlot = findMatchingGridOrderByOpenOrder({ orderId: chainOrderId, type: chainOrder.type, price: chainOrder.price, size: chainOrder.size }, {
766
+ orders: mgr.orders,
767
+ assets: mgr.assets,
768
+ calcToleranceFn: (p, s, t) => calculatePriceTolerance(p, s, t, mgr.assets),
769
+ allowSpreadType: true,
770
+ skipSizeMatch: true,
771
+ requireAvailableSlot: true,
772
+ excludeGridOrderIds: matchedGridOrderIds
773
+ });
774
+ if (adoptedSlot && !matchedGridOrderIds.has(adoptedSlot.id) && !adoptedSlot.orderId) {
775
+ const precision = (chainOrder.type === ORDER_TYPES.SELL) ? assetAPrecision : assetBPrecision;
776
+ const chainInt = floatToBlockchainInt(chainOrder.size, precision);
777
+ const adoptedRaw = rawChainOrders.get(chainOrderId);
778
+ const adoptedState = chainInt > 0 ? ORDER_STATES.PARTIAL : ORDER_STATES.VIRTUAL;
779
+ const adoptedBtsFeeState = (adoptedRaw) ? (() => {
780
+ const rawFee = toFiniteNumber(adoptedRaw.deferred_fee, null);
781
+ return rawFee !== null && rawFee > 0 ? { deferredFee: blockchainToFloat(rawFee, BTS_PRECISION) } : undefined;
782
+ })() : undefined;
783
+ const adoptedOrder = {
784
+ ...adoptedSlot,
785
+ orderId: chainOrderId,
786
+ type: chainOrder.type,
787
+ state: adoptedState,
788
+ size: chainOrder.size,
789
+ price: chainOrder.price,
790
+ rawOnChain: adoptedRaw
791
+ ? { ...adoptedRaw, fetchedAt: Date.now() }
792
+ : adoptedRaw,
793
+ ...(adoptedBtsFeeState ? { btsFeeState: adoptedBtsFeeState } : {}),
794
+ };
795
+ matchedGridOrderIds.add(adoptedSlot.id);
796
+ chainOrderIdsOnGrid.add(chainOrderId);
797
+ await mgr._applyOrderUpdate(adoptedOrder, 'sync-pass2-adopt-orphan', { skipAccounting, fee: 0 });
798
+ updatedOrders.push(adoptedOrder);
799
+ mgr.logger?.log?.(`[SYNC] Orphaned chain order ${chainOrderId} (${chainOrder.type}, price=${chainOrder.price}, ` +
800
+ `size=${chainOrder.size}) adopted into slot ${adoptedSlot.id} (was ${adoptedSlot.type})`, 'warn');
801
+ }
802
+ else {
803
+ const precision = (chainOrder.type === ORDER_TYPES.SELL) ? assetAPrecision : assetBPrecision;
804
+ const candidateDiagnostics = describeNearestAdoptionCandidates(mgr, chainOrder, precision, (p, s, t) => calculatePriceTolerance(p, s, t, mgr.assets), matchedGridOrderIds);
805
+ const driftTag = computeOutOfToleranceDriftTag(mgr, chainOrder, (p, s, t) => calculatePriceTolerance(p, s, t, mgr.assets));
806
+ const unmatchedEntry = {
807
+ chainOrderId,
808
+ type: chainOrder.type,
809
+ price: chainOrder.price,
810
+ size: chainOrder.size,
811
+ raw: rawChainOrders.get(chainOrderId),
812
+ candidateDiagnostics
813
+ };
814
+ if (driftTag) {
815
+ unmatchedEntry.reason = 'price-drift-orphan';
816
+ unmatchedEntry.candidateSlotId = driftTag.candidateSlotId;
817
+ unmatchedEntry.candidateSlotPrice = driftTag.candidateSlotPrice;
818
+ unmatchedEntry.priceDiff = driftTag.priceDiff;
819
+ unmatchedEntry.tolerance = driftTag.tolerance;
820
+ }
821
+ unmatchedChainOrders.push(unmatchedEntry);
822
+ mgr.logger?.log?.(`[SYNC] Unmatched chain order ${chainOrderId} (${chainOrder.type}, price=${chainOrder.price}, ` +
823
+ `size=${chainOrder.size}): no adoptable slot found` +
824
+ (driftTag ? ` (price-drift-orphan slot=${driftTag.candidateSlotId}@${driftTag.candidateSlotPrice} diff=${driftTag.priceDiff} tol=${driftTag.tolerance})` : '') +
825
+ `. Nearest candidates: ${candidateDiagnostics}`, 'warn');
826
+ }
827
+ }
828
+ }
829
+ return { filledOrders, updatedOrders, ordersNeedingCorrection, unmatchedChainOrders };
830
+ }
831
+ /**
832
+ * Process one incremental fill-history event.
833
+ * Returns `{ filledOrders, updatedOrders, partialFill, requiresOpenOrdersSync }`.
834
+ * @param {Object} fill - Fill history event object
835
+ * @param {Object} [options] - Persistence mode options
836
+ * @returns {Promise<Object>} { filledOrders, updatedOrders, partialFill, requiresOpenOrdersSync }
837
+ */
838
+ async syncFromFillHistory(fill, options = {}) {
839
+ const mgr = this.manager;
840
+ const persistenceMode = resolveProcessedFillPersistenceMode(options);
841
+ if (!fill || !fill.op || !fill.op[1])
842
+ return { filledOrders: [], updatedOrders: [], partialFill: false };
843
+ const fillOp = fill.op[1];
844
+ const blockNum = fill.block_num;
845
+ const historyId = fill.id;
846
+ const isMaker = fillOp.is_maker !== false; // Default missing flag to maker for consistency with accounting
847
+ const orderId = fillOp.order_id;
848
+ const fillKey = buildFillKey({ orderId, blockNum, historyId });
849
+ const paysAmountRaw = toFiniteNumber(fillOp.pays?.amount);
850
+ const paysAssetId = fillOp.pays ? fillOp.pays.asset_id : null;
851
+ const receivesAmountRaw = toFiniteNumber(fillOp.receives?.amount);
852
+ const receivesAssetId = fillOp.receives ? fillOp.receives.asset_id : null;
853
+ mgr.logger.log(`[SYNC] Processing fill ${historyId} at block ${blockNum} for order ${orderId} (maker=${isMaker}). Pays=${paysAmountRaw}@${paysAssetId}, Receives=${receivesAmountRaw}@${receivesAssetId}`, 'debug');
854
+ if (!fillKey) {
855
+ mgr.logger.log(`[SYNC] Missing replay-safe fill key for order ${orderId} block ${blockNum}; deferring to open-orders sync`, 'warn');
856
+ return { filledOrders: [], updatedOrders: [], partialFill: false, requiresOpenOrdersSync: true };
857
+ }
858
+ // Optimistically update account totals to reflect the fill
859
+ // This prevents fund invariant violations during the window between fill detection and next blockchain fetch
860
+ const appliedAccounting = await mgr.accountant.processFillAccounting(fillOp, fillKey, { persistenceMode });
861
+ if (!appliedAccounting) {
862
+ mgr.logger.log(`[SYNC] Replay detected for fill ${fillKey}; skipping duplicate order mutation`, 'debug');
863
+ return { filledOrders: [], updatedOrders: [], partialFill: false };
864
+ }
865
+ // Lock the order to prevent concurrent modifications from createOrder/cancelOrder/sync
866
+ // This is critical to prevent TOCTOU races where fill processing updates a stale order
867
+ const orderIdsToLock = new Set([orderId]);
868
+ mgr.lockOrders([...orderIdsToLock]);
869
+ try {
870
+ mgr.pauseFundRecalc();
871
+ try {
872
+ const paysAmount = paysAmountRaw;
873
+ const assetAPrecision = mgr.assets?.assetA?.precision;
874
+ const assetBPrecision = mgr.assets?.assetB?.precision;
875
+ if (assetAPrecision === undefined || assetBPrecision === undefined) {
876
+ mgr.logger?.log?.('Error: manager.assets precision missing in syncFromFillHistory', 'error');
877
+ return { filledOrders: [], updatedOrders: [], partialFill: false };
878
+ }
879
+ let matchedGridOrder = null;
880
+ for (const gridOrder of mgr.orders.values()) {
881
+ if (gridOrder.orderId === orderId && (gridOrder.state === ORDER_STATES.ACTIVE || gridOrder.state === ORDER_STATES.PARTIAL)) {
882
+ matchedGridOrder = gridOrder;
883
+ break;
884
+ }
885
+ }
886
+ if (!matchedGridOrder) {
887
+ mgr.logger.log(`[SYNC] Fill for order ${orderId} ignored: order not found in active grid.`, 'debug');
888
+ return { filledOrders: [], updatedOrders: [], partialFill: false };
889
+ }
890
+ const orderType = matchedGridOrder.type;
891
+ const currentSize = toFiniteNumber(matchedGridOrder.size);
892
+ // CRITICAL: Sells are sized in AssetA, Buys are sized in AssetB
893
+ const precision = (orderType === ORDER_TYPES.SELL) ? assetAPrecision : assetBPrecision;
894
+ let filledAmount = 0;
895
+ if (orderType === ORDER_TYPES.SELL) {
896
+ if (paysAssetId === mgr.assets.assetA.id)
897
+ filledAmount = blockchainToFloat(paysAmount, precision, true);
898
+ }
899
+ else {
900
+ if (paysAssetId === mgr.assets.assetB.id)
901
+ filledAmount = blockchainToFloat(paysAmount, precision, true);
902
+ }
903
+ mgr.logger.log(`[SYNC] Order ${orderId} (${orderType}) currentSize=${currentSize}, filledAmount=${filledAmount}`, 'debug');
904
+ const currentSizeIntFromGrid = floatToBlockchainInt(currentSize, precision);
905
+ const rawForSaleInt = toFiniteNumber(matchedGridOrder?.rawOnChain?.for_sale, null);
906
+ let effectiveRawForSale = rawForSaleInt;
907
+ let chainConfirmsEmpty = false;
908
+ let chainRefetched = false;
909
+ // -----------------------------------------------------------------
910
+ // Drift-only refetch: the cache is self-correcting across sequential
911
+ // websocket fills, but if a fill was ever missed (reconnect, restart,
912
+ // reorg, external activity), the cache and the grid can disagree.
913
+ // The only signal we can detect from the in-memory state is a
914
+ // consistent drift: cached for_sale < grid size. Chain `for_sale`
915
+ // can only decrease, so a smaller cached value is an obvious
916
+ // inconsistency that warrants a targeted refetch.
917
+ //
918
+ // We do NOT refetch on a time-based TTL. A stale-but-consistent
919
+ // cache is still correct; a fresh-but-wrong cache is the actual
920
+ // bug we're guarding against, and the drift signal catches that.
921
+ // The refetch is best-effort: a network error falls back to the
922
+ // cache with a warning, and the open-orders sync is the ultimate
923
+ // recovery channel.
924
+ // -----------------------------------------------------------------
925
+ const driftSignal = Number.isFinite(rawForSaleInt)
926
+ && Math.round(rawForSaleInt) < currentSizeIntFromGrid;
927
+ if (driftSignal) {
928
+ try {
929
+ const { readSingleOrder } = require('../chain_orders');
930
+ const fresh = await readSingleOrder(orderId, 3000);
931
+ if (fresh) {
932
+ const freshForSale = toFiniteNumber(fresh.for_sale, null);
933
+ if (Number.isFinite(freshForSale)) {
934
+ effectiveRawForSale = freshForSale;
935
+ chainRefetched = true;
936
+ mgr.logger.log(`[SYNC] Drift detected for ${orderId} (cached=${rawForSaleInt} < grid=${currentSizeIntFromGrid}); ` +
937
+ `refetched for_sale=${freshForSale}`, 'warn');
938
+ }
939
+ }
940
+ else {
941
+ // Chain returned null — the order is genuinely absent.
942
+ // This is the authoritative "chain confirms empty"
943
+ // signal (vs. an RPC failure which would have thrown).
944
+ chainConfirmsEmpty = true;
945
+ mgr.logger.log(`[SYNC] Drift refetch for ${orderId} returned null; chain confirms empty`, 'info');
946
+ }
947
+ }
948
+ catch (refetchErr) {
949
+ mgr.logger.log(`[SYNC] Drift refetch for ${orderId} failed; falling back to cache: ` +
950
+ `${refetchErr?.message || refetchErr}`, 'warn');
951
+ }
952
+ }
953
+ // Chain-confirmed-empty gate: only true when the drift refetch
954
+ // returned null (i.e., the chain is definitively saying the order
955
+ // is gone). We deliberately do not trust a cached 0 from a
956
+ // never-refetched source — that would misclassify a 0.003 XRP
957
+ // residual as a full fill.
958
+ if (chainRefetched
959
+ && Number.isFinite(effectiveRawForSale)
960
+ && Math.round(effectiveRawForSale) <= 0) {
961
+ chainConfirmsEmpty = true;
962
+ }
963
+ const currentSizeInt = Number.isFinite(effectiveRawForSale)
964
+ ? Math.max(0, Math.round(effectiveRawForSale))
965
+ : currentSizeIntFromGrid;
966
+ const filledAmountInt = floatToBlockchainInt(filledAmount, precision);
967
+ const newSizeInt = Math.max(0, currentSizeInt - filledAmountInt);
968
+ const newSize = blockchainToFloat(newSizeInt, precision, true); // needed for partial fills
969
+ if (Number.isFinite(effectiveRawForSale) && currentSizeInt !== currentSizeIntFromGrid) {
970
+ mgr.logger.log(`[SYNC] Using rawOnChain.for_sale baseline for ${orderId}: raw=${currentSizeInt}, grid=${currentSizeIntFromGrid}` +
971
+ (chainRefetched ? ' (refetched)' : ''), 'debug');
972
+ }
973
+ // CRITICAL (v0.5.1 Robustness + chain-confirmed-empty gate):
974
+ // An order is "effectively" full if EITHER:
975
+ // 1. The chain has confirmed the order is gone (the drift
976
+ // refetch above returned null), OR
977
+ // 2. The grid itself is at 0 (no drift between grid and chain), OR
978
+ // 3. The OTHER side rounds to 0 (the chain will close the order
979
+ // regardless of the residual on the size side — see
980
+ // test_ghost_order_fix.ts for the intended behaviour).
981
+ //
982
+ // We deliberately do NOT use `newSizeInt <= 0` as a fast-path
983
+ // full-fill signal: with a stale cached rawOnChain, that arithmetic
984
+ // can be wrong and misclassify a partial fill as full. The
985
+ // 30-satoshi residual that triggered the bot-stale incident is the
986
+ // motivating case.
987
+ const gridAlsoEmpty = currentSizeIntFromGrid <= 0;
988
+ let isEffectivelyFull = chainConfirmsEmpty || gridAlsoEmpty;
989
+ if (!isEffectivelyFull) {
990
+ // Check the "other" side's precision. If the remaining amount to receive/pay
991
+ // rounds to 0 on the blockchain, the order will be closed regardless of newSizeInt.
992
+ const otherPrecision = (orderType === ORDER_TYPES.SELL) ? assetBPrecision : assetAPrecision;
993
+ const price = matchedGridOrder.price;
994
+ const otherSize = (orderType === ORDER_TYPES.SELL) ? newSize * price : newSize / price;
995
+ if (floatToBlockchainInt(otherSize, otherPrecision) <= 0) {
996
+ mgr.logger.log(`[SYNC] Order ${orderId} (slot ${matchedGridOrder.id}) other-side (${otherSize}) rounds to 0. Treating as full fill to trigger rotation.`, 'info');
997
+ isEffectivelyFull = true;
998
+ }
999
+ }
1000
+ const filledOrders = [];
1001
+ const updatedOrders = [];
1002
+ if (isEffectivelyFull) {
1003
+ mgr.logger.log(`[SYNC] Full fill for order ${orderId} (slot ${matchedGridOrder.id}).`, 'info');
1004
+ const filledOrder = {
1005
+ ...matchedGridOrder,
1006
+ blockNum: blockNum,
1007
+ historyId: historyId,
1008
+ isMaker: isMaker // Preserve maker/taker flag for accurate fee calculation
1009
+ };
1010
+ const spreadOrder = convertToSpreadPlaceholder(matchedGridOrder);
1011
+ const fullOk = await mgr._updateOrder(spreadOrder, 'handle-fill-full', { skipAccounting: false, fee: 0 });
1012
+ if (fullOk === false) {
1013
+ mgr.logger.log(`[SYNC] Failed to convert filled order ${orderId} to spread placeholder`, 'warn');
1014
+ }
1015
+ filledOrders.push(filledOrder);
1016
+ return { filledOrders, updatedOrders, partialFill: false };
1017
+ }
1018
+ else {
1019
+ mgr.logger.log(`[SYNC] Partial fill for order ${orderId} (slot ${matchedGridOrder.id}): newSize=${newSize}`, 'info');
1020
+ const filledPortion = {
1021
+ ...matchedGridOrder,
1022
+ size: filledAmount,
1023
+ isPartial: true,
1024
+ blockNum: blockNum,
1025
+ historyId: historyId,
1026
+ isMaker: isMaker // Preserve maker/taker flag for accurate fee calculation
1027
+ };
1028
+ const { btsFeeState, ...matchedWithoutDeferredFee } = matchedGridOrder;
1029
+ let updatedOrder = { ...matchedWithoutDeferredFee, state: ORDER_STATES.PARTIAL };
1030
+ // Update cached raw order integer instead of deleting it.
1031
+ // If the drift refetch above pulled a fresh chain value, use
1032
+ // it as the new baseline (subtracting this fill) and stamp
1033
+ // fetchedAt so the cache is consistent for the next fill.
1034
+ if (updatedOrder.rawOnChain && updatedOrder.rawOnChain.for_sale !== undefined) {
1035
+ const baselineForSale = (chainRefetched && Number.isFinite(effectiveRawForSale))
1036
+ ? effectiveRawForSale
1037
+ : toFiniteNumber(updatedOrder.rawOnChain.for_sale);
1038
+ const nextForSaleInt = Math.max(0, Math.round(baselineForSale) - filledAmountInt);
1039
+ updatedOrder.rawOnChain = {
1040
+ ...updatedOrder.rawOnChain,
1041
+ for_sale: String(nextForSaleInt),
1042
+ fetchedAt: Date.now()
1043
+ };
1044
+ }
1045
+ const nextOrder = await applyChainSizeToGridOrder(mgr, updatedOrder, newSize);
1046
+ if (nextOrder) {
1047
+ updatedOrder = { ...updatedOrder, ...nextOrder };
1048
+ }
1049
+ const partialOk = await mgr._updateOrder(updatedOrder, 'handle-fill-partial', { skipAccounting: false, fee: 0 });
1050
+ if (partialOk === false) {
1051
+ mgr.logger.log(`[SYNC] Failed to update partially filled order ${orderId}`, 'warn');
1052
+ }
1053
+ updatedOrders.push(updatedOrder);
1054
+ filledOrders.push(filledPortion);
1055
+ return { filledOrders, updatedOrders, partialFill: true };
1056
+ }
1057
+ }
1058
+ finally {
1059
+ await mgr.resumeFundRecalc();
1060
+ }
1061
+ }
1062
+ finally {
1063
+ mgr.unlockOrders([...orderIdsToLock]);
1064
+ }
1065
+ }
1066
+ /**
1067
+ * High-level dispatcher for different blockchain synchronization sources.
1068
+ * Routes to the appropriate sync strategy based on the data source.
1069
+ *
1070
+ * SOURCES AND STRATEGIES:
1071
+ * ========================================================================
1072
+ * source: 'createOrder'
1073
+ * Purpose: Grid order was successfully placed on-chain
1074
+ * Data: { gridOrderId, chainOrderId, isPartialPlacement, fee }
1075
+ * Action:
1076
+ * 1. Look up grid order by gridOrderId
1077
+ * 2. Assign the returned chainOrderId (so we can find it later)
1078
+ * 3. Transition state based on isPartialPlacement:
1079
+ * - false → ACTIVE (full order placed)
1080
+ * - true → PARTIAL (placed as partial, likely due to insufficient funds)
1081
+ * 4. Update optimistic chainFree balance (deduct fees if BTS pair)
1082
+ * Fund Impact: Funds transition from free → locked/committed
1083
+ *
1084
+ * source: 'cancelOrder'
1085
+ * Purpose: Grid order was successfully cancelled on-chain
1086
+ * Data: The chainOrderId to cancel
1087
+ * Action:
1088
+ * 1. Find grid order by orderId (reverse lookup)
1089
+ * 2. Transition to VIRTUAL (order no longer on-chain)
1090
+ * 3. Clear orderId so it can be re-used
1091
+ * 4. Update optimistic chainFree balance (add funds back as free)
1092
+ * Fund Impact: Funds transition from locked → free
1093
+ * Note: This is used for direct cancellations (not rotation/consolidation)
1094
+ *
1095
+ * source: 'readOpenOrders' or 'periodicBlockchainFetch'
1096
+ * Purpose: Full snapshot sync of all open orders from blockchain
1097
+ * Data: Array of chain orders from blockchain API
1098
+ * Action: Delegates to syncFromOpenOrders() for full reconciliation
1099
+ * Use Case: Periodic health check or startup initialization
1100
+ *
1101
+ * FUND TRACKING:
1102
+ * Both 'createOrder' and 'cancelOrder' call updateOptimisticFreeBalance() to
1103
+ * keep the optimistic chainFree balance in sync with actual on-chain state.
1104
+ * This prevents fund leaks where placed orders weren't deducted or cancelled
1105
+ * orders weren't released.
1106
+ *
1107
+ * RETURNS: { newOrders, ordersNeedingCorrection }
1108
+ * Most callers use ordersNeedingCorrection to flag price corrections needed.
1109
+ * Only syncFromOpenOrders() populates ordersNeedingCorrection.
1110
+ *
1111
+ * @param {Object} chainData - Chain event data
1112
+ * @param {string} source - Source identifier ('createOrder', 'cancelOrder', 'readOpenOrders', etc.)
1113
+ * @param {Object} [options] - Sync options forwarded to open-order reconciliation
1114
+ * @returns {Promise<Object>} { newOrders, ordersNeedingCorrection }
1115
+ */
1116
+ async synchronizeWithChain(chainData, source, options = {}) {
1117
+ const mgr = this.manager;
1118
+ if (!mgr.assets)
1119
+ return { newOrders: [], ordersNeedingCorrection: [] };
1120
+ switch (source) {
1121
+ // ... [existing cases: createOrder, cancelOrder] ...
1122
+ case 'createOrder': {
1123
+ const { gridOrderId, chainOrderId, isPartialPlacement, expectedType, fee } = chainData;
1124
+ const runCreate = async () => {
1125
+ // Lock order to prevent concurrent modifications during state transition
1126
+ mgr.lockOrders([gridOrderId]);
1127
+ try {
1128
+ const gridOrder = mgr.orders.get(gridOrderId);
1129
+ if (gridOrder) {
1130
+ // Check if this chain order already exists on grid (rotation case)
1131
+ // If so, fee was already paid when original order was placed - don't deduct again
1132
+ // CRITICAL: Look for ANY order with this orderId, even if it's been transitioned to VIRTUAL
1133
+ const existingOrder = Array.from(mgr.orders.values()).find((o) => o.orderId === chainOrderId && o.id !== gridOrderId);
1134
+ const isRotation = !!existingOrder;
1135
+ // For rotation: transition the old order to VIRTUAL, freeing its capital
1136
+ if (isRotation && existingOrder) {
1137
+ if (!isOrderVirtual(existingOrder)) {
1138
+ const oldVirtualOrder = { ...virtualizeOrder(existingOrder), size: 0 };
1139
+ await mgr._applyOrderUpdate(oldVirtualOrder, 'rotation-cleanup', {
1140
+ skipAccounting: chainData.skipAccounting || false,
1141
+ fee: 0
1142
+ });
1143
+ }
1144
+ else if (hasOnChainId(existingOrder)) {
1145
+ // Already VIRTUAL but still has orderId (from rebalance)
1146
+ // Just clear the orderId to reflect blockchain state
1147
+ const clearedOrder = { ...virtualizeOrder(existingOrder), size: 0 };
1148
+ await mgr._applyOrderUpdate(clearedOrder, 'fill-cleanup', {
1149
+ skipAccounting: chainData.skipAccounting || false,
1150
+ fee: 0
1151
+ });
1152
+ }
1153
+ }
1154
+ const newState = isPartialPlacement ? ORDER_STATES.PARTIAL : ORDER_STATES.ACTIVE;
1155
+ const normalizedExpectedType = (expectedType === ORDER_TYPES.BUY || expectedType === ORDER_TYPES.SELL)
1156
+ ? expectedType
1157
+ : null;
1158
+ const updatedOrder = {
1159
+ ...gridOrder,
1160
+ type: normalizedExpectedType || gridOrder.type,
1161
+ state: newState,
1162
+ orderId: chainOrderId
1163
+ };
1164
+ // Restore btsFeeState from raw chain order data if provided.
1165
+ // After a grid reset, in-memory orders have no fee state but the
1166
+ // chain's limit_order_object stores deferred_fee. Passing it through
1167
+ // chainData.deferredFee allows reconstructing btsFeeState so the fee
1168
+ // lifecycle (cancel refunds, fill maker discounts) uses correct values.
1169
+ if (chainData.deferredFee !== undefined && chainData.deferredFee !== null) {
1170
+ updatedOrder.btsFeeState = { deferredFee: Math.max(0, chainData.deferredFee) };
1171
+ }
1172
+ // Deduced fee (createFee or updateFee) must always be applied to reflect blockchain cost
1173
+ const actualFee = fee;
1174
+ await mgr._applyOrderUpdate(updatedOrder, 'fill-place', {
1175
+ skipAccounting: chainData.skipAccounting || false,
1176
+ fee: actualFee
1177
+ });
1178
+ }
1179
+ }
1180
+ finally {
1181
+ mgr.unlockOrders([gridOrderId]);
1182
+ }
1183
+ };
1184
+ // CRITICAL: serialize with concurrent _applyOrderUpdate on the global
1185
+ // _ordersByState / _ordersByType Sets and the frozen manager.orders Map.
1186
+ // Internal callers that already hold _gridLock (e.g. reconcileGridOrders)
1187
+ // opt out via { gridLockAlreadyHeld: true } to avoid deadlock with the
1188
+ // non-reentrant AsyncLock.
1189
+ if (options?.gridLockAlreadyHeld) {
1190
+ await runCreate();
1191
+ }
1192
+ else if (mgr._gridLock && typeof mgr._gridLock.acquire === 'function') {
1193
+ await mgr._gridLock.acquire(runCreate);
1194
+ }
1195
+ else {
1196
+ throw new Error('synchronizeWithChain(createOrder): _gridLock is missing — cannot proceed without lock');
1197
+ }
1198
+ break;
1199
+ }
1200
+ case 'cancelOrder': {
1201
+ const orderId = typeof chainData === 'object' && chainData !== null
1202
+ ? chainData.orderId
1203
+ : chainData;
1204
+ const clearSize = !!(typeof chainData === 'object' && chainData !== null && chainData.clearSize);
1205
+ let btsFeeData;
1206
+ try {
1207
+ btsFeeData = getAssetFees('BTS');
1208
+ }
1209
+ catch (err) {
1210
+ mgr.logger?.log?.(`[FILL-FEE] Failed to load BTS cancel fee cache: ${err.message}. Using zero-fee fallback.`, 'warn');
1211
+ btsFeeData = {
1212
+ total: 0,
1213
+ createFee: 0,
1214
+ updateFee: 0,
1215
+ cancelFee: 0,
1216
+ makerNetFee: 0,
1217
+ takerNetFee: 0,
1218
+ netFee: 0,
1219
+ isMaker: true,
1220
+ };
1221
+ }
1222
+ const runCancel = async () => {
1223
+ const gridOrder = findMatchingGridOrderByOpenOrder({ orderId }, { orders: mgr.orders, assets: mgr.assets, calcToleranceFn: (p, s, t) => calculatePriceTolerance(p, s, t, mgr.assets), logger: mgr.logger });
1224
+ if (gridOrder) {
1225
+ // Lock both chain orderId and grid order ID to prevent concurrent modifications
1226
+ const orderIds = [orderId, gridOrder.id].filter(Boolean);
1227
+ mgr.lockOrders(orderIds);
1228
+ try {
1229
+ // Re-fetch to ensure we have latest state after acquiring lock
1230
+ const currentGridOrder = mgr.orders.get(gridOrder.id);
1231
+ if (currentGridOrder && currentGridOrder.orderId === orderId) {
1232
+ const nextOrder = clearSize
1233
+ ? { ...virtualizeOrder(currentGridOrder), size: 0 }
1234
+ : virtualizeOrder(currentGridOrder);
1235
+ await mgr._applyOrderUpdate(nextOrder, 'cancel-order', {
1236
+ skipAccounting: false,
1237
+ fee: btsFeeData?.cancelFee || 0
1238
+ });
1239
+ }
1240
+ }
1241
+ finally {
1242
+ mgr.unlockOrders(orderIds);
1243
+ }
1244
+ }
1245
+ else {
1246
+ // CRITICAL: Even if order not in grid, the cancellation fee was still paid on blockchain
1247
+ // Deduct it from account totals to prevent drift.
1248
+ const btsSide = (mgr.config.assetA === 'BTS') ? ORDER_TYPES.SELL : (mgr.config.assetB === 'BTS') ? ORDER_TYPES.BUY : null;
1249
+ if (btsSide && btsFeeData?.cancelFee > 0) {
1250
+ await mgr.accountant.adjustTotalBalance(btsSide, -btsFeeData.cancelFee, 'cancel-order-unmatched-fee');
1251
+ }
1252
+ }
1253
+ };
1254
+ // CRITICAL: same _gridLock rationale as the createOrder case above.
1255
+ if (options?.gridLockAlreadyHeld) {
1256
+ await runCancel();
1257
+ }
1258
+ else if (mgr._gridLock && typeof mgr._gridLock.acquire === 'function') {
1259
+ await mgr._gridLock.acquire(runCancel);
1260
+ }
1261
+ else {
1262
+ throw new Error('synchronizeWithChain(cancelOrder): _gridLock is missing — cannot proceed without lock');
1263
+ }
1264
+ break;
1265
+ }
1266
+ case 'readOpenOrders':
1267
+ // Plain open-order syncs do not imply a fresh account balance fetch,
1268
+ // so they still use optimistic accounting deltas.
1269
+ return this.syncFromOpenOrders(chainData, {
1270
+ skipAccounting: false,
1271
+ fillLockAlreadyHeld: options?.fillLockAlreadyHeld
1272
+ });
1273
+ case 'periodicBlockchainFetch': {
1274
+ // This source is used after fetchAccountTotals() has refreshed
1275
+ // authoritative chain free/locked totals. Applying optimistic
1276
+ // commitment deltas here double-deducts newly adopted or resized
1277
+ // open orders from already-fetched free balances.
1278
+ return this.syncFromOpenOrders(chainData, {
1279
+ skipAccounting: true,
1280
+ fillLockAlreadyHeld: options?.fillLockAlreadyHeld
1281
+ });
1282
+ }
1283
+ }
1284
+ return { newOrders: [], ordersNeedingCorrection: [] };
1285
+ }
1286
+ /**
1287
+ * Fetch account balances from blockchain and update optimistic fund totals.
1288
+ * This is a critical method for financial accuracy and must be called periodically.
1289
+ *
1290
+ * BALANCE FETCHING:
1291
+ * ========================================================================
1292
+ * This method queries the blockchain for the actual account balances in both
1293
+ * assetA and assetB. It retrieves:
1294
+ * - total: Total balance (including locked amounts)
1295
+ * - free: Available balance (not locked in orders)
1296
+ *
1297
+ * These are stored in mgr.accountTotals as:
1298
+ * - sell: assetA total (what we can sell)
1299
+ * - sellFree: assetA available
1300
+ * - buy: assetB total (what we can buy with)
1301
+ * - buyFree: assetB available
1302
+ *
1303
+ * IMPORTANCE FOR FUND TRACKING:
1304
+ * The grid maintains an "optimistic" free balance that tracks fund deductions
1305
+ * as orders transition states. However, the blockchain is the source of truth.
1306
+ * Periodically fetching actual balances allows us to:
1307
+ *
1308
+ * 1. RECONCILE: Detect if optimistic state diverged from reality
1309
+ * Example: If we think buyFree=1000 but blockchain says 950,
1310
+ * something was deducted (fee, slippage, etc.) that we didn't track.
1311
+ *
1312
+ * 2. RECOVER: Identify "orphaned" funds that got stuck somewhere
1313
+ * If actual > optimistic, we can reabsorb the extra into available pool.
1314
+ *
1315
+ * 3. PREVENT OVERSPEND: Use actual totals as the hard ceiling
1316
+ * Even if optimistic calc says we have X funds, we never exceed actual total.
1317
+ *
1318
+ * FUND FORMULA:
1319
+ * At any time, this should hold:
1320
+ * chainTotal = chainFree + chainCommitted
1321
+ * Where:
1322
+ * chainTotal = actual on-chain total from blockchain
1323
+ * chainFree = free balance (unallocated)
1324
+ * chainCommitted = sum of all ACTIVE/PARTIAL order sizes on-chain
1325
+ *
1326
+ * ASSET INITIALIZATION:
1327
+ * First calls initializeAssets() to ensure assetA and assetB metadata is loaded.
1328
+ * Without this, we can't convert between blockchain precision and float values.
1329
+ *
1330
+ * ERROR HANDLING:
1331
+ * Gracefully handles lookup failures. If blockchain fetch fails, we don't crash
1332
+ * but instead log a warning. The system continues with last-known balances.
1333
+ *
1334
+ * @returns {Promise<void>}
1335
+ */
1336
+ async fetchAccountBalancesAndSetTotals() {
1337
+ const mgr = this.manager;
1338
+ try {
1339
+ const { BitShares } = require('../bitshares_client');
1340
+ if (!BitShares || !BitShares.db)
1341
+ return;
1342
+ const accountIdOrName = mgr.accountId || mgr.account || null;
1343
+ if (!accountIdOrName)
1344
+ return;
1345
+ try {
1346
+ await this.initializeAssets();
1347
+ }
1348
+ catch (err) {
1349
+ mgr.logger.log(`[SYNC] initializeAssets failed: ${err.message}`, 'warn');
1350
+ }
1351
+ const assetAId = mgr.assets?.assetA?.id;
1352
+ const assetBId = mgr.assets?.assetB?.id;
1353
+ if (!assetAId || !assetBId)
1354
+ return;
1355
+ const { getOnChainAssetBalances } = require('../chain_orders');
1356
+ const { NATIVE_CLIENT } = require('../constants');
1357
+ const assetList = [assetAId, assetBId];
1358
+ // For non-BTS pairs, also fetch core asset (BTS) balance for fee management
1359
+ if (mgr.config.assetA !== 'BTS' && mgr.config.assetB !== 'BTS') {
1360
+ assetList.push(NATIVE_CLIENT.CHAIN.CORE_ASSET_ID);
1361
+ }
1362
+ const lookup = await getOnChainAssetBalances(accountIdOrName, assetList);
1363
+ const aInfo = lookup?.[assetAId] || lookup?.[mgr.config.assetA];
1364
+ const bInfo = lookup?.[assetBId] || lookup?.[mgr.config.assetB];
1365
+ if (aInfo && bInfo) {
1366
+ await mgr.setAccountTotals({ sell: aInfo.total, sellFree: aInfo.free, buy: bInfo.total, buyFree: bInfo.free });
1367
+ }
1368
+ // Store BTS balance for non-BTS pairs
1369
+ if (mgr.config.assetA !== 'BTS' && mgr.config.assetB !== 'BTS') {
1370
+ const btsInfo = lookup?.[NATIVE_CLIENT.CHAIN.CORE_ASSET_ID];
1371
+ if (btsInfo) {
1372
+ mgr.btsBalance = { free: btsInfo.free, total: btsInfo.total, locked: 0 };
1373
+ }
1374
+ }
1375
+ }
1376
+ catch (err) {
1377
+ mgr.logger.log(`Failed to fetch on-chain balances: ${err.message}`, 'warn');
1378
+ }
1379
+ }
1380
+ /**
1381
+ * Initialize asset metadata for assetA and assetB.
1382
+ * This must be called before any blockchain operations, as asset metadata
1383
+ * (ID, precision) is required for all conversions and lookups.
1384
+ *
1385
+ * WHY ASSET METADATA MATTERS:
1386
+ * ========================================================================
1387
+ * The blockchain and grid use different representations for amounts:
1388
+ *
1389
+ * Blockchain: Uses integers (atomic units based on asset precision)
1390
+ * - BTS: precision 5 → 1 BTS = 100000 satoshis
1391
+ * - USDT: precision 6 → 1 USDT = 1000000 satoshis
1392
+ * Storage on-chain is always integer to prevent floating-point errors
1393
+ *
1394
+ * Grid: Uses floats for all calculations
1395
+ * - Easier to work with for price/size calculations
1396
+ * - Must round-trip correctly through blockchain precision
1397
+ *
1398
+ * Asset Metadata Needed:
1399
+ * 1. asset_id: Required to match orders on-chain
1400
+ * When we see an order selling assetA for assetB, we identify it by comparing
1401
+ * the asset_ids in the sell_price object.
1402
+ *
1403
+ * 2. precision: Required for float ↔ integer conversions
1404
+ * floatToBlockchainInt(1.5, precision=5) = 150000
1405
+ * blockchainToFloat(150000, precision=5) = 1.5
1406
+ *
1407
+ * Without precision, we can't:
1408
+ * - Compare order sizes (float vs blockchain int)
1409
+ * - Calculate fills (precision matters at extreme sizes)
1410
+ * - Match chain orders to grid orders (need correct ID)
1411
+ * - Convert fill amounts to grid sizes
1412
+ *
1413
+ * INITIALIZATION STRATEGY:
1414
+ * Assets are looked up asynchronously via the BitShares API.
1415
+ * The lookup is idempotent: if assets are already initialized, returns immediately.
1416
+ * This allows safe calls from multiple places without redundant lookups.
1417
+ *
1418
+ * ERROR HANDLING:
1419
+ * If asset lookup fails (asset doesn't exist, API error, etc.), the error
1420
+ * is propagated (not caught). This is intentional - a missing asset is a
1421
+ * configuration error that must be fixed before the bot can operate.
1422
+ *
1423
+ * @returns {Promise<void>}
1424
+ */
1425
+ async initializeAssets() {
1426
+ const mgr = this.manager;
1427
+ if (mgr.assets)
1428
+ return;
1429
+ const { BitShares } = require('../bitshares_client');
1430
+ const fetchAssetWithFallback = async (symbol, side) => {
1431
+ try {
1432
+ return await lookupAsset(BitShares, symbol);
1433
+ }
1434
+ catch (err) {
1435
+ // If blockchain lookup fails, check for persisted fallback
1436
+ if (mgr.accountOrders) {
1437
+ const persistedAssets = mgr.accountOrders.loadPersistedAssets();
1438
+ const assetData = (side === 'A') ? persistedAssets?.assetA : persistedAssets?.assetB;
1439
+ if (assetData && assetData.symbol === symbol && typeof assetData.precision === 'number') {
1440
+ mgr.logger.log(`Blockchain lookup failed for ${symbol}: ${err.message}. Persisted data: id=${assetData.id}, precision=${assetData.precision} — refusing stale fallback`, 'error');
1441
+ }
1442
+ }
1443
+ throw err;
1444
+ }
1445
+ };
1446
+ try {
1447
+ mgr.assets = {
1448
+ assetA: await fetchAssetWithFallback(mgr.config.assetA, 'A'),
1449
+ assetB: await fetchAssetWithFallback(mgr.config.assetB, 'B')
1450
+ };
1451
+ }
1452
+ catch (err) {
1453
+ mgr.logger.log(`Asset metadata lookup failed: ${err.message}`, 'error');
1454
+ throw err;
1455
+ }
1456
+ }
1457
+ }
1458
+ module.exports = SyncEngine;
1459
+ //# sourceMappingURL=sync_engine.js.map