@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,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&¶ms.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;
|