@tradelayerprotocol/tradelayer 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/.claude/settings.local.json +13 -0
  2. package/.claude/skills/tl-algo/SKILL.md +255 -0
  3. package/.gitattributes +2 -0
  4. package/.github/workflows/publish.yaml +26 -0
  5. package/4mm.js +163 -0
  6. package/LICENSE +21 -0
  7. package/NPMSwapRefactor.zip +0 -0
  8. package/README.md +217 -0
  9. package/address.sh +26 -0
  10. package/algoAPI.js +581 -0
  11. package/analyzepsbt.js +92 -0
  12. package/apiEx.js +99 -0
  13. package/bb_hyperscalper.js +290 -0
  14. package/bbo_demo.js +111 -0
  15. package/buyer.js +622 -0
  16. package/client.js +50 -0
  17. package/createTxTest.js +26 -0
  18. package/createWallet.js +75 -0
  19. package/daytrader.js +531 -0
  20. package/decodeTest.js +69 -0
  21. package/fundingManager.js +144 -0
  22. package/index.js +4 -0
  23. package/listener.js +27 -0
  24. package/litecoreTxBuilder.js +1128 -0
  25. package/mmEx.js +356 -0
  26. package/networks.js +51 -0
  27. package/orderbook.js +200 -0
  28. package/package.json +34 -0
  29. package/perTradeQueue.js +36 -0
  30. package/projectsTLNPMTLNPM/package-lock.json +162 -0
  31. package/projectsTLNPMTLNPM/package.json +5 -0
  32. package/quick.js +32 -0
  33. package/quickFut.js +37 -0
  34. package/quickSell.js +37 -0
  35. package/relayerClient.js +117 -0
  36. package/run4mm.js +80 -0
  37. package/run_bbo_tracker.js +241 -0
  38. package/seller.js +443 -0
  39. package/session.js +45 -0
  40. package/setup-lin-ltc.sh +139 -0
  41. package/setup-lin.sh +203 -0
  42. package/setup-win-ltc.bat +108 -0
  43. package/setup-win.bat +167 -0
  44. package/spam_screamer_futures.js +222 -0
  45. package/tradelayer.js/.gitattributes +2 -0
  46. package/tradelayer.js/README.md +2 -0
  47. package/tradelayer.js/oldTests/activationTest.js +6 -0
  48. package/tradelayer.js/oldTests/base58.test.js +23 -0
  49. package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
  50. package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
  51. package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
  52. package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
  53. package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
  54. package/tradelayer.js/oldTests/createPayload.js +0 -0
  55. package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
  56. package/tradelayer.js/oldTests/decode.js +205 -0
  57. package/tradelayer.js/oldTests/decodeTest.js +50 -0
  58. package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
  59. package/tradelayer.js/oldTests/encodeDecode.js +340 -0
  60. package/tradelayer.js/oldTests/expressTest.js +29 -0
  61. package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
  62. package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
  63. package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
  64. package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
  65. package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
  66. package/tradelayer.js/oldTests/indexTest.js +26 -0
  67. package/tradelayer.js/oldTests/initTokensTest.js +32 -0
  68. package/tradelayer.js/oldTests/interfaceChild.js +129 -0
  69. package/tradelayer.js/oldTests/listenerChild.js +112 -0
  70. package/tradelayer.js/oldTests/opdecode.js +26 -0
  71. package/tradelayer.js/oldTests/options.js +79 -0
  72. package/tradelayer.js/oldTests/optxtest.js +116 -0
  73. package/tradelayer.js/oldTests/optxtest1.js +64 -0
  74. package/tradelayer.js/oldTests/oracle.test.js +32 -0
  75. package/tradelayer.js/oldTests/orderbook.test.js +36 -0
  76. package/tradelayer.js/oldTests/parsing.js +93 -0
  77. package/tradelayer.js/oldTests/payload.js +13 -0
  78. package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
  79. package/tradelayer.js/oldTests/property.test.js +53 -0
  80. package/tradelayer.js/oldTests/propertyLevel.js +75 -0
  81. package/tradelayer.js/oldTests/propertyTest.js +32 -0
  82. package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
  83. package/tradelayer.js/oldTests/salter.js +14 -0
  84. package/tradelayer.js/oldTests/tally.js +81 -0
  85. package/tradelayer.js/oldTests/tally.test.js +48 -0
  86. package/tradelayer.js/oldTests/tally2.js +124 -0
  87. package/tradelayer.js/oldTests/tally3.js +142 -0
  88. package/tradelayer.js/oldTests/tallyDiag.js +38 -0
  89. package/tradelayer.js/oldTests/testGetRaw.js +40 -0
  90. package/tradelayer.js/oldTests/testHexConvert.js +47 -0
  91. package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
  92. package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
  93. package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
  94. package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
  95. package/tradelayer.js/oldTests/testOPReturn.js +102 -0
  96. package/tradelayer.js/oldTests/testPayload.js +23 -0
  97. package/tradelayer.js/oldTests/testRaw.js +50 -0
  98. package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
  99. package/tradelayer.js/oldTests/testTxBuild +28 -0
  100. package/tradelayer.js/oldTests/testTxBuild.js +42 -0
  101. package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
  102. package/tradelayer.js/oldTests/txUtilsA.js +515 -0
  103. package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
  104. package/tradelayer.js/oldTests/vaults.js +72 -0
  105. package/tradelayer.js/oldTests/volumeIndex.js +117 -0
  106. package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
  107. package/tradelayer.js/output_base64.txt +1 -0
  108. package/tradelayer.js/package-lock.json +9967 -0
  109. package/tradelayer.js/package.json +61 -0
  110. package/tradelayer.js/server/index.js +88 -0
  111. package/tradelayer.js/server/litecoind.exe +0 -0
  112. package/tradelayer.js/src/activation.js +303 -0
  113. package/tradelayer.js/src/adjuster.js +77 -0
  114. package/tradelayer.js/src/amm.js +400 -0
  115. package/tradelayer.js/src/base256.js +55 -0
  116. package/tradelayer.js/src/base94.js +79 -0
  117. package/tradelayer.js/src/channels.js +1163 -0
  118. package/tradelayer.js/src/clearing.js +3109 -0
  119. package/tradelayer.js/src/clearlist.js +364 -0
  120. package/tradelayer.js/src/client.js +295 -0
  121. package/tradelayer.js/src/consensus.js +613 -0
  122. package/tradelayer.js/src/contractRegistry.js +964 -0
  123. package/tradelayer.js/src/db.js +89 -0
  124. package/tradelayer.js/src/init.js +24 -0
  125. package/tradelayer.js/src/insurance.js +347 -0
  126. package/tradelayer.js/src/interface.js +218 -0
  127. package/tradelayer.js/src/interfaceExpress.js +178 -0
  128. package/tradelayer.js/src/iou.js +509 -0
  129. package/tradelayer.js/src/listener.js +226 -0
  130. package/tradelayer.js/src/logic.js +1702 -0
  131. package/tradelayer.js/src/main.js +927 -0
  132. package/tradelayer.js/src/marginMap.js +2165 -0
  133. package/tradelayer.js/src/options.js +126 -0
  134. package/tradelayer.js/src/oracle.js +394 -0
  135. package/tradelayer.js/src/orderbook.js +4123 -0
  136. package/tradelayer.js/src/persistence.js +554 -0
  137. package/tradelayer.js/src/property.js +411 -0
  138. package/tradelayer.js/src/reOrg.js +41 -0
  139. package/tradelayer.js/src/scaling.js +145 -0
  140. package/tradelayer.js/src/tally.js +1275 -0
  141. package/tradelayer.js/src/tradeHistoryManager.js +552 -0
  142. package/tradelayer.js/src/txDecoder.js +584 -0
  143. package/tradelayer.js/src/txEncoder.js +610 -0
  144. package/tradelayer.js/src/txIndex.js +502 -0
  145. package/tradelayer.js/src/txUtils.js +1392 -0
  146. package/tradelayer.js/src/types.js +429 -0
  147. package/tradelayer.js/src/validity.js +3077 -0
  148. package/tradelayer.js/src/vaults.js +430 -0
  149. package/tradelayer.js/src/vesting.js +491 -0
  150. package/tradelayer.js/src/volumeIndex.js +618 -0
  151. package/tradelayer.js/src/walletInterface.js +220 -0
  152. package/tradelayer.js/src/walletListener.js +665 -0
  153. package/tradelayer.js/tests/256decode.js +82 -0
  154. package/tradelayer.js/tests/UTXOracle.js +205 -0
  155. package/tradelayer.js/tests/base94test.js +23 -0
  156. package/tradelayer.js/tests/cancelTxTest.js +62 -0
  157. package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
  158. package/tradelayer.js/tests/decimalTest.js +65 -0
  159. package/tradelayer.js/tests/decoderTest.js +100 -0
  160. package/tradelayer.js/tests/deltaCount.js +47 -0
  161. package/tradelayer.js/tests/deltaCount2.js +60 -0
  162. package/tradelayer.js/tests/interfaceTest.js +37 -0
  163. package/tradelayer.js/tests/mainTest.js +53 -0
  164. package/tradelayer.js/tests/makeActivationTest.js +24 -0
  165. package/tradelayer.js/tests/maxHeightTest.js +49 -0
  166. package/tradelayer.js/tests/reverseHash.js +72 -0
  167. package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
  168. package/tradelayer.js/tests/tallyTest.js +40 -0
  169. package/tradelayer.js/tests/testBuybacks.js +46 -0
  170. package/tradelayer.js/tests/testCodeHash.js +49 -0
  171. package/tradelayer.js/tests/testConsensusHash.js +91 -0
  172. package/tradelayer.js/tests/testDecode.js +30 -0
  173. package/tradelayer.js/tests/testEncodingLengths.js +129 -0
  174. package/tradelayer.js/tests/testGetTx +32 -0
  175. package/tradelayer.js/tests/testGetTx.js +32 -0
  176. package/tradelayer.js/tests/testHexHash.js +32 -0
  177. package/tradelayer.js/tests/testIndexHash.js +35 -0
  178. package/tradelayer.js/tests/testInitContracts.js +38 -0
  179. package/tradelayer.js/tests/testMaxConsensus.js +12 -0
  180. package/tradelayer.js/tests/testMaxSynth.js +44 -0
  181. package/tradelayer.js/tests/testMint.js +21 -0
  182. package/tradelayer.js/tests/testNetwork.js +33 -0
  183. package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
  184. package/tradelayer.js/tests/testRebates.js +32 -0
  185. package/tradelayer.js/tests/testRedeem.js +22 -0
  186. package/tradelayer.js/tests/testTokenTrade.js +39 -0
  187. package/tradelayer.js/tests/testTxBuild.js +42 -0
  188. package/tradelayer.js/tests/testUTXOTrade.js +27 -0
  189. package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
  190. package/tradelayer.js/tests/tradeFutures.js +40 -0
  191. package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
  192. package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
  193. package/tradelayer.js/tests/txScanTest.js +134 -0
  194. package/tradelayer.js/tests/validateTest.js +136 -0
  195. package/tradelayer.js/tests/vestingTest.js +37 -0
  196. package/tradelayer.js/utils/activateMainnet.js +59 -0
  197. package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
  198. package/tradelayer.js/utils/autocompactdb.js +23 -0
  199. package/tradelayer.js/utils/base64toHex.js +32 -0
  200. package/tradelayer.js/utils/broadcastDoge.js +38 -0
  201. package/tradelayer.js/utils/calcRedeem.js +19 -0
  202. package/tradelayer.js/utils/checkNetwork.js +27 -0
  203. package/tradelayer.js/utils/createAddress.js +48 -0
  204. package/tradelayer.js/utils/createAttestation.js +133 -0
  205. package/tradelayer.js/utils/createContract.js +118 -0
  206. package/tradelayer.js/utils/createOracle.js +94 -0
  207. package/tradelayer.js/utils/createwallet.js +20 -0
  208. package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
  209. package/tradelayer.js/utils/crossTokenTrades.js +62 -0
  210. package/tradelayer.js/utils/dumpPriv.js +29 -0
  211. package/tradelayer.js/utils/generateChannel.js +34 -0
  212. package/tradelayer.js/utils/getInfo.js +21 -0
  213. package/tradelayer.js/utils/hardWipe.js +20 -0
  214. package/tradelayer.js/utils/hexTo64.js +16 -0
  215. package/tradelayer.js/utils/importAddress.js +28 -0
  216. package/tradelayer.js/utils/importpriv.js +20 -0
  217. package/tradelayer.js/utils/issueOracleContract.js +67 -0
  218. package/tradelayer.js/utils/issueTokens.js +41 -0
  219. package/tradelayer.js/utils/listunspent.js +66 -0
  220. package/tradelayer.js/utils/litecoinClient.js +30 -0
  221. package/tradelayer.js/utils/loadwallet.js +20 -0
  222. package/tradelayer.js/utils/publishOracle.js +113 -0
  223. package/tradelayer.js/utils/sendActivation.js +21 -0
  224. package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
  225. package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
  226. package/tradelayer.js/utils/sendCommit.js +24 -0
  227. package/tradelayer.js/utils/sendDoge.js +62 -0
  228. package/tradelayer.js/utils/sendDogeMain.js +67 -0
  229. package/tradelayer.js/utils/sendDogeTx.js +46 -0
  230. package/tradelayer.js/utils/sendLTC.js +63 -0
  231. package/tradelayer.js/utils/sendMainnet.js +62 -0
  232. package/tradelayer.js/utils/sendTransfer.js +19 -0
  233. package/tradelayer.js/utils/sendVestTest.js +88 -0
  234. package/tradelayer.js/utils/sendWithdrawal.js +26 -0
  235. package/tradelayer.js/utils/simpleStart.js +8 -0
  236. package/tradelayer.js/utils/startStop.js +27 -0
  237. package/tradelayer.js/utils/structuredTrades.js +136 -0
  238. package/tradelayer.js/utils/verifySignature.js +90 -0
  239. package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
  240. package/tradelayer.js/utils/walletCache.js +172 -0
  241. package/tradelayer.js/utils/walletContractInterface.js +48 -0
  242. package/tradelayer.js/utils/walletFetchTxs.js +66 -0
  243. package/tradelayer.js/utils/walletUtils.js +97 -0
  244. package/tradelayer.js/utils/wipeDB.js +55 -0
  245. package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
  246. package/txEncoder.js +529 -0
  247. package/utility.js +28 -0
  248. package/verifymessage.js +38 -0
  249. package/ws-transport.js +311 -0
