@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,1275 @@
|
|
|
1
|
+
var dbInstance = require('./db.js')
|
|
2
|
+
var TxUtils = require('./txUtils.js')
|
|
3
|
+
var PropertyList = require('./property.js')
|
|
4
|
+
const uuid = require('uuid');
|
|
5
|
+
const BigNumber = require('bignumber.js');
|
|
6
|
+
const Insurance = require('./insurance.js')
|
|
7
|
+
const Orderbooks = require('./orderbook.js')
|
|
8
|
+
|
|
9
|
+
const SATS = new BigNumber(1e8);
|
|
10
|
+
const RD = BigNumber.ROUND_DOWN;
|
|
11
|
+
|
|
12
|
+
function toSatsDecimal(x) {
|
|
13
|
+
// raw sats (can be fractional before flooring)
|
|
14
|
+
return new BigNumber(x).times(SATS);
|
|
15
|
+
}
|
|
16
|
+
function toSats(x) {
|
|
17
|
+
// integer sats, never creates value
|
|
18
|
+
return toSatsDecimal(x).integerValue(RD);
|
|
19
|
+
}
|
|
20
|
+
function fromSats(s) {
|
|
21
|
+
return new BigNumber(s).div(SATS);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Dust record is per (propertyId, contractId) in feeCache DB.
|
|
26
|
+
* We store potential fractional sats as a decimal BigNumber "dustSats"
|
|
27
|
+
* so many <1-sat remainders eventually roll up to whole sats.
|
|
28
|
+
*
|
|
29
|
+
* Schema: { dustSats: "decimal-string" }
|
|
30
|
+
* Key: `dust::<propertyId>-<contractId>`
|
|
31
|
+
*/
|
|
32
|
+
async function _loadDust(db, key) {
|
|
33
|
+
try {
|
|
34
|
+
const row = await db.get(`dust::${key}`);
|
|
35
|
+
const dustSats = new BigNumber(row?.dustSats ?? 0);
|
|
36
|
+
return { dustSats };
|
|
37
|
+
} catch {
|
|
38
|
+
return { dustSats: new BigNumber(0) };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function _saveDust(db, key, dustSats) {
|
|
43
|
+
await db.put(`dust::${key}`, { dustSats: dustSats.toString() });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Accumulate dust (can be fractional sat units). When it reaches ≥ 1 sat,
|
|
48
|
+
* credit whole sats to Insurance and keep the fractional remainder.
|
|
49
|
+
*
|
|
50
|
+
* creditFn({wholeSats}) must deposit exactly `wholeSats` (integer) sats to Insurance.
|
|
51
|
+
*/
|
|
52
|
+
async function _accumulateDust(db, key, addDustSatsDecimal, creditFn) {
|
|
53
|
+
if (!addDustSatsDecimal || new BigNumber(addDustSatsDecimal).eq(0)) return;
|
|
54
|
+
|
|
55
|
+
const { dustSats } = await _loadDust(db, key);
|
|
56
|
+
const next = dustSats.plus(addDustSatsDecimal);
|
|
57
|
+
const whole = next.integerValue(RD);
|
|
58
|
+
const frac = next.minus(whole);
|
|
59
|
+
|
|
60
|
+
if (whole.gt(0)) {
|
|
61
|
+
await creditFn({ wholeSats: whole });
|
|
62
|
+
}
|
|
63
|
+
await _saveDust(db, key, frac);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class TallyMap {
|
|
67
|
+
static instance;
|
|
68
|
+
|
|
69
|
+
constructor(path) {
|
|
70
|
+
if (!TallyMap.instance) {
|
|
71
|
+
this.addresses = new Map();
|
|
72
|
+
this.feeCache = new Map(); // Map for storing fees for each propertyId
|
|
73
|
+
TallyMap.instance = this;
|
|
74
|
+
this.modFlag = false
|
|
75
|
+
}
|
|
76
|
+
return TallyMap.instance;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Ensures that only one instance of TallyMap exists and attempts to load it from dbInstance.
|
|
81
|
+
* @param {number} blockHeight - The block height for which to load the tally map.
|
|
82
|
+
* @returns {Promise<TallyMap>} - A promise that resolves to the singleton instance of the TallyMap.
|
|
83
|
+
*/
|
|
84
|
+
static async getInstance() {
|
|
85
|
+
if (!TallyMap.instance) {
|
|
86
|
+
TallyMap.instance = new TallyMap();
|
|
87
|
+
}
|
|
88
|
+
await TallyMap.loadFromDB();
|
|
89
|
+
return TallyMap.instance;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static async setModFlag(flag){
|
|
93
|
+
this.modFlag = flag
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async verifyPropertyIds() {
|
|
98
|
+
let propertyIndex = await PropertyList.getPropertyIndex()
|
|
99
|
+
|
|
100
|
+
for (const [address, properties] of this.addresses.entries()) {
|
|
101
|
+
for (const propertyId in properties) {
|
|
102
|
+
if (!this.propertyIndex.has(propertyId)) {
|
|
103
|
+
console.error(`Invalid propertyId ${propertyId} found for address ${address}`);
|
|
104
|
+
// Handle the error - either remove the invalid entry or log it for further investigation
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static async updateBalance(address, propertyId, availableChange, reservedChange, marginChange, vestingChange, type, block,txid) {
|
|
111
|
+
console.log('inside updateBalance for '+address, propertyId, availableChange, reservedChange, marginChange, vestingChange, type, block)
|
|
112
|
+
|
|
113
|
+
if(availableChange==null||reservedChange==null||marginChange==null||vestingChange==null||isNaN(availableChange)||isNaN(reservedChange)||isNaN(marginChange)||isNaN(vestingChange)){
|
|
114
|
+
throw new Error('Somehow null passed into updateBalance... avail. '+availableChange + ' reserved '+ reservedChange + ' margin' + marginChange + ' vesting '+vestingChange )
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof propertyId === 'string' && propertyId.startsWith('s-')) {
|
|
118
|
+
// Handle synthetic token
|
|
119
|
+
} else if (!Number.isInteger(propertyId)) {
|
|
120
|
+
return Error(`Invalid propertyId: ${propertyId}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if (typeof availableChange !== 'number'){
|
|
125
|
+
console.log(`string passed in: ${availableChange}`);
|
|
126
|
+
availableChange = new BigNumber(availableChange).toNumber()
|
|
127
|
+
console.log('new availableChange '+availableChange)
|
|
128
|
+
}
|
|
129
|
+
if(typeof reservedChange !== 'number'){
|
|
130
|
+
console.log(`string passed in: ${reservedChange}`);
|
|
131
|
+
reservedChange = new BigNumber(reservedChange).toNumber()
|
|
132
|
+
}
|
|
133
|
+
if(typeof marginChange !== 'number'){
|
|
134
|
+
console.log(`string passed in: ${marginChange}`);
|
|
135
|
+
marginChange = new BigNumber(marginChange).toNumber()
|
|
136
|
+
console.log('new margin Change '+marginChange)
|
|
137
|
+
}
|
|
138
|
+
if(typeof vestingChange !== 'number'){
|
|
139
|
+
console.log(`string passed in: ${vestingChange}`);
|
|
140
|
+
vestingChange = new BigNumber(vestingChange).toNumber()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await TallyMap.loadFromDB();
|
|
144
|
+
if (!TallyMap.addresses.has(address)) {
|
|
145
|
+
TallyMap.addresses.set(address, {});
|
|
146
|
+
}
|
|
147
|
+
const addressObj = TallyMap.addresses.get(address);
|
|
148
|
+
|
|
149
|
+
console.log('addressObj being changed '+propertyId + ' for addr '+JSON.stringify(addressObj[propertyId]))
|
|
150
|
+
|
|
151
|
+
if (!addressObj[propertyId]) {
|
|
152
|
+
addressObj[propertyId] = { amount: 0, available: 0, reserved: 0, margin: 0, vesting: 0 };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check and update available balance
|
|
156
|
+
// Assuming addressObj[propertyId] and the respective change variables are already BigNumber instances
|
|
157
|
+
// Example for available balance
|
|
158
|
+
|
|
159
|
+
const originalAvailableBalance = new BigNumber(addressObj[propertyId].available);
|
|
160
|
+
const newAvailableBalance = originalAvailableBalance.plus(availableChange);
|
|
161
|
+
console.log('avail. balance change '+originalAvailableBalance, newAvailableBalance.toNumber(),availableChange)
|
|
162
|
+
if (newAvailableBalance.isLessThan(0)) {
|
|
163
|
+
throw new Error("Available balance cannot go negative " + originalAvailableBalance.toString() + ' change ' + availableChange.toString());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
addressObj[propertyId].available = newAvailableBalance.toNumber();
|
|
167
|
+
|
|
168
|
+
// Repeat the pattern for reserved, margin, and vesting balances
|
|
169
|
+
|
|
170
|
+
// Example for reserved balance
|
|
171
|
+
const originalReservedBalance = new BigNumber(addressObj[propertyId].reserved);
|
|
172
|
+
const newReservedBalance = originalReservedBalance.plus(reservedChange);
|
|
173
|
+
console.log('reserve. balance change '+originalReservedBalance, newReservedBalance.toNumber(),availableChange)
|
|
174
|
+
|
|
175
|
+
if (newReservedBalance.isLessThan(0)) {
|
|
176
|
+
throw new Error("Reserved balance cannot go negative " + originalReservedBalance.toString() + ' change ' + reservedChange.toString());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
addressObj[propertyId].reserved = newReservedBalance.toNumber();
|
|
180
|
+
|
|
181
|
+
// Example for margin balance
|
|
182
|
+
const originalMarginBalance = new BigNumber(addressObj[propertyId].margin);
|
|
183
|
+
const newMarginBalance = originalMarginBalance.plus(marginChange);
|
|
184
|
+
console.log('old and new margin balance '+originalMarginBalance+' '+newMarginBalance)
|
|
185
|
+
if (newMarginBalance.isLessThan(0)) {
|
|
186
|
+
throw new Error("Margin balance cannot go negative " + originalMarginBalance.toString() + ' change ' + marginChange.toString());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
addressObj[propertyId].margin = newMarginBalance.toNumber();
|
|
190
|
+
|
|
191
|
+
// Example for vesting balance
|
|
192
|
+
const originalVestingBalance = new BigNumber(addressObj[propertyId].vesting);
|
|
193
|
+
const newVestingBalance = originalVestingBalance.plus(vestingChange);
|
|
194
|
+
|
|
195
|
+
if (newVestingBalance.isLessThan(0)) {
|
|
196
|
+
throw new Error("Vesting balance cannot go negative " + originalVestingBalance.toString() + ' change ' + vestingChange.toString());
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
addressObj[propertyId].vesting = newVestingBalance.toNumber();
|
|
200
|
+
|
|
201
|
+
// Update the total amount
|
|
202
|
+
addressObj[propertyId].amount = this.calculateTotal(addressObj[propertyId]);
|
|
203
|
+
|
|
204
|
+
if (typeof addressObj[propertyId].channelBalance === 'undefined') {
|
|
205
|
+
addressObj[propertyId].channelBalance = 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if(availableChange==0&&reservedChange==0&&marginChange==0&&vestingChange==0){
|
|
210
|
+
|
|
211
|
+
}else{
|
|
212
|
+
await TallyMap.recordTallyMapDelta(address, block, propertyId, addressObj[propertyId].amount, availableChange, reservedChange, marginChange, vestingChange, 0, type,txid)
|
|
213
|
+
}
|
|
214
|
+
TallyMap.addresses.set(address, addressObj); // Update the map with the modified address object
|
|
215
|
+
//console.log('Updated balance for address:', JSON.stringify(addressObj), 'with propertyId:', propertyId);
|
|
216
|
+
await TallyMap.saveToDB(block); // Save changes to the database
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static async updateChannelBalance(address, propertyId, channelChange, type,block) {
|
|
220
|
+
await TallyMap.loadFromDB();
|
|
221
|
+
|
|
222
|
+
// Initialize the address if it doesn't exist
|
|
223
|
+
if (!TallyMap.addresses.has(address)) {
|
|
224
|
+
TallyMap.addresses.set(address, {});
|
|
225
|
+
}
|
|
226
|
+
const addressObj = TallyMap.addresses.get(address);
|
|
227
|
+
|
|
228
|
+
// Initialize the propertyId if it doesn't exist
|
|
229
|
+
if (!addressObj[propertyId]) {
|
|
230
|
+
addressObj[propertyId] = { amount: 0, available: 0, reserved: 0, margin: 0, vesting: 0, channelBalance: 0 };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Handle undefined channel balance and set it to 0 if necessary
|
|
234
|
+
if (typeof addressObj[propertyId].channelBalance === 'undefined') {
|
|
235
|
+
addressObj[propertyId].channelBalance = 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Update channel balance
|
|
239
|
+
const originalChannelBalance = new BigNumber(addressObj[propertyId].channelBalance);
|
|
240
|
+
const newChannelBalance = originalChannelBalance.plus(channelChange);
|
|
241
|
+
|
|
242
|
+
if (newChannelBalance.isLessThan(0)) {
|
|
243
|
+
throw new Error(`Channel balance cannot go negative for property ${propertyId}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Update the channel balance
|
|
247
|
+
addressObj[propertyId].channelBalance = newChannelBalance.toNumber();
|
|
248
|
+
addressObj[propertyId].amount = this.calculateTotal(addressObj[propertyId]);
|
|
249
|
+
// Record the channel balance change
|
|
250
|
+
if (channelChange !== 0) {
|
|
251
|
+
await TallyMap.recordTallyMapDelta(
|
|
252
|
+
address,
|
|
253
|
+
block,
|
|
254
|
+
propertyId,
|
|
255
|
+
addressObj[propertyId].amount,
|
|
256
|
+
0, // No change in available
|
|
257
|
+
0, // No change in reserved
|
|
258
|
+
0, // No change in margin
|
|
259
|
+
0, // No change in vesting
|
|
260
|
+
channelChange,
|
|
261
|
+
type
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Save the updated object back to the map
|
|
266
|
+
TallyMap.addresses.set(address, addressObj);
|
|
267
|
+
await TallyMap.saveToDB(block); // Save the updated balance to the database
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static async getTotalForProperty(propertyId) {
|
|
271
|
+
const instance = await TallyMap.getInstance();
|
|
272
|
+
let totalBalance = new BigNumber(0);
|
|
273
|
+
|
|
274
|
+
// Convert propertyId to a string to match stored keys
|
|
275
|
+
const propertyKey = String(propertyId);
|
|
276
|
+
|
|
277
|
+
// Iterate over all addresses in tallyMap
|
|
278
|
+
for (const [address, properties] of TallyMap.addresses.entries()) {
|
|
279
|
+
|
|
280
|
+
if (properties[propertyKey]) {
|
|
281
|
+
const balance = properties[propertyKey];
|
|
282
|
+
console.log('property balance '+JSON.stringify(balance))
|
|
283
|
+
// Ensure all balance components are properly defined
|
|
284
|
+
const available = new BigNumber(balance.available || 0);
|
|
285
|
+
const reserved = new BigNumber(balance.reserved || 0);
|
|
286
|
+
const margin = new BigNumber(balance.margin || 0);
|
|
287
|
+
const channel = new BigNumber(balance.channelBalance || 0);
|
|
288
|
+
if (available.isNaN() || reserved.isNaN() || margin.isNaN() || channel.isNaN()) {
|
|
289
|
+
console.error(`🚨 NaN detected in balance calculation for property ${propertyKey}`, {
|
|
290
|
+
available: available.toFixed(),
|
|
291
|
+
reserved: reserved.toFixed(),
|
|
292
|
+
margin: margin.toFixed(),
|
|
293
|
+
channel: channel.toFixed(),
|
|
294
|
+
});
|
|
295
|
+
continue; // Skip this entry
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Add up all the valid balances
|
|
299
|
+
totalBalance = totalBalance.plus(available).plus(reserved).plus(margin).plus(channel);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return totalBalance;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
static calculateTotal(balanceObj) {
|
|
307
|
+
return BigNumber(balanceObj.available).plus(balanceObj.reserved).plus(balanceObj.margin).plus(balanceObj.channel).decimalPlaces(8).toNumber();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static async setInitializationFlag() {
|
|
311
|
+
const db = await dbInstance.getDatabase('tallyMap');
|
|
312
|
+
await db.updateAsync(
|
|
313
|
+
{ _id: '$TLinit' },
|
|
314
|
+
{ _id: '$TLinit', initialized: true },
|
|
315
|
+
{ upsert: true }
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static async checkInitializationFlag() {
|
|
320
|
+
const db = await dbInstance.getDatabase('tallyMap');
|
|
321
|
+
const result = await db.findOneAsync({ _id: '$TLinit' });
|
|
322
|
+
if(result==undefined){return false}
|
|
323
|
+
return result ? result.initialized : false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
static async getAddressBalances(address) {
|
|
328
|
+
await TallyMap.loadFromDB();
|
|
329
|
+
|
|
330
|
+
// Log the serialized form of the data from the DB
|
|
331
|
+
//console.log('Serialized data from DB:', JSON.stringify([...instance.addresses]));
|
|
332
|
+
|
|
333
|
+
// Check if the address exists in the map
|
|
334
|
+
if (!TallyMap.addresses.has(address)) {
|
|
335
|
+
console.log(`No data found for address: ${address}`);
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const addressObj = TallyMap.addresses.get(address);
|
|
340
|
+
//console.log(`Data for address ${address}:`, addressObj);
|
|
341
|
+
const balances = [];
|
|
342
|
+
for (const propertyId in addressObj) {
|
|
343
|
+
//console.log('bleh' +propertyId+' '+JSON.stringify(addressObj))
|
|
344
|
+
const info = await PropertyList.getPropertyData(propertyId)
|
|
345
|
+
if (Object.hasOwnProperty.call(addressObj, propertyId)) {
|
|
346
|
+
const balanceObj = addressObj[propertyId];
|
|
347
|
+
let ticker = ''
|
|
348
|
+
if(info!=null&&info.ticker){ticker=info.ticker}
|
|
349
|
+
//console.log(propertyId, JSON.stringify(balanceObj),JSON.stringify(info))
|
|
350
|
+
balances.push({
|
|
351
|
+
propertyId: propertyId,
|
|
352
|
+
ticker: info.ticker,
|
|
353
|
+
amount: balanceObj.amount,
|
|
354
|
+
available: balanceObj.available,
|
|
355
|
+
reserved: balanceObj.reserved,
|
|
356
|
+
margin: balanceObj.margin,
|
|
357
|
+
vesting: balanceObj.vesting,
|
|
358
|
+
channel: balanceObj.channelBalance
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
//console.log(`Balances for address ${address}:`, balances);
|
|
363
|
+
return balances;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Retrieves the total tally for a given property ID across all addresses.
|
|
368
|
+
* @param {number|string} propertyId - The property ID to aggregate balances for.
|
|
369
|
+
* @returns {Promise<Object>} - An object representing the total tally for the given property.
|
|
370
|
+
*/
|
|
371
|
+
static async getTotalTally(propertyId) {
|
|
372
|
+
const instance = await TallyMap.getInstance();
|
|
373
|
+
const totalTally = {
|
|
374
|
+
amount: 0,
|
|
375
|
+
available: 0,
|
|
376
|
+
reserved: 0,
|
|
377
|
+
margin: 0,
|
|
378
|
+
vesting: 0,
|
|
379
|
+
channelBalance: 0
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
for (const properties of TallyMap.addresses.values()) {
|
|
383
|
+
if (properties[propertyId]) {
|
|
384
|
+
totalTally.amount += properties[propertyId].amount || 0;
|
|
385
|
+
totalTally.available += properties[propertyId].available || 0;
|
|
386
|
+
totalTally.reserved += properties[propertyId].reserved || 0;
|
|
387
|
+
totalTally.margin += properties[propertyId].margin || 0;
|
|
388
|
+
totalTally.vesting += properties[propertyId].vesting || 0;
|
|
389
|
+
totalTally.channelBalance += properties[propertyId].channelBalance || 0;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return totalTally;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Checks if a sender has a sufficient balance of a specific property.
|
|
398
|
+
* @param {string} senderAddress - The address of the sender.
|
|
399
|
+
* @param {number} propertyId - The ID of the property to check.
|
|
400
|
+
* @param {number} requiredAmount - The amount required for the transaction.
|
|
401
|
+
* @returns {Promise<{hasSufficient: boolean, reason: string}>} - An object indicating if the balance is sufficient and a reason if it's not.
|
|
402
|
+
*/
|
|
403
|
+
static async hasSufficientBalance(senderAddress, propertyId, requiredAmount) {
|
|
404
|
+
try {
|
|
405
|
+
const senderTally = await this.getTally(senderAddress, propertyId);
|
|
406
|
+
console.log('Checking senderTally in has hasSufficientBalance', senderAddress, propertyId, requiredAmount, JSON.stringify(senderTally));
|
|
407
|
+
|
|
408
|
+
if(!senderTally || senderTally.available === undefined||senderTally==0){
|
|
409
|
+
return { hasSufficient: false, reason: 'undefined', shortfall: requiredAmount };
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
//console.log('Available tokens:', senderTally.available, 'Required amount:', requiredAmount);
|
|
413
|
+
if(senderTally.available < requiredAmount){
|
|
414
|
+
const availBN = new BigNumber(senderTally.available)
|
|
415
|
+
const shortfall = new BigNumber(requiredAmount).minus(availBN).decimalPlaces(8).toNumber()
|
|
416
|
+
console.log('shortfall calc '+requiredAmount+' '+senderTally.available+' '+shortfall)
|
|
417
|
+
return { hasSufficient: false, reason: 'Insufficient available balance', shortfall:shortfall, available:senderTally.available };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return { hasSufficient: true, reason: '' };
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error('Error in hasSufficientBalance:', error);
|
|
423
|
+
return { hasSufficient: false, reason: 'Unexpected error checking balance' };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
static async hasSufficientReserve(senderAddress, propertyId, requiredAmount) {
|
|
428
|
+
try {
|
|
429
|
+
const senderTally = await this.getTally(senderAddress, propertyId);
|
|
430
|
+
console.log('Checking senderTally in has hasSufficientReserve', senderAddress, propertyId, requiredAmount, JSON.stringify(senderTally));
|
|
431
|
+
|
|
432
|
+
if (!senderTally || senderTally.reserved === undefined) {
|
|
433
|
+
return { hasSufficient: false, reason: 'undefined', shortfall: requiredAmount };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
console.log('Reserve tokens:', senderTally.reserved, 'Required amount:', requiredAmount);
|
|
437
|
+
|
|
438
|
+
if (senderTally.reserved < requiredAmount) {
|
|
439
|
+
let requiredBN = new BigNumber(requiredAmount)
|
|
440
|
+
let reservedBN = new BigNumber(senderTally.reserved)
|
|
441
|
+
let shortfall= requiredBN.minus(reservedBN).toNumber()
|
|
442
|
+
console.log('insufficient tokens ' +shortfall)
|
|
443
|
+
return { hasSufficient: false, reason: 'Insufficient available balance', shortfall: shortfall };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return { hasSufficient: true, reason: '' };
|
|
447
|
+
} catch (error) {
|
|
448
|
+
console.error('Error in hasSufficientBalance:', error);
|
|
449
|
+
return { hasSufficient: false, reason: 'Unexpected error checking balance' };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
static async hasSufficientMargin(senderAddress, propertyId, requiredAmount) {
|
|
454
|
+
try {
|
|
455
|
+
const senderTally = await this.getTally(senderAddress, propertyId);
|
|
456
|
+
console.log('Checking senderTally in has hasSufficientMargin', senderAddress, propertyId, requiredAmount, JSON.stringify(senderTally));
|
|
457
|
+
|
|
458
|
+
if (!senderTally || senderTally.margin === undefined) {
|
|
459
|
+
return { hasSufficient: false, reason: 'undefined', shortfall: requiredAmount };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
console.log('Margin tokens:', senderTally.margin, 'Required amount:', requiredAmount);
|
|
463
|
+
|
|
464
|
+
if (senderTally.margin < requiredAmount) {
|
|
465
|
+
let requiredBN = new BigNumber(requiredAmount)
|
|
466
|
+
let marginBN = new BigNumber(senderTally.margin)
|
|
467
|
+
let shortfall= requiredBN.minus(marginBN).toNumber()
|
|
468
|
+
console.log('insufficient tokens ' +shortfall)
|
|
469
|
+
return { hasSufficient: false, reason: 'Insufficient available balance', shortfall: shortfall };
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return { hasSufficient: true, reason: '' };
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error('Error in hasSufficientBalance:', error);
|
|
475
|
+
return { hasSufficient: false, reason: 'Unexpected error checking balance' };
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
static async hasSufficientChannel(senderAddress, propertyId, requiredAmount) {
|
|
480
|
+
try {
|
|
481
|
+
const senderTally = await this.getTally(senderAddress, propertyId);
|
|
482
|
+
console.log('Checking senderTally in has hasSufficientChannel', senderAddress, propertyId, requiredAmount, JSON.stringify(senderTally));
|
|
483
|
+
|
|
484
|
+
if (!senderTally || senderTally.channel === undefined) {
|
|
485
|
+
return { hasSufficient: false, reason: 'undefined' };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
console.log('Channel tokens:', senderTally.channel, 'Required amount:', requiredAmount);
|
|
489
|
+
|
|
490
|
+
if (senderTally.channel < requiredAmount) {
|
|
491
|
+
let requiredBN = new BigNumber(requiredAmount)
|
|
492
|
+
let channelBN = new BigNumber(senderTally.channel)
|
|
493
|
+
let shortfall= requiredBN.minus(channelBN).toNumber()
|
|
494
|
+
console.log('insufficient tokens ' +shortfall)
|
|
495
|
+
return { hasSufficient: false, reason: 'Insufficient available balance', shortfall: shortfall };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return { hasSufficient: true, reason: '' };
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.error('Error in hasSufficientBalance:', error);
|
|
501
|
+
return { hasSufficient: false, reason: 'Unexpected error checking balance' };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
static async saveToDB(block) {
|
|
506
|
+
try {
|
|
507
|
+
const db = await dbInstance.getDatabase('tallyMap');
|
|
508
|
+
const serializedData = JSON.stringify([...this.addresses]);
|
|
509
|
+
console.log('saving tallymap')
|
|
510
|
+
// Use upsert option
|
|
511
|
+
await db.updateAsync({ _id: 'tallyMap' }, { $set: {block: block, data: serializedData } }, { upsert: true });
|
|
512
|
+
//console.log('TallyMap saved successfully.');
|
|
513
|
+
} catch (error) {
|
|
514
|
+
console.error('Error saving TallyMap:', error);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static async loadFromDB() {
|
|
519
|
+
try {
|
|
520
|
+
const query = { _id: 'tallyMap' };
|
|
521
|
+
const db = await dbInstance.getDatabase('tallyMap')
|
|
522
|
+
const result = await db.findOneAsync(query);
|
|
523
|
+
|
|
524
|
+
if (result && result.data) {
|
|
525
|
+
// Deserialize the data from a JSON string to an array
|
|
526
|
+
const mapDataArray = JSON.parse(result.data);
|
|
527
|
+
// Convert the array back into a Map
|
|
528
|
+
this.addresses = new Map(mapDataArray.map(([key, value]) => [key, value]));
|
|
529
|
+
return this.addresses
|
|
530
|
+
} else {
|
|
531
|
+
console.log('failed to load tallyMap, starting a new map')
|
|
532
|
+
return this.addresses = new Map(); // Ensure addresses is always a Map
|
|
533
|
+
}
|
|
534
|
+
} catch (error) {
|
|
535
|
+
console.error('Error loading tally map from dbInstance:', error);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
static async saveFeeCacheToDB(propertyId, feeAmount, contractid) {
|
|
540
|
+
if (propertyId === undefined || feeAmount === undefined) {
|
|
541
|
+
console.error('Property ID or fee amount is undefined.');
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
console.log('Inside save fee cache ' + propertyId + ' ' + feeAmount);
|
|
545
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
546
|
+
try {
|
|
547
|
+
const roundedFee = Number(new BigNumber(feeAmount).toFixed(8)); // ✅ Ensures max 8 decimal places
|
|
548
|
+
const serializedFeeAmount = roundedFee
|
|
549
|
+
// Convert propertyId to a string if it's not already a string
|
|
550
|
+
const cacheId = String(propertyId)+String(contractid);
|
|
551
|
+
|
|
552
|
+
await db.updateAsync(
|
|
553
|
+
{ _id: cacheId }, // Query to find the document
|
|
554
|
+
{ $set: { value: serializedFeeAmount, contract: contractid } }, // Update the value field
|
|
555
|
+
{ upsert: true } // Insert a new document if it doesn't exist
|
|
556
|
+
);
|
|
557
|
+
console.log('FeeCache for property ' + propertyId + ' saved successfully.');
|
|
558
|
+
} catch (error) {
|
|
559
|
+
console.error('Error saving FeeCache:', error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
static async loadFeeCacheFromDB() {
|
|
564
|
+
let fees = new Map();
|
|
565
|
+
|
|
566
|
+
try {
|
|
567
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
568
|
+
const results = await db.findAsync({});
|
|
569
|
+
|
|
570
|
+
if (!results || results.length === 0) {
|
|
571
|
+
//console.log("⚠️ No fee cache entries found.");
|
|
572
|
+
return fees;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
//console.log(`✅ Loaded ${results.length} fee cache entries.`);
|
|
576
|
+
|
|
577
|
+
for (let result of results) {
|
|
578
|
+
//console.log(`📝 DB Entry: ${JSON.stringify(result)}`);
|
|
579
|
+
|
|
580
|
+
if (!result._id) {
|
|
581
|
+
console.warn(`⚠️ Skipping malformed entry (missing _id): ${JSON.stringify(result)}`);
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (typeof result.value === "undefined") {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if(!result.stash){result.stash=0}
|
|
589
|
+
let feeData = {
|
|
590
|
+
value: parseFloat(result.value), // ✅ Ensure value is a number
|
|
591
|
+
contract: result.contract || null,
|
|
592
|
+
stash: parseFloat(result.stash)
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
//console.log(`🔹 Fee Cache Parsed - Key: ${result._id}, Value: ${feeData.value}, Contract: ${feeData.contract}`);
|
|
596
|
+
|
|
597
|
+
fees.set(result._id, feeData);
|
|
598
|
+
}
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error(`🚨 Error loading fee cache:`, error);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return fees;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
static async loadFeeCacheForProperty(id) {
|
|
607
|
+
try {
|
|
608
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
609
|
+
const result = await db.findAsync({});
|
|
610
|
+
console.log('📄 Database contents:', JSON.stringify(result, null, 2));
|
|
611
|
+
|
|
612
|
+
let total = new BigNumber(0);
|
|
613
|
+
|
|
614
|
+
for(const doc of result){
|
|
615
|
+
if (doc._id.startsWith(`${id}-`)) {
|
|
616
|
+
const value = new BigNumber(doc.value || 0);
|
|
617
|
+
const stash = new BigNumber(doc.stash || 0);
|
|
618
|
+
total = total.plus(value).plus(stash);
|
|
619
|
+
console.log(`➕ Matched ${doc._id}: value=${value.toFixed()}, stash=${stash.toFixed()}, running total=${total.toFixed()}`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
console.log(`✅ FeeCache total for property ${id}: ${total.toFixed()}`);
|
|
624
|
+
return total;
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.error('❌ Error loading fee cache from dbInstance:', error);
|
|
627
|
+
return new BigNumber(0);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
static async loadTallyBlobDirect() {
|
|
633
|
+
try {
|
|
634
|
+
const dbInstance = require('./db.js')
|
|
635
|
+
const db = await dbInstance.getDatabase('tallyMap');
|
|
636
|
+
const result = await db.findOneAsync({ _id: 'tallyMap' });
|
|
637
|
+
|
|
638
|
+
if (!result) {
|
|
639
|
+
console.log("[TALLY] No tallyMap found in DB.");
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// result = { _id, block, data: "JSON_STRING" }
|
|
644
|
+
return result;
|
|
645
|
+
|
|
646
|
+
} catch (err) {
|
|
647
|
+
console.error("[TALLY] Error loading tally map:", err);
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Method to update fee cache for a property
|
|
653
|
+
// tally.js
|
|
654
|
+
|
|
655
|
+
static async resolveBlock(explicitBlock) {
|
|
656
|
+
if (explicitBlock !== undefined && explicitBlock !== null) return explicitBlock;
|
|
657
|
+
try {
|
|
658
|
+
const consensus = await dbInstance.getDatabase('consensus');
|
|
659
|
+
const t = await consensus.findOneAsync({ _id: 'TrackHeight' });
|
|
660
|
+
const m = await consensus.findOneAsync({ _id: 'MaxProcessedHeight' });
|
|
661
|
+
return (t && t.value) || (m && m.value) || null;
|
|
662
|
+
} catch { return null; }
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
static async loadFeeRow(db, cacheId) {
|
|
666
|
+
const row = await db.findOneAsync({ _id: cacheId });
|
|
667
|
+
return {
|
|
668
|
+
value: new BigNumber(row ? row.value || 0 : 0),
|
|
669
|
+
stash: new BigNumber(row ? row.stash || 0 : 0),
|
|
670
|
+
contract: row ? row.contract : undefined,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
static async saveFeeRow(db, cacheId, { value, stash, contract }) {
|
|
674
|
+
await db.updateAsync(
|
|
675
|
+
{ _id: cacheId },
|
|
676
|
+
{
|
|
677
|
+
$set: {
|
|
678
|
+
value: new BigNumber(value).decimalPlaces(8).toNumber(),
|
|
679
|
+
stash: new BigNumber(stash).decimalPlaces(8).toNumber(),
|
|
680
|
+
contract: contract,
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
{ upsert: true }
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/*
|
|
688
|
+
* updateFeeCache with full tracing:
|
|
689
|
+
* - Logs every decision point: spot, TL-spot, derivative, oracle/non-oracle.
|
|
690
|
+
* - Logs sat math: raw, rounded, half-split, dust.
|
|
691
|
+
* - Logs stash/insurance writes with exact integer sats.
|
|
692
|
+
*/static async updateFeeCache(propertyId, amount, contractId, block) {
|
|
693
|
+
const ContractRegistry = require('./contractRegistry.js');
|
|
694
|
+
|
|
695
|
+
try {
|
|
696
|
+
const blockArg = arguments.length >= 6 ? arguments[5] : block;
|
|
697
|
+
|
|
698
|
+
console.log(`\n====== updateFeeCache() @ block ${blockArg} ======`);
|
|
699
|
+
console.log(`INPUT propertyId=${propertyId}, amount=${amount}, contractId=${contractId}`);
|
|
700
|
+
|
|
701
|
+
// Convert → sats
|
|
702
|
+
let rawSats = toSatsDecimal(amount);
|
|
703
|
+
console.log(`rawSats (pre-floor) = ${rawSats.toString()}`);
|
|
704
|
+
|
|
705
|
+
rawSats = rawSats.integerValue(BigNumber.ROUND_FLOOR);
|
|
706
|
+
console.log(`rawSats (floored sats) = ${rawSats.toString()}`);
|
|
707
|
+
|
|
708
|
+
// Require even sats (should happen automatically from calculateFee)
|
|
709
|
+
let feeSats = rawSats;
|
|
710
|
+
|
|
711
|
+
console.log(`feeSats (final even sats) = ${feeSats.toString()}`);
|
|
712
|
+
|
|
713
|
+
if (feeSats.lte(0)) {
|
|
714
|
+
console.log(`EXIT: feeSats <= 0`);
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
719
|
+
const blk = await TallyMap.resolveBlock(blockArg);
|
|
720
|
+
const effContractId = (contractId == null) ? '1' : String(contractId);
|
|
721
|
+
|
|
722
|
+
const isOracleContract = await ContractRegistry.isOracleContract(contractId);
|
|
723
|
+
console.log(`Resolved: effContractId=${effContractId}, isOracle=${isOracleContract}`);
|
|
724
|
+
|
|
725
|
+
//
|
|
726
|
+
// 1) SPOT NON-TL
|
|
727
|
+
//
|
|
728
|
+
if (effContractId === '1' && propertyId != 1) {
|
|
729
|
+
const cacheId = `${propertyId}-1`;
|
|
730
|
+
console.log(`SPOT NON-TL route → stash only → cacheId=${cacheId}`);
|
|
731
|
+
|
|
732
|
+
const row0 = await TallyMap.loadFeeRow(db, cacheId);
|
|
733
|
+
console.log(`Pre-stash row: stash=${row0.stash.toString()}`);
|
|
734
|
+
|
|
735
|
+
const addTokens = fromSats(feeSats);
|
|
736
|
+
console.log(`Adding to stash: +${addTokens.toString()} tokens`);
|
|
737
|
+
|
|
738
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
739
|
+
stash: row0.stash.plus(addTokens),
|
|
740
|
+
contract: '1'
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
const row1 = await TallyMap.loadFeeRow(db, cacheId);
|
|
744
|
+
console.log(`Post-stash row: stash=${row1.stash.toString()}`);
|
|
745
|
+
|
|
746
|
+
console.log(`→ running buyback`);
|
|
747
|
+
return await TallyMap.feeCacheBuy(blockArg);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
//
|
|
751
|
+
// 2) SPOT TL-TOKEN (propertyId==1)
|
|
752
|
+
//
|
|
753
|
+
if (effContractId === '1' && propertyId == 1) {
|
|
754
|
+
console.log(`SPOT TL route → 100% to insurance`);
|
|
755
|
+
|
|
756
|
+
const insurance = await Insurance.getInstance(effContractId, false);
|
|
757
|
+
|
|
758
|
+
console.log(`insurance.deposit: +${fromSats(feeSats).toFixed(8)} (LTC)`);
|
|
759
|
+
await insurance.deposit(propertyId, fromSats(feeSats).toFixed(8), blk);
|
|
760
|
+
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
//
|
|
765
|
+
// 3) DERIVATIVES
|
|
766
|
+
//
|
|
767
|
+
console.log(`DERIVATIVES route (contract!=1)`);
|
|
768
|
+
|
|
769
|
+
const insuranceSats = feeSats.idiv(2); // floor
|
|
770
|
+
const stashSats = feeSats.minus(insuranceSats);
|
|
771
|
+
|
|
772
|
+
console.log(`50/50 split: insuranceSats=${insuranceSats}, stashSats=${stashSats}`);
|
|
773
|
+
|
|
774
|
+
if (!insuranceSats.plus(stashSats).eq(feeSats)) {
|
|
775
|
+
console.log(`⚠ Split mismatch (summing glitch), fixing stash = fee-insurance`);
|
|
776
|
+
stashSats = stashSats.minus(1);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
//
|
|
780
|
+
// Insurance write
|
|
781
|
+
//
|
|
782
|
+
try {
|
|
783
|
+
console.log(`Insurance deposit: contract=${effContractId}, isOracle=${isOracleContract}`);
|
|
784
|
+
console.log(`insurance.deposit: +${fromSats(insuranceSats).toFixed(8)} (LTC)`);
|
|
785
|
+
|
|
786
|
+
const insurance = await Insurance.getInstance(effContractId, isOracleContract);
|
|
787
|
+
|
|
788
|
+
await insurance.deposit(
|
|
789
|
+
propertyId,
|
|
790
|
+
fromSats(insuranceSats).toFixed(8),
|
|
791
|
+
blk
|
|
792
|
+
);
|
|
793
|
+
} catch (e) {
|
|
794
|
+
console.error(`❌ Insurance deposit ERROR for contract ${effContractId}:`, e);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
//
|
|
798
|
+
// Stash write
|
|
799
|
+
//
|
|
800
|
+
const cacheId = `${propertyId}-${effContractId}`;
|
|
801
|
+
console.log(`Stash write → cacheId=${cacheId}`);
|
|
802
|
+
|
|
803
|
+
const stashRow0 = await TallyMap.loadFeeRow(db, cacheId);
|
|
804
|
+
console.log(`Pre-stash stash=${stashRow0.stash.toString()}`);
|
|
805
|
+
|
|
806
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
807
|
+
stash: stashRow0.stash.plus(fromSats(stashSats)),
|
|
808
|
+
contract: effContractId
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
const stashRow1 = await TallyMap.loadFeeRow(db, cacheId);
|
|
812
|
+
console.log(`Post-stash stash=${stashRow1.stash.toString()}`);
|
|
813
|
+
|
|
814
|
+
//
|
|
815
|
+
// 4) Buyback
|
|
816
|
+
//
|
|
817
|
+
console.log(`→ running buyback`);
|
|
818
|
+
const ret = await TallyMap.feeCacheBuy(blockArg);
|
|
819
|
+
|
|
820
|
+
console.log(`====== END updateFeeCache() @ block ${blockArg} ======\n`);
|
|
821
|
+
return ret;
|
|
822
|
+
|
|
823
|
+
} catch (e) {
|
|
824
|
+
console.error('🚨 Error in updateFeeCache:', e);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
static async feeCacheBuy(block) {
|
|
830
|
+
const fees = await TallyMap.loadFeeCacheFromDB();
|
|
831
|
+
if (!fees || fees.size === 0) return;
|
|
832
|
+
|
|
833
|
+
for (const [key, feeData] of fees.entries()) {
|
|
834
|
+
if (!feeData || !feeData.contract) continue;
|
|
835
|
+
|
|
836
|
+
const [property, contractId] = key.split('-');
|
|
837
|
+
const value = new BigNumber(feeData.value || 0);
|
|
838
|
+
const stash = new BigNumber(feeData.stash || 0);
|
|
839
|
+
const total = value.plus(stash);
|
|
840
|
+
if (total.lte(0)) continue;
|
|
841
|
+
|
|
842
|
+
const orderBookKey = `1-${property}`;
|
|
843
|
+
const orderbook = await Orderbooks.getOrderbookInstance(orderBookKey);
|
|
844
|
+
const ob = orderbook.orderBooks[orderBookKey] || { buy: [], sell: [] };
|
|
845
|
+
const hasSell = Array.isArray(ob.sell) && ob.sell.length > 0;
|
|
846
|
+
|
|
847
|
+
if (!hasSell) continue; // nothing to do this block
|
|
848
|
+
|
|
849
|
+
// Place a single buy using total (value + stash)
|
|
850
|
+
const order = {
|
|
851
|
+
offeredPropertyId: property,
|
|
852
|
+
desiredPropertyId: 1,
|
|
853
|
+
amountOffered: total.toNumber(),
|
|
854
|
+
amountExpected: 0.00000001,
|
|
855
|
+
blockTime: block,
|
|
856
|
+
sender: 'feeCache',
|
|
857
|
+
};
|
|
858
|
+
order.price = orderbook.calculatePrice(order.amountOffered, order.amountExpected);
|
|
859
|
+
|
|
860
|
+
const reply = await orderbook.insertOrder(order, orderBookKey, false, false);
|
|
861
|
+
|
|
862
|
+
// Pre-deduct: set value→0 and spend all stash
|
|
863
|
+
if (value.gt(0)) await TallyMap.adjustFeeCache(property, contractId, { valueDelta: value.negated() });
|
|
864
|
+
if (stash.gt(0)) await TallyMap.adjustFeeCache(property, contractId, { stashDelta: stash.negated() });
|
|
865
|
+
|
|
866
|
+
const matchResult = await orderbook.matchTokenOrders(reply);
|
|
867
|
+
if (matchResult.matches && matchResult.matches.length > 0) {
|
|
868
|
+
await orderbook.processTokenMatches(matchResult.matches, block, null, false);
|
|
869
|
+
} else {
|
|
870
|
+
// restore back to stash if no matches (race)
|
|
871
|
+
if (value.gt(0)) await TallyMap.adjustFeeCache(property, contractId, { stashDelta: value });
|
|
872
|
+
if (stash.gt(0)) await TallyMap.adjustFeeCache(property, contractId, { stashDelta: stash });
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
await orderbook.saveOrderBook(orderBookKey);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* accrueFee:
|
|
883
|
+
* - SPOT (contractId null/undefined): 50/50 split in integer sats → half Insurance NOW, half -> VALUE.
|
|
884
|
+
* - CONTRACT:
|
|
885
|
+
* - Native -> 100% to VALUE (no insurance now).
|
|
886
|
+
* - Oracle -> 50/50 split in integer sats → half Insurance NOW, half -> STASH.
|
|
887
|
+
* - All rounding remainders funnel into dust and later pay to Insurance.
|
|
888
|
+
*/
|
|
889
|
+
static async accrueFee(propertyId, amount, contractId, block) {
|
|
890
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
891
|
+
|
|
892
|
+
// Work in sats
|
|
893
|
+
const rawSats = toSatsDecimal(amount);
|
|
894
|
+
const feeSats = rawSats.integerValue(RD);
|
|
895
|
+
if (feeSats.lte(0)) return;
|
|
896
|
+
|
|
897
|
+
const isSpot = (contractId === null || contractId === undefined);
|
|
898
|
+
const effContractId = isSpot ? '1' : String(contractId);
|
|
899
|
+
const cacheId = `${propertyId}-${effContractId}`;
|
|
900
|
+
const dustKey = cacheId;
|
|
901
|
+
const blk = await TallyMap.resolveBlock(block);
|
|
902
|
+
|
|
903
|
+
// Accumulate conversion dust
|
|
904
|
+
const convDust = rawSats.minus(feeSats); // [0,1)
|
|
905
|
+
await _accumulateDust(db, dustKey, convDust, async ({ wholeSats }) => {
|
|
906
|
+
const insurance = await Insurance.getInstance(effContractId, effContractId !== '1');
|
|
907
|
+
await insurance.deposit(propertyId, fromSats(wholeSats).toNumber(), blk);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
const row = await TallyMap.loadFeeRow(db, cacheId);
|
|
911
|
+
|
|
912
|
+
if (isSpot) {
|
|
913
|
+
// 50/50 split in integer sats
|
|
914
|
+
const insuranceSats = feeSats.idiv(2);
|
|
915
|
+
const valueSats = feeSats.minus(insuranceSats);
|
|
916
|
+
|
|
917
|
+
try {
|
|
918
|
+
const ins = await Insurance.getInstance('1', false);
|
|
919
|
+
await ins.deposit('1', fromSats(insuranceSats).toNumber(), blk);
|
|
920
|
+
} catch (e) {
|
|
921
|
+
console.error('❌ Spot fee insurance deposit failed:', e);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
925
|
+
value: row.value.plus(fromSats(valueSats)),
|
|
926
|
+
stash: row.stash,
|
|
927
|
+
contract: '1',
|
|
928
|
+
});
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Contract path
|
|
933
|
+
const isNative = await ContractRegistry.isNativeContract(effContractId).catch(() => false);
|
|
934
|
+
if (!isNative) {
|
|
935
|
+
// ORACLE: half to insurance now, exact remainder to STASH
|
|
936
|
+
const insuranceSats = feeSats.idiv(2);
|
|
937
|
+
const stashSats = feeSats.minus(insuranceSats);
|
|
938
|
+
|
|
939
|
+
try {
|
|
940
|
+
const ins = await Insurance.getInstance(effContractId, true);
|
|
941
|
+
await ins.deposit(propertyId, fromSats(insuranceSats).toNumber(), blk);
|
|
942
|
+
} catch (e) {
|
|
943
|
+
console.error(`❌ Insurance deposit failed for contract ${effContractId}:`, e);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
947
|
+
value: row.value,
|
|
948
|
+
stash: row.stash.plus(fromSats(stashSats)),
|
|
949
|
+
contract: effContractId,
|
|
950
|
+
});
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// NATIVE: 100% to VALUE
|
|
955
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
956
|
+
value: row.value.plus(fromSats(feeSats)),
|
|
957
|
+
stash: row.stash,
|
|
958
|
+
contract: effContractId,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* NEW: adjustFeeCache(propertyId, contractId, { valueDelta?, stashDelta? })
|
|
963
|
+
* - Used by clearing to spend from VALUE/STASH when matching.
|
|
964
|
+
*/
|
|
965
|
+
static async adjustFeeCache(propertyId, contractId, deltas) {
|
|
966
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
967
|
+
const effectiveContractId = (contractId === null || contractId === undefined) ? '1' : String(contractId);
|
|
968
|
+
const cacheId = `${propertyId}-${effectiveContractId}`;
|
|
969
|
+
const row = await TallyMap.loadFeeRow(db, cacheId);
|
|
970
|
+
|
|
971
|
+
const valueDelta = new BigNumber(deltas?.valueDelta || 0).decimalPlaces(8);
|
|
972
|
+
const stashDelta = new BigNumber(deltas?.stashDelta || 0).decimalPlaces(8);
|
|
973
|
+
|
|
974
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
975
|
+
value: row.value.plus(valueDelta),
|
|
976
|
+
stash: row.stash.plus(stashDelta),
|
|
977
|
+
contract: effectiveContractId,
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// ---------- 4) drawOnFeeCache (compat shim; pull from VALUE/STASH) ----------
|
|
982
|
+
/**
|
|
983
|
+
* drawOnFeeCache(propertyId, contractId='1', opts?)
|
|
984
|
+
* - Minimal compat: withdraw up to `opts.max` from VALUE first (then STASH if allowStash).
|
|
985
|
+
* - Returns { spent } in token units (8 d.p.).
|
|
986
|
+
*/
|
|
987
|
+
static async drawOnFeeCache(propertyId, contractId = '1', opts = {}) {
|
|
988
|
+
const { max = null, allowStash = false } = opts;
|
|
989
|
+
const db = await dbInstance.getDatabase('feeCache');
|
|
990
|
+
const effContractId = (contractId == null) ? '1' : String(contractId);
|
|
991
|
+
const cacheId = `${propertyId}-${effContractId}`;
|
|
992
|
+
const row = await TallyMap.loadFeeRow(db, cacheId);
|
|
993
|
+
|
|
994
|
+
const want = (max == null) ? row.value : BigNumber.min(new BigNumber(max), row.value);
|
|
995
|
+
if (want.lte(0)) {
|
|
996
|
+
if (!allowStash) return { spent: new BigNumber(0) };
|
|
997
|
+
const wantFromStash = (max == null) ? row.stash : BigNumber.min(new BigNumber(max), row.stash);
|
|
998
|
+
if (wantFromStash.lte(0)) return { spent: new BigNumber(0) };
|
|
999
|
+
|
|
1000
|
+
// spend from STASH (sat-accurate)
|
|
1001
|
+
const raw = toSatsDecimal(wantFromStash);
|
|
1002
|
+
const sats = raw.integerValue(RD);
|
|
1003
|
+
const spent = fromSats(sats);
|
|
1004
|
+
|
|
1005
|
+
await _accumulateDust(db, `adj:${cacheId}:stash`, raw.minus(sats), async ({ wholeSats }) => {
|
|
1006
|
+
const r = await TallyMap.loadFeeRow(db, cacheId);
|
|
1007
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
1008
|
+
value: r.value,
|
|
1009
|
+
stash: r.stash.plus(fromSats(wholeSats)),
|
|
1010
|
+
contract: effContractId,
|
|
1011
|
+
});
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
1015
|
+
value: row.value,
|
|
1016
|
+
stash: row.stash.minus(spent),
|
|
1017
|
+
contract: effContractId,
|
|
1018
|
+
});
|
|
1019
|
+
return { spent };
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// spend from VALUE
|
|
1023
|
+
const raw = toSatsDecimal(want);
|
|
1024
|
+
const sats = raw.integerValue(RD);
|
|
1025
|
+
const spent = fromSats(sats);
|
|
1026
|
+
|
|
1027
|
+
await _accumulateDust(db, `adj:${cacheId}:value`, raw.minus(sats), async ({ wholeSats }) => {
|
|
1028
|
+
const r = await TallyMap.loadFeeRow(db, cacheId);
|
|
1029
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
1030
|
+
value: r.value.plus(fromSats(wholeSats)),
|
|
1031
|
+
stash: r.stash,
|
|
1032
|
+
contract: effContractId,
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
await TallyMap.saveFeeRow(db, cacheId, {
|
|
1037
|
+
value: row.value.minus(spent),
|
|
1038
|
+
stash: row.stash,
|
|
1039
|
+
contract: effContractId,
|
|
1040
|
+
});
|
|
1041
|
+
return { spent };
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
async applyDeltasSinceLastHeight(lastHeight) {
|
|
1045
|
+
// Retrieve and apply all deltas from lastHeight to the current height
|
|
1046
|
+
for (let height = lastHeight + 1; height <= currentBlockHeight; height++) {
|
|
1047
|
+
const serializedDelta = await dbInstance.get(`tallyMapDelta-${height}`);
|
|
1048
|
+
if (serializedDelta) {
|
|
1049
|
+
const delta = JSON.parse(serializedDelta);
|
|
1050
|
+
this.applyDelta(delta);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// Function to record a delta
|
|
1056
|
+
static async recordTallyMapDelta(address, block, propertyId, total, availableChange, reservedChange, marginChange, vestingChange, channelChange, type,txid){
|
|
1057
|
+
const newUuid = uuid.v4();
|
|
1058
|
+
const db = await dbInstance.getDatabase('tallyMapDelta');
|
|
1059
|
+
let deltaKey = `${address}-${propertyId}-${newUuid}`;
|
|
1060
|
+
deltaKey+='-'+block
|
|
1061
|
+
const tally = TallyMap.getTally(address, propertyId)
|
|
1062
|
+
if(!txid){txid=''}
|
|
1063
|
+
total = tally.available+tally.reserved+tally.margin+tally.channel+tally.vesting
|
|
1064
|
+
const delta = { address, block, property: propertyId, total: total, avail: availableChange, res: reservedChange, mar: marginChange, vest: vestingChange, channel: channelChange, type, tx: txid };
|
|
1065
|
+
|
|
1066
|
+
console.log('saving delta ' + JSON.stringify(delta));
|
|
1067
|
+
|
|
1068
|
+
try {
|
|
1069
|
+
// Try to find an existing document based on the key
|
|
1070
|
+
const existingDocument = await db.findOneAsync({ _id: deltaKey });
|
|
1071
|
+
|
|
1072
|
+
if (existingDocument) {
|
|
1073
|
+
// If the document exists, update it
|
|
1074
|
+
await db.updateAsync({ _id: deltaKey }, { $set: { data: delta } });
|
|
1075
|
+
} else {
|
|
1076
|
+
// If the document doesn't exist, insert a new one
|
|
1077
|
+
await db.insertAsync({ _id: deltaKey, data: delta });
|
|
1078
|
+
}
|
|
1079
|
+
TallyMap.setModFlag(true)
|
|
1080
|
+
|
|
1081
|
+
return; // Return success or handle as needed
|
|
1082
|
+
} catch (error) {
|
|
1083
|
+
console.error('Error saving delta:', error);
|
|
1084
|
+
throw error; // Rethrow the error or handle as needed
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
static async didReceiveClearingProfitThisBlock(address, blockHeight) {
|
|
1089
|
+
const db = await dbInstance.getDatabase('tallyMapDelta');
|
|
1090
|
+
|
|
1091
|
+
return new Promise((resolve, reject) => {
|
|
1092
|
+
db.find(
|
|
1093
|
+
{
|
|
1094
|
+
"data.address": address,
|
|
1095
|
+
"data.block": blockHeight
|
|
1096
|
+
},
|
|
1097
|
+
(err, docs) => {
|
|
1098
|
+
if (err) return reject(err);
|
|
1099
|
+
|
|
1100
|
+
const hasClearing = docs.some(doc =>
|
|
1101
|
+
(doc.data.type === 'clearing')
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
resolve(hasClearing);
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Function to apply a delta to the TallyMap
|
|
1111
|
+
applyDeltaToTallyMap(delta) {
|
|
1112
|
+
const { address, propertyId, amountChange } = delta;
|
|
1113
|
+
// Logic to apply the change to TallyMap
|
|
1114
|
+
TallyMap.updateBalance(address, propertyId, amountChange);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
async saveDeltaTodbInstance(blockHeight, delta) {
|
|
1118
|
+
const serializedDelta = JSON.stringify(delta);
|
|
1119
|
+
await dbInstance.getDatabase('tallyMap').insert(`tallyMapDelta-${blockHeight}`, serializedDelta);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Function to save the aggregated block delta
|
|
1123
|
+
async saveBlockDelta(blockHeight, blockDelta) {
|
|
1124
|
+
const deltaKey = `blockDelta-${blockHeight}`;
|
|
1125
|
+
await dbInstance.getDatabase('tallyMap').insert(deltaKey, JSON.stringify(blockDelta));
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// Function to load all deltas for a block
|
|
1129
|
+
async loadDeltasForBlock(blockHeight) {
|
|
1130
|
+
// Load and parse all deltas from the database for the given block height
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
totalTokens(propertyId) {
|
|
1134
|
+
let total = 0;
|
|
1135
|
+
for (const addressObj of this.addresses.values()) {
|
|
1136
|
+
if (addressObj[propertyId]) {
|
|
1137
|
+
total += addressObj[propertyId].available + addressObj[propertyId].reserved;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return total;
|
|
1141
|
+
}
|
|
1142
|
+
// Get the tally for a specific address and property
|
|
1143
|
+
static async getTally(address, propertyId) {
|
|
1144
|
+
await TallyMap.loadFromDB(); // Ensure instance is loaded
|
|
1145
|
+
if (!TallyMap.addresses.has(address)) {
|
|
1146
|
+
console.log("can't find address in tallyMap")
|
|
1147
|
+
return 0;
|
|
1148
|
+
}
|
|
1149
|
+
const addressObj = TallyMap.addresses.get(address);
|
|
1150
|
+
console.log('inside getTally '+propertyId+' '+JSON.stringify(addressObj))
|
|
1151
|
+
if (!addressObj[propertyId]) {
|
|
1152
|
+
console.log("can't find property in address "+address+propertyId+ ' '+JSON.stringify(addressObj) )
|
|
1153
|
+
return 0;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
const returnObj = {amount: addressObj[propertyId].amount,
|
|
1157
|
+
available: addressObj[propertyId].available,
|
|
1158
|
+
reserved: addressObj[propertyId].reserved,
|
|
1159
|
+
margin: addressObj[propertyId].margin,
|
|
1160
|
+
vesting:addressObj[propertyId].vesting,
|
|
1161
|
+
channel: addressObj[propertyId].channelBalance}
|
|
1162
|
+
|
|
1163
|
+
console.log('return obj '+address+' '+JSON.stringify(returnObj))
|
|
1164
|
+
|
|
1165
|
+
return returnObj
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
getAddressBalances(address) {
|
|
1169
|
+
//console.log('ze tally map'+this.addresses)
|
|
1170
|
+
const balances = [];
|
|
1171
|
+
if (this.addresses.has(address)) {
|
|
1172
|
+
const properties = this.addresses.get(address);
|
|
1173
|
+
for (const [propertyId, balanceData] of Object.entries(properties)) {
|
|
1174
|
+
balances.push({
|
|
1175
|
+
propertyId: propertyId,
|
|
1176
|
+
balance: balanceData
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
return balances;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Retrieves all addresses that have a balance for a given property.
|
|
1185
|
+
* @param {number} propertyId - The property ID to check balances for.
|
|
1186
|
+
* @return {Array} - An array of addresses that have a balance for the specified property.
|
|
1187
|
+
*/
|
|
1188
|
+
static async getAddressesWithBalanceForProperty(propertyId) {
|
|
1189
|
+
const addressesWithBalances = [];
|
|
1190
|
+
|
|
1191
|
+
try {
|
|
1192
|
+
// Get the tallyMap document
|
|
1193
|
+
const tallyMapDoc = await dbInstance.getDatabase('tallyMap').findOneAsync({ _id: 'tallyMap' });
|
|
1194
|
+
|
|
1195
|
+
// Ensure we got the document and the data field exists
|
|
1196
|
+
if (!tallyMapDoc || !tallyMapDoc.data) {
|
|
1197
|
+
console.error('No tallyMap document found or data is missing');
|
|
1198
|
+
return addressesWithBalances;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// Parse the stringified data into a usable array
|
|
1202
|
+
const parsedData = JSON.parse(tallyMapDoc.data);
|
|
1203
|
+
|
|
1204
|
+
// Iterate over the parsed data and find addresses with the specified propertyId
|
|
1205
|
+
for (const [address, balances] of parsedData) {
|
|
1206
|
+
if (balances[propertyId]) {
|
|
1207
|
+
const balanceInfo = balances[propertyId];
|
|
1208
|
+
if (balanceInfo.available > 0 || balanceInfo.vesting > 0) {
|
|
1209
|
+
addressesWithBalances.push({
|
|
1210
|
+
address: address,
|
|
1211
|
+
available: balanceInfo.available,
|
|
1212
|
+
reserved: balanceInfo.reserved,
|
|
1213
|
+
margin: balanceInfo.margin,
|
|
1214
|
+
vesting: balanceInfo.vesting,
|
|
1215
|
+
channelBalance: balanceInfo.channelBalance
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
console.log('Found addresses for property', propertyId, addressesWithBalances);
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
console.error('Error querying addresses with balance for propertyId:', propertyId, error);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
return addressesWithBalances;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
static async applyVesting(propertyId, vestingAmount, block) {
|
|
1230
|
+
console.log('insideApply vesting '+vestingAmount)
|
|
1231
|
+
if(vestingAmount<1e-8){return}
|
|
1232
|
+
// Get the list of addresses with balances for the given propertyId
|
|
1233
|
+
const addressesWithBalances = await this.getAddressesWithBalanceForProperty(propertyId);
|
|
1234
|
+
const propertyInfo = await PropertyList.getPropertyData(propertyId)
|
|
1235
|
+
// Retrieve the total number of tokens for the propertyId from the propertyList
|
|
1236
|
+
const totalTokens = propertyInfo.totalInCirculation;
|
|
1237
|
+
vestingAmount = new BigNumber(vestingAmount)
|
|
1238
|
+
// Iterate over each address to apply the vesting amount
|
|
1239
|
+
for (const { address, available, reserved, margin, vesting, channelBalance } of addressesWithBalances) {
|
|
1240
|
+
console.log(JSON.stringify(addressesWithBalances))
|
|
1241
|
+
console.log('inside apply vesting '+address+' '+available+' '+vesting+' '+totalTokens)
|
|
1242
|
+
// Calculate the total balance for this address (amount + reserved)
|
|
1243
|
+
const totalBalanceForAddress = new BigNumber(available);
|
|
1244
|
+
|
|
1245
|
+
// Calculate the percentage this balance represents of the total tokens
|
|
1246
|
+
const percentageOfTotalTokens = totalBalanceForAddress.dividedBy(totalTokens);
|
|
1247
|
+
console.log('percentage '+percentageOfTotalTokens, vestingAmount)
|
|
1248
|
+
// Apply the vesting amount proportionally to this address
|
|
1249
|
+
const vestingShare = vestingAmount.multipliedBy(percentageOfTotalTokens);
|
|
1250
|
+
console.log(vestingAmount)
|
|
1251
|
+
console.log(vestingShare.toNumber()+' '+totalBalanceForAddress+' '+percentageOfTotalTokens)
|
|
1252
|
+
// Depending on propertyId, apply the vesting rules:
|
|
1253
|
+
if (propertyId === 2) {
|
|
1254
|
+
// Move tokens from vesting in propertyId 2 to available in propertyId 1
|
|
1255
|
+
await this.updateBalance(
|
|
1256
|
+
address, 2, 0, 0, 0, vestingShare.negated().toNumber(), 'vestingDebit', block // Debit vesting from propertyId 2
|
|
1257
|
+
);
|
|
1258
|
+
await this.updateBalance(
|
|
1259
|
+
address, 1, vestingShare.toNumber(), 0, 0, 0, 'vestingCredit', block // Credit available in propertyId 1
|
|
1260
|
+
);
|
|
1261
|
+
} else if (propertyId === 3) {
|
|
1262
|
+
// Move tokens from vesting in propertyId 3 to available in propertyId 4
|
|
1263
|
+
await this.updateBalance(
|
|
1264
|
+
address, 3, 0, 0, 0, vestingShare.negated().toNumber(), 'vestingDebit', block // Debit vesting from propertyId 3
|
|
1265
|
+
);
|
|
1266
|
+
await this.updateBalance(
|
|
1267
|
+
address, 4, vestingShare.toNumber(), 0, 0, 0, 'vestingCredit', block // Credit available in propertyId 4
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
return
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
module.exports = TallyMap;
|