@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.
- package/.claude/settings.local.json +13 -0
- package/.claude/skills/tl-algo/SKILL.md +255 -0
- package/.gitattributes +2 -0
- package/.github/workflows/publish.yaml +26 -0
- package/4mm.js +163 -0
- package/LICENSE +21 -0
- package/NPMSwapRefactor.zip +0 -0
- package/README.md +217 -0
- package/address.sh +26 -0
- package/algoAPI.js +581 -0
- package/analyzepsbt.js +92 -0
- package/apiEx.js +99 -0
- package/bb_hyperscalper.js +290 -0
- package/bbo_demo.js +111 -0
- package/buyer.js +622 -0
- package/client.js +50 -0
- package/createTxTest.js +26 -0
- package/createWallet.js +75 -0
- package/daytrader.js +531 -0
- package/decodeTest.js +69 -0
- package/fundingManager.js +144 -0
- package/index.js +4 -0
- package/listener.js +27 -0
- package/litecoreTxBuilder.js +1128 -0
- package/mmEx.js +356 -0
- package/networks.js +51 -0
- package/orderbook.js +200 -0
- package/package.json +34 -0
- package/perTradeQueue.js +36 -0
- package/projectsTLNPMTLNPM/package-lock.json +162 -0
- package/projectsTLNPMTLNPM/package.json +5 -0
- package/quick.js +32 -0
- package/quickFut.js +37 -0
- package/quickSell.js +37 -0
- package/relayerClient.js +117 -0
- package/run4mm.js +80 -0
- package/run_bbo_tracker.js +241 -0
- package/seller.js +443 -0
- package/session.js +45 -0
- package/setup-lin-ltc.sh +139 -0
- package/setup-lin.sh +203 -0
- package/setup-win-ltc.bat +108 -0
- package/setup-win.bat +167 -0
- package/spam_screamer_futures.js +222 -0
- package/tradelayer.js/.gitattributes +2 -0
- package/tradelayer.js/README.md +2 -0
- package/tradelayer.js/oldTests/activationTest.js +6 -0
- package/tradelayer.js/oldTests/base58.test.js +23 -0
- package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
- package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
- package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
- package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
- package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
- package/tradelayer.js/oldTests/createPayload.js +0 -0
- package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
- package/tradelayer.js/oldTests/decode.js +205 -0
- package/tradelayer.js/oldTests/decodeTest.js +50 -0
- package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
- package/tradelayer.js/oldTests/encodeDecode.js +340 -0
- package/tradelayer.js/oldTests/expressTest.js +29 -0
- package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
- package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
- package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
- package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
- package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
- package/tradelayer.js/oldTests/indexTest.js +26 -0
- package/tradelayer.js/oldTests/initTokensTest.js +32 -0
- package/tradelayer.js/oldTests/interfaceChild.js +129 -0
- package/tradelayer.js/oldTests/listenerChild.js +112 -0
- package/tradelayer.js/oldTests/opdecode.js +26 -0
- package/tradelayer.js/oldTests/options.js +79 -0
- package/tradelayer.js/oldTests/optxtest.js +116 -0
- package/tradelayer.js/oldTests/optxtest1.js +64 -0
- package/tradelayer.js/oldTests/oracle.test.js +32 -0
- package/tradelayer.js/oldTests/orderbook.test.js +36 -0
- package/tradelayer.js/oldTests/parsing.js +93 -0
- package/tradelayer.js/oldTests/payload.js +13 -0
- package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
- package/tradelayer.js/oldTests/property.test.js +53 -0
- package/tradelayer.js/oldTests/propertyLevel.js +75 -0
- package/tradelayer.js/oldTests/propertyTest.js +32 -0
- package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
- package/tradelayer.js/oldTests/salter.js +14 -0
- package/tradelayer.js/oldTests/tally.js +81 -0
- package/tradelayer.js/oldTests/tally.test.js +48 -0
- package/tradelayer.js/oldTests/tally2.js +124 -0
- package/tradelayer.js/oldTests/tally3.js +142 -0
- package/tradelayer.js/oldTests/tallyDiag.js +38 -0
- package/tradelayer.js/oldTests/testGetRaw.js +40 -0
- package/tradelayer.js/oldTests/testHexConvert.js +47 -0
- package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
- package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
- package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
- package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
- package/tradelayer.js/oldTests/testOPReturn.js +102 -0
- package/tradelayer.js/oldTests/testPayload.js +23 -0
- package/tradelayer.js/oldTests/testRaw.js +50 -0
- package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
- package/tradelayer.js/oldTests/testTxBuild +28 -0
- package/tradelayer.js/oldTests/testTxBuild.js +42 -0
- package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
- package/tradelayer.js/oldTests/txUtilsA.js +515 -0
- package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
- package/tradelayer.js/oldTests/vaults.js +72 -0
- package/tradelayer.js/oldTests/volumeIndex.js +117 -0
- package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
- package/tradelayer.js/output_base64.txt +1 -0
- package/tradelayer.js/package-lock.json +9967 -0
- package/tradelayer.js/package.json +61 -0
- package/tradelayer.js/server/index.js +88 -0
- package/tradelayer.js/server/litecoind.exe +0 -0
- package/tradelayer.js/src/activation.js +303 -0
- package/tradelayer.js/src/adjuster.js +77 -0
- package/tradelayer.js/src/amm.js +400 -0
- package/tradelayer.js/src/base256.js +55 -0
- package/tradelayer.js/src/base94.js +79 -0
- package/tradelayer.js/src/channels.js +1163 -0
- package/tradelayer.js/src/clearing.js +3109 -0
- package/tradelayer.js/src/clearlist.js +364 -0
- package/tradelayer.js/src/client.js +295 -0
- package/tradelayer.js/src/consensus.js +613 -0
- package/tradelayer.js/src/contractRegistry.js +964 -0
- package/tradelayer.js/src/db.js +89 -0
- package/tradelayer.js/src/init.js +24 -0
- package/tradelayer.js/src/insurance.js +347 -0
- package/tradelayer.js/src/interface.js +218 -0
- package/tradelayer.js/src/interfaceExpress.js +178 -0
- package/tradelayer.js/src/iou.js +509 -0
- package/tradelayer.js/src/listener.js +226 -0
- package/tradelayer.js/src/logic.js +1702 -0
- package/tradelayer.js/src/main.js +927 -0
- package/tradelayer.js/src/marginMap.js +2165 -0
- package/tradelayer.js/src/options.js +126 -0
- package/tradelayer.js/src/oracle.js +394 -0
- package/tradelayer.js/src/orderbook.js +4123 -0
- package/tradelayer.js/src/persistence.js +554 -0
- package/tradelayer.js/src/property.js +411 -0
- package/tradelayer.js/src/reOrg.js +41 -0
- package/tradelayer.js/src/scaling.js +145 -0
- package/tradelayer.js/src/tally.js +1275 -0
- package/tradelayer.js/src/tradeHistoryManager.js +552 -0
- package/tradelayer.js/src/txDecoder.js +584 -0
- package/tradelayer.js/src/txEncoder.js +610 -0
- package/tradelayer.js/src/txIndex.js +502 -0
- package/tradelayer.js/src/txUtils.js +1392 -0
- package/tradelayer.js/src/types.js +429 -0
- package/tradelayer.js/src/validity.js +3077 -0
- package/tradelayer.js/src/vaults.js +430 -0
- package/tradelayer.js/src/vesting.js +491 -0
- package/tradelayer.js/src/volumeIndex.js +618 -0
- package/tradelayer.js/src/walletInterface.js +220 -0
- package/tradelayer.js/src/walletListener.js +665 -0
- package/tradelayer.js/tests/256decode.js +82 -0
- package/tradelayer.js/tests/UTXOracle.js +205 -0
- package/tradelayer.js/tests/base94test.js +23 -0
- package/tradelayer.js/tests/cancelTxTest.js +62 -0
- package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
- package/tradelayer.js/tests/decimalTest.js +65 -0
- package/tradelayer.js/tests/decoderTest.js +100 -0
- package/tradelayer.js/tests/deltaCount.js +47 -0
- package/tradelayer.js/tests/deltaCount2.js +60 -0
- package/tradelayer.js/tests/interfaceTest.js +37 -0
- package/tradelayer.js/tests/mainTest.js +53 -0
- package/tradelayer.js/tests/makeActivationTest.js +24 -0
- package/tradelayer.js/tests/maxHeightTest.js +49 -0
- package/tradelayer.js/tests/reverseHash.js +72 -0
- package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
- package/tradelayer.js/tests/tallyTest.js +40 -0
- package/tradelayer.js/tests/testBuybacks.js +46 -0
- package/tradelayer.js/tests/testCodeHash.js +49 -0
- package/tradelayer.js/tests/testConsensusHash.js +91 -0
- package/tradelayer.js/tests/testDecode.js +30 -0
- package/tradelayer.js/tests/testEncodingLengths.js +129 -0
- package/tradelayer.js/tests/testGetTx +32 -0
- package/tradelayer.js/tests/testGetTx.js +32 -0
- package/tradelayer.js/tests/testHexHash.js +32 -0
- package/tradelayer.js/tests/testIndexHash.js +35 -0
- package/tradelayer.js/tests/testInitContracts.js +38 -0
- package/tradelayer.js/tests/testMaxConsensus.js +12 -0
- package/tradelayer.js/tests/testMaxSynth.js +44 -0
- package/tradelayer.js/tests/testMint.js +21 -0
- package/tradelayer.js/tests/testNetwork.js +33 -0
- package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
- package/tradelayer.js/tests/testRebates.js +32 -0
- package/tradelayer.js/tests/testRedeem.js +22 -0
- package/tradelayer.js/tests/testTokenTrade.js +39 -0
- package/tradelayer.js/tests/testTxBuild.js +42 -0
- package/tradelayer.js/tests/testUTXOTrade.js +27 -0
- package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
- package/tradelayer.js/tests/tradeFutures.js +40 -0
- package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
- package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
- package/tradelayer.js/tests/txScanTest.js +134 -0
- package/tradelayer.js/tests/validateTest.js +136 -0
- package/tradelayer.js/tests/vestingTest.js +37 -0
- package/tradelayer.js/utils/activateMainnet.js +59 -0
- package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
- package/tradelayer.js/utils/autocompactdb.js +23 -0
- package/tradelayer.js/utils/base64toHex.js +32 -0
- package/tradelayer.js/utils/broadcastDoge.js +38 -0
- package/tradelayer.js/utils/calcRedeem.js +19 -0
- package/tradelayer.js/utils/checkNetwork.js +27 -0
- package/tradelayer.js/utils/createAddress.js +48 -0
- package/tradelayer.js/utils/createAttestation.js +133 -0
- package/tradelayer.js/utils/createContract.js +118 -0
- package/tradelayer.js/utils/createOracle.js +94 -0
- package/tradelayer.js/utils/createwallet.js +20 -0
- package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
- package/tradelayer.js/utils/crossTokenTrades.js +62 -0
- package/tradelayer.js/utils/dumpPriv.js +29 -0
- package/tradelayer.js/utils/generateChannel.js +34 -0
- package/tradelayer.js/utils/getInfo.js +21 -0
- package/tradelayer.js/utils/hardWipe.js +20 -0
- package/tradelayer.js/utils/hexTo64.js +16 -0
- package/tradelayer.js/utils/importAddress.js +28 -0
- package/tradelayer.js/utils/importpriv.js +20 -0
- package/tradelayer.js/utils/issueOracleContract.js +67 -0
- package/tradelayer.js/utils/issueTokens.js +41 -0
- package/tradelayer.js/utils/listunspent.js +66 -0
- package/tradelayer.js/utils/litecoinClient.js +30 -0
- package/tradelayer.js/utils/loadwallet.js +20 -0
- package/tradelayer.js/utils/publishOracle.js +113 -0
- package/tradelayer.js/utils/sendActivation.js +21 -0
- package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
- package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
- package/tradelayer.js/utils/sendCommit.js +24 -0
- package/tradelayer.js/utils/sendDoge.js +62 -0
- package/tradelayer.js/utils/sendDogeMain.js +67 -0
- package/tradelayer.js/utils/sendDogeTx.js +46 -0
- package/tradelayer.js/utils/sendLTC.js +63 -0
- package/tradelayer.js/utils/sendMainnet.js +62 -0
- package/tradelayer.js/utils/sendTransfer.js +19 -0
- package/tradelayer.js/utils/sendVestTest.js +88 -0
- package/tradelayer.js/utils/sendWithdrawal.js +26 -0
- package/tradelayer.js/utils/simpleStart.js +8 -0
- package/tradelayer.js/utils/startStop.js +27 -0
- package/tradelayer.js/utils/structuredTrades.js +136 -0
- package/tradelayer.js/utils/verifySignature.js +90 -0
- package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
- package/tradelayer.js/utils/walletCache.js +172 -0
- package/tradelayer.js/utils/walletContractInterface.js +48 -0
- package/tradelayer.js/utils/walletFetchTxs.js +66 -0
- package/tradelayer.js/utils/walletUtils.js +97 -0
- package/tradelayer.js/utils/wipeDB.js +55 -0
- package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
- package/txEncoder.js +529 -0
- package/utility.js +28 -0
- package/verifymessage.js +38 -0
- package/ws-transport.js +311 -0
package/mmEx.js
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mmEx_dualLayer.js — fast BBO trackers + slosh (≤5 bps), hedge on fills
|
|
3
|
+
*
|
|
4
|
+
* Requirements satisfied:
|
|
5
|
+
* - 2 tracking orders per side that closely follow Binance bid/ask (fast)
|
|
6
|
+
* - A "slosh" set of deeper passive orders kept within 5 bps; stay put unless trespass/out-of-band
|
|
7
|
+
* - Hedge on any detected fill (order disappears without our cancel)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const WebSocket = require('ws');
|
|
15
|
+
const ccxt = require('ccxt');
|
|
16
|
+
const { apiKey, secret } = require('./keys.js');
|
|
17
|
+
const ApiWrapper = require('./algoAPI.js');
|
|
18
|
+
|
|
19
|
+
// ===== Config =====
|
|
20
|
+
const LOG_PATH = path.join(process.env.HOME || process.env.USERPROFILE || '.', 'Downloads', 'mmEx.dual.log');
|
|
21
|
+
|
|
22
|
+
const TL_WS_HOST = 'ws://172.26.37.103';
|
|
23
|
+
const TL_WS_PORT = 3001;
|
|
24
|
+
const TL_NETWORK = 'LTCTEST';
|
|
25
|
+
const TL_ADDR = 'tltc1qvlwcnwlhnja7wlj685ptwxej75mms9nyv7vuy8';
|
|
26
|
+
|
|
27
|
+
const BASE_ID = 0; // LTC
|
|
28
|
+
const QUOTE_ID = 5; // USDTt
|
|
29
|
+
const SYMBOL = 'LTC/USDT';
|
|
30
|
+
|
|
31
|
+
// Tracking layer (fast)
|
|
32
|
+
const TRACK_LEVELS_PER_SIDE = 2;
|
|
33
|
+
const TRACK_EDGE_BPS = 1.0; // first level distance from BBO
|
|
34
|
+
const TRACK_STEP_BPS = 1.0; // gap between tracking levels
|
|
35
|
+
const TRACK_SIZE = 0.10;
|
|
36
|
+
const TRACK_DEBOUNCE_MS = 150; // per side
|
|
37
|
+
|
|
38
|
+
// Slosh layer (slow)
|
|
39
|
+
const SLOSH_LEVELS_PER_SIDE = 3; // number of slosh levels per side
|
|
40
|
+
const SLOSH_MAX_BPS = 5.0; // must remain within 5 bps to stay
|
|
41
|
+
const SLOSH_START_BPS = 2.0; // first slosh level distance from BBO
|
|
42
|
+
const SLOSH_STEP_BPS = 1.5; // between levels, not to exceed 5 bps
|
|
43
|
+
const SLOSH_SIZE = 0.15;
|
|
44
|
+
const SLOSH_DEBOUNCE_MS = 1200; // slower refresh
|
|
45
|
+
|
|
46
|
+
// Safety / plumbing
|
|
47
|
+
const CANCEL_TIMEOUT_MS = 1500;
|
|
48
|
+
const PLACE_TIMEOUT_MS = 1500;
|
|
49
|
+
const EXCH_SPREAD_MIN = 0.005; // don't quote if exchange spread too tight
|
|
50
|
+
const EXCH_SPREAD_MAX = 1.00; // ignore if absurd
|
|
51
|
+
const HEDGE_SLIPPAGE_F = 0.25; // market hedge slippage factor vs last BBO
|
|
52
|
+
|
|
53
|
+
// ===== Logging =====
|
|
54
|
+
const logStream = fs.createWriteStream(LOG_PATH, { flags: 'a' });
|
|
55
|
+
function log(...a) {
|
|
56
|
+
const line = `[${new Date().toISOString()}] ${a.map(String).join(' ')}\n`;
|
|
57
|
+
if (!logStream.destroyed) logStream.write(line);
|
|
58
|
+
console.log(...a);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ===== External deps =====
|
|
62
|
+
const binance = new ccxt.binance({ apiKey, secret, enableRateLimit: true });
|
|
63
|
+
const api = new ApiWrapper(
|
|
64
|
+
TL_WS_HOST, TL_WS_PORT,
|
|
65
|
+
true, // debug
|
|
66
|
+
true, // autoConnect
|
|
67
|
+
{ address: TL_ADDR, otherAddrs: [] },
|
|
68
|
+
TL_NETWORK
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// ===== State =====
|
|
72
|
+
let bestBid = null, bestAsk = null;
|
|
73
|
+
|
|
74
|
+
const layer = {
|
|
75
|
+
TRACK: { BUY: [], SELL: [] }, // items: {uuid, px, sz}
|
|
76
|
+
SLOSH: { BUY: [], SELL: [] }
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const lastTouch = {
|
|
80
|
+
TRACK: { BUY: 0, SELL: 0 },
|
|
81
|
+
SLOSH: { BUY: 0, SELL: 0 }
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ours = new Set(); // all open order UUIDs we believe are live
|
|
85
|
+
const oursMeta = new Map(); // uuid -> {side, layer:'TRACK'|'SLOSH', px, sz}
|
|
86
|
+
|
|
87
|
+
function now() { return Date.now(); }
|
|
88
|
+
function bps(x) { return x / 10000; }
|
|
89
|
+
function tooSoon(kind, side, ms) { return now() - lastTouch[kind][side] < ms; }
|
|
90
|
+
function touch(kind, side) { lastTouch[kind][side] = now(); }
|
|
91
|
+
|
|
92
|
+
function toTLBuy(price, amount) {
|
|
93
|
+
return { type: 'SPOT', action: 'BUY', props: { id_for_sale: QUOTE_ID, id_desired: BASE_ID, price, amount, transfer: false } };
|
|
94
|
+
}
|
|
95
|
+
function toTLSell(price, amount) {
|
|
96
|
+
return { type: 'SPOT', action: 'SELL', props: { id_for_sale: BASE_ID, id_desired: QUOTE_ID, price, amount, transfer: false } };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function withTimeout(p, ms, tag) {
|
|
100
|
+
let t; const killer = new Promise((_, rej) => t = setTimeout(() => rej(new Error(`${tag} timeout ${ms}ms`)), ms));
|
|
101
|
+
try { return await Promise.race([p, killer]); }
|
|
102
|
+
finally { clearTimeout(t); }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ===== Binance WS (depth) =====
|
|
106
|
+
const ws = new WebSocket('wss://stream.binance.com:9443/ws');
|
|
107
|
+
ws.on('open', () => {
|
|
108
|
+
ws.send(JSON.stringify({ method: 'SUBSCRIBE', params: ['ltcusdt@depth'], id: 1 }));
|
|
109
|
+
log('Subscribed Binance: ltcusdt@depth');
|
|
110
|
+
});
|
|
111
|
+
ws.on('message', (raw) => {
|
|
112
|
+
try {
|
|
113
|
+
const d = JSON.parse(raw);
|
|
114
|
+
const b = Number(d?.b?.[0]?.[0]);
|
|
115
|
+
const a = Number(d?.a?.[0]?.[0]);
|
|
116
|
+
if (Number.isFinite(b) && Number.isFinite(a) && a > b) {
|
|
117
|
+
bestBid = b; bestAsk = a;
|
|
118
|
+
}
|
|
119
|
+
} catch {}
|
|
120
|
+
});
|
|
121
|
+
ws.on('error', (e) => log('WS error', e.message || e));
|
|
122
|
+
|
|
123
|
+
// ===== TL ops =====
|
|
124
|
+
async function place(kind, side, px, sz) {
|
|
125
|
+
const det = side === 'BUY' ? toTLBuy(px, sz) : toTLSell(px, sz);
|
|
126
|
+
const uuid = await withTimeout(api.sendOrder(det), PLACE_TIMEOUT_MS, 'place');
|
|
127
|
+
const id = uuid?.orderUuid || uuid;
|
|
128
|
+
layer[kind][side].push({ uuid: id, px, sz });
|
|
129
|
+
ours.add(id);
|
|
130
|
+
oursMeta.set(id, { side, layer: kind, px, sz });
|
|
131
|
+
log('PLACED', kind, side, px.toFixed(6), 'uuid=', id);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function cancel(kind, side, idx, reason) {
|
|
135
|
+
const item = layer[kind][side][idx];
|
|
136
|
+
if (!item) return;
|
|
137
|
+
const id = item.uuid;
|
|
138
|
+
try {
|
|
139
|
+
await withTimeout(api.cancelOrder(id), CANCEL_TIMEOUT_MS, 'cancel');
|
|
140
|
+
log('CANCELED', kind, side, item.px.toFixed(6), 'uuid=', id, 'reason=', reason);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
log('CANCEL FAIL', kind, side, id, e.message || e);
|
|
143
|
+
} finally {
|
|
144
|
+
layer[kind][side].splice(idx, 1);
|
|
145
|
+
ours.delete(id);
|
|
146
|
+
oursMeta.delete(id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function genTargets_TRACK(bid, ask) {
|
|
151
|
+
const bids = [], asks = [];
|
|
152
|
+
for (let i = 0; i < TRACK_LEVELS_PER_SIDE; i++) {
|
|
153
|
+
bids.push({ px: bid * (1 - bps(TRACK_EDGE_BPS + i * TRACK_STEP_BPS)), sz: TRACK_SIZE });
|
|
154
|
+
asks.push({ px: ask * (1 + bps(TRACK_EDGE_BPS + i * TRACK_STEP_BPS)), sz: TRACK_SIZE });
|
|
155
|
+
}
|
|
156
|
+
return { bids, asks };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function genTargets_SLOSH(bid, ask) {
|
|
160
|
+
const bids = [], asks = [];
|
|
161
|
+
for (let i = 0; i < SLOSH_LEVELS_PER_SIDE; i++) {
|
|
162
|
+
const dist = Math.min(SLOSH_START_BPS + i * SLOSH_STEP_BPS, SLOSH_MAX_BPS);
|
|
163
|
+
bids.push({ px: bid * (1 - bps(dist)), sz: SLOSH_SIZE, dist });
|
|
164
|
+
asks.push({ px: ask * (1 + bps(dist)), sz: SLOSH_SIZE, dist });
|
|
165
|
+
}
|
|
166
|
+
return { bids, asks };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Keep order if: still within SLOSH_MAX_BPS of current BBO and not trespassing
|
|
170
|
+
function sloshStillValid(side, px, bid, ask) {
|
|
171
|
+
if (side === 'BUY') {
|
|
172
|
+
const relBps = Math.abs((bid - px) / bid) * 10000;
|
|
173
|
+
return px <= bid && relBps <= SLOSH_MAX_BPS;
|
|
174
|
+
} else {
|
|
175
|
+
const relBps = Math.abs((px - ask) / ask) * 10000;
|
|
176
|
+
return px >= ask && relBps <= SLOSH_MAX_BPS;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function pickMissing(existingPx, targets, tolBps) {
|
|
181
|
+
const out = [];
|
|
182
|
+
for (const t of targets) {
|
|
183
|
+
const near = existingPx.some(px => Math.abs(px - t.px) <= (t.px * bps(tolBps)));
|
|
184
|
+
if (!near) out.push(t);
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ===== Hedging on fills =====
|
|
190
|
+
// We detect fills by diffs: If a UUID disappears from server-open-orders and we did NOT cancel it, treat as fill
|
|
191
|
+
async function hedgeOnFill(side, px, sz) {
|
|
192
|
+
try {
|
|
193
|
+
// simple market hedge opposite to TL side
|
|
194
|
+
if (!Number.isFinite(bestBid) || !Number.isFinite(bestAsk)) return;
|
|
195
|
+
const ref = side === 'BUY' ? bestAsk : bestBid;
|
|
196
|
+
const price = side === 'BUY'
|
|
197
|
+
? ref * (1 - bps(HEDGE_SLIPPAGE_F)) // we sold on TL, buy on Binance a bit below ask if allowed
|
|
198
|
+
: ref * (1 + bps(HEDGE_SLIPPAGE_F)); // we bought on TL, sell on Binance a bit above bid if allowed
|
|
199
|
+
|
|
200
|
+
const hedgeSide = side === 'BUY' ? 'buy' : 'sell'; // reverse? we *bought* on TL -> hedge by *sell*; adjust:
|
|
201
|
+
const takerSide = (side === 'BUY') ? 'sell' : 'buy';
|
|
202
|
+
await binance.createOrder(SYMBOL, 'market', takerSide, sz, undefined);
|
|
203
|
+
log('HEDGE', takerSide, sz, SYMBOL, 'ok (fill detected at TL)');
|
|
204
|
+
} catch (e) {
|
|
205
|
+
log('HEDGE FAIL', e.message || e);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Hook: wire to your ApiWrapper’s stream of account updates / orders list
|
|
210
|
+
// Expect a payload like: { event: 'PLACED_ORDERS', openedOrders: [...uuids...] }
|
|
211
|
+
api.onMessage?.((msg) => {
|
|
212
|
+
try {
|
|
213
|
+
if (!msg) return;
|
|
214
|
+
// normalize possible shapes
|
|
215
|
+
const ev = msg.event || msg.type || '';
|
|
216
|
+
if (String(ev).toUpperCase().includes('PLACED') && Array.isArray(msg.openedOrders)) {
|
|
217
|
+
const openNow = new Set((msg.openedOrders || []).map(o => o.uuid || o.orderUuid || o));
|
|
218
|
+
for (const id of Array.from(ours)) {
|
|
219
|
+
if (!openNow.has(id)) {
|
|
220
|
+
// if it disappeared and we didn't remove it locally => filled (or canceled by match)
|
|
221
|
+
const meta = oursMeta.get(id);
|
|
222
|
+
if (meta) {
|
|
223
|
+
log('FILL-DETECTED', id, meta.side, meta.layer, meta.px);
|
|
224
|
+
hedgeOnFill(meta.side, meta.px, meta.sz).catch(()=>{});
|
|
225
|
+
// clean local state if still present in a layer
|
|
226
|
+
for (const KIND of ['TRACK','SLOSH']) {
|
|
227
|
+
for (const SIDE of ['BUY','SELL']) {
|
|
228
|
+
const idx = layer[KIND][SIDE].findIndex(x => x.uuid === id);
|
|
229
|
+
if (idx >= 0) layer[KIND][SIDE].splice(idx, 1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
ours.delete(id);
|
|
233
|
+
oursMeta.delete(id);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
} catch {}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ===== Reconcilers =====
|
|
242
|
+
async function reconcileTRACK(bid, ask) {
|
|
243
|
+
// debounce per side
|
|
244
|
+
for (const side of ['BUY','SELL']) {
|
|
245
|
+
if (tooSoon('TRACK', side, TRACK_DEBOUNCE_MS)) continue;
|
|
246
|
+
touch('TRACK', side);
|
|
247
|
+
|
|
248
|
+
const targets = genTargets_TRACK(bid, ask);
|
|
249
|
+
const tgt = side === 'BUY' ? targets.bids : targets.asks;
|
|
250
|
+
const existing = layer.TRACK[side];
|
|
251
|
+
|
|
252
|
+
// cancel if too many or trespass BBO (shouldn’t happen, but guard)
|
|
253
|
+
for (let i = existing.length - 1; i >= 0; i--) {
|
|
254
|
+
const { px } = existing[i];
|
|
255
|
+
const trespass = (side === 'BUY') ? (px > bid) : (px < ask);
|
|
256
|
+
const tooMany = existing.length > TRACK_LEVELS_PER_SIDE;
|
|
257
|
+
if (trespass || tooMany) {
|
|
258
|
+
await cancel('TRACK', side, i, trespass ? 'trespass' : 'excess');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// place missing near BBO (tolerance half a step)
|
|
263
|
+
const miss = pickMissing(existing.map(e => e.px), tgt, TRACK_STEP_BPS * 0.6);
|
|
264
|
+
for (const m of miss.slice(0, Math.max(0, TRACK_LEVELS_PER_SIDE - existing.length))) {
|
|
265
|
+
await place('TRACK', side, m.px, m.sz);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function reconcileSLOSH(bid, ask) {
|
|
271
|
+
for (const side of ['BUY','SELL']) {
|
|
272
|
+
if (tooSoon('SLOSH', side, SLOSH_DEBOUNCE_MS)) continue;
|
|
273
|
+
touch('SLOSH', side);
|
|
274
|
+
|
|
275
|
+
const targets = genTargets_SLOSH(bid, ask);
|
|
276
|
+
const tgt = side === 'BUY' ? targets.bids : targets.asks;
|
|
277
|
+
const existing = layer.SLOSH[side];
|
|
278
|
+
|
|
279
|
+
// prune invalid: outside 5 bps window or trespass
|
|
280
|
+
for (let i = existing.length - 1; i >= 0; i--) {
|
|
281
|
+
const { px } = existing[i];
|
|
282
|
+
if (!sloshStillValid(side, px, bid, ask)) {
|
|
283
|
+
await cancel('SLOSH', side, i, 'out-of-band');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// place up to desired count; tolerance slightly wider than track
|
|
288
|
+
const miss = pickMissing(existing.map(e => e.px), tgt, Math.min(SLOSH_MAX_BPS, SLOSH_STEP_BPS));
|
|
289
|
+
for (const m of miss.slice(0, Math.max(0, SLOSH_LEVELS_PER_SIDE - layer.SLOSH[side].length))) {
|
|
290
|
+
// clamp distances to SLOSH_MAX_BPS
|
|
291
|
+
const bbo = side === 'BUY' ? bid : ask;
|
|
292
|
+
const rel = Math.abs((m.px - bbo) / bbo) * 10000;
|
|
293
|
+
if (rel <= SLOSH_MAX_BPS) await place('SLOSH', side, m.px, m.sz);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ===== Main tick =====
|
|
299
|
+
async function tick() {
|
|
300
|
+
if (!Number.isFinite(bestBid) || !Number.isFinite(bestAsk)) return;
|
|
301
|
+
const spread = bestAsk - bestBid;
|
|
302
|
+
if (spread < EXCH_SPREAD_MIN || spread > EXCH_SPREAD_MAX) return;
|
|
303
|
+
|
|
304
|
+
await reconcileTRACK(bestBid, bestAsk);
|
|
305
|
+
await reconcileSLOSH(bestBid, bestAsk);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ===== Graceful shutdown =====
|
|
309
|
+
async function shutdown() {
|
|
310
|
+
log('Shutdown: cancel all…');
|
|
311
|
+
for (const KIND of ['TRACK','SLOSH']) {
|
|
312
|
+
for (const SIDE of ['BUY','SELL']) {
|
|
313
|
+
for (let i = layer[KIND][SIDE].length - 1; i >= 0; i--) {
|
|
314
|
+
try { await cancel(KIND, SIDE, i, 'shutdown'); } catch {}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
process.exit(0);
|
|
319
|
+
}
|
|
320
|
+
process.on('SIGINT', shutdown);
|
|
321
|
+
process.on('SIGTERM', shutdown);
|
|
322
|
+
|
|
323
|
+
// ===== Boot =====
|
|
324
|
+
(async () => {
|
|
325
|
+
log('mmEx_dualLayer starting…');
|
|
326
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
327
|
+
setInterval(() => { tick().catch(e => log('tick err', e.message || e)); }, 120);
|
|
328
|
+
|
|
329
|
+
// Optional: periodic reconcile of fills if your wrapper has a getter
|
|
330
|
+
// If ApiWrapper exposes api.getMyOpenOrders(), uncomment this poller:
|
|
331
|
+
/*
|
|
332
|
+
setInterval(async () => {
|
|
333
|
+
try {
|
|
334
|
+
const list = await api.getMyOpenOrders();
|
|
335
|
+
const openNow = new Set((list || []).map(o => o.uuid || o.orderUuid || o));
|
|
336
|
+
for (const id of Array.from(ours)) {
|
|
337
|
+
if (!openNow.has(id)) {
|
|
338
|
+
const meta = oursMeta.get(id);
|
|
339
|
+
if (meta) {
|
|
340
|
+
log('FILL-DETECTED(POLL)', id, meta.side, meta.layer, meta.px);
|
|
341
|
+
hedgeOnFill(meta.side, meta.px, meta.sz).catch(()=>{});
|
|
342
|
+
ours.delete(id);
|
|
343
|
+
oursMeta.delete(id);
|
|
344
|
+
for (const KIND of ['TRACK','SLOSH']) {
|
|
345
|
+
for (const SIDE of ['BUY','SELL']) {
|
|
346
|
+
const idx = layer[KIND][SIDE].findIndex(x => x.uuid === id);
|
|
347
|
+
if (idx >= 0) layer[KIND][SIDE].splice(idx, 1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
} catch {}
|
|
354
|
+
}, 800);
|
|
355
|
+
*/
|
|
356
|
+
})();
|
package/networks.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const BTC = {
|
|
2
|
+
messagePrefix: '\x18Bitcoin Signed Message:\n',
|
|
3
|
+
bech32: 'bc',
|
|
4
|
+
bip32: {
|
|
5
|
+
public: 0x0488b21e,
|
|
6
|
+
private: 0x0488ade4,
|
|
7
|
+
},
|
|
8
|
+
pubKeyHash: 0x00,
|
|
9
|
+
scriptHash: 0x05,
|
|
10
|
+
wif: 0x80,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const BTCTEST = {
|
|
14
|
+
messagePrefix: '\x18Bitcoin Testnet Signed Message:\n',
|
|
15
|
+
bech32: 'tb',
|
|
16
|
+
bip32: {
|
|
17
|
+
public: 0x043587cf,
|
|
18
|
+
private: 0x04358394,
|
|
19
|
+
},
|
|
20
|
+
pubKeyHash: 0x6f,
|
|
21
|
+
scriptHash: 0xc4,
|
|
22
|
+
wif: 0xef,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const LTC = {
|
|
26
|
+
messagePrefix: '\x19Litecoin Signed Message:\n',
|
|
27
|
+
bech32: 'ltc',
|
|
28
|
+
bip32: {
|
|
29
|
+
public: 0x019da462,
|
|
30
|
+
private: 0x019d9cfe,
|
|
31
|
+
},
|
|
32
|
+
pubKeyHash: 0x30,
|
|
33
|
+
scriptHash: 0x32,
|
|
34
|
+
wif: 0xb0,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const LTCTEST = {
|
|
38
|
+
messagePrefix: '\x19Litecoin Testnet Signed Message:\n',
|
|
39
|
+
bech32: 'tltc',
|
|
40
|
+
bip32: {
|
|
41
|
+
public: 0x0436f6e1,
|
|
42
|
+
private: 0x0436ef7d,
|
|
43
|
+
},
|
|
44
|
+
pubKeyHash: 0x6f,
|
|
45
|
+
scriptHash: 0x3a,
|
|
46
|
+
wif: 0xef,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const networks = { BTC, LTC, LTCTEST, BTCTEST };
|
|
50
|
+
|
|
51
|
+
module.exports = networks
|
package/orderbook.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
const SellSwapper = require('./seller.js')
|
|
2
|
+
const BuySwapper = require('./buyer.js')
|
|
3
|
+
const BigNumber = require('bignumber.js')
|
|
4
|
+
|
|
5
|
+
class OrderbookSession {
|
|
6
|
+
constructor(socket, myInfo, client,test) {
|
|
7
|
+
console.log('initializing orderbook '+JSON.stringify(myInfo))
|
|
8
|
+
this.socket = socket;
|
|
9
|
+
this.myInfo = myInfo;
|
|
10
|
+
this.client = client;
|
|
11
|
+
this.test = test
|
|
12
|
+
|
|
13
|
+
// Start the session and listen for various events
|
|
14
|
+
this.startSession();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
setInfo(myInfo){
|
|
18
|
+
this.myInfo=myInfo
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Start the session and manage connection lifecycle
|
|
22
|
+
startSession() {
|
|
23
|
+
this.socket.on('connection', () => {
|
|
24
|
+
console.log('Connected to the orderbook server.');
|
|
25
|
+
this.subscribeToOrderbook();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
this.socket.on('disconnect', () => {
|
|
29
|
+
console.log('Disconnected from the orderbook server.');
|
|
30
|
+
// Reconnection logic could be added here if necessary
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
this.handleOrderMatches();
|
|
34
|
+
this.handleNewOrders();
|
|
35
|
+
this.handleOrderUpdates();
|
|
36
|
+
this.handleClosedOrders();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Subscribe to orderbook updates after connection
|
|
40
|
+
subscribeToOrderbook() {
|
|
41
|
+
// Subscribe to specific assets or full orderbook
|
|
42
|
+
this.socket.emit('subscribe', { event: 'update-orderbook', assets: ['LTC', 'BTC'] });
|
|
43
|
+
console.log('Subscribed to orderbook data for assets: LTC, BTC.');
|
|
44
|
+
|
|
45
|
+
// Listening for general orderbook updates
|
|
46
|
+
this.socket.on('update-orderbook', (data) => {
|
|
47
|
+
console.log('Orderbook Updated:', data);
|
|
48
|
+
// You can update your local orderbook or UI here
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Handle new orders
|
|
53
|
+
handleNewOrders() {
|
|
54
|
+
this.socket.on('new-order', (newOrderData) => {
|
|
55
|
+
console.log('socket in new order '+JSON.stringify(this.socket)+' '+this.socket)
|
|
56
|
+
console.log('New Order:', newOrderData);
|
|
57
|
+
// You can update the UI or alert the user about new orders
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
this.socket.on('many-orders', (bulkOrdersData) => {
|
|
61
|
+
console.log('Many Orders Received:', bulkOrdersData);
|
|
62
|
+
// Handle bulk order updates (e.g., refreshing the full orderbook)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle updates to existing orders in the orderbook
|
|
67
|
+
handleOrderUpdates() {
|
|
68
|
+
this.socket.on('update-orderbook', (updateData) => {
|
|
69
|
+
console.log('Orderbook Update:', updateData);
|
|
70
|
+
// Handle any updates to the orderbook here
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle order closing or cancellations
|
|
75
|
+
handleClosedOrders() {
|
|
76
|
+
this.socket.on('close-order', (orderUUID) => {
|
|
77
|
+
console.log(`Order ${orderUUID} closed or canceled.`);
|
|
78
|
+
// Remove the order from the UI or local state
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle matched orders and initiate trade swaps
|
|
83
|
+
handleOrderMatches() {
|
|
84
|
+
this.socket.on('new-channel', async (swapConfig) => {
|
|
85
|
+
const tradeInfo = swapConfig?.tradeInfo;
|
|
86
|
+
console.log('inside handleOrderMatches on algo '+JSON.stringify(swapConfig))
|
|
87
|
+
if (!tradeInfo?.buyer || !tradeInfo?.seller) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
//console.log('swap config '+JSON.stringify(swapConfig))
|
|
92
|
+
if(!swapConfig.tradeInfo.buyer||!swapConfig.tradeInfo.seller){return}
|
|
93
|
+
try {
|
|
94
|
+
const { tradeInfo, isBuyer } = swapConfig; // Extract the relevant trade info and buyer/seller flag
|
|
95
|
+
const { buyer, seller, props, type } = tradeInfo; // Get buyer/seller info and trade properties
|
|
96
|
+
|
|
97
|
+
console.log('new channel match '+JSON.stringify(swapConfig)+' trade info'+JSON.stringify(tradeInfo))
|
|
98
|
+
// Make sure the buyer/seller addresses are properly matched
|
|
99
|
+
console.log('my address'+this.myInfo.keypair.address, +' buyer.address '+buyer.keypair.address+' seller.address '+seller.keypair.address)
|
|
100
|
+
if(!this.myInfo.keypair.address){
|
|
101
|
+
const address = await this.getUTXOBalances()
|
|
102
|
+
if(!address||!this.myInfo.keypair.address){console.log('houston we have a problem')}
|
|
103
|
+
}
|
|
104
|
+
if (this.myInfo.keypair.address === buyer.keypair.address){
|
|
105
|
+
console.log('Initiating Buy Swap...');
|
|
106
|
+
await this.initiateBuySwap(type, tradeInfo, buyer, seller);
|
|
107
|
+
} else if (this.myInfo.keypair.address === seller.keypair.address){
|
|
108
|
+
console.log('Initiating Sell Swap...');
|
|
109
|
+
await this.initiateSellSwap(type, tradeInfo, buyer, seller);
|
|
110
|
+
} else {
|
|
111
|
+
console.log('Address mismatch, cannot proceed with swap.');
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error('Error handling matched order:', error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Initialize buy swap
|
|
120
|
+
async initiateBuySwap(typeTrade, tradeInfo, buyerInfo, sellerInfo) {
|
|
121
|
+
const key = [tradeInfo.buyer?.uuid, tradeInfo.seller?.uuid].join('-');
|
|
122
|
+
try {
|
|
123
|
+
const buySwapper = new BuySwapper(typeTrade, tradeInfo, buyerInfo, sellerInfo, this.client, this.socket,this.test,key);
|
|
124
|
+
const res = await buySwapper.onReady();
|
|
125
|
+
if (res.error) {
|
|
126
|
+
console.error(`Buy Swap Failed: ${res.error}`);
|
|
127
|
+
} else {
|
|
128
|
+
console.log(`Buy Swap Complete: ${res.data}`);
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('Error initiating Buy Swap:', error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Initialize sell swap
|
|
136
|
+
async initiateSellSwap(typeTrade, tradeInfo, buyerInfo, sellerInfo) {
|
|
137
|
+
const key = [tradeInfo.buyer?.uuid, tradeInfo.seller?.uuid].join('-');
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const sellSwapper = new SellSwapper(typeTrade, tradeInfo, sellerInfo, buyerInfo, this.client, this.socket,this.test,key);
|
|
141
|
+
const res = await sellSwapper.onReady();
|
|
142
|
+
if (res.error) {
|
|
143
|
+
console.error(`Sell Swap Failed: ${res.error}`);
|
|
144
|
+
} else {
|
|
145
|
+
console.log(`Sell Swap Complete: ${res.data}`);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Error initiating Sell Swap:', error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async getUTXOBalances(address) {
|
|
153
|
+
try {
|
|
154
|
+
const utxos = await this.listUnspent(); // Fetch sunspent transactions
|
|
155
|
+
console.log('utxos returned 2nd pass in orderbook '+JSON.stringify(utxos))
|
|
156
|
+
let totalBalance = new BigNumber(0);
|
|
157
|
+
|
|
158
|
+
for (const utxo of utxos) {
|
|
159
|
+
console.log('scanning utxos '+utxo.address+' '+utxo.amount)
|
|
160
|
+
if (utxo.address === address){
|
|
161
|
+
totalBalance += utxo.amount; // Sum balances for the specific address
|
|
162
|
+
} else if (!this.myInfo.keypair.address){
|
|
163
|
+
this.myInfo.keypair.address = utxo.address;
|
|
164
|
+
this.myInfo.keypair.pubkey = await this.getPubKeyFromAddress(utxo.address); // Get pubkey for the new address
|
|
165
|
+
console.log('logging pubkey ' +this.myInfo.keypair.pubkey+' '+this.myInfo.keypair.address)
|
|
166
|
+
totalBalance += utxo.amount;
|
|
167
|
+
} else if (address === '' && this.myInfo.keypair.address){
|
|
168
|
+
const pubkey = await this.getPubKeyFromAddress(utxo.address);
|
|
169
|
+
this.myInfo.otherAddrs.push({ address: utxo.address, pubkey: pubkey });
|
|
170
|
+
totalBalance += utxo.amount;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`Total UTXO balance for address ${this.myInfo.keypair.address}:`, totalBalance);
|
|
175
|
+
|
|
176
|
+
return this.myInfo.keypair.address
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('Error fetching UTXO balances:', error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
listUnspent(...params) {
|
|
183
|
+
return util.promisify(this.client.cmd.bind(this.client, 'listunspent'))(...params);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async getPubKeyFromAddress(address) {
|
|
187
|
+
try {
|
|
188
|
+
const addressInfo = await this.getAddressInfo(address);
|
|
189
|
+
if (addressInfo && addressInfo.pubkey) {
|
|
190
|
+
return addressInfo.pubkey;
|
|
191
|
+
} else {
|
|
192
|
+
throw new Error('Public key not found for address');
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error('Error fetching pubkey:', error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = OrderbookSession
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tradelayerprotocol/tradelayer",
|
|
3
|
+
"version": "1.9.1",
|
|
4
|
+
"description": "NPM package for interacting with TradeLayer orderbook and building Litecoin/Token/Futures transactions.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"axios": "^1.7.7",
|
|
8
|
+
"bigint-base-converter": "^0.1.3",
|
|
9
|
+
"bignumber.js": "^9.1.2",
|
|
10
|
+
"bip32": "^5.0.0",
|
|
11
|
+
"bip39": "^3.1.0",
|
|
12
|
+
"bitcoin": "^3.0.3",
|
|
13
|
+
"bitcoinjs-lib": "^6.1.6",
|
|
14
|
+
"bitcore-lib-ltc": "^10.0.21",
|
|
15
|
+
"ccxt": "^4.4.77",
|
|
16
|
+
"dotenv": "^16.4.5",
|
|
17
|
+
"ecpair": "^3.0.0",
|
|
18
|
+
"isomorphic-ws": "^5.0.0",
|
|
19
|
+
"litecoin": "^2.0.5",
|
|
20
|
+
"socket.io-client": "^4.7.5",
|
|
21
|
+
"tiny-secp256k1": "^2.2.4",
|
|
22
|
+
"tradelayer": "^1.5.2",
|
|
23
|
+
"ws": "^8.18.0"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/patrickdugan/tlnpm"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"start": "node algoAPI.js"
|
|
31
|
+
},
|
|
32
|
+
"author": "Your Name",
|
|
33
|
+
"license": "MIT"
|
|
34
|
+
}
|
package/perTradeQueue.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// perTradeQueue.js
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Run async functions sequentially *per key*.
|
|
6
|
+
* Calls for different keys can run in parallel.
|
|
7
|
+
*/
|
|
8
|
+
const pending = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} key e.g. `${buyerUuid}-${sellerUuid}`
|
|
12
|
+
* @param {() => Promise<any>} fn async work to run
|
|
13
|
+
*/
|
|
14
|
+
function runForKey(key, fn) {
|
|
15
|
+
if (!key) {
|
|
16
|
+
// no key → just run without queuing
|
|
17
|
+
return Promise.resolve().then(fn);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const prev = pending.get(key) || Promise.resolve();
|
|
21
|
+
|
|
22
|
+
const next = prev
|
|
23
|
+
.catch(() => {}) // swallow errors from previous tasks
|
|
24
|
+
.then(() => fn())
|
|
25
|
+
.finally(() => {
|
|
26
|
+
// clear if this is still the tail
|
|
27
|
+
if (pending.get(key) === next) pending.delete(key);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
pending.set(key, next);
|
|
31
|
+
return next;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
runForKey,
|
|
36
|
+
};
|