@@ -0,0 +1,241 @@
1
+ 'use strict';
2
+ /**
3
+ * bbo_tracker_hf.js
4
+ * Keeps exactly 2 live orders (1 BUY at best bid, 1 SELL at best ask).
5
+ * - Cancels & replaces on >= TICK_TOL ticks of movement.
6
+ * - Per-side sequencing so cancels/places never overlap.
7
+ * - Token-bucket rate limit to avoid flooding server.
8
+ * - Skips updates if Binance BBO is stale.
9
+ * - Logs Binance BBO and every place/cancel.
10
+ *
11
+ * Requires:
12
+ * - ccxt installed (for Binance ticker).
13
+ * - ./algoAPI.js exposing class ApiWrapper with:
14
+ * - constructor(host, port, debug, autoConnect, {address, otherAddrs}, network)
15
+ * - sendOrder(orderDetails) -> Promise<string> (UUID)
16
+ * - cancelOrder(uuid) -> Promise<void>
17
+ *
18
+ * Run: node run_bbo_tracker.js
19
+ */
20
+
21
+
22
+ const ccxt = require('ccxt');
23
+ const ApiWrapper = require('./algoAPI.js');
24
+
25
+ // ===== Config =====
26
+ const CFG = {
27
+ // TL / server
28
+ TL_WS_HOST: '172.81.181.19',
29
+ TL_WS_PORT: 3001,
30
+ TL_NETWORK: 'LTCTEST',
31
+ TL_ADDR: 'tltc1qh4se4w23draju8ef82vdvelz3zj8egflrg2gve',
32
+ TL_PUB: '0342ded4128b00d324eee8bba8c716fc84db004adb63adbeb19b5ac06f8f3b2ab9',
33
+
34
+ BASE_ID: 0, // LTC
35
+ QUOTE_ID: 5, // USDTt
36
+
37
+ // Market source
38
+ SYMBOL_CCXT: 'LTC/USDT',
39
+
40
+ // Behavior
41
+ POLL_MS: 50, // Binance poll interval
42
+ SIZE: 0.10, // order size
43
+ TICK: 0.0001, // price tick
44
+ EDGE_BPS: 0.0, // pad off BBO (0 = exact mirror)
45
+
46
+ // HF stability
47
+ STALE_MS: 250, // if BBO older than this, skip
48
+ MIN_REPLACE_MS: 50, // min gap per-side between replaces
49
+ TICK_TOL: 1.0, // require >= this many ticks to replace
50
+ MAX_OPS_PER_SEC: 200, // global cap (place+cancel) across both sides
51
+ CANCEL_PLACE_GAP_MS: 10, // small delay after cancel before place
52
+
53
+ // Timeouts
54
+ PLACE_TIMEOUT_MS: 5000,
55
+ CANCEL_TIMEOUT_MS: 5000,
56
+ };
57
+
58
+ // ===== External deps =====
59
+ const binance = new ccxt.binance({ enableRateLimit: true });
60
+ const api = new ApiWrapper(
61
+ CFG.TL_WS_HOST,
62
+ CFG.TL_WS_PORT,
63
+ true, // debug (or tlOn) — unchanged
64
+ true, // autoConnect — unchanged
65
+ CFG.TL_ADDR,
66
+ CFG.TL_PUB,
67
+ CFG.TL_NETWORK
68
+ );
69
+
70
+
71
+ // ===== Helpers =====
72
+ const sleep = (ms) => new Promise(r => setTimeout(r, ms));
73
+ const bps = (x) => x / 10000.0;
74
+ const roundDown = (x, tick) => Math.floor(x / tick) * tick;
75
+ const roundUp = (x, tick) => Math.ceil(x / tick) * tick;
76
+ const num = (v, d=8) => Number(Number(v).toFixed(d));
77
+
78
+ async function withTimeout(p, ms, tag) {
79
+ let t; const killer = new Promise((_, rej) => { t = setTimeout(() => rej(new Error(`${tag||'op'} timeout`)), ms); });
80
+ try { return await Promise.race([p, killer]); } finally { clearTimeout(t); }
81
+ }
82
+
83
+ function pxChanged(oldPx, newPx, tick, tolTicks) {
84
+ if (oldPx == null) return true;
85
+ return Math.abs(oldPx - newPx) >= tick * tolTicks;
86
+ }
87
+
88
+ // ===== TL order builders (canonical TL shape via props) =====
89
+ function toTLBuy(cfg, price, amount) {
90
+ return {
91
+ type: 'SPOT',
92
+ action: 'BUY',
93
+ keypair: {
94
+ address: cfg.TL_ADDR,
95
+ pubkey: cfg.TL_PUB
96
+ },
97
+ props: {
98
+ id_for_sale: cfg.BASE_ID, // selling quote (USDTt)
99
+ id_desired: cfg.QUOTE_ID, // buying base (TLTC)
100
+ price: Number(price),
101
+ amount: Number(amount),
102
+ transfer: false
103
+ },
104
+ isLimitOrder: true
105
+ };
106
+ }
107
+
108
+ function toTLSell(cfg, price, amount) {
109
+ return {
110
+ type: 'SPOT',
111
+ action: 'SELL',
112
+ keypair: {
113
+ address: cfg.TL_ADDR,
114
+ pubkey: cfg.TL_PUB
115
+ },
116
+ props: {
117
+ id_for_sale: cfg.QUOTE_ID, // selling base (TLTC)
118
+ id_desired: cfg.BASE_ID, // receiving quote (USDTt)
119
+ price: Number(price),
120
+ amount: Number(amount),
121
+ transfer: false
122
+ },
123
+ isLimitOrder: true
124
+ };
125
+ }
126
+
127
+
128
+ // ===== Token-bucket rate limiter =====
129
+ class TokenBucket {
130
+ constructor(rps) { this.capacity = rps; this.tokens = rps; this.last = Date.now(); }
131
+ take(n = 1) {
132
+ const now = Date.now();
133
+ const dt = (now - this.last) / 1000;
134
+ this.tokens = Math.min(this.capacity, this.tokens + dt * this.capacity);
135
+ this.last = now;
136
+ if (this.tokens >= n) { this.tokens -= n; return true; }
137
+ return false;
138
+ }
139
+ }
140
+ const bucket = new TokenBucket(CFG.MAX_OPS_PER_SEC);
141
+
142
+ // ===== State =====
143
+ let live = { BUY: null, SELL: null }; // { uuid, px }
144
+ let sideBusy = { BUY: false, SELL: false };// per-side sequencing lock
145
+ let sideLast = { BUY: 0, SELL: 0 }; // last replace time per-side
146
+ let lastBBO = { bid: null, ask: null };
147
+ let lastBBOts = 0;
148
+
149
+ // ===== TL ops =====
150
+ async function place(side, px, sz) {
151
+ if (sideBusy[side]) return;
152
+ if (!bucket.take()) return;
153
+ sideBusy[side] = true;
154
+ try {
155
+ const det = side === 'BUY' ? toTLBuy(CFG, px, sz) : toTLSell(CFG, px, sz);
156
+ console.log('[TL] sendOrder request', det);
157
+ const uuid = await withTimeout(api.sendOrder(det), CFG.PLACE_TIMEOUT_MS, 'place');
158
+ const id = uuid?.orderUuid || uuid;
159
+ live[side] = { uuid: id, px };
160
+ sideLast[side] = Date.now();
161
+ console.log('PLACED', side, num(px, 6), 'uuid=', id);
162
+ } catch (e) {
163
+ console.log('PLACE FAIL', side, px, e?.message || e);
164
+ } finally {
165
+ sideBusy[side] = false;
166
+ }
167
+ }
168
+
169
+ async function cancel(side, reason) {
170
+ const cur = live[side];
171
+ if (!cur?.uuid) return;
172
+ if (sideBusy[side]) return;
173
+ if (!bucket.take()) return;
174
+ sideBusy[side] = true;
175
+ try {
176
+ await withTimeout(api.cancelOrder(cur.uuid), CFG.CANCEL_TIMEOUT_MS, 'cancel');
177
+ console.log('CANCELED', side, num(cur.px, 6), 'uuid=', cur.uuid, 'reason=', reason);
178
+ live[side] = null;
179
+ } catch (e) {
180
+ console.log('CANCEL FAIL', side, cur.uuid, e.message || e);
181
+ } finally {
182
+ sideBusy[side] = false;
183
+ }
184
+ }
185
+
186
+ function targetsFromBBO(bid, ask) {
187
+ const b = Math.max(0, roundDown(bid * (1 - (CFG.EDGE_BPS / 10000.0)), CFG.TICK));
188
+ const a = Math.max(0, roundUp( ask * (1 + (CFG.EDGE_BPS / 10000.0)), CFG.TICK));
189
+ return { buyPx: b, sellPx: a };
190
+ }
191
+
192
+ // ===== Main loop =====
193
+ async function tickOnce() {
194
+ // 1) Fetch BBO
195
+ const tkr = await binance.fetchTicker(CFG.SYMBOL_CCXT);
196
+ const bid = Number(tkr.bid), ask = Number(tkr.ask);
197
+ if (!isFinite(bid) || !isFinite(ask)) return;
198
+ lastBBO = { bid, ask }; lastBBOts = Date.now();
199
+ console.log(`[BINANCE] ${CFG.SYMBOL_CCXT} bid=${bid} ask=${ask}`);
200
+
201
+ // 2) Freshness check
202
+ if (Date.now() - lastBBOts > CFG.STALE_MS) return;
203
+
204
+ // 3) Compute targets
205
+ const { buyPx, sellPx } = targetsFromBBO(bid, ask);
206
+
207
+ // 4) BUY side replace logic
208
+ if (!sideBusy.BUY) {
209
+ const old = live.BUY?.px ?? null;
210
+ const ageOk = Date.now() - sideLast.BUY >= CFG.MIN_REPLACE_MS;
211
+ if (ageOk && pxChanged(old, buyPx, CFG.TICK, CFG.TICK_TOL)) {
212
+ await cancel('BUY', 'replace');
213
+ if (CFG.CANCEL_PLACE_GAP_MS) await sleep(CFG.CANCEL_PLACE_GAP_MS);
214
+ await place('BUY', buyPx, CFG.SIZE);
215
+ }
216
+ }
217
+
218
+ // 5) SELL side replace logic
219
+ if (!sideBusy.SELL) {
220
+ const old = live.SELL?.px ?? null;
221
+ const ageOk = Date.now() - sideLast.SELL >= CFG.MIN_REPLACE_MS;
222
+ if (ageOk && pxChanged(old, sellPx, CFG.TICK, CFG.TICK_TOL)) {
223
+ await cancel('SELL', 'replace');
224
+ if (CFG.CANCEL_PLACE_GAP_MS) await sleep(CFG.CANCEL_PLACE_GAP_MS);
225
+ await place('SELL', sellPx, CFG.SIZE);
226
+ }
227
+ }
228
+ }
229
+
230
+ (async () => {
231
+ console.log('Starting BBO tracker (2 orders, HF-stable)…');
232
+ await sleep(10000);
233
+ while (true) {
234
+ try {
235
+ await tickOnce();
236
+ } catch (e) {
237
+ console.log('Loop error:', e.message || e);
238
+ }
239
+ await sleep(CFG.POLL_MS);
240
+ }
241
+ })();
package/seller.js ADDED
@@ -0,0 +1,443 @@
1
+ const litecore = require('bitcore-lib-ltc');
2
+ const Encode = require('./tradelayer.js/src/txEncoder.js');
3
+ const { buildLitecoinTransaction, buildTokenTradeTransaction, buildFuturesTransaction, getUTXOFromCommit,signPsbtRawTx } = require('./litecoreTxBuilder');
4
+ const WalletListener = require('./tradelayer.js/src/walletInterface.js');
5
+ const util = require('util');
6
+ const BigNumber = require('bignumber.js');
7
+
8
+ class SellSwapper {
9
+ constructor(typeTrade, tradeInfo, sellerInfo, buyerInfo, client, socket,test,tradeUUID) {
10
+ this.typeTrade = typeTrade;
11
+ this.tradeInfo = tradeInfo;
12
+ this.sellerInfo = sellerInfo;
13
+ this.buyerInfo = buyerInfo;
14
+ this.myInfo = sellerInfo
15
+ this.cpInfo = buyerInfo
16
+ this.socket = socket;
17
+ this.client = client;
18
+ this.test = test
19
+ this.multySigChannelData = null
20
+ this.tradeStartTime = Date.now();
21
+ this.tradeUUID = tradeUUID
22
+ // Promisify methods for the given client
23
+ this.getRawTransactionAsync = util.promisify(this.client.getRawTransaction.bind(this.client));
24
+ this.getBlockDataAsync = util.promisify(this.client.getBlock.bind(this.client));
25
+ this.createRawTransactionAsync = util.promisify(this.client.createRawTransaction.bind(this.client));
26
+ this.listUnspentAsync = util.promisify(this.client.cmd.bind(this.client, 'listunspent'));
27
+ this.decoderawtransactionAsync = util.promisify(this.client.cmd.bind(this.client, 'decoderawtransaction'));
28
+ this.dumpprivkeyAsync = util.promisify(this.client.cmd.bind(this.client, 'dumpprivkey'));
29
+ this.sendrawtransactionAsync = util.promisify(this.client.cmd.bind(this.client, 'sendrawtransaction'));
30
+ this.validateAddress = util.promisify(this.client.cmd.bind(this.client, 'validateaddress'));
31
+ this.getBlockCountAsync = util.promisify(this.client.cmd.bind(this.client, 'getblockcount'));
32
+ this.addMultisigAddressAsync = util.promisify(this.client.cmd.bind(this.client, 'addmultisigaddress'));
33
+ this.signrawtransactionwithwalletAsync = util.promisify(this.client.cmd.bind(this.client, 'signrawtransactionwithwallet'));
34
+ this.signrawtransactionwithkeyAsync = util.promisify(this.client.cmd.bind(this.client, 'signrawtransactionwithkey'));
35
+
36
+ this.handleOnEvents();
37
+ this.onReady();
38
+ this.initTrade();
39
+ }
40
+
41
+ logTime(stage) {
42
+ const currentTime = Date.now();
43
+ console.log(`Time taken for ${stage}: ${currentTime - this.tradeStartTime} ms`);
44
+ }
45
+
46
+ onReady() {
47
+ return new Promise((resolve, reject) => {
48
+ this.readyRes = resolve;
49
+ // If the readyRes is not called within 60 seconds, terminate the trade
50
+ setTimeout(() => this.terminateTrade('Undefined Error code 1'), 60000);
51
+ });
52
+ }
53
+
54
+ bip67SortPubKeys(pubKeys) {
55
+ return [...pubKeys].sort((a, b) =>
56
+ Buffer.from(a, 'hex').compare(Buffer.from(b, 'hex'))
57
+ );
58
+ }
59
+
60
+ async ensureFuturesMargin(tradeProps) {
61
+ if (this._futuresMargin) return this._futuresMargin;
62
+
63
+ if (!tradeProps || !tradeProps.contract_id || !tradeProps.price || !tradeProps.amount) {
64
+ throw new Error('Invalid futures trade props for margin calculation');
65
+ }
66
+
67
+ const { contract_id, price, amount } = tradeProps;
68
+
69
+ // 1) Fetch contract info
70
+ console.log('contract id '+contract_id)
71
+ const contractInfo = await WalletListener.getContractInfo(contract_id);
72
+ if (!contractInfo) {
73
+ throw new Error(`No contract info for contract ${contract_id}`);
74
+ }
75
+
76
+ // 2) Per-contract margin
77
+ const perContractMargin = await WalletListener.getInitialMargin(
78
+ contract_id,
79
+ price
80
+ );
81
+
82
+ if (!perContractMargin || perContractMargin <= 0) {
83
+ throw new Error('Invalid per-contract margin');
84
+ }
85
+
86
+ // 3) Scale by number of contracts
87
+ const initMargin = perContractMargin * amount;
88
+
89
+ // 4) Collateral propertyId
90
+ const collateral = contractInfo.collateralPropertyId;
91
+
92
+ if (!collateral || initMargin <= 0) {
93
+ throw new Error('Computed invalid futures margin parameters');
94
+ }
95
+
96
+ this._futuresMargin = {
97
+ collateral,
98
+ initMargin,
99
+ perContractMargin,
100
+ leverage: contractInfo.leverage,
101
+ inverse: contractInfo.inverse
102
+ };
103
+
104
+ console.log('[FUTURES] margin prepared', this._futuresMargin);
105
+ return this._futuresMargin;
106
+ }
107
+
108
+
109
+ async sendTxWithSpecRetry(rawTx) {
110
+ const _sendTxWithRetry = async (rawTx, retriesLeft, ms) => {
111
+ try {
112
+ // Attempt to send the transaction
113
+ const result = await this.sendrawtransactionAsync(rawTx);
114
+ // If there's an error and retries are left, try again
115
+ if (result.error && result.error.includes('bad-txns-inputs-missingorspent') && retriesLeft > 0) {
116
+ await new Promise(resolve => setTimeout(resolve, ms));
117
+ console.log('Retrying to send the transaction... Remaining retries:', retriesLeft);
118
+ return _sendTxWithRetry(rawTx, retriesLeft - 1, ms);
119
+ }
120
+ // If successful, return the result
121
+ return result;
122
+ } catch (error) {
123
+ // If an error occurs during sendrawtransactionAsync, handle it here
124
+ console.error('Error during transaction send:', error.message);
125
+ if (retriesLeft > 0) {
126
+ console.log('Retrying after error... Remaining retries:', retriesLeft);
127
+ await new Promise(resolve => setTimeout(resolve, ms));
128
+ return _sendTxWithRetry(rawTx, retriesLeft - 1, ms);
129
+ }
130
+ return { error: 'Transaction failed after retries' }; // Return an error after all retries
131
+ }
132
+ }
133
+
134
+ // Start the retry process with 15 retries and 800ms interval
135
+ return _sendTxWithRetry(rawTx, 15, 1200);
136
+ }
137
+
138
+
139
+ removePreviousListeners() {
140
+ // Correctly using template literals with backticks
141
+ this.socket.off(`${this.cpInfo.socketId}::swap`);
142
+ }
143
+
144
+ terminateTrade(reason){
145
+ // Emit the TERMINATE_TRADE event to the socket
146
+ const eventData = {event:'TERMINATE_TRADE', socketId: this.myInfo.socketId, reason: reason};
147
+ const tag = `${this.myInfo.socketId}::swap`; // Correct string concatenation
148
+ this.socket.emit(tag, eventData);
149
+ this.removePreviousListeners();
150
+ }
151
+
152
+ handleOnEvents() {
153
+ this.removePreviousListeners()
154
+ const eventName = `${this.buyerInfo.socketId}::swap`;
155
+ this.socket.on(eventName, async (eventData) => {
156
+ const { socketId, data } = eventData;
157
+ if (eventData.data?.tradeUUID && eventData.data.tradeUUID !== this.tradeUUID){
158
+ return;
159
+ }
160
+ switch (eventData.eventName) {
161
+ case 'BUYER:STEP2':
162
+ await this.onStep2(socketId, data);
163
+ break;
164
+ case 'BUYER:STEP4':
165
+ await this.onStep4(socketId, data.psbtHex, data.commitTxId);
166
+ break;
167
+ case 'BUYER:STEP6':
168
+ await this.onStep6(socketId, data);
169
+ break;
170
+ default:
171
+ break;
172
+ }
173
+ });
174
+ }
175
+
176
+ async initTrade() {
177
+ try {
178
+ const pubKeys = bip67SortPubKeys([
179
+ this.cpInfo.keypair.pubkey,
180
+ this.myInfo.keypair.pubkey,
181
+ ]);
182
+ if (this.typeTrade === 'SPOT' && 'propIdDesired' in this.tradeInfo.props){
183
+ let { propIdDesired, propIdForSale } = this.tradeInfo.props;
184
+ if(propIdDesired==0||propIdForSale==0){
185
+ pubKeys = [this.buyerInfo.keypair.pubkey,this.sellerInfo.keypair.pubkey];
186
+ }
187
+ }
188
+ console.log('pubkeys for multisig '+JSON.stringify(pubKeys))
189
+ const multisigAddress = await this.addMultisigAddressAsync(2, pubKeys);
190
+ this.multySigChannelData = multisigAddress
191
+
192
+ console.log('generating multisig in sell init '+JSON.stringify(multisigAddress))
193
+ const validateMS = await this.validateAddress(multisigAddress.address.toString());
194
+ console.log('validated '+JSON.stringify(validateMS))
195
+ if (validateMS.error || !validateMS.isvalid) throw new Error(`Multisig address validation failed`);
196
+
197
+ this.multySigChannelData = { address: multisigAddress.address.toString(), redeemScript: multisigAddress.redeemScript.toString(), scriptPubKey: validateMS.scriptPubKey };
198
+ console.log('checking this.multisig '+JSON.stringify(this.multySigChannelData))
199
+ console.log('my info socket id '+this.myInfo.socketId+' '+this.sellerInfo.socketId)
200
+ const swapEvent = { eventName: 'SELLER:STEP1', socketId: this.myInfo.socketId, data: this.multySigChannelData };
201
+ console.log('show socket obj '+JSON.stringify(this.socket.emit)+' '+JSON.stringify(this.socket)+' '+this.socket)
202
+ this.socket.emit(`${this.myInfo.socketId}::swap`, swapEvent);
203
+ } catch (error) {
204
+ console.error(`InitTrade Error: ${error.message}`);
205
+ }
206
+ }
207
+
208
+ async onStep2(cpId) {
209
+ this.logTime('Step 2 Start');
210
+
211
+ // --- basic guards (same behavior, clearer logs) ---
212
+ if (!this.multySigChannelData?.address) {
213
+ throw new Error(`No Multisig Address`);
214
+ }
215
+ if (cpId !== this.buyerInfo?.socketId) {
216
+ throw new Error(`Connection Error`);
217
+ }
218
+
219
+ // --- extract trade props; support SPOT (propIdDesired/amountDesired) and FUTURES (collateral/initMargin) ---
220
+ const tprops = this.tradeInfo?.props ?? {};
221
+ console.log('props in step 2 '+JSON.stringify(tprops))
222
+ const isFutures = ('contract_id' in tprops)
223
+
224
+ // SPOT defaults (original names)
225
+ const propIdForSale = tprops.propIdForSale ?? tprops.propertyId ?? 0;
226
+ const amountDesired = tprops.amountForSale ?? tprops.amount ?? 0;
227
+ const transfer = !!(tprops.transfer ?? false);
228
+
229
+ // FUTURES defaults (desktop parity)
230
+ let initMargin =0
231
+ let collateral = 0
232
+
233
+ if(isFutures){
234
+ const margin = await this.ensureFuturesMargin(tprops)
235
+ initMargin= margin.initMargin
236
+ collateral= margin.collateral
237
+ }
238
+
239
+ // --- Column A/B detection (use RPC if available; otherwise default 'A') ---
240
+ let isColumnA = true;
241
+ try {
242
+ if (typeof WalletListener?.getColumn === 'function') {
243
+ const col = await WalletListener.getColumn(
244
+ this.sellerInfo?.keypair?.address,
245
+ this.buyerInfo?.keypair?.address
246
+ );
247
+ const tag = col?.data ?? col; // some impls return {data:'A'|'B'}
248
+ isColumnA = (tag === 'A');
249
+ } else {
250
+ // your original hardcode
251
+ const columnRes = 'A';
252
+ // NOTE: your old code used columnRes.data; that would always be undefined.
253
+ isColumnA = (columnRes === 'A');
254
+ }
255
+ } catch (_) {
256
+ // fall back to A, no crash
257
+ isColumnA = true;
258
+ }
259
+
260
+ // --- build TL payload (commit/transfer; spot vs futures) ---
261
+ let payload;
262
+ if (transfer) {
263
+ // transfer path uses desired SPOT fields; if FUTURES provided, prefer futures collateral/initMargin
264
+ const propertyId = isFutures ? collateral : propIdForSale;
265
+ const amount = isFutures ? initMargin : amountDesired;
266
+
267
+ payload = Encode.encodeTransfer({
268
+ propertyId,
269
+ amount,
270
+ isColumnA,
271
+ destinationAddr: this.multySigChannelData.address,
272
+ });
273
+ } else {
274
+ // commit path; keep your encodeCommit API
275
+ const propertyId = isFutures ? collateral : propIdForSale;
276
+ const amount = isFutures ? initMargin : amountDesired;
277
+
278
+ payload = Encode.encodeCommit({
279
+ amount,
280
+ propertyId,
281
+ channelAddress: this.multySigChannelData.address,
282
+ });
283
+ }
284
+
285
+ // --- UTXO selection (largest-first) ---
286
+ console.log('calling list unspent ' + this.sellerInfo?.keypair?.address);
287
+ const utxos = await this.listUnspentAsync(0, 999999, [this.sellerInfo.keypair.address]) ?? [];
288
+ if (!Array.isArray(utxos) || utxos.length === 0) {
289
+ throw new Error('No UTXOs found for seller');
290
+ }
291
+
292
+ const sortedUTXOs = utxos.sort((a, b) =>
293
+ new BigNumber(b?.amount ?? 0).comparedTo(a?.amount ?? 0)
294
+ );
295
+
296
+ const largestUtxo = sortedUTXOs[0];console.log('Largest UTXO:', JSON.stringify(largestUtxo));
297
+ const commitUTXOs = [{
298
+ txid: largestUtxo?.txid ?? largestUtxo?.txId,
299
+ vout: largestUtxo?.vout ?? largestUtxo?.n ?? 0,
300
+ scriptPubKey: largestUtxo?.scriptPubKey,
301
+ amount: largestUtxo?.amount
302
+ }];
303
+
304
+ console.log('commitUTXOs:', JSON.stringify(commitUTXOs));
305
+
306
+ // --- OP_RETURN payload hex ---
307
+ const hexPayload = Buffer.from(payload ?? '', 'utf8').toString('hex');
308
+ console.log('payload ' + payload + ' hex ' + hexPayload);
309
+
310
+ // --- inputs/outputs (don’t assume vout 0 for the channel; we’ll decode below) ---
311
+ const _insForRawTx = commitUTXOs.map(({ txid, vout }) => ({ txid, vout }));
312
+
313
+ const dust = 0.000056;
314
+ const feeSats = 0.000030; // tweak to match current fee market
315
+ const change = new BigNumber(largestUtxo?.amount ?? 0).minus(dust).minus(feeSats).toNumber();
316
+ if (!(change > 0)) {
317
+ throw new Error('Insufficient UTXO for dust+fee');
318
+ }
319
+
320
+ const _outsForRawTx = [
321
+ { [this.multySigChannelData.address]: dust },
322
+ { [this.myInfo.keypair.address]: change },
323
+ { data: hexPayload }
324
+ ];
325
+
326
+ console.log(
327
+ 'inputs for create raw tx ' + JSON.stringify(_insForRawTx) +
328
+ ' outs ' + JSON.stringify(_outsForRawTx)
329
+ );
330
+
331
+ // --- create / decode / sign / send (same surface as your original) ---
332
+ let crtRes = await this.createRawTransactionAsync(_insForRawTx, _outsForRawTx);
333
+
334
+ const decoded = await this.decoderawtransactionAsync(crtRes);
335
+ console.log('decoded ' + JSON.stringify(decoded));
336
+ console.log('created commit tx ' + crtRes + ' type of ' + typeof(crtRes));
337
+
338
+ const wif = await this.dumpprivkeyAsync(this.myInfo.keypair.address);
339
+ const signResKey = await this.signrawtransactionwithkeyAsync(crtRes, [wif]);
340
+ console.log('signed with key ' + JSON.stringify(signResKey));
341
+
342
+ const sendRes = await this.sendrawtransactionAsync(signResKey?.hex);
343
+ if (!sendRes) return new Error(`Failed to broadcast the transaction`);
344
+ console.log('sent commit ' + JSON.stringify(sendRes));
345
+
346
+ // --- locate the actual channel vout by address (don’t hardcode index 0) ---
347
+ const voutArr = decoded?.vout ?? [];
348
+ const channelOut = voutArr.find(o =>
349
+ o?.scriptPubKey?.addresses?.[0] === this.multySigChannelData?.address
350
+ ) || voutArr.find(o => o?.scriptPubKey?.asm?.includes(this.multySigChannelData?.address));
351
+
352
+ if (!channelOut) {
353
+ throw new Error('No matching vout for commit UTXO');
354
+ }
355
+
356
+ const utxoData = {
357
+ amount: channelOut.value ?? dust,
358
+ vout: channelOut.n ?? 0,
359
+ txid: sendRes,
360
+ scriptPubKey: this.multySigChannelData.scriptPubKey,
361
+ redeemScript: this.multySigChannelData.redeemScript,
362
+ };
363
+
364
+ const swapEvent = { eventName: 'SELLER:STEP3', socketId: this.myInfo.socketId, data: utxoData };
365
+ // keep your surface; if you want the NPM style route, flip this to myInfo
366
+ this.socket.emit(`${this.sellerInfo.socketId}::swap`, swapEvent);
367
+ }
368
+
369
+ async onStep4(cpId, psbtHex, commitTxId /* optional: only provided on token-channel flows */) {
370
+ this.logTime('Step 4 Start');
371
+ if (this._step4InFlight) return;
372
+ this._step4InFlight = true;
373
+ //try {
374
+ console.log('cpiID '+cpId +' buyer socket '+this.buyerInfo.socketId)
375
+ console.log('deets '+psbtHex+' '+commitTxId)
376
+ // 1) basic sanity
377
+ if (cpId !== this.buyerInfo?.socketId) return new Error(`Connection Error`);
378
+ if (!psbtHex) return new Error(`Missing PSBT Hex`);
379
+
380
+ // 2) optional anti-RBF check on the commit tx (if caller supplies it)
381
+ if (commitTxId) {
382
+ try {
383
+ // Prefer an async wrapper if you have it; else fallback to raw RPC
384
+ const res = await this.getRawTransactionAsync(commitTxId, true);
385
+
386
+ console.log('res '+JSON.stringify(res))
387
+ const vins = res.vin;
388
+ if (!Array.isArray(vins)) throw new Error('vin missing');
389
+
390
+ // BIP-125: any sequence < 0xFFFFFFFE signals opt-in RBF
391
+ const isRbf = vins.some(v => {
392
+ const seq = (v?.sequence ?? 0xffffffff) >>> 0;
393
+ return seq < 0xfffffffe;
394
+ });
395
+ console.log('is RBF? '+isRbf)
396
+ if(isRbf) throw new Error('RBF-enabled commit tx detected; aborting.');
397
+ } catch (e) {
398
+ return new Error(`Anti-RBF check failed: ${e?.message || e}`);
399
+ }
400
+ }
401
+
402
+ // 3) pick network + sign PSBT with our WIF (your existing flow)
403
+ let network = this.test ? 'LTCTEST' : 'LTC';
404
+ const wif = await this.dumpprivkeyAsync(this.myInfo.keypair.address);
405
+ const signRes = await signPsbtRawTx({ wif, network, psbtHex }, this.client);
406
+ if (!signRes?.data?.psbtHex) return new Error(`Failed to sign the PSBT`);
407
+ console.log('sign res '+JSON.stringify(signRes))
408
+ if(signRes.data.isFinished){
409
+ const sentTx = await this.sendTxWithSpecRetry(signRes.data.hex);
410
+ const data = { txid: sentTx, seller: true, trade: this.tradeInfo };
411
+ this.logTime('Tx Broadcast');
412
+ this.socket.emit(`${this.sellerInfo.socketId}::complete`, data);
413
+ return
414
+ }
415
+ // 4) hand signed PSBT to seller for finalization/broadcast
416
+ const swapEvent = {
417
+ eventName: 'SELLER:STEP5',
418
+ socketId: this.myInfo.socketId,
419
+ data: signRes.data.psbtHex
420
+ };
421
+ this.socket.emit(`${this.sellerInfo.socketId}::swap`, swapEvent);
422
+
423
+ //} catch (error) {
424
+ console.error(`Step 4 Error: ${error?.message || error}`);
425
+ //} finally {
426
+ // this._step4InFlight = false;
427
+ //}
428
+ }
429
+
430
+ async onStep6(cpId, finalTx) {
431
+ this.logTime('Step 6 Start');
432
+ try {
433
+ if (cpId !== this.buyerInfo.socketId){console.log(`Connection Error`)};
434
+
435
+ const data = { txid: finalTx, seller: true, trade: this.tradeInfo };
436
+ this.socket.emit(`${this.sellerInfo.socketId}::complete`, data);
437
+ } catch (error) {
438
+ console.error(`Step 6 Error: ${error.message}`);
439
+ }
440
+ }
441
+ }
442
+
443
+ module.exports = SellSwapper;