@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,964 @@
1
+ const db = require('./db')
2
+ const path = require('path');
3
+ const util = require('util');
4
+ //const TxUtils = require('./txUtils.js')
5
+ const BigNumber = require('bignumber.js')
6
+ const AMMPool = require('./amm.js')
7
+ const VolumeIndex = require('./volumeIndex.js')
8
+ const OracleRegistry = require('./oracle.js')
9
+ const PropertyManager = require('./property.js')
10
+ const Channels = require('./channels.js')
11
+
12
+ class ContractRegistry {
13
+ constructor() {
14
+ // ... Other initializations ...
15
+ this.contractList = new Map()
16
+ this.oracleList = new Map(); // Initialize if needed
17
+ this.nativeList = new Map(); // Initialize if needed
18
+ this.modFlag = false
19
+ }
20
+
21
+ static async setModFlag(flag){
22
+ this.modFlag = flag
23
+ return
24
+ }
25
+
26
+ static async loadContractSeries() {
27
+ //console.log('loading contract list for this instance '+JSON.stringify(instance))
28
+ const instance = ContractRegistry.getInstance(); // Access singleton instance
29
+ //console.log('loading contract list for this instance '+JSON.stringify(instance))
30
+ try {
31
+ const base = await db.getDatabase('contractList')
32
+ const docs = await base.findAsync({ type: 'contractSeries' });
33
+ return instance.contractSeries = new Map(docs.map(doc => [doc.id, doc.data]));
34
+ } catch (error) {
35
+ console.error('Error loading contract series data:', error);
36
+ }
37
+ }
38
+
39
+ // contractRegistry.js (add somewhere in the class or attach to the instance)
40
+ static async lookupInverseNativeByNotionalPid(basePid) {
41
+ const all = await ContractRegistry.getAllContracts(); // returns docs.map(doc.data)
42
+ const matches = [];
43
+
44
+ console.log('all contracts '+JSON.stringify(all))
45
+
46
+ for (const meta of all) {
47
+ if (!meta || typeof meta !== 'object') continue;
48
+ const cid = meta.id;
49
+ if (!cid) continue;
50
+
51
+ const inverse = await ContractRegistry.isInverse(cid);
52
+ const native = await ContractRegistry.isNativeContract(cid);
53
+ console.log('inverse, native? '+inverse+' '+native)
54
+ if (!inverse || !native) continue;
55
+
56
+ // match the underlying property we’re hedging
57
+ if (Number(meta.collateralPropertyId) !== Number(basePid)) continue;
58
+
59
+ matches.push({
60
+ contractId: cid,
61
+ seriesId: cid,
62
+ symbol: meta.symbol || meta.ticker || meta.name || `contract-${cid}`,
63
+ notionalPropertyId: Number(meta.notionalPropertyId),
64
+ // notional per contract is accessible via getNotionalValue(contractId, mark)
65
+ // but we return meta here and compute with the live mark later
66
+ });
67
+ }
68
+ return matches;
69
+ }
70
+
71
+ static async isDuplicateNativeContract(collateralPropertyId, onChainDataPair, notionalPropertyId) {
72
+ try {
73
+ // Load contract series if not already loaded
74
+ if (!ContractRegistry.getInstance().contractSeries) {
75
+ await ContractRegistry.loadContractSeries();
76
+ }
77
+ const instance = ContractRegistry.getInstance();
78
+ const contractSeries = instance.contractSeries;
79
+
80
+ // Iterate over contract series to find a duplicate
81
+ for (const [id, contract] of contractSeries) {
82
+ console.log('inside isDuplicateNativeContract '+id, JSON.stringify(contract), contract.collateralPropertyId, collateralPropertyId,onChainDataPair)
83
+ if (contract.native && contract.collateralPropertyId === collateralPropertyId) {
84
+ console.log('ding')
85
+ for (const pair of contract.onChainData) {
86
+ if ((pair[0] === onChainDataPair[0] && pair[1] === onChainDataPair[1])||(pair[0] === onChainDataPair[1] && pair[1] === onChainDataPair[0])&&contract.notionalPropertyId==notionalPropertyId) {
87
+ console.log('dong')
88
+ return true;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ console.log('contratulations')
94
+ return false;
95
+ } catch (error) {
96
+ console.error('Error checking for duplicate native contract:', error);
97
+ return false;
98
+ }
99
+ }
100
+
101
+ // Singleton instance getter
102
+ static getInstance() {
103
+ if (!this.instance) {
104
+ console.log('no instance detected creating new contract List obj')
105
+ this.instance = new ContractRegistry();
106
+ }
107
+ return this.instance;
108
+ }
109
+
110
+ static async createContractSeries(sender, params, block) {
111
+ // Load the current contract list from the database
112
+ const contractListDB = await db.getDatabase('contractList');
113
+ const currentContractList = await contractListDB.findAsync({ type: 'contractSeries' });
114
+ const contractList = new Map(currentContractList.map(doc => [doc.id, doc.data]));
115
+
116
+ // Generate a unique ID for the new contract series
117
+ const seriesId = await ContractRegistry.getNextIdFromMap(contractList);
118
+ const thisAMM = new AMMPool(0,1,10,seriesId)
119
+ if(params.whitelist==undefined||params.whitelist==null){
120
+ params.whitelist=0
121
+ }
122
+ if(params.native){
123
+ let propertyData1 = await PropertyManager.getPropertyData(params.notionalPropertyId)
124
+ let propertyData2 = await PropertyManager.getPropertyData(params.collateralPropertyId)
125
+ if(params.notionalPropertyId==0){
126
+ propertyData1={ticker:"LTC"}
127
+ console.log('property data in create contract series special ed. '+JSON.stringify(propertyData1)+' '+JSON.stringify(propertyData2))
128
+ }
129
+ params.ticker = propertyData1.ticker+"/"+propertyData2.ticker+"-PERP"
130
+
131
+ }else if(!params.native&&params.underlyingOracleId!=0){
132
+ const oracleInfo = await OracleRegistry.getOracleInfo(params.underlyingOracleId)
133
+ params.ticker = oracleInfo.name.ticker+"-OPERP"+params.underlyingOracleId+"-"+seriesId
134
+ console.log('params in create oracle contract '+JSON.stringify(params))
135
+ }
136
+
137
+
138
+ // Create the contract series object
139
+ const contractSeries = {
140
+ id: seriesId,
141
+ ticker:params.ticker||seriesId+"-PERP",
142
+ issuer: sender,
143
+ native: params.native,
144
+ underlyingOracleId: params.underlyingOracleId,
145
+ onChainData: params.onChainData,
146
+ notionalPropertyId: params.notionalPropertyId,
147
+ notionalValue: params.notionalValue,
148
+ collateralPropertyId: params.collateralPropertyId,
149
+ leverage: params.leverage,
150
+ expiryPeriod: params.expiryPeriod,
151
+ series: params.series,
152
+ inverse: params.inverse,
153
+ fee: params.fee,
154
+ whitelist: params.whitelist,
155
+ contracts: {
156
+ expired: [],
157
+ unexpired: await ContractRegistry.generateContracts(params.expiryPeriod, params.series, seriesId, block)
158
+ },
159
+ ammPool: thisAMM // Add the AMM object to the contract series
160
+ };
161
+
162
+ // Add the new contract series to the contract list
163
+ contractList.set(seriesId, contractSeries);
164
+
165
+ // Save the updated contract list back to the database
166
+ await ContractRegistry.saveDataToDb(contractList, 'contractSeries');
167
+
168
+ console.log(`New contract series created: ID ${seriesId}`);
169
+ return seriesId; // Return the new series ID
170
+ }
171
+
172
+ static async getAMM(contractId) {
173
+ console.log('inside get AMM')
174
+ const contractInfo = await ContractRegistry.getContractInfo(contractId);
175
+ if (contractInfo && contractInfo.amm) {
176
+ // Assuming the AMM object is stored inside the contractInfo object
177
+ return contractInfo.amm;
178
+ } else {
179
+ throw new Error(`AMM object not found for contract ID ${contractId}`);
180
+ }
181
+ }
182
+
183
+ // Function to update AMM object when LPs pledge or redeem
184
+ static async updateAMM(contractId, lpAddress, pledgeAmount, redeemAmount) {
185
+ if (!this.contractList.has(contractId)) {
186
+ throw new Error(`Contract ID ${contractId} not found in contract registry`);
187
+ }
188
+
189
+ const { ammPool } = this.contractList.get(contractId);
190
+
191
+ // Update the AMM object based on LPs pledge or redeem
192
+ if (pledgeAmount !== null && pledgeAmount > 0) {
193
+ // Pledge scenario: Add liquidity
194
+ ammPool.insertCapital(lpAddress, pledgeAmount);
195
+ } else if (redeemAmount !== null && redeemAmount > 0) {
196
+ // Redeem scenario: Remove liquidity
197
+ ammPool.redeemCapital(lpAddress, redeemAmount);
198
+ } else {
199
+ throw new Error(`Invalid pledgeAmount (${pledgeAmount}) or redeemAmount (${redeemAmount})`);
200
+ }
201
+
202
+ // Save the updated contract list back to the database
203
+ await this.saveAllData();
204
+ }
205
+
206
+
207
+ static async getNextIdFromMap(contractList) {
208
+ let maxId = 0;
209
+ for (const [key] of contractList.entries()) {
210
+ const currentId = parseInt(key);
211
+ if (currentId > maxId) {
212
+ maxId = currentId;
213
+ }
214
+ }
215
+ return maxId + 1;
216
+ }
217
+
218
+
219
+ static async getAllPerpContracts() {
220
+ try {
221
+ const contractListDB = await db.getDatabase('contractList');
222
+ const contracts = await contractListDB.findAsync({ type: 'contractSeries' });
223
+
224
+ if (!contracts || contracts.length === 0) {
225
+ console.log("⚠️ No contracts found in the registry.");
226
+ return [];
227
+ }
228
+
229
+ // **Filter contracts using both expiryPeriod == 0 OR ticker contains "PERP"**
230
+ const perpContracts = contracts
231
+ .filter(doc =>
232
+ doc.data.expiryPeriod === 0 ||
233
+ (doc.data.ticker && doc.data.ticker.includes("PERP"))
234
+ )
235
+ .map(doc => doc.data.id);
236
+
237
+ return perpContracts;
238
+
239
+ } catch (error) {
240
+ console.error("❌ Error fetching perpetual contracts:", error);
241
+ return [];
242
+ }
243
+ }
244
+
245
+
246
+ // Generate contracts within the series
247
+ static async generateContracts(expiryPeriod, series, seriesId, block) {
248
+ let contracts = [];
249
+ let expirationBlock = parseInt(block) + parseInt(expiryPeriod);
250
+
251
+ for (let i = 0; i < series; i++) {
252
+ contracts.push({
253
+ id: `${seriesId}-${expirationBlock}`,
254
+ expirationBlock: expirationBlock,
255
+ });
256
+ expirationBlock += parseInt(expiryPeriod);
257
+ }
258
+ return contracts;
259
+ }
260
+
261
+ static loadContractsFromDB() {
262
+ db.getDatabase('contractList').findAsync()
263
+ .then(docs => {
264
+ docs.forEach(doc => {
265
+ const { type, seriesId } = doc;
266
+ if (type === 'oracle') {
267
+ this.oracleList.set(seriesId, doc.data);
268
+ } else {
269
+ this.nativeList.set(seriesId, doc.data);
270
+ }
271
+ });
272
+ return
273
+ })
274
+ .catch(error => {
275
+ console.error('Error loading contracts from DB:', error);
276
+ throw error;
277
+ });
278
+ }
279
+
280
+ static async saveDataToDb(dataMap, dataType) {
281
+ const dbInstance = await db.getDatabase('contractList');
282
+ const dataArray = Array.from(dataMap.entries()).map(([id, data]) => ({
283
+ id, data, type: dataType
284
+ }));
285
+
286
+ for (const entry of dataArray) {
287
+ await dbInstance.updateAsync({ id: entry.id }, entry, { upsert: true });
288
+ }
289
+ }
290
+
291
+
292
+ // Function to save contract series, oracle contracts, or native contracts
293
+ static async saveAllData() {
294
+ const instance = ContractRegistry.getInstance();
295
+ await this.saveDataToDb(instance.contractList, 'contractSeries');
296
+ await this.saveDataToDb(instance.oracleList, 'oracleContracts');
297
+ await this.saveDataToDb(instance.nativeList, 'nativeContracts');
298
+ }
299
+
300
+
301
+ static async getNextId() {
302
+ const instance = ContractRegistry.getInstance(); // Access singleton instance
303
+ console.log('getting next id for instance '+JSON.stringify(instance))
304
+ let maxId = 0;
305
+ for (const [key] of instance.contractList.entries()) {
306
+ const currentId = parseInt(key);
307
+ if (currentId > maxId) {
308
+ maxId = currentId;
309
+ }
310
+ }
311
+ return maxId + 1;
312
+ }
313
+
314
+ static isValidSeriesId(seriesId) {
315
+ const instance = ContractRegistry.getInstance(); // Access singleton instance
316
+ // Check if the seriesId exists in the contract series registry
317
+ // The registry could be a database, a map, or any other data structure
318
+ // that stores information about the contract series in your system
319
+ if (instance.contractList.has(seriesId)) {
320
+ return true; // The seriesId is valid
321
+ } else {
322
+ return false; // The seriesId is not valid
323
+ }
324
+ }
325
+
326
+ static async getContractSeries(seriesId) {
327
+ const contractListDB = await db.getDatabase('contractList');
328
+ const doc = await contractListDB.findOneAsync({ id: seriesId, type: 'contractSeries' });
329
+ return doc ? doc.data : null;
330
+ }
331
+
332
+ // ... other methods ...
333
+
334
+ // Function to generate unique series ID
335
+ static async getNextId() {
336
+ const contractListDB = await db.getDatabase('contractList');
337
+ const docs = await contractListDB.findAsync({ type: 'contractSeries' });
338
+ let maxId = docs.reduce((max, doc) => Math.max(max, parseInt(doc.id)), 0);
339
+ return maxId + 1;
340
+ }
341
+
342
+ static async getAllContracts() {
343
+ const contractListDB = await db.getDatabase('contractList');
344
+ const docs = await contractListDB.findAsync({ type: 'contractSeries' });
345
+ return docs.map(doc => doc.data);
346
+ }
347
+
348
+ /**
349
+ * Returns an array of contract IDs where the collateral matches collateralId.
350
+ * @param {string} address - The trader's address (not used in filtering, but included for compatibility)
351
+ * @param {number} collateralId - The collateral property ID to filter by
352
+ * @returns {Promise<number[]>} - A promise that resolves to an array of contract IDs
353
+ */
354
+ static async getAllContractsForCollateral(address, collateralId) {
355
+ // Fetch contract data from DB
356
+ const contractList = await ContractRegistry.getAllContracts();
357
+
358
+ if (!contractList || contractList.length === 0) {
359
+ console.log(`⚠️ No contracts found in database.`);
360
+ return [];
361
+ }
362
+
363
+ // Filter contracts that match the collateralId
364
+ const contractIds = contractList
365
+ .filter(contract => contract.collateralPropertyId === collateralId)
366
+ .map(contract => contract.id); // Ensure we're extracting the correct ID field
367
+
368
+ console.log(`🔎 Found ${contractIds.length} contracts using collateral ${collateralId} for address ${address}.`);
369
+ return contractIds; // Returns an array usable in for...of loops
370
+ }
371
+
372
+ async hasOpenPositions(contract) {
373
+ try {
374
+ // Load the margin map for the contract's series ID
375
+ let marginMap = await MarginMap.loadMarginMap(contract.seriesId);
376
+ // Check if the margin map has any non-zero positions for this contract
377
+ for (let [address, positionData] of marginMap.margins.entries()) {
378
+ if (positionData.contracts > 0) {
379
+ return true; // Found an open position
380
+ }
381
+ }
382
+ return false; // No open positions found
383
+ } catch (error) {
384
+ console.error('Error checking open positions for contract:', contract.seriesId, error);
385
+ throw error;
386
+ }
387
+ }
388
+
389
+
390
+ static async getContractType(contractId) {
391
+ console.log('inside get contract type')
392
+ const contractInfo = await this.getContractInfo(contractId);
393
+ if (!contractInfo) {
394
+ throw new Error("Contract type not found for contract ID: " + contractId);
395
+ }
396
+ return contractInfo.native ? 'native' : 'oracle';
397
+ }
398
+
399
+ static async isNativeContract(contractId){
400
+ //console.log('inside isNative')
401
+ const contractInfo = await this.getContractInfo(contractId);
402
+ return contractInfo ? contractInfo.native : false;
403
+ }
404
+
405
+ static async getContractInfo(contractId) {
406
+ console.log('retrieving db info for contract ' + contractId);
407
+ const contractListDB = await db.getDatabase('contractList');
408
+ const doc = await contractListDB.findOneAsync({
409
+ id: parseInt(contractId, 10), // <-- coerce to number
410
+ type: 'contractSeries'
411
+ });
412
+ if (!doc) {
413
+ return null;
414
+ }
415
+ console.log(doc);
416
+ return doc.data;
417
+ }
418
+
419
+ static async getNotionalValue(contractId, mark) {
420
+
421
+ // Assuming contractData is the data structure for the contract
422
+
423
+ //console.log('inside get notional '+contractId)
424
+ const contractData = await ContractRegistry.getContractInfo(contractId);
425
+ console.log('blaiven '+JSON.stringify(contractData))
426
+ const BNMark = new BigNumber(mark)
427
+ const BNNotional = new BigNumber(contractData.notionalValue)
428
+ console.log('checking notional and mark in getNotionalValue '+contractData.notionalValue +' '+mark)
429
+ try {
430
+ if (contractData.native && contractData.inverse) {
431
+ console.log(`Calculating Notional Value for Inverse Native Contract`);
432
+
433
+ const notionalValue = new BigNumber(1)
434
+ .dividedBy(BNMark)
435
+ .multipliedBy(BNNotional)
436
+ .decimalPlaces(8)
437
+ .toNumber();
438
+
439
+ console.log(`Calculated Notional Value: ${notionalValue}`);
440
+
441
+ return{notionalValue:notionalValue, notionalPerContract:contractData.notionalValue};
442
+ }else if (!contractData.native && !contractData.inverse) {
443
+ console.log(`Calculating Notional Value for Linear Contract`+BNNotional+' '+BNMark);
444
+ const notionalValue = BNNotional.times(BNMark).decimalPlaces(8).toNumber();
445
+
446
+ console.log(`Calculated Notional Value: ${notionalValue}`);
447
+ return{notionalValue:notionalValue, notionalPerContract:contractData.notionalValue};
448
+ }else if (!contractData.native && contractData.inverse) {
449
+ console.log(`Calculating Notional Value for Inverse Oracle Contract`);
450
+
451
+ const latestPrice = await OracleRegistry.getOraclePrice(contractData.underlyingOracleId);
452
+ const notionalValue = new BigNumber(1)
453
+ .dividedBy(BNMark)
454
+ .multipliedBy(BNNotional)
455
+ .decimalPlaces(8)
456
+ .toNumber();
457
+
458
+ console.log(`Calculated Notional Value: ${value}`);
459
+ return {notionalValue:notionalValue, perContractNotional:contractData.notionalValue};
460
+ }
461
+ } catch (error) {
462
+ console.error(`Error retrieving notional value for contractId ${contractId}:`, error);
463
+ throw error;
464
+ }
465
+ };
466
+
467
+ static async isInverse(contractId) {
468
+ // Call the existing getContractInfo function
469
+
470
+ //console.log('inside isInverse')
471
+ const contractInfo = await this.getContractInfo(contractId);
472
+
473
+ // Check if contractInfo exists and has the 'inverse' property
474
+ if (contractInfo && typeof contractInfo.inverse !== 'undefined') {
475
+ return contractInfo.inverse;
476
+ }
477
+
478
+ // Return false by default if the contract is not found or doesn't have the 'inverse' property
479
+ return false;
480
+ }
481
+
482
+ // Function to get initial margin requirement for a contract
483
+ static async getInitialMargin(contractId, price){
484
+ //console.log('checking contractId inside getInitialMargin '+contractId)
485
+ const contractInfo = await ContractRegistry.getContractInfo(contractId);
486
+ if (!contractInfo) {
487
+ throw new Error(`Contract info not found for contract ID: ${contractId}`);
488
+ }
489
+ console.log('getting contractInfo inside getInit Margin ' +JSON.stringify(contractInfo))
490
+ let inverse = contractInfo.inverse;
491
+ let notionalValue = contractInfo.notionalValue
492
+ let leverage = contractInfo.leverage || 10
493
+ let priceBN = new BigNumber(price)
494
+ let leverageBN = new BigNumber(leverage)
495
+ let notionalBN = new BigNumber(notionalValue)
496
+ console.log('inside getInitialMargin, inverse:'+inverse+ 'notional '+ notionalValue + 'lvg. '+ leverage + 'at price '+price)
497
+ if (inverse) {
498
+ // For inverse contracts, margin is calculated based on notional value
499
+ console.log('calc. init. margin inverse '+notionalValue+' '+priceBN+' '+leverage)
500
+ let margin = notionalBN.dividedBy(priceBN).div(leverageBN).decimalPlaces(8, BigNumber.ROUND_CEIL).toNumber();
501
+ console.log(margin)
502
+ return margin
503
+ } else {
504
+ /*
505
+ // For linear contracts, check collateral and calculate based on oracle price or property value
506
+ const collateralValue = await ContractRegistry.getCollateralValue(contractInfo);
507
+ return BigNumber(collateralValue).div(leverage);
508
+ */
509
+
510
+ return notionalBN.times(price).div(leverageBN).decimalPlaces(8).toNumber();
511
+ }
512
+ }
513
+
514
+ // Helper function to get collateral value for linear contracts
515
+ static async getCollateralValue(contractInfo) {
516
+ const PropertyManager = require('./property.js')
517
+ const OracleList = require('./oracle.js')
518
+ const { collateralPropertyId, underlyingOracleId } = contractInfo;
519
+ if (collateralPropertyId) {
520
+ // If collateral is a property, use its value
521
+ const propertyData = await PropertyManager.getPropertyData(collateralPropertyId);
522
+ return propertyData ? propertyData.value : 0; // Example value fetching logic
523
+ } else if (underlyingOracleId) {
524
+ // If collateral is based on an oracle, use the latest price
525
+ const latestPrice = await OracleRegistry.getOracleData(underlyingOracleId);
526
+ return latestPrice || 0; // Example oracle price fetching logic
527
+ }
528
+ return 0; // Default to 0 if no valid collateral source
529
+ }
530
+
531
+ // Method to get the collateral property ID for a given contract ID
532
+ static async getCollateralId(contractId) {
533
+ // Load contract information
534
+
535
+ //console.log('inside get collateralPropertyId')
536
+ const contractInfo = await ContractRegistry.getContractInfo(contractId);
537
+
538
+ // Check if contract information is available
539
+ if (!contractInfo) {
540
+ console.log(`Contract info not found for contract ID: ${contractId}`);
541
+ }
542
+ //console.log('getting contract info for '+contractId +' '+JSON.stringify(contractInfo.collateralPropertyId))
543
+ // Return the collateral property ID from the contract information
544
+ //console.log('returning collateral id '+contractInfo.collateralPropertyId+ ' type of '+typeof contractInfo.collateralPropertyId)
545
+ return contractInfo.collateralPropertyId;
546
+ }
547
+
548
+ // In the contract order addition process
549
+ static async moveCollateralToReserve(sender, contractId, amount,price, block,txid,inProcess) {
550
+ const TallyMap = require('./tally.js')
551
+ const initialMarginPerContract = await ContractRegistry.getInitialMargin(contractId, price);
552
+ console.log('initialMarginPerContract '+initialMarginPerContract)
553
+ const collateralPropertyId = await ContractRegistry.getCollateralId(contractId)
554
+ console.log('collateralPropertyId '+collateralPropertyId)
555
+ const amountBN = new BigNumber(Math.abs(amount))
556
+ const initialMarginBN = new BigNumber(initialMarginPerContract)
557
+ const totalInitialMargin = initialMarginBN.times(amountBN).decimalPlaces(8).toNumber();
558
+ console.log('Total Initial Margin to reserve ' +totalInitialMargin+' '+sender+' '+collateralPropertyId)
559
+ // Move collateral to reserved position
560
+ const hasSufficientBalance = await TallyMap.hasSufficientBalance(sender,collateralPropertyId,totalInitialMargin)
561
+ console.log(hasSufficientBalance.hasSufficient)
562
+ if(hasSufficientBalance.hasSufficient){
563
+ let reason ='contractReserveInitMargin'
564
+ if(inProcess){reason = 'contractReserveFromMatchProcess'}
565
+ await TallyMap.updateBalance(sender, collateralPropertyId, -totalInitialMargin, totalInitialMargin, 0, 0, reason,block,txid);
566
+ return totalInitialMargin
567
+ }else{
568
+ return null
569
+ }
570
+
571
+ }
572
+
573
+ static async moveCollateralToMargin(
574
+ sender,
575
+ contractId,
576
+ amount,
577
+ price,
578
+ orderPrice,
579
+ side,
580
+ initMargin,
581
+ channel,
582
+ channelAddr,
583
+ block,
584
+ feeInfo,
585
+ maker,
586
+ flag,
587
+ txid,
588
+ position
589
+ ){
590
+ const TallyMap = require('./tally.js');
591
+ const MarginMap = require('./marginMap.js');
592
+ const BigNumber = require('bignumber.js');
593
+ const Channels = require('./channels.js');
594
+
595
+ const marginMap = await MarginMap.getInstance(contractId);
596
+
597
+ const initialMarginPerContract = await ContractRegistry.getInitialMargin(contractId, price);
598
+ const compareInitMargin = await ContractRegistry.getInitialMargin(contractId, orderPrice);
599
+ console.log('comparing realized price margin with orderPrice margin ' + initMargin + ' ' + compareInitMargin);
600
+
601
+ const collateralPropertyId = await ContractRegistry.getCollateralId(contractId);
602
+
603
+ // initMargin is already a TOTAL in your call-sites (marginUsed), keep it as-is
604
+ let totalInitialMargin = new BigNumber(initMargin || 0).decimalPlaces(8).toNumber();
605
+ const totalComparedMargin = new BigNumber(compareInitMargin).times(amount).decimalPlaces(8).toNumber();
606
+
607
+ console.log('Total Initial Margin ' + totalInitialMargin + ' ' + amount + ' ' + initMargin + ' ' + price);
608
+ console.log('about to calc. reserve-vs-fill delta ' + orderPrice + ' ' + price + ' ' + totalComparedMargin + ' ' + totalInitialMargin + ' ' + side + ' ' + maker);
609
+
610
+ // ------------------------------------------------------------
611
+ // Reconcile reserve at orderPrice vs required at fill price
612
+ // diff = reserved(orderPrice) - required(fill)
613
+ // diff > 0 => refund from reserve to available
614
+ // diff < 0 => top up reserve from available (if possible)
615
+ // NOTE: keep your original gating: only non-maker, non-channel
616
+ // ------------------------------------------------------------
617
+ if (channel === false && maker === false) {
618
+ const diff = new BigNumber(totalComparedMargin).minus(totalInitialMargin).decimalPlaces(8).toNumber();
619
+
620
+ if (diff > 0) {
621
+ console.log(`returning excess margin ${diff} to ${sender}`);
622
+ await TallyMap.updateBalance(
623
+ sender,
624
+ collateralPropertyId,
625
+ diff, // available +
626
+ -diff, // reserve -
627
+ 0, 0,
628
+ 'returnExcessMargin',
629
+ block
630
+ );
631
+ } else if (diff < 0) {
632
+ const topUp = new BigNumber(diff).abs().decimalPlaces(8).toNumber();
633
+ console.log(`topping up reserve ${topUp} for ${sender}`);
634
+ const has = await TallyMap.hasSufficientBalance(sender, collateralPropertyId, topUp);
635
+ if (has.hasSufficient) {
636
+ await TallyMap.updateBalance(
637
+ sender,
638
+ collateralPropertyId,
639
+ -topUp, // available -
640
+ topUp, // reserve +
641
+ 0, 0,
642
+ 'topUpReserveToFillPriceMargin',
643
+ block
644
+ );
645
+ } else {
646
+ console.log(`topUpReserveToFillPriceMargin skipped: insufficient available for ${sender}, need=${topUp}`);
647
+ }
648
+ }
649
+ }
650
+
651
+ console.log('checking feeInfo obj again ' + JSON.stringify(feeInfo));
652
+
653
+ if (feeInfo.buyFeeFromReserve && side === true) {
654
+ totalInitialMargin = new BigNumber(totalInitialMargin).minus(feeInfo.buyerFee).decimalPlaces(8).toNumber();
655
+ } else if (feeInfo.sellFeeFromReserve && side === false) {
656
+ totalInitialMargin = new BigNumber(totalInitialMargin).minus(feeInfo.sellerFee).decimalPlaces(8).toNumber();
657
+ }
658
+
659
+ // ------------------------------------------------------------
660
+ // Move init margin into margin bucket
661
+ // Priority: reserve -> available -> (optional) existing margin
662
+ // This prevents negative available crashes.
663
+ // ------------------------------------------------------------
664
+ if (channel === false) {
665
+ console.log('attention Will Robinson ' + totalInitialMargin);
666
+
667
+ // IMPORTANT: your getTally(address) path logs "undefined" and does not return a usable prop object
668
+ const propObj = await TallyMap.getTally(sender, collateralPropertyId) || {};
669
+
670
+ const availBal = new BigNumber(propObj.available || 0);
671
+ const resBal = new BigNumber(propObj.reserved || 0);
672
+ const marBal = new BigNumber(propObj.margin || 0);
673
+
674
+ // use as much reserve as possible
675
+ const reserveDebitBN = BigNumber.minimum(resBal, new BigNumber(totalInitialMargin));
676
+ const reserveDebit = reserveDebitBN.decimalPlaces(8).toNumber();
677
+
678
+ const remainingBN = new BigNumber(totalInitialMargin).minus(reserveDebitBN).decimalPlaces(8);
679
+ const remaining = remainingBN.toNumber();
680
+
681
+ // Case A: reserve alone covers it
682
+ if (remaining <= 0) {
683
+ if (reserveDebit > 0) {
684
+ await TallyMap.updateBalance(
685
+ sender,
686
+ collateralPropertyId,
687
+ 0,
688
+ -reserveDebit,
689
+ reserveDebit,
690
+ 0,
691
+ 'contractTradeInitMargin',
692
+ block
693
+ );
694
+ }
695
+ }
696
+ // Case B: reserve + available covers it
697
+ else if (availBal.gte(remaining)) {
698
+ await TallyMap.updateBalance(
699
+ sender,
700
+ collateralPropertyId,
701
+ -remaining, // take remaining from available
702
+ -reserveDebit, // take what we can from reserve
703
+ totalInitialMargin,
704
+ 0,
705
+ 'contractTradeInitMargin',
706
+ block
707
+ );
708
+ }
709
+ // Case C: reserve + existing margin covers it (no available debit)
710
+ else if (marBal.gte(remaining)) {
711
+ // move reserve portion (if any) into margin
712
+ if (reserveDebit > 0) {
713
+ await TallyMap.updateBalance(
714
+ sender,
715
+ collateralPropertyId,
716
+ 0,
717
+ -reserveDebit,
718
+ reserveDebit,
719
+ 0,
720
+ 'contractTradeInitMargin',
721
+ block
722
+ );
723
+ }
724
+ // remaining is assumed to already be sitting inside margin collateral
725
+ // (cross-margin style), so no tally movement needed.
726
+ }
727
+ // Case D: available + existing margin covers remaining (drain available, rest from margin)
728
+ else if (availBal.plus(marBal).gte(remaining)) {
729
+ const fromAvailBN = availBal.decimalPlaces(8);
730
+ const fromAvail = fromAvailBN.toNumber();
731
+
732
+ if (reserveDebit > 0) {
733
+ await TallyMap.updateBalance(
734
+ sender,
735
+ collateralPropertyId,
736
+ 0,
737
+ -reserveDebit,
738
+ reserveDebit,
739
+ 0,
740
+ 'contractTradeInitMargin',
741
+ block
742
+ );
743
+ }
744
+
745
+ if (fromAvail > 0) {
746
+ await TallyMap.updateBalance(
747
+ sender,
748
+ collateralPropertyId,
749
+ -fromAvail,
750
+ 0,
751
+ fromAvail,
752
+ 0,
753
+ 'contractTradeInitMargin',
754
+ block
755
+ );
756
+ }
757
+ // remainder-from-margin: no movement
758
+ }
759
+ // Case E: cannot fund
760
+ else {
761
+ throw new Error(
762
+ `Insufficient collateral for contractTradeInitMargin sender=${sender} prop=${collateralPropertyId} need=${totalInitialMargin} avail=${availBal.toNumber()} res=${resBal.toNumber()} mar=${marBal.toNumber()}`
763
+ );
764
+ }
765
+ }
766
+ else if (channel === true) {
767
+ let hasChannel = await TallyMap.hasSufficientChannel(channelAddr, collateralPropertyId, totalInitialMargin);
768
+ console.log('about to move initMargin from channel ' + channelAddr + ' ' + collateralPropertyId + ' ' + totalInitialMargin);
769
+
770
+ if (hasChannel.hasSufficient) {
771
+ await TallyMap.updateChannelBalance(channelAddr, collateralPropertyId, -totalInitialMargin, 'debitChannelContractTradeInitMargin', block);
772
+ await TallyMap.updateBalance(sender, collateralPropertyId, 0, 0, totalInitialMargin, 0, 'creditChannelContractTradeInitMargin', block);
773
+ await Channels.debitInitMarginFromChannel(channelAddr, sender, collateralPropertyId, totalInitialMargin, block);
774
+ } else {
775
+ if (hasChannel.reason != 'undefined') {
776
+ let shortfallBN = new BigNumber(hasChannel.shortfall);
777
+ let channelDebit = new BigNumber(totalInitialMargin).minus(shortfallBN).decimalPlaces(8).toNumber();
778
+
779
+ await TallyMap.updateChannelBalance(channelAddr, collateralPropertyId, -channelDebit, 'contractTradeInitMargin', block);
780
+ await TallyMap.updateBalance(sender, collateralPropertyId, -shortfallBN.toNumber(), 0, totalInitialMargin, 0, 'contractTradeInitMargin', block);
781
+
782
+ await Channels.debitInitMarginFromChannel(channelAddr, sender, collateralPropertyId, channelDebit, block);
783
+ } else {
784
+ throw new Error("reserve balance is undefined in tallymap for " + collateralPropertyId);
785
+ }
786
+ }
787
+ }
788
+
789
+ console.log('about to setInitialMargin ' + sender + contractId + ' ' + totalInitialMargin);
790
+ position = await marginMap.setInitialMargin(sender, contractId, totalInitialMargin, block, position);
791
+ return position;
792
+ }
793
+
794
+
795
+
796
+ static async getPriceAtBlock(contractId, blockHeight) {
797
+ let isOracleContract = await ContractRegistry.isOracleContract(contractId);
798
+ let oracleId = null;
799
+ let propertyId1 = null;
800
+ let propertyId2 = null;
801
+ let latestData;
802
+ let oracleDataDB = await db.getDatabase('contractList')
803
+ if (isOracleContract) {
804
+ oracleId = await ContractRegistry.getOracleId(contractId);
805
+ latestData = await oracleDataDB.findAsync({ oracleId: oracleId });
806
+ } else {
807
+ let info = await ContractRegistry.getContractInfo(contractId);
808
+ propertyId1 = info.onChainData[0][0];
809
+ propertyId2 = info.onChainData[0][1];
810
+ const pair = propertyId1+'-'+propertyId2
811
+ latestData = await VolumeIndex.getLastPrice(pair,blockHeight)
812
+ return latestData
813
+ console.log('inside get price at block '+typeof latestData, JSON.stringify(latestData))
814
+ }
815
+ // Filter data to get updates before the given blockHeight
816
+ const filteredData = latestData.filter(entry => entry.blockHeight < blockHeight);
817
+
818
+ if (filteredData.length === 0) {
819
+ // No data available before the given blockHeight
820
+ return null;
821
+ }
822
+
823
+ // Sort filtered data by block height in descending order
824
+ const sortedData = filteredData.sort((a, b) => b.blockHeight - a.blockHeight);
825
+ const latestBlockData = sortedData[0]; // Get the latest data before the given blockHeight
826
+ const lastPriceEntry = latestBlockData[latestBlockData.length - 1];
827
+ const priceBlockHeight = lastPriceEntry.blockHeight; // Block height of the price data
828
+
829
+ // Check if the block height of the price data is less than the provided blockHeight
830
+ if (priceBlockHeight >= blockHeight) {
831
+ // If not, find the latest data before the provided blockHeight
832
+ for (let i = 1; i < sortedData.length; i++) {
833
+ const blockData = sortedData[i];
834
+ const blockDataPrice = blockData[blockData.length - 1].blockHeight;
835
+ if (blockDataPrice < blockHeight) {
836
+ const lastPriceEntry = blockData[blockData.length - 1];
837
+ return lastPriceEntry.data.price;
838
+ }
839
+ }
840
+ return null; // No valid price data found before the provided blockHeight
841
+ }
842
+ return lastPriceEntry.data.price;
843
+ }
844
+
845
+ // Determine if a contract is an oracle contract
846
+ static async isOracleContract(contractId) {
847
+ const contractInfo = await ContractRegistry.getContractInfo(contractId);
848
+ return contractInfo && contractInfo.native === false;
849
+ }
850
+
851
+ // Determine a contract's oracle
852
+ static async getOracleId(contractId) {
853
+ const contractInfo = await ContractRegistry.getContractInfo(contractId);
854
+ //console.log(contractInfo.native,Boolean(contractInfo.native===false))
855
+ return contractInfo.underlyingOracleId;
856
+ }
857
+
858
+ static async getLatestOracleData(oracleId){
859
+ // Access the database where oracle data is stored
860
+ const oracleDataDB = await db.getDatabase('oracleData');
861
+ // Query the database for the latest oracle data for the given contract
862
+
863
+ const latestData = await oracleDataDB.findOneAsync({ oracleId: oracleId });
864
+ if (latestData) {
865
+ const sortedData = [latestData].sort((a, b) => b.blockHeight - a.blockHeight);
866
+ const latestBlockData = sortedData[0];
867
+
868
+ return latestBlockData
869
+ }else{
870
+ console.log('no oracle data found '+JSON.stringify(latestData))
871
+ return null
872
+ }
873
+
874
+ }
875
+
876
+ // Calculate the 1-hour funding rate for an oracle contract
877
+ static async calculateFundingRate(contractId) {
878
+ const isOracle = await ContractRegistry.isOracleContract(contractId);
879
+ if (!isOracle) {
880
+ return 0; // Return zero for non-oracle contracts
881
+ }
882
+
883
+ // Get oracle data for the last 24 blocks
884
+ const Oracles = require('./Oracles');
885
+ const oracleData = await Oracles.getLast24BlocksData(contractId);
886
+ const avgOraclePrice = ContractRegistry.calculateAveragePrice(oracleData);
887
+
888
+ // Placeholder for the logic to get the average trade price for the contract
889
+ // const avgTradePrice = ...;
890
+
891
+ // Calculate the funding rate based on the difference between oracle price and trade price
892
+ const priceDifference = avgTradePrice / avgOraclePrice;
893
+ let fundingRate = 0;
894
+
895
+ if (priceDifference > 1.0005) {
896
+ fundingRate = (priceDifference - 1.0005) * oracleData.length; // Example calculation
897
+ } else if (priceDifference < 0.9995) {
898
+ fundingRate = (0.9995 - priceDifference) * oracleData.length; // Example calculation
899
+ }
900
+
901
+ return fundingRate;
902
+ }
903
+
904
+ async applyFundingRateToSystem(contractId,block) {
905
+ const fundingRate = await ContractsRegistry.calculateFundingRate(contractId);
906
+
907
+ // Apply funding rate to marginMap+tallyMap
908
+ for (const [address, position] of marginMap.entries()) {
909
+ if (position.contractId === contractId) {
910
+ const fundingAmount = calculateFundingAmount(position.size, fundingRate);
911
+ TallyMap.updateBalance(address, contractId, fundingAmount,0,0,0,'funding',block);
912
+ marginMap.updatePosition(address, contractId, fundingAmount);
913
+ }
914
+ }
915
+
916
+ // Apply funding rate to vaulted contracts
917
+ for (const [vaultId, vault] of SynthRegistry.vaults.entries()) {
918
+ if (vault.contractId === contractId) {
919
+ const fundingAmount = ContractRegistry.calculateFundingAmount(vault.contractBalance, fundingRate);
920
+ SynthRegistry.applyPerpetualSwapFunding(vaultId, contractId, fundingAmount);
921
+ }
922
+ }
923
+
924
+ // Save changes
925
+ await TallyMap.save();
926
+ await marginMap.save();
927
+ await SynthRegistry.saveVaults();
928
+ }
929
+
930
+ static calculateFundingAmount(contractSize, fundingRate) {
931
+ return contractSize * fundingRate;
932
+ }
933
+
934
+
935
+ // Calculate the average price from oracle data
936
+ static calculateAveragePrice(oracleData) {
937
+ if (!oracleData || oracleData.length === 0) return 0;
938
+
939
+ const total = oracleData.reduce((acc, data) => acc + data.price, 0);
940
+ return total / oracleData.length;
941
+ }
942
+
943
+ // Save funding event for a contract
944
+ static async saveFundingEvent(contractId, fundingRate, blockHeight) {
945
+ const dbInstance = require('./db.js');
946
+ const fundingEvent = { contractId, fundingRate, blockHeight };
947
+ await dbInstance.getDatabase('fundingEvents').insertAsync(fundingEvent);
948
+ }
949
+
950
+ // Load funding events for a contract
951
+ static async loadFundingEvents(contractId) {
952
+ const dbInstance = require('./db.js');
953
+ const fundingEvents = await dbInstance.getDatabase('fundingEvents').findAsync({ contractId: contractId });
954
+ return fundingEvents.map(doc => doc);
955
+ }
956
+ }
957
+
958
+ // Usage:
959
+
960
+ /*const oracleContracts = registry.getContractsByOracle(5);
961
+
962
+ const propertyContracts = registry.getContractsByProperties(1, 2);*/
963
+
964
+ module.exports = ContractRegistry;