@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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// options.js
|
|
2
|
+
// Singleton helper for option tickers, reduce/flip bookkeeping, rPNL, and simple valuation.
|
|
3
|
+
// No state stored; singleton export for convenience.
|
|
4
|
+
|
|
5
|
+
class OptionsEngine {
|
|
6
|
+
// "series-expiry-(C|P)-strike?"
|
|
7
|
+
isOptionTicker(t) {
|
|
8
|
+
return typeof t === 'string' && /^.+?-\d+-(C|P)(-\d+(\.\d+)?)?$/.test(t);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
parseTicker(t) {
|
|
12
|
+
if (!t || typeof t !== 'string') return null;
|
|
13
|
+
const parts = t.split('-');
|
|
14
|
+
if (parts.length < 3) return null;
|
|
15
|
+
const seriesId = parts[0];
|
|
16
|
+
const expiryBlock = parseInt(parts[1], 10);
|
|
17
|
+
const cp = parts[2];
|
|
18
|
+
const type = cp === 'C' ? 'Call' : (cp === 'P' ? 'Put' : null);
|
|
19
|
+
const strike = parts[3] ? parseFloat(parts[3]) : null;
|
|
20
|
+
if (!seriesId || Number.isNaN(expiryBlock) || !type) return null;
|
|
21
|
+
return { seriesId, expiryBlock, type, strike, raw: t };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Given existing signed qty and a signed delta, compute how much is closing vs flipping.
|
|
26
|
+
* existing >0 long, <0 short. delta >0 buy, <0 sell.
|
|
27
|
+
*/
|
|
28
|
+
computeReduceFlip(existingQty, deltaQty) {
|
|
29
|
+
const ex = Number(existingQty) || 0;
|
|
30
|
+
const d = Number(deltaQty) || 0;
|
|
31
|
+
const after = ex + d;
|
|
32
|
+
|
|
33
|
+
const exSide = ex > 0 ? 'LONG' : ex < 0 ? 'SHORT' : null;
|
|
34
|
+
const dSide = d > 0 ? 'LONG' : d < 0 ? 'SHORT' : null;
|
|
35
|
+
|
|
36
|
+
let closedQty = 0;
|
|
37
|
+
let flipQty = 0;
|
|
38
|
+
|
|
39
|
+
if (ex !== 0 && d !== 0 && Math.sign(ex) !== Math.sign(d)) {
|
|
40
|
+
closedQty = Math.min(Math.abs(ex), Math.abs(d));
|
|
41
|
+
if (Math.abs(d) > Math.abs(ex)) {
|
|
42
|
+
flipQty = Math.abs(d) - Math.abs(ex);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const afterSide = after > 0 ? 'LONG' : after < 0 ? 'SHORT' : null;
|
|
47
|
+
|
|
48
|
+
return { existing: ex, delta: d, after, exSide, dSide, afterSide, closedQty, flipQty };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Realized PnL when reducing an option position.
|
|
53
|
+
* Long reduce: (trade - avg) * qty
|
|
54
|
+
* Short reduce: (avg - trade) * qty
|
|
55
|
+
*/
|
|
56
|
+
rpnlForClose(exSide, closedQty, tradePrice, avgPrice) {
|
|
57
|
+
const q = Number(closedQty) || 0;
|
|
58
|
+
const p = Number(tradePrice) || 0;
|
|
59
|
+
const a = Number(avgPrice) || 0;
|
|
60
|
+
if (!q || !exSide) return 0;
|
|
61
|
+
return exSide === 'LONG' ? (p - a) * q : (a - p) * q;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Intrinsic value at spot S (European payoff at mark).
|
|
66
|
+
*/
|
|
67
|
+
intrinsic(type, K, S) {
|
|
68
|
+
const k = Number(K) || 0;
|
|
69
|
+
const s = Number(S) || 0;
|
|
70
|
+
if (type === 'Call') return Math.max(0, s - k);
|
|
71
|
+
return Math.max(0, k - s);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Very light EU price proxy using Bachelier (normal) to avoid heavy math deps:
|
|
76
|
+
* price ≈ intrinsic + v * sqrt(T) * phi(0) (with a tiny convexity tweak)
|
|
77
|
+
* where v = vol (in price units), T in years.
|
|
78
|
+
* If you store a per-series vol index σ_annual (as decimal), you can use
|
|
79
|
+
* a Bachelier-like proxy with v = σ_annual * S for calls; puts symmetric.
|
|
80
|
+
* If vol is missing, fallback to intrinsic.
|
|
81
|
+
*/
|
|
82
|
+
priceEUApprox(type, S, K, volAnnual, daysToExpiry) {
|
|
83
|
+
const s = Number(S) || 0;
|
|
84
|
+
const k = Number(K) || 0;
|
|
85
|
+
const T = Math.max(0, Number(daysToExpiry || 0)) / 365;
|
|
86
|
+
const iv = this.intrinsic(type, k, s);
|
|
87
|
+
if (!volAnnual || !T) return iv;
|
|
88
|
+
|
|
89
|
+
const sigma = Number(volAnnual); // if you store as decimal (e.g. 0.6)
|
|
90
|
+
const v = sigma * s; // price vol in Bachelier
|
|
91
|
+
const noise = v * Math.sqrt(T) * 0.3989; // ≈ φ(0) ~ 0.3989
|
|
92
|
+
// keep it conservative: intrinsic plus a slice of noise
|
|
93
|
+
return Math.max(iv, iv + noise);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Maintenance for *naked* shorts (10x leverage padding, your rule of thumb).
|
|
98
|
+
* Use strike/10 as generic notional for puts, and S/10 for calls (conservative).
|
|
99
|
+
* You can tune this per-series if you store policy on the registry.
|
|
100
|
+
*/
|
|
101
|
+
nakedMaintenance(type, K, S) {
|
|
102
|
+
if (type === 'Call') return (Number(S) || 0) / 10; // ~10x leverage on spot notional
|
|
103
|
+
return (Number(K) || 0) / 10; // puts on strike notional
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Mark-to-model exposure for a set of option positions (for liquidation offsets).
|
|
108
|
+
* positions: [{ type:'Call'|'Put', strike, qty (signed), avgPrice?, expiryBlock }]
|
|
109
|
+
* Returns total premium value (signed) at current S, using vol index and time.
|
|
110
|
+
*/
|
|
111
|
+
mtmExposure(positions, S, volAnnual, blocksToExpiry, blocksPerDay) {
|
|
112
|
+
const bpd = Math.max(1, Number(blocksPerDay || 144)); // default ~ Bitcoin-like
|
|
113
|
+
const days = Math.max(0, Number(blocksToExpiry || 0) / bpd);
|
|
114
|
+
let prem = 0, intr = 0;
|
|
115
|
+
for (const p of (positions || [])) {
|
|
116
|
+
const price = this.priceEUApprox(p.type, S, p.strike, volAnnual, days);
|
|
117
|
+
prem += price * (Number(p.qty) || 0);
|
|
118
|
+
intr += this.intrinsic(p.type, p.strike, S) * (Number(p.qty) || 0);
|
|
119
|
+
}
|
|
120
|
+
return { premium: prem, intrinsic: intr };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Export as a singleton instance.
|
|
125
|
+
const Options = new OptionsEngine();
|
|
126
|
+
module.exports = Options;
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
var db = require('./db')
|
|
2
|
+
var BigNumber = require('bignumber.js')
|
|
3
|
+
const Insurance = require('./insurance.js')
|
|
4
|
+
|
|
5
|
+
class OracleList {
|
|
6
|
+
static instance = null;
|
|
7
|
+
static lastOracleUpdateBlock = new Map();
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
if (!OracleList.instance) {
|
|
11
|
+
this.oracles = new Map(); // Initialize the oracles map only once
|
|
12
|
+
OracleList.instance = this;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return OracleList.instance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static async getAllOracles() {
|
|
19
|
+
const instance = OracleList.getInstance();
|
|
20
|
+
await OracleList.load(); // Make sure the oracles are loaded
|
|
21
|
+
|
|
22
|
+
// Convert the Map of oracles to an array
|
|
23
|
+
return Array.from(instance.oracles.values());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async addOracle(oracleId, oracleData) {
|
|
27
|
+
try {
|
|
28
|
+
// Add to in-memory map
|
|
29
|
+
this.oracles.set(oracleId, oracleData);
|
|
30
|
+
|
|
31
|
+
// Add to NeDB database (if applicable)
|
|
32
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
33
|
+
await oracleDB.insertAsync({ _id: oracleId, ...oracleData });
|
|
34
|
+
|
|
35
|
+
console.log(`Oracle added: ID ${oracleId}`);
|
|
36
|
+
return true; // Indicate success
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`Error adding oracle: ID ${oracleId}`, error);
|
|
39
|
+
throw error; // Re-throw the error for the caller to handle
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static async getOracleInfo(oracleId) {
|
|
44
|
+
const instance = OracleList.getInstance();
|
|
45
|
+
|
|
46
|
+
// Check if in-memory map is empty and load if necessary
|
|
47
|
+
if (instance.oracles.size === 0) {
|
|
48
|
+
await OracleList.load();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Oracle key to search for
|
|
52
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
53
|
+
|
|
54
|
+
// Check in the in-memory map
|
|
55
|
+
const oracle = instance.oracles.get(oracleKey);
|
|
56
|
+
if (oracle) {
|
|
57
|
+
return oracle;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// If not found in-memory, optionally check the database
|
|
61
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
62
|
+
console.log('oracle key '+oracleKey)
|
|
63
|
+
const dbOracle = await oracleDB.findOneAsync({ _id: oracleKey });
|
|
64
|
+
console.log('db oracle '+ JSON.stringify(dbOracle))
|
|
65
|
+
if (dbOracle) {
|
|
66
|
+
return dbOracle;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`Oracle data not found for oracle ID: ${oracleId}`);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static async getOraclePrice(oracleId) {
|
|
74
|
+
// Prepare the query to find all entries with the specified oracleId
|
|
75
|
+
const oracleDB = await db.getDatabase('oracleData');
|
|
76
|
+
const oracleData = await oracleDB.findAsync({ oracleId: oracleId });
|
|
77
|
+
|
|
78
|
+
// Check if any data was returned
|
|
79
|
+
if (oracleData.length === 0) {
|
|
80
|
+
return 1
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Find the latest data point by blockHeight
|
|
84
|
+
const latestDataPoint = oracleData.reduce((latest, entry) => {
|
|
85
|
+
return (entry.blockHeight > latest.blockHeight) ? entry : latest;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log('Latest oracle data:', JSON.stringify(latestDataPoint));
|
|
89
|
+
return latestDataPoint.data.price;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static async publishData(oracleId, price, high, low, close, blockHeight) {
|
|
93
|
+
const lastBlock = OracleList.lastOracleUpdateBlock.get(oracleId);
|
|
94
|
+
|
|
95
|
+
if (lastBlock !== undefined && lastBlock >= blockHeight) {
|
|
96
|
+
console.log(`⛔ Oracle ${oracleId} already updated at block ${lastBlock}. Skipping block ${blockHeight}.`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// mark as updated
|
|
101
|
+
OracleList.lastOracleUpdateBlock.set(oracleId, blockHeight);
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const instance = OracleList.getInstance();
|
|
105
|
+
|
|
106
|
+
// Prepare oracle data
|
|
107
|
+
const oracleData = { price, high, low, close };
|
|
108
|
+
const lastPrice = await OracleList.getOraclePrice(oracleId)
|
|
109
|
+
console.log('last price '+lastPrice)
|
|
110
|
+
const priceBN = new BigNumber(price)
|
|
111
|
+
const lastPriceBN = new BigNumber(lastPrice)
|
|
112
|
+
const circuitLimitUp = new BigNumber(1.05).times(lastPriceBN).decimalPlaces(4).toNumber()
|
|
113
|
+
const circuitLimitDown = new BigNumber(0.95).times(lastPriceBN).decimalPlaces(4).toNumber()
|
|
114
|
+
console.log('price, limits '+price, lastPrice, circuitLimitDown, circuitLimitUp)
|
|
115
|
+
console.log('ergo, >limit up , <limit down' + Boolean(price>circuitLimitUp)+' '+Boolean(price<circuitLimitDown))
|
|
116
|
+
if(lastPrice!=1){
|
|
117
|
+
if(price>circuitLimitUp){
|
|
118
|
+
oracleData.price = circuitLimitUp
|
|
119
|
+
}else if(price <circuitLimitDown){
|
|
120
|
+
oracleData.price = circuitLimitDown
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Update in-memory oracle data (optional)
|
|
124
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
125
|
+
instance.oracles.set(oracleKey, oracleData);
|
|
126
|
+
|
|
127
|
+
// Save oracle data to the database
|
|
128
|
+
await instance.saveOracleData(oracleId, oracleData, blockHeight);
|
|
129
|
+
|
|
130
|
+
console.log(`Data published to oracle ${oracleId} for block height ${blockHeight}`);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(`Error publishing data to oracle ${oracleId} at block height ${blockHeight}:`, error);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Static method to get the singleton instance
|
|
138
|
+
static getInstance() {
|
|
139
|
+
if (!OracleList.instance) {
|
|
140
|
+
OracleList.instance = new OracleList();
|
|
141
|
+
}
|
|
142
|
+
return OracleList.instance;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static async load() {
|
|
146
|
+
try {
|
|
147
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
148
|
+
const oracles = await oracleDB.findAsync({});
|
|
149
|
+
|
|
150
|
+
const instance = OracleList.getInstance();
|
|
151
|
+
for (const oracle of oracles) {
|
|
152
|
+
instance.oracles.set(oracle._id, oracle);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log('Oracles loaded from the database');
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error('Error loading oracles from the database:', error);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static async isAdmin(senderAddress, oracleId) {
|
|
162
|
+
try {
|
|
163
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
164
|
+
console.log('checking admin for oracle key '+oracleKey)
|
|
165
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
166
|
+
const oracleData = await oracleDB.findOneAsync({ _id: oracleKey });
|
|
167
|
+
|
|
168
|
+
if (oracleData && oracleData.name.adminAddress === senderAddress) {
|
|
169
|
+
return true; // The sender is the admin
|
|
170
|
+
} else {
|
|
171
|
+
return false; // The sender is not the admin
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(`Error verifying admin for oracle ${oracleId}:`, error);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static async verifyAdmin(oracleId, adminAddress) {
|
|
180
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
181
|
+
|
|
182
|
+
// Check in-memory map first
|
|
183
|
+
const instance = OracleList.getInstance();
|
|
184
|
+
let oracle = instance.oracles.get(oracleKey);
|
|
185
|
+
|
|
186
|
+
// If not found in-memory, check the database
|
|
187
|
+
if (!oracle) {
|
|
188
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
189
|
+
oracle = await oracleDB.findOneAsync({ _id: oracleKey });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Verify admin address
|
|
193
|
+
return oracle && oracle.adminAddress === adminAddress;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
static async updateAdmin(oracleId, newAdminAddress, backup) {
|
|
198
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
199
|
+
const instance = OracleList.getInstance();
|
|
200
|
+
|
|
201
|
+
// Get the NeDB datastore for oracles
|
|
202
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
203
|
+
|
|
204
|
+
// Fetch the current oracle data
|
|
205
|
+
const oracle = await oracleDB.findOneAsync({ _id: oracleKey });
|
|
206
|
+
|
|
207
|
+
if (!oracle) {
|
|
208
|
+
throw new Error('Oracle not found');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if(backup){
|
|
212
|
+
oracle.backupAddress=newAdminAddress
|
|
213
|
+
}else{
|
|
214
|
+
// Update the admin address
|
|
215
|
+
oracle.adminAddress = newAdminAddress;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Update the oracle in the database
|
|
219
|
+
await oracleDB.updateAsync({ _id: oracleKey }, { $set: { adminAddress: newAdminAddress } }, {});
|
|
220
|
+
|
|
221
|
+
// Optionally, update the in-memory map if you are maintaining one
|
|
222
|
+
this.oracles.set(oracleKey, oracle);
|
|
223
|
+
|
|
224
|
+
console.log(`Oracle ID ${oracleId} admin updated to ${newAdminAddress}`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
static async createOracle(name, adminAddress) {
|
|
228
|
+
const instance = OracleList.getInstance(); // Get the singleton instance
|
|
229
|
+
const oracleId = OracleList.getNextId();
|
|
230
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
231
|
+
|
|
232
|
+
const newOracle = {
|
|
233
|
+
_id: oracleKey, // NeDB uses _id as the primary key
|
|
234
|
+
id: oracleId,
|
|
235
|
+
name: name,
|
|
236
|
+
adminAddress: adminAddress,
|
|
237
|
+
data: {} // Initial data, can be empty or preset values
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Get the NeDB datastore for oracles
|
|
241
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
// Save the new oracle to the database
|
|
245
|
+
await oracleDB.insertAsync(newOracle);
|
|
246
|
+
|
|
247
|
+
// Also save the new oracle to the in-memory map
|
|
248
|
+
instance.oracles.set(oracleKey, newOracle);
|
|
249
|
+
|
|
250
|
+
console.log(`New oracle created: ID ${oracleId}, Name: ${name}`);
|
|
251
|
+
return oracleId; // Return the new oracle ID
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error('Error creating new oracle:', error);
|
|
254
|
+
throw error; // Re-throw the error for the caller to handle
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static getNextId() {
|
|
259
|
+
const instance = OracleList.getInstance(); // Get the singleton instance
|
|
260
|
+
let maxId = 0;
|
|
261
|
+
for (const key of instance.oracles.keys()) {
|
|
262
|
+
const currentId = parseInt(key.split('-')[1]);
|
|
263
|
+
if (currentId > maxId) {
|
|
264
|
+
maxId = currentId;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return maxId + 1;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async saveOracleData(oracleId, data, blockHeight) {
|
|
271
|
+
const oracleDataDB = await db.getDatabase('oracleData');
|
|
272
|
+
const recordKey = `oracle-${oracleId}-${blockHeight}`;
|
|
273
|
+
console.log('saving published oracle data to key '+recordKey)
|
|
274
|
+
const oracleDataRecord = {
|
|
275
|
+
_id: recordKey,
|
|
276
|
+
oracleId,
|
|
277
|
+
data,
|
|
278
|
+
blockHeight
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
await oracleDataDB.updateAsync(
|
|
283
|
+
{ _id: recordKey },
|
|
284
|
+
oracleDataRecord,
|
|
285
|
+
{ upsert: true }
|
|
286
|
+
);
|
|
287
|
+
console.log(`Oracle data record saved successfully: ${recordKey}`);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error(`Error saving oracle data record: ${recordKey}`, error);
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async loadOracleData(oracleId, startBlockHeight = 0, endBlockHeight = Number.MAX_SAFE_INTEGER) {
|
|
295
|
+
const oracleDataDB = await db.getDatabase('oracleData');
|
|
296
|
+
try {
|
|
297
|
+
const query = {
|
|
298
|
+
oracleId: oracleId,
|
|
299
|
+
blockHeight: { $gte: startBlockHeight, $lte: endBlockHeight }
|
|
300
|
+
};
|
|
301
|
+
const oracleDataRecords = await oracleDataDB.findAsync(query);
|
|
302
|
+
return oracleDataRecords.map(record => ({
|
|
303
|
+
blockHeight: record.blockHeight,
|
|
304
|
+
data: record.data
|
|
305
|
+
}));
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error(`Error loading oracle data for oracleId ${oracleId}:`, error);
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static async closeOracle(oracleId) {
|
|
313
|
+
const instance = OracleList.getInstance();
|
|
314
|
+
const oracleKey = `oracle-${oracleId}`;
|
|
315
|
+
const oracleDB = await db.getDatabase('oracleList');
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
// Fetch the current oracle data
|
|
319
|
+
const oracle = await oracleDB.findOneAsync({ _id: oracleKey });
|
|
320
|
+
|
|
321
|
+
if (!oracle) {
|
|
322
|
+
throw new Error('Oracle not found');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Mark the oracle as closed
|
|
326
|
+
oracle.closed = true;
|
|
327
|
+
|
|
328
|
+
// Update the oracle in the database
|
|
329
|
+
await oracleDB.updateAsync({ _id: oracleKey }, { $set: { closed: true } }, {});
|
|
330
|
+
|
|
331
|
+
// Update the in-memory map if maintaining one
|
|
332
|
+
instance.oracles.set(oracleKey, oracle);
|
|
333
|
+
|
|
334
|
+
console.log(`Oracle ID ${oracleId} has been closed`);
|
|
335
|
+
|
|
336
|
+
// Call the insurance fund to perform the payout
|
|
337
|
+
await Insurance.liquidate(oracle.adminAddress,true);
|
|
338
|
+
|
|
339
|
+
console.log(`Payout for Oracle ID ${oracleId} completed`);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
console.error(`Error closing oracle ${oracleId}:`, error);
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Fetches VWAP for an oracle-based contract over `trailingBlocks`
|
|
349
|
+
* @param {number} oracleId - The oracle ID
|
|
350
|
+
* @param {number} blockHeight - The current block height
|
|
351
|
+
* @param {number} trailingBlocks - The number of blocks to look back
|
|
352
|
+
* @returns {Promise<number|null>} - The calculated VWAP or null if no data
|
|
353
|
+
*/
|
|
354
|
+
static async getTWAP(oracleId, blockHeight, trailingBlocks) {
|
|
355
|
+
try {
|
|
356
|
+
const oracleDB = await db.getDatabase('oracleData');
|
|
357
|
+
const blockStart = blockHeight - trailingBlocks;
|
|
358
|
+
|
|
359
|
+
// Query oracle data within the block range
|
|
360
|
+
const oracleData = await oracleDB.findAsync({
|
|
361
|
+
oracleId,
|
|
362
|
+
blockHeight: { $gte: blockStart, $lte: blockHeight }
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
if (!oracleData || oracleData.length === 0) {
|
|
366
|
+
//console.warn(`⚠️ No Oracle VWAP data for oracle ${oracleId} in blocks ${blockStart}-${blockHeight}`);
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Calculate VWAP
|
|
371
|
+
let totalVolume = new BigNumber(0);
|
|
372
|
+
let sumVolumeTimesPrice = new BigNumber(0);
|
|
373
|
+
|
|
374
|
+
for (const entry of oracleData) {
|
|
375
|
+
const price = new BigNumber(entry.data.price);
|
|
376
|
+
const volume = new BigNumber(1); // Assume equal weight for each oracle entry
|
|
377
|
+
|
|
378
|
+
totalVolume = totalVolume.plus(volume);
|
|
379
|
+
sumVolumeTimesPrice = sumVolumeTimesPrice.plus(volume.times(price));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (totalVolume.isZero()) return null;
|
|
383
|
+
|
|
384
|
+
return sumVolumeTimesPrice.dividedBy(totalVolume).decimalPlaces(8).toNumber();
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.error(`❌ Error fetching VWAP for oracle ${oracleId}:`, error);
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Additional methods for managing oracles
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
module.exports = OracleList;
|