hive-stream 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/DOCUMENTATION.md +693 -1
  2. package/README.md +251 -1
  3. package/dist/adapters/mongodb.adapter.js +21 -30
  4. package/dist/adapters/mongodb.adapter.js.map +1 -1
  5. package/dist/adapters/postgresql.adapter.js +16 -16
  6. package/dist/adapters/postgresql.adapter.js.map +1 -1
  7. package/dist/adapters/sqlite.adapter.js +17 -17
  8. package/dist/adapters/sqlite.adapter.js.map +1 -1
  9. package/dist/api.js +2 -0
  10. package/dist/api.js.map +1 -1
  11. package/dist/builders.d.ts +444 -0
  12. package/dist/builders.js +1609 -0
  13. package/dist/builders.js.map +1 -0
  14. package/dist/config.d.ts +10 -1
  15. package/dist/config.js +90 -4
  16. package/dist/config.js.map +1 -1
  17. package/dist/contracts/coinflip.contract.js +18 -21
  18. package/dist/contracts/coinflip.contract.js.map +1 -1
  19. package/dist/contracts/dice.contract.js +18 -21
  20. package/dist/contracts/dice.contract.js.map +1 -1
  21. package/dist/contracts/helpers.d.ts +2 -0
  22. package/dist/contracts/helpers.js +18 -11
  23. package/dist/contracts/helpers.js.map +1 -1
  24. package/dist/contracts/nft.contract.js +5 -10
  25. package/dist/contracts/nft.contract.js.map +1 -1
  26. package/dist/contracts/rps.contract.js +35 -23
  27. package/dist/contracts/rps.contract.js.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/metadata.d.ts +13 -0
  32. package/dist/metadata.js +256 -0
  33. package/dist/metadata.js.map +1 -1
  34. package/dist/streamer.d.ts +148 -12
  35. package/dist/streamer.js +1102 -53
  36. package/dist/streamer.js.map +1 -1
  37. package/dist/types/hive-stream.d.ts +623 -0
  38. package/dist/utils.d.ts +475 -0
  39. package/dist/utils.js +1618 -8
  40. package/dist/utils.js.map +1 -1
  41. package/package.json +4 -5
package/dist/streamer.js CHANGED
@@ -6,10 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Streamer = void 0;
7
7
  const api_1 = require("./api");
8
8
  const sqlite_adapter_1 = require("./adapters/sqlite.adapter");
9
- const utils_1 = require("@hiveio/dhive/lib/utils");
10
9
  const actions_1 = require("./actions");
11
10
  const dhive_1 = require("@hiveio/dhive");
12
- const utils_2 = require("./utils");
11
+ const bignumber_js_1 = __importDefault(require("bignumber.js"));
12
+ const utils_1 = require("./utils");
13
+ const builders_1 = require("./builders");
13
14
  const config_1 = require("./config");
14
15
  const hive_provider_1 = require("./providers/hive-provider");
15
16
  const sscjs_1 = __importDefault(require("sscjs"));
@@ -21,6 +22,21 @@ class Streamer {
21
22
  postSubscriptions = [];
22
23
  transferSubscriptions = [];
23
24
  escrowSubscriptions = [];
25
+ voteSubscriptions = [];
26
+ delegateSubscriptions = [];
27
+ powerUpSubscriptions = [];
28
+ powerDownSubscriptions = [];
29
+ claimRewardsSubscriptions = [];
30
+ witnessVoteSubscriptions = [];
31
+ followSubscriptions = [];
32
+ reblogSubscriptions = [];
33
+ accountUpdateSubscriptions = [];
34
+ deleteCommentSubscriptions = [];
35
+ limitOrderSubscriptions = [];
36
+ savingsSubscriptions = [];
37
+ convertSubscriptions = [];
38
+ blockSubscriptions = [];
39
+ anyOperationSubscriptions = [];
24
40
  attempts = 0;
25
41
  config = (0, config_1.createConfig)();
26
42
  client;
@@ -72,7 +88,149 @@ class Streamer {
72
88
  accountCache = new Map();
73
89
  cacheTimeout = 300000; // 5 minutes
74
90
  maxCacheSize = 1000;
75
- utils = utils_2.Utils;
91
+ lastCacheCleanup = Date.now();
92
+ utils = utils_1.Utils;
93
+ money = {
94
+ parseAssetAmount: (rawAmount) => utils_1.Utils.parseAssetAmount(rawAmount),
95
+ formatAmount: (amount, precision) => utils_1.Utils.formatAmount(amount, precision),
96
+ formatAssetAmount: (amount, symbol, precision) => utils_1.Utils.formatAssetAmount(amount, symbol, precision),
97
+ calculatePercentageAmount: (amount, percentage, precision) => utils_1.Utils.calculatePercentageAmount(amount, percentage, precision),
98
+ calculateBasisPointsAmount: (amount, basisPoints, precision) => utils_1.Utils.calculateBasisPointsAmount(amount, basisPoints, precision),
99
+ splitAmountByBasisPoints: (amount, basisPoints, precision) => utils_1.Utils.splitAmountByBasisPoints(amount, basisPoints, precision),
100
+ splitAmountByPercentage: (amount, percentages, precision) => utils_1.Utils.splitAmountByPercentage(amount, percentages, precision),
101
+ splitAmountByWeights: (amount, weights, precision) => utils_1.Utils.splitAmountByWeights(amount, weights, precision),
102
+ };
103
+ flows = {
104
+ incomingTransfers: (account) => new builders_1.IncomingTransfersBuilder(this, account),
105
+ autoBurnIncomingTransfers: (options = {}) => this.autoBurnIncomingTransfers(options),
106
+ autoForwardIncomingTransfers: (options) => this.autoForwardIncomingTransfers(options),
107
+ autoRefundIncomingTransfers: (options = {}) => this.autoRefundIncomingTransfers(options),
108
+ autoSplitIncomingTransfers: (options) => this.autoSplitIncomingTransfers(options),
109
+ autoRouteIncomingTransfers: (options) => this.autoRouteIncomingTransfers(options),
110
+ planIncomingTransferRoutes: (transfer, options) => this.planIncomingTransferRoutes(transfer, options),
111
+ };
112
+ ops = {
113
+ transfer: () => new builders_1.HiveTransferBuilder(this),
114
+ burn: () => new builders_1.HiveBurnBuilder(this),
115
+ escrowTransfer: () => new builders_1.HiveEscrowTransferBuilder(this),
116
+ recurrentTransfer: () => new builders_1.HiveRecurrentTransferBuilder(this),
117
+ createProposal: () => new builders_1.HiveProposalBuilder(this),
118
+ transferEngine: () => new builders_1.HiveEngineTokenTransferBuilder(this),
119
+ burnEngine: () => new builders_1.HiveEngineTokenBurnBuilder(this),
120
+ issueEngine: () => new builders_1.HiveEngineTokenIssueBuilder(this),
121
+ voteProposals: () => new builders_1.HiveProposalVotesBuilder(this),
122
+ removeProposals: () => new builders_1.HiveRemoveProposalsBuilder(this),
123
+ upvote: () => new builders_1.HiveVoteBuilder(this, 'upvote'),
124
+ downvote: () => new builders_1.HiveVoteBuilder(this, 'downvote'),
125
+ follow: () => new builders_1.HiveFollowBuilder(this, 'follow'),
126
+ unfollow: () => new builders_1.HiveFollowBuilder(this, 'unfollow'),
127
+ mute: () => new builders_1.HiveFollowBuilder(this, 'mute'),
128
+ reblog: () => new builders_1.HiveReblogBuilder(this),
129
+ powerUp: () => new builders_1.HivePowerUpBuilder(this),
130
+ powerDown: () => new builders_1.HivePowerDownBuilder(this),
131
+ cancelPowerDown: () => new builders_1.HivePowerDownBuilder(this, true),
132
+ delegate: () => new builders_1.HiveDelegateBuilder(this),
133
+ undelegate: () => new builders_1.HiveDelegateBuilder(this, true),
134
+ claimRewards: () => new builders_1.HiveClaimRewardsBuilder(this),
135
+ witnessVote: () => new builders_1.HiveWitnessVoteBuilder(this),
136
+ setProxy: () => new builders_1.HiveSetProxyBuilder(this),
137
+ clearProxy: () => new builders_1.HiveSetProxyBuilder(this, true),
138
+ updateProfile: () => new builders_1.HiveUpdateProfileBuilder(this),
139
+ transferToSavings: () => new builders_1.HiveSavingsTransferBuilder(this, 'to'),
140
+ transferFromSavings: () => new builders_1.HiveSavingsTransferBuilder(this, 'from'),
141
+ convert: () => new builders_1.HiveConvertBuilder(this),
142
+ collateralizedConvert: () => new builders_1.HiveCollateralizedConvertBuilder(this),
143
+ deleteComment: () => new builders_1.HiveDeleteCommentBuilder(this),
144
+ limitOrder: () => new builders_1.HiveLimitOrderBuilder(this),
145
+ cancelOrder: () => new builders_1.HiveCancelOrderBuilder(this),
146
+ withdrawRoute: () => new builders_1.HiveWithdrawRouteBuilder(this),
147
+ commentOptions: () => new builders_1.HiveCommentOptionsBuilder(this),
148
+ post: () => new builders_1.HivePostBuilder(this),
149
+ stakeEngine: () => new builders_1.HiveEngineStakeBuilder(this),
150
+ unstakeEngine: () => new builders_1.HiveEngineUnstakeBuilder(this),
151
+ buyEngine: () => new builders_1.HiveEngineMarketOrderBuilder(this, 'buy'),
152
+ sellEngine: () => new builders_1.HiveEngineMarketOrderBuilder(this, 'sell'),
153
+ cancelEngineOrder: () => new builders_1.HiveEngineCancelOrderBuilder(this),
154
+ delegateEngine: () => new builders_1.HiveEngineDelegateBuilder(this),
155
+ undelegateEngine: () => new builders_1.HiveEngineDelegateBuilder(this, true),
156
+ subscribeCommunity: () => new builders_1.HiveCommunityOperationBuilder(this, 'subscribe'),
157
+ unsubscribeCommunity: () => new builders_1.HiveCommunityOperationBuilder(this, 'unsubscribe'),
158
+ cancelRecurrentTransfer: () => new builders_1.HiveRecurrentTransferBuilder(this),
159
+ };
160
+ query = {
161
+ getDynamicGlobalProperties: () => this.client.database.getDynamicGlobalProperties(),
162
+ getChainProperties: () => this.client.database.getChainProperties(),
163
+ getCurrentMedianHistoryPrice: () => this.client.database.getCurrentMedianHistoryPrice(),
164
+ getRewardFund: (name = 'post') => this.client.database.call('get_reward_fund', [name]),
165
+ getFollowers: (account, start = '', type = 'blog', limit = 1000) => this.client.call('condenser_api', 'get_followers', [account, start, type, limit]),
166
+ getFollowing: (account, start = '', type = 'blog', limit = 1000) => this.client.call('condenser_api', 'get_following', [account, start, type, limit]),
167
+ getFollowCount: (account) => this.client.call('condenser_api', 'get_follow_count', [account]),
168
+ getContent: (author, permlink) => this.client.call('condenser_api', 'get_content', [author, permlink]),
169
+ getContentReplies: (author, permlink) => this.client.call('condenser_api', 'get_content_replies', [author, permlink]),
170
+ getDiscussions: (by, query) => this.client.database.getDiscussions(by, query),
171
+ getBlog: (account, options = {}) => this.client.database.getDiscussions('blog', { tag: account, limit: 20, ...options }),
172
+ getFeed: (account, options = {}) => this.client.database.getDiscussions('feed', { tag: account, limit: 20, ...options }),
173
+ getTrending: (options = {}) => this.client.database.getDiscussions('trending', { limit: 20, ...options }),
174
+ getHot: (options = {}) => this.client.database.getDiscussions('hot', { limit: 20, ...options }),
175
+ getCreated: (options = {}) => this.client.database.getDiscussions('created', { limit: 20, ...options }),
176
+ getActiveVotes: (author, permlink) => this.client.call('condenser_api', 'get_active_votes', [author, permlink]),
177
+ getVestingDelegations: (account, from = '', limit = 100) => this.client.database.getVestingDelegations(account, from, limit),
178
+ getAccountHistory: (account, from = -1, limit = 100) => this.client.database.getAccountHistory(account, from, limit),
179
+ getOrderBook: (limit = 50) => this.client.call('condenser_api', 'get_order_book', [limit]),
180
+ getOpenOrders: (account) => this.client.call('condenser_api', 'get_open_orders', [account]),
181
+ getRCMana: (account) => this.client.rc.getRCMana(account),
182
+ getVPMana: (account) => this.client.rc.getVPMana(account),
183
+ findRCAccounts: (accounts) => this.client.rc.findRCAccounts(accounts),
184
+ getCommunity: (name) => this.client.call('bridge', 'get_community', { name }),
185
+ listCommunities: (options = {}) => this.client.call('bridge', 'list_communities', { limit: 100, ...options }),
186
+ getAccountNotifications: (account, options = {}) => this.client.call('bridge', 'account_notifications', { account, limit: 50, ...options }),
187
+ listAllSubscriptions: (account) => this.client.call('bridge', 'list_all_subscriptions', { account }),
188
+ findTransaction: (transactionId) => this.client.transaction.findTransaction(transactionId),
189
+ getWitnessByAccount: (account) => this.client.call('condenser_api', 'get_witness_by_account', [account]),
190
+ getWitnesses: (ids) => this.client.call('condenser_api', 'get_witnesses', [ids]),
191
+ getWitnessesByVote: (from, limit) => this.client.call('condenser_api', 'get_witnesses_by_vote', [from, limit]),
192
+ getBlock: (blockNumber) => this.client.database.getBlock(blockNumber),
193
+ getBlockHeader: (blockNumber) => this.client.database.getBlockHeader(blockNumber),
194
+ getOperations: (blockNumber, onlyVirtual = false) => this.client.database.getOperations(blockNumber, onlyVirtual),
195
+ getConfig: () => this.client.database.getConfig(),
196
+ lookupAccounts: (lowerBound, limit) => this.client.call('condenser_api', 'lookup_accounts', [lowerBound, limit]),
197
+ lookupWitnessAccounts: (lowerBound, limit) => this.client.call('condenser_api', 'lookup_witness_accounts', [lowerBound, limit]),
198
+ getConversionRequests: (account) => this.client.call('condenser_api', 'get_conversion_requests', [account]),
199
+ getCollateralizedConversionRequests: (account) => this.client.call('condenser_api', 'get_collateralized_conversion_requests', [account]),
200
+ getSavingsWithdrawFrom: (account) => this.client.call('condenser_api', 'get_savings_withdraw_from', [account]),
201
+ getProposals: (options = {}) => this.client.call('condenser_api', 'list_proposals', [
202
+ options.start || '',
203
+ options.limit || 100,
204
+ options.order || 'by_total_votes',
205
+ options.order_direction || 'descending',
206
+ options.status || 'votable'
207
+ ]),
208
+ };
209
+ engine = {
210
+ getTokenBalances: (account) => this.hive.find('tokens', 'balances', { account }),
211
+ getTokenBalance: (account, symbol) => this.hive.findOne('tokens', 'balances', { account, symbol }),
212
+ getToken: (symbol) => this.hive.findOne('tokens', 'tokens', { symbol }),
213
+ getTokens: (query = {}, limit = 1000, offset = 0) => this.hive.find('tokens', 'tokens', query, limit, offset),
214
+ getMarketBuyBook: (symbol, limit = 100, offset = 0) => this.hive.find('market', 'buyBook', { symbol }, limit, offset, [{ index: 'priceDec', descending: true }]),
215
+ getMarketSellBook: (symbol, limit = 100, offset = 0) => this.hive.find('market', 'sellBook', { symbol }, limit, offset, [{ index: 'priceDec', descending: false }]),
216
+ getMarketHistory: (symbol, limit = 100, offset = 0) => this.hive.find('market', 'tradesHistory', { symbol }, limit, offset, [{ index: '_id', descending: true }]),
217
+ getMarketMetrics: (query = {}) => this.hive.find('market', 'metrics', query),
218
+ getNFT: (symbol) => this.hive.findOne('nft', 'nfts', { symbol }),
219
+ getNFTInstances: (symbol, query = {}, limit = 100, offset = 0) => this.hive.find('nft', `${symbol}instances`, query, limit, offset),
220
+ getNFTBalance: (account, symbol) => this.hive.find('nft', `${symbol}instances`, { account }),
221
+ getNFTSellBook: (symbol, limit = 100, offset = 0) => this.hive.find('nftmarket', `${symbol}sellBook`, {}, limit, offset),
222
+ getPendingUnstakes: (account, symbol) => {
223
+ const query = { account };
224
+ if (symbol) {
225
+ query.symbol = symbol;
226
+ }
227
+ return this.hive.find('tokens', 'pendingUnstakes', query);
228
+ },
229
+ getDelegations: (from, symbol) => this.hive.find('tokens', 'delegations', { from, symbol }),
230
+ getContractInfo: (name) => this.hive.getContractInfo(name),
231
+ find: (contract, table, query, limit = 1000, offset = 0) => this.hive.find(contract, table, query, limit, offset),
232
+ findOne: (contract, table, query) => this.hive.findOne(contract, table, query),
233
+ };
76
234
  constructor(userConfig = {}) {
77
235
  this.config = (0, config_1.createConfig)(userConfig);
78
236
  this.lastBlockNumber = this.config.LAST_BLOCK_NUMBER;
@@ -116,6 +274,299 @@ class Streamer {
116
274
  config: this.config
117
275
  };
118
276
  }
277
+ createTransferEvent(op, blockNumber, blockId, prevBlockId, trxId, blockTime) {
278
+ const rawAmount = String(op?.amount || '');
279
+ let amount = '';
280
+ let asset = '';
281
+ try {
282
+ const parsed = utils_1.Utils.parseAssetAmount(rawAmount);
283
+ amount = parsed.amount;
284
+ asset = parsed.asset;
285
+ }
286
+ catch (error) {
287
+ amount = '';
288
+ asset = '';
289
+ }
290
+ return {
291
+ op,
292
+ transfer: {
293
+ from: op?.from || '',
294
+ to: op?.to || '',
295
+ rawAmount,
296
+ amount,
297
+ asset,
298
+ memo: op?.memo
299
+ },
300
+ block: {
301
+ number: blockNumber,
302
+ id: blockId,
303
+ previousId: prevBlockId,
304
+ time: blockTime
305
+ },
306
+ transaction: {
307
+ id: trxId
308
+ }
309
+ };
310
+ }
311
+ normalizeTransferPreviewInput(transfer) {
312
+ if (typeof transfer === 'object' && transfer !== null && 'transfer' in transfer && 'transaction' in transfer && 'block' in transfer) {
313
+ return transfer;
314
+ }
315
+ const op = typeof transfer === 'string'
316
+ ? { amount: transfer }
317
+ : transfer || {};
318
+ return this.createTransferEvent(op, 0, '', '', '', new Date(0));
319
+ }
320
+ resolveAccount(account) {
321
+ const resolved = account || this.username || this.config.USERNAME;
322
+ if (!resolved) {
323
+ throw new Error('Account is required');
324
+ }
325
+ return resolved;
326
+ }
327
+ async dedupeHas(store, key) {
328
+ return Boolean(await store.has(key));
329
+ }
330
+ async dedupeAdd(store, key) {
331
+ await store.add(key);
332
+ }
333
+ normalizeDedupeStore(dedupeStore) {
334
+ const store = dedupeStore || new Set();
335
+ if (store instanceof Set) {
336
+ return {
337
+ has: (key) => store.has(key),
338
+ add: (key) => {
339
+ store.add(key);
340
+ if (store.size > 10000) {
341
+ const oldest = store.values().next().value;
342
+ store.delete(oldest);
343
+ }
344
+ }
345
+ };
346
+ }
347
+ return store;
348
+ }
349
+ isZeroAmountFlowError(error) {
350
+ return error instanceof Error
351
+ && (error.message === 'Burn amount must be greater than zero' || error.message === 'Route amount must be greater than zero');
352
+ }
353
+ resolveFlowBasisPoints(options, context, allowUnspecified = true) {
354
+ const percentage = options.percentage ?? options.percent;
355
+ const basisPoints = options.basisPoints;
356
+ if (percentage !== undefined && basisPoints !== undefined) {
357
+ throw new Error(`${context} accepts either percentage or basisPoints, not both`);
358
+ }
359
+ if (percentage === undefined && basisPoints === undefined) {
360
+ if (allowUnspecified) {
361
+ return null;
362
+ }
363
+ throw new Error(`${context} requires percentage or basisPoints`);
364
+ }
365
+ if (basisPoints !== undefined) {
366
+ if (!Number.isInteger(basisPoints) || basisPoints < 0 || basisPoints > 10000) {
367
+ throw new Error(`${context} basisPoints must be an integer between 0 and 10000`);
368
+ }
369
+ return basisPoints;
370
+ }
371
+ const percentageValue = new bignumber_js_1.default(percentage);
372
+ if (percentageValue.isNaN() || !percentageValue.isFinite()) {
373
+ throw new Error(`${context} percentage must be a valid number`);
374
+ }
375
+ if (percentageValue.lt(0) || percentageValue.gt(100)) {
376
+ throw new Error(`${context} percentage must be between 0 and 100`);
377
+ }
378
+ const bps = percentageValue.multipliedBy(100);
379
+ if (!bps.isInteger()) {
380
+ throw new Error(`${context} percentage supports up to 2 decimal places; use basisPoints for finer control`);
381
+ }
382
+ return bps.toNumber();
383
+ }
384
+ resolveFlowMode(route) {
385
+ return route.mode === 'onTop' ? 'onTop' : 'base';
386
+ }
387
+ normalizeBaseRouteAllocations(routes) {
388
+ const allocations = routes.map((route, index) => this.resolveFlowBasisPoints(route, `Route ${index + 1}`, true));
389
+ const unspecified = allocations.filter((allocation) => allocation === null).length;
390
+ const explicitTotal = allocations.reduce((sum, allocation) => sum + (allocation || 0), 0);
391
+ if (unspecified > 1) {
392
+ throw new Error('Only one flow route can omit percentage or basisPoints');
393
+ }
394
+ if (explicitTotal > 10000) {
395
+ throw new Error('Flow route allocations cannot exceed 10000 basis points');
396
+ }
397
+ if (unspecified === 0 && explicitTotal !== 10000) {
398
+ throw new Error('Flow route allocations must total 100%');
399
+ }
400
+ if (unspecified === 1) {
401
+ const remainder = 10000 - explicitTotal;
402
+ return allocations.map((allocation) => allocation === null ? remainder : allocation);
403
+ }
404
+ return allocations;
405
+ }
406
+ normalizeOnTopRouteAllocations(routes) {
407
+ const allocations = routes.map((route, index) => this.resolveFlowBasisPoints(route, `On-top route ${index + 1}`, false));
408
+ const explicitTotal = allocations.reduce((sum, allocation) => sum + allocation, 0);
409
+ if (explicitTotal <= 0) {
410
+ throw new Error('On-top flow route allocations must be greater than zero');
411
+ }
412
+ return allocations;
413
+ }
414
+ resolvePlannedRouteMemo(event, route, index, defaultMemo) {
415
+ if (typeof route.memo === 'function') {
416
+ return route.memo(event);
417
+ }
418
+ if (typeof route.memo === 'string') {
419
+ return route.memo;
420
+ }
421
+ if (typeof defaultMemo === 'function') {
422
+ return defaultMemo(event, route, index);
423
+ }
424
+ return defaultMemo || '';
425
+ }
426
+ resolveGroupSplitStrategy(route) {
427
+ if (route.split) {
428
+ return route.split;
429
+ }
430
+ return route.group.some((recipient) => recipient.weight !== undefined) ? 'weighted' : 'equal';
431
+ }
432
+ resolveGroupWeights(route, routeIndex) {
433
+ if (!Array.isArray(route.group) || route.group.length === 0) {
434
+ throw new Error(`Route ${routeIndex + 1} group must include at least one account`);
435
+ }
436
+ if (this.resolveGroupSplitStrategy(route) === 'equal') {
437
+ return route.group.map(() => 1);
438
+ }
439
+ return route.group.map((recipient, groupIndex) => {
440
+ if (recipient.weight === undefined) {
441
+ return 1;
442
+ }
443
+ const weight = new bignumber_js_1.default(recipient.weight);
444
+ if (weight.isNaN() || !weight.isFinite() || weight.lte(0)) {
445
+ throw new Error(`Route ${routeIndex + 1} recipient ${groupIndex + 1} weight must be greater than zero`);
446
+ }
447
+ return weight.toString();
448
+ });
449
+ }
450
+ expandPlannedRoute(event, route, routeIndex, amount, asset, memo) {
451
+ const mode = this.resolveFlowMode(route);
452
+ if (route.type === 'burn') {
453
+ return [{
454
+ type: 'burn',
455
+ mode,
456
+ amount,
457
+ asset,
458
+ memo,
459
+ routeIndex
460
+ }];
461
+ }
462
+ if ('group' in route) {
463
+ const weights = this.resolveGroupWeights(route, routeIndex);
464
+ const amounts = utils_1.Utils.splitAmountByWeights(amount, weights);
465
+ return route.group.map((recipient, groupIndex) => {
466
+ const destination = typeof recipient.account === 'function' ? recipient.account(event) : recipient.account;
467
+ if (typeof destination !== 'string' || destination.trim().length === 0) {
468
+ throw new Error(`Route ${routeIndex + 1} recipient ${groupIndex + 1} destination account is required`);
469
+ }
470
+ return {
471
+ type: 'transfer',
472
+ mode,
473
+ amount: amounts[groupIndex],
474
+ asset,
475
+ memo,
476
+ to: destination.trim(),
477
+ routeIndex,
478
+ groupIndex
479
+ };
480
+ });
481
+ }
482
+ const destination = typeof route.to === 'function' ? route.to(event) : route.to;
483
+ if (typeof destination !== 'string' || destination.trim().length === 0) {
484
+ throw new Error(`Route ${routeIndex + 1} destination account is required`);
485
+ }
486
+ return [{
487
+ type: 'transfer',
488
+ mode,
489
+ amount,
490
+ asset,
491
+ memo,
492
+ to: destination.trim(),
493
+ routeIndex
494
+ }];
495
+ }
496
+ sumPlannedRouteAmounts(plan) {
497
+ return plan.reduce((sum, route) => sum.plus(route.amount), new bignumber_js_1.default(0)).toFixed(3);
498
+ }
499
+ buildPlannedIncomingTransferRoutes(event, routes, allowedSymbols, defaultMemo) {
500
+ if (!Array.isArray(routes) || routes.length === 0) {
501
+ throw new Error('At least one flow route is required');
502
+ }
503
+ const parsed = utils_1.Utils.parseAssetAmount(event.transfer.rawAmount);
504
+ if (Array.isArray(allowedSymbols) && allowedSymbols.length > 0 && !allowedSymbols.includes(parsed.asset)) {
505
+ throw new Error(`Asset '${parsed.asset}' is not allowed for this flow`);
506
+ }
507
+ const baseRoutes = routes.filter((route) => this.resolveFlowMode(route) === 'base');
508
+ const onTopRoutes = routes.filter((route) => this.resolveFlowMode(route) === 'onTop');
509
+ if (baseRoutes.length === 0) {
510
+ throw new Error('At least one base flow route is required');
511
+ }
512
+ const baseAllocations = this.normalizeBaseRouteAllocations(baseRoutes);
513
+ let baseAmount = parsed.amount;
514
+ let onTopRouteAmounts = [];
515
+ if (onTopRoutes.length > 0) {
516
+ const onTopAllocations = this.normalizeOnTopRouteAllocations(onTopRoutes);
517
+ const pools = utils_1.Utils.splitAmountByWeights(parsed.value, [10000, ...onTopAllocations]);
518
+ baseAmount = pools[0];
519
+ onTopRouteAmounts = pools.slice(1);
520
+ }
521
+ const baseRouteAmounts = utils_1.Utils.splitAmountByBasisPoints(baseAmount, baseAllocations);
522
+ const routesPlan = routes.flatMap((route, index) => {
523
+ const memo = this.resolvePlannedRouteMemo(event, route, index, defaultMemo);
524
+ const amount = this.resolveFlowMode(route) === 'base'
525
+ ? baseRouteAmounts.shift()
526
+ : onTopRouteAmounts.shift();
527
+ return this.expandPlannedRoute(event, route, index, amount, parsed.asset, memo);
528
+ });
529
+ const onTopPlannedRoutes = routesPlan.filter((route) => route.mode === 'onTop');
530
+ return {
531
+ incomingAmount: utils_1.Utils.formatAssetAmount(parsed.value, parsed.asset),
532
+ asset: parsed.asset,
533
+ baseAmount,
534
+ onTopAmount: this.sumPlannedRouteAmounts(onTopPlannedRoutes),
535
+ routes: routesPlan
536
+ };
537
+ }
538
+ async executePlannedFlowRoutes(from, plan, ignoreZeroAmount) {
539
+ const results = [];
540
+ for (const route of plan) {
541
+ if (route.amount === '0.000') {
542
+ if (ignoreZeroAmount) {
543
+ continue;
544
+ }
545
+ throw new Error('Route amount must be greater than zero');
546
+ }
547
+ if (route.type === 'burn') {
548
+ results.push(await this.burnHiveTokens(from, route.amount, route.asset, route.memo));
549
+ continue;
550
+ }
551
+ results.push(await this.transferHiveTokens(from, route.to, route.amount, route.asset, route.memo));
552
+ }
553
+ return results;
554
+ }
555
+ calculateSingleFlowAmount(transfer, basisPoints, allowedSymbols, errorContext) {
556
+ const rawAmount = typeof transfer === 'string' ? transfer : transfer?.amount || '';
557
+ const parsed = utils_1.Utils.parseAssetAmount(rawAmount);
558
+ if (Array.isArray(allowedSymbols) && allowedSymbols.length > 0 && !allowedSymbols.includes(parsed.asset)) {
559
+ throw new Error(`Asset '${parsed.asset}' is not allowed for ${errorContext}`);
560
+ }
561
+ const amount = basisPoints === null ? parsed.amount : utils_1.Utils.calculateBasisPointsAmount(parsed.value, basisPoints);
562
+ if (amount === '0.000') {
563
+ throw new Error('Route amount must be greater than zero');
564
+ }
565
+ return {
566
+ amount,
567
+ asset: parsed.asset
568
+ };
569
+ }
119
570
  async initializeContract(contract) {
120
571
  if (this.initializedContracts.has(contract.name)) {
121
572
  return;
@@ -364,11 +815,16 @@ class Streamer {
364
815
  * @param config
365
816
  */
366
817
  setConfig(config) {
367
- const normalized = (0, config_1.normalizeConfigInput)(config);
368
- const shouldRecreateClient = normalized.API_NODES !== undefined;
369
- const shouldRecreateHiveEngine = normalized.HIVE_ENGINE_API !== undefined;
370
- const shouldSyncApiServer = normalized.API_ENABLED !== undefined || normalized.API_PORT !== undefined;
371
- Object.assign(this.config, normalized);
818
+ const normalizedInput = (0, config_1.normalizeConfigInput)(config);
819
+ const nextConfig = (0, config_1.createConfig)({
820
+ ...this.config,
821
+ ...normalizedInput,
822
+ env: config.env,
823
+ });
824
+ const shouldRecreateClient = JSON.stringify(this.config.API_NODES) !== JSON.stringify(nextConfig.API_NODES);
825
+ const shouldRecreateHiveEngine = this.config.HIVE_ENGINE_API !== nextConfig.HIVE_ENGINE_API;
826
+ const shouldSyncApiServer = this.config.API_ENABLED !== nextConfig.API_ENABLED || this.config.API_PORT !== nextConfig.API_PORT;
827
+ Object.assign(this.config, nextConfig);
372
828
  // Set keys and username incase they have changed
373
829
  this.username = this.config.USERNAME;
374
830
  this.postingKey = this.config.POSTING_KEY;
@@ -446,7 +902,7 @@ class Streamer {
446
902
  await this.blockProvider.destroy?.();
447
903
  this.adapterInitialized = false;
448
904
  this.adapterInitializationPromise = null;
449
- await (0, utils_1.sleep)(25);
905
+ await utils_1.Utils.sleep(25);
450
906
  }
451
907
  async startApiServer(port = this.config.API_PORT) {
452
908
  await this.ensureAdapterReady();
@@ -513,7 +969,7 @@ class Streamer {
513
969
  console.log(`Head block number: `, props.head_block_number);
514
970
  console.log(`Last block number: `, this.lastBlockNumber);
515
971
  }
516
- const BLOCKS_BEHIND = parseInt(this.config.BLOCKS_BEHIND_WARNING, 10);
972
+ const BLOCKS_BEHIND = this.config.BLOCKS_BEHIND_WARNING;
517
973
  const maxBatchSize = Math.max(1, this.config.CATCH_UP_BATCH_SIZE || 1);
518
974
  const blocksBehind = Math.max(0, props.head_block_number - this.lastBlockNumber);
519
975
  const blocksToProcess = Math.min(blocksBehind, maxBatchSize);
@@ -567,7 +1023,7 @@ class Streamer {
567
1023
  }
568
1024
  // The block doesn't exist, wait and try again
569
1025
  if (!block) {
570
- await utils_2.Utils.sleep(this.config.BLOCK_CHECK_INTERVAL);
1026
+ await utils_1.Utils.sleep(this.config.BLOCK_CHECK_INTERVAL);
571
1027
  return;
572
1028
  }
573
1029
  // Get the block date and time
@@ -579,11 +1035,15 @@ class Streamer {
579
1035
  }
580
1036
  this.blockId = block.block_id;
581
1037
  this.previousBlockId = block.previous;
582
- this.transactionId = block.transaction_ids[1];
1038
+ this.transactionId = block.transaction_ids[0];
583
1039
  this.blockTime = blockTime;
584
1040
  if (this.adapter?.processBlock) {
585
- this.adapter.processBlock(block);
1041
+ await this.adapter.processBlock(block);
586
1042
  }
1043
+ // Fire onBlock subscribers
1044
+ this.blockSubscriptions.forEach(sub => {
1045
+ sub.callback(block, this.lastBlockNumber);
1046
+ });
587
1047
  // Hive operations are order-sensitive, so process them sequentially.
588
1048
  const transactions = block.transactions;
589
1049
  const transactionIds = block.transaction_ids;
@@ -606,6 +1066,30 @@ class Streamer {
606
1066
  }
607
1067
  this.lastBlockNumber = blockNumber;
608
1068
  this.saveStateThrottled();
1069
+ this.cleanupCaches();
1070
+ }
1071
+ cleanupCaches() {
1072
+ const now = Date.now();
1073
+ if (now - this.lastCacheCleanup < 60000) {
1074
+ return;
1075
+ }
1076
+ this.lastCacheCleanup = now;
1077
+ if (this.blockCache.size > 100) {
1078
+ const entriesToRemove = this.blockCache.size - 100;
1079
+ const iterator = this.blockCache.keys();
1080
+ for (let i = 0; i < entriesToRemove; i++) {
1081
+ const key = iterator.next().value;
1082
+ this.blockCache.delete(key);
1083
+ }
1084
+ }
1085
+ for (const [key, value] of this.accountCache) {
1086
+ if (now - value.timestamp > this.cacheTimeout) {
1087
+ this.accountCache.delete(key);
1088
+ }
1089
+ }
1090
+ if (this.transactionCache.size > this.maxCacheSize) {
1091
+ this.transactionCache.clear();
1092
+ }
609
1093
  }
610
1094
  async processOperation(op, blockNumber, blockId, prevBlockId, trxId, blockTime) {
611
1095
  const operationType = op[0];
@@ -620,6 +1104,10 @@ class Streamer {
620
1104
  if (this.adapter?.processOperation) {
621
1105
  await this.adapter.processOperation(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
622
1106
  }
1107
+ // Fire onAnyOperation subscribers
1108
+ this.anyOperationSubscriptions.forEach(sub => {
1109
+ sub.callback(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
1110
+ });
623
1111
  // Operation is a "comment" which could either be a post or comment
624
1112
  if (operationType === 'comment') {
625
1113
  // This is a post
@@ -639,16 +1127,26 @@ class Streamer {
639
1127
  if (operationType === 'transfer') {
640
1128
  const sender = operationData?.from;
641
1129
  const rawAmount = operationData?.amount;
642
- const amountParts = typeof rawAmount === 'string' ? rawAmount.split(' ') : [];
1130
+ let amount = '';
1131
+ let asset = '';
1132
+ try {
1133
+ const parsedAmount = utils_1.Utils.parseAssetAmount(String(rawAmount || ''));
1134
+ amount = parsedAmount.amount;
1135
+ asset = parsedAmount.asset;
1136
+ }
1137
+ catch (error) {
1138
+ amount = '';
1139
+ asset = '';
1140
+ }
643
1141
  const transferInfo = {
644
1142
  from: sender,
645
1143
  to: operationData?.to,
646
1144
  rawAmount: rawAmount || '',
647
- amount: amountParts[0] || '',
648
- asset: amountParts[1] || '',
1145
+ amount,
1146
+ asset,
649
1147
  memo: operationData?.memo
650
1148
  };
651
- const json = utils_2.Utils.jsonParse(operationData.memo);
1149
+ const json = utils_1.Utils.jsonParse(operationData.memo);
652
1150
  const payload = this.normalizeContractPayload(json?.[this.config.PAYLOAD_IDENTIFIER]);
653
1151
  if (payload) {
654
1152
  if (this?.adapter?.processTransfer) {
@@ -668,11 +1166,9 @@ class Streamer {
668
1166
  });
669
1167
  await this.dispatchContractAction(payload, context);
670
1168
  }
671
- this.transferSubscriptions.forEach(sub => {
672
- if (sub.account === operationData.to) {
673
- sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
674
- }
675
- });
1169
+ await Promise.all(this.transferSubscriptions
1170
+ .filter((sub) => sub.account === operationData.to)
1171
+ .map((sub) => Promise.resolve(sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime))));
676
1172
  }
677
1173
  // This is a custom JSON operation
678
1174
  if (operationType === 'custom_json') {
@@ -687,7 +1183,7 @@ class Streamer {
687
1183
  sender = operationData.required_posting_auths[0];
688
1184
  isSignedWithActiveKey = false;
689
1185
  }
690
- const json = utils_2.Utils.jsonParse(operationData.json);
1186
+ const json = utils_1.Utils.jsonParse(operationData.json);
691
1187
  const payload = id === this.config.JSON_ID
692
1188
  ? this.normalizeContractPayload(json?.[this.config.PAYLOAD_IDENTIFIER])
693
1189
  : null;
@@ -727,7 +1223,7 @@ class Streamer {
727
1223
  let hasVerificationErrors = false;
728
1224
  try {
729
1225
  const txInfo = await this.hive.getTransactionInfo(trxId);
730
- const logs = txInfo && txInfo.logs ? utils_2.Utils.jsonParse(txInfo.logs) : null;
1226
+ const logs = txInfo && txInfo.logs ? utils_1.Utils.jsonParse(txInfo.logs) : null;
731
1227
  hasVerificationErrors = Boolean(txInfo && logs && typeof logs.errors !== 'undefined');
732
1228
  }
733
1229
  catch (e) {
@@ -743,7 +1239,7 @@ class Streamer {
743
1239
  // Recurrent transfers carry payloads in memo, similar to transfer.
744
1240
  if (operationType === 'recurrent_transfer') {
745
1241
  const sender = operationData?.from;
746
- const json = utils_2.Utils.jsonParse(operationData?.memo);
1242
+ const json = utils_1.Utils.jsonParse(operationData?.memo);
747
1243
  const payload = this.normalizeContractPayload(json?.[this.config.PAYLOAD_IDENTIFIER]);
748
1244
  if (payload) {
749
1245
  const context = this.buildContractContext('recurrent_transfer', blockNumber, blockId, prevBlockId, trxId, blockTime, {
@@ -763,7 +1259,7 @@ class Streamer {
763
1259
  await this.adapter.processEscrow(operationType, operationData, operationMetadata);
764
1260
  }
765
1261
  if (operationType === 'escrow_transfer') {
766
- const jsonMeta = utils_2.Utils.jsonParse(operationData?.json_meta);
1262
+ const jsonMeta = utils_1.Utils.jsonParse(operationData?.json_meta);
767
1263
  const payload = this.normalizeContractPayload(jsonMeta?.[this.config.PAYLOAD_IDENTIFIER]);
768
1264
  if (payload) {
769
1265
  const context = this.buildContractContext('escrow_transfer', blockNumber, blockId, prevBlockId, trxId, blockTime, {
@@ -783,6 +1279,89 @@ class Streamer {
783
1279
  }
784
1280
  });
785
1281
  }
1282
+ // Vote operations
1283
+ if (operationType === 'vote') {
1284
+ this.voteSubscriptions.forEach(sub => {
1285
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1286
+ });
1287
+ }
1288
+ // Delegation operations
1289
+ if (operationType === 'delegate_vesting_shares') {
1290
+ this.delegateSubscriptions.forEach(sub => {
1291
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1292
+ });
1293
+ }
1294
+ // Power up (transfer_to_vesting)
1295
+ if (operationType === 'transfer_to_vesting') {
1296
+ this.powerUpSubscriptions.forEach(sub => {
1297
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1298
+ });
1299
+ }
1300
+ // Power down (withdraw_vesting)
1301
+ if (operationType === 'withdraw_vesting') {
1302
+ this.powerDownSubscriptions.forEach(sub => {
1303
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1304
+ });
1305
+ }
1306
+ // Claim reward balance
1307
+ if (operationType === 'claim_reward_balance') {
1308
+ this.claimRewardsSubscriptions.forEach(sub => {
1309
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1310
+ });
1311
+ }
1312
+ // Witness vote
1313
+ if (operationType === 'account_witness_vote') {
1314
+ this.witnessVoteSubscriptions.forEach(sub => {
1315
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1316
+ });
1317
+ }
1318
+ // Follow/unfollow/mute/reblog via custom_json with id 'follow'
1319
+ if (operationType === 'custom_json' && operationData?.id === 'follow') {
1320
+ const json = utils_1.Utils.jsonParse(operationData.json);
1321
+ if (Array.isArray(json) && json.length === 2) {
1322
+ const [type, payload] = json;
1323
+ if (type === 'follow' && payload && (this.followSubscriptions.length > 0)) {
1324
+ this.followSubscriptions.forEach(sub => {
1325
+ sub.callback(payload, blockNumber, blockId, prevBlockId, trxId, blockTime);
1326
+ });
1327
+ }
1328
+ if (type === 'reblog' && payload && (this.reblogSubscriptions.length > 0)) {
1329
+ this.reblogSubscriptions.forEach(sub => {
1330
+ sub.callback(payload, blockNumber, blockId, prevBlockId, trxId, blockTime);
1331
+ });
1332
+ }
1333
+ }
1334
+ }
1335
+ // Account updates
1336
+ if (operationType === 'account_update' || operationType === 'account_update2') {
1337
+ this.accountUpdateSubscriptions.forEach(sub => {
1338
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1339
+ });
1340
+ }
1341
+ // Delete comment
1342
+ if (operationType === 'delete_comment') {
1343
+ this.deleteCommentSubscriptions.forEach(sub => {
1344
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1345
+ });
1346
+ }
1347
+ // Limit orders
1348
+ if (operationType === 'limit_order_create' || operationType === 'limit_order_create2' || operationType === 'limit_order_cancel') {
1349
+ this.limitOrderSubscriptions.forEach(sub => {
1350
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1351
+ });
1352
+ }
1353
+ // Savings transfers
1354
+ if (operationType === 'transfer_to_savings' || operationType === 'transfer_from_savings' || operationType === 'cancel_transfer_from_savings') {
1355
+ this.savingsSubscriptions.forEach(sub => {
1356
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1357
+ });
1358
+ }
1359
+ // Convert operations
1360
+ if (operationType === 'convert' || operationType === 'collateralized_convert') {
1361
+ this.convertSubscriptions.forEach(sub => {
1362
+ sub.callback(operationData, blockNumber, blockId, prevBlockId, trxId, blockTime);
1363
+ });
1364
+ }
786
1365
  }
787
1366
  normalizeContractPayload(payload) {
788
1367
  if (!payload || typeof payload !== 'object') {
@@ -1021,73 +1600,317 @@ class Streamer {
1021
1600
  }
1022
1601
  }
1023
1602
  saveToHiveApi(from, data) {
1024
- return utils_2.Utils.transferHiveTokens(this.client, this.config, from, 'hiveapi', '0.001', 'HIVE', data);
1603
+ return utils_1.Utils.transferHiveTokens(this.client, this.config, from, 'hiveapi', '0.001', 'HIVE', data);
1025
1604
  }
1026
1605
  getAccountTransfers(account, from = -1, limit = 100) {
1027
- return utils_2.Utils.getAccountTransfers(this.client, account, from, limit);
1606
+ return utils_1.Utils.getAccountTransfers(this.client, account, from, limit);
1028
1607
  }
1029
1608
  transferHiveTokens(from, to, amount, symbol, memo = '') {
1030
- return utils_2.Utils.transferHiveTokens(this.client, this.config, from, to, amount, symbol, memo);
1609
+ return utils_1.Utils.transferHiveTokens(this.client, this.config, from, to, amount, symbol, memo);
1610
+ }
1611
+ burnHiveTokens(from, amount, symbol, memo = '') {
1612
+ return utils_1.Utils.burnHiveTokens(this.client, this.config, from, amount, symbol, memo);
1613
+ }
1614
+ burnTransferPortion(from, transfer, basisPoints, memo = '', allowedSymbols = ['HIVE', 'HBD']) {
1615
+ const rawAmount = typeof transfer === 'string' ? transfer : transfer?.amount || '';
1616
+ const parsed = utils_1.Utils.parseAssetAmount(rawAmount);
1617
+ if (Array.isArray(allowedSymbols) && allowedSymbols.length > 0 && !allowedSymbols.includes(parsed.asset)) {
1618
+ throw new Error(`Asset '${parsed.asset}' is not allowed for burn`);
1619
+ }
1620
+ const burnAmount = utils_1.Utils.calculateBasisPointsAmount(parsed.value, basisPoints);
1621
+ if (burnAmount === '0.000') {
1622
+ throw new Error('Burn amount must be greater than zero');
1623
+ }
1624
+ return this.burnHiveTokens(from, burnAmount, parsed.asset, memo);
1625
+ }
1626
+ burnTransferPercentage(from, transfer, percentage, memo = '', allowedSymbols = ['HIVE', 'HBD']) {
1627
+ const rawAmount = typeof transfer === 'string' ? transfer : transfer?.amount || '';
1628
+ const parsed = utils_1.Utils.parseAssetAmount(rawAmount);
1629
+ if (Array.isArray(allowedSymbols) && allowedSymbols.length > 0 && !allowedSymbols.includes(parsed.asset)) {
1630
+ throw new Error(`Asset '${parsed.asset}' is not allowed for burn`);
1631
+ }
1632
+ const burnAmount = utils_1.Utils.calculatePercentageAmount(parsed.value, percentage);
1633
+ if (burnAmount === '0.000') {
1634
+ throw new Error('Burn amount must be greater than zero');
1635
+ }
1636
+ return this.burnHiveTokens(from, burnAmount, parsed.asset, memo);
1637
+ }
1638
+ autoBurnIncomingTransfers(options = {}) {
1639
+ const basisPoints = this.resolveFlowBasisPoints(options, 'autoBurnIncomingTransfers', false);
1640
+ const account = this.resolveAccount(options.account);
1641
+ const normalizedStore = this.normalizeDedupeStore(options.dedupeStore);
1642
+ const allowedSymbols = options.allowedSymbols || ['HIVE', 'HBD'];
1643
+ const ignoreZeroAmount = options.ignoreZeroAmount !== false;
1644
+ const callback = async (op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
1645
+ const event = this.createTransferEvent(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
1646
+ try {
1647
+ if (await this.dedupeHas(normalizedStore, trxId)) {
1648
+ return;
1649
+ }
1650
+ const memo = typeof options.memo === 'function' ? options.memo(event) : options.memo || '';
1651
+ const amount = this.calculateSingleFlowAmount(op, basisPoints, allowedSymbols, 'burn');
1652
+ const result = await this.burnHiveTokens(account, amount.amount, amount.asset, memo);
1653
+ await this.dedupeAdd(normalizedStore, trxId);
1654
+ if (options.onBurned) {
1655
+ await options.onBurned(result, event);
1656
+ }
1657
+ }
1658
+ catch (error) {
1659
+ if (ignoreZeroAmount && this.isZeroAmountFlowError(error)) {
1660
+ return;
1661
+ }
1662
+ if (options.onError) {
1663
+ await options.onError(error, event);
1664
+ return;
1665
+ }
1666
+ throw error;
1667
+ }
1668
+ };
1669
+ this.onTransfer(account, callback);
1670
+ return {
1671
+ account,
1672
+ stop: () => {
1673
+ this.removeTransferSubscription(account, callback);
1674
+ }
1675
+ };
1676
+ }
1677
+ autoForwardIncomingTransfers(options) {
1678
+ if (!options || typeof options.to !== 'string' || options.to.trim().length === 0) {
1679
+ throw new Error('autoForwardIncomingTransfers requires a destination account');
1680
+ }
1681
+ const account = this.resolveAccount(options.account);
1682
+ const normalizedStore = this.normalizeDedupeStore(options.dedupeStore);
1683
+ const allowedSymbols = options.allowedSymbols || ['HIVE', 'HBD'];
1684
+ const ignoreZeroAmount = options.ignoreZeroAmount !== false;
1685
+ const basisPoints = this.resolveFlowBasisPoints(options, 'autoForwardIncomingTransfers', true);
1686
+ const destination = options.to.trim();
1687
+ const callback = async (op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
1688
+ const event = this.createTransferEvent(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
1689
+ try {
1690
+ if (await this.dedupeHas(normalizedStore, trxId)) {
1691
+ return;
1692
+ }
1693
+ const memo = typeof options.memo === 'function' ? options.memo(event) : options.memo || '';
1694
+ const amount = this.calculateSingleFlowAmount(op, basisPoints, allowedSymbols, 'forward');
1695
+ const result = await this.transferHiveTokens(account, destination, amount.amount, amount.asset, memo);
1696
+ await this.dedupeAdd(normalizedStore, trxId);
1697
+ if (options.onForwarded) {
1698
+ await options.onForwarded(result, event);
1699
+ }
1700
+ }
1701
+ catch (error) {
1702
+ if (ignoreZeroAmount && this.isZeroAmountFlowError(error)) {
1703
+ return;
1704
+ }
1705
+ if (options.onError) {
1706
+ await options.onError(error, event);
1707
+ return;
1708
+ }
1709
+ throw error;
1710
+ }
1711
+ };
1712
+ this.onTransfer(account, callback);
1713
+ return {
1714
+ account,
1715
+ stop: () => {
1716
+ this.removeTransferSubscription(account, callback);
1717
+ }
1718
+ };
1719
+ }
1720
+ autoRefundIncomingTransfers(options = {}) {
1721
+ const account = this.resolveAccount(options.account);
1722
+ const normalizedStore = this.normalizeDedupeStore(options.dedupeStore);
1723
+ const allowedSymbols = options.allowedSymbols || ['HIVE', 'HBD'];
1724
+ const ignoreZeroAmount = options.ignoreZeroAmount !== false;
1725
+ const basisPoints = this.resolveFlowBasisPoints(options, 'autoRefundIncomingTransfers', true);
1726
+ const callback = async (op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
1727
+ const event = this.createTransferEvent(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
1728
+ try {
1729
+ if (await this.dedupeHas(normalizedStore, trxId)) {
1730
+ return;
1731
+ }
1732
+ const memo = typeof options.memo === 'function' ? options.memo(event) : options.memo || '';
1733
+ const amount = this.calculateSingleFlowAmount(op, basisPoints, allowedSymbols, 'refund');
1734
+ const result = await this.transferHiveTokens(account, event.transfer.from, amount.amount, amount.asset, memo);
1735
+ await this.dedupeAdd(normalizedStore, trxId);
1736
+ if (options.onRefunded) {
1737
+ await options.onRefunded(result, event);
1738
+ }
1739
+ }
1740
+ catch (error) {
1741
+ if (ignoreZeroAmount && this.isZeroAmountFlowError(error)) {
1742
+ return;
1743
+ }
1744
+ if (options.onError) {
1745
+ await options.onError(error, event);
1746
+ return;
1747
+ }
1748
+ throw error;
1749
+ }
1750
+ };
1751
+ this.onTransfer(account, callback);
1752
+ return {
1753
+ account,
1754
+ stop: () => {
1755
+ this.removeTransferSubscription(account, callback);
1756
+ }
1757
+ };
1758
+ }
1759
+ autoSplitIncomingTransfers(options) {
1760
+ if (!options || !Array.isArray(options.recipients) || options.recipients.length === 0) {
1761
+ throw new Error('autoSplitIncomingTransfers requires at least one recipient');
1762
+ }
1763
+ const defaultMemo = options.memo;
1764
+ return this.autoRouteIncomingTransfers({
1765
+ account: options.account,
1766
+ routes: options.recipients.map((recipient, index) => ({
1767
+ to: recipient.account,
1768
+ percentage: recipient.percentage,
1769
+ percent: recipient.percent,
1770
+ basisPoints: recipient.basisPoints,
1771
+ memo: recipient.memo || (typeof defaultMemo === 'function'
1772
+ ? (event) => defaultMemo(event, { account: recipient.account }, index)
1773
+ : defaultMemo)
1774
+ })),
1775
+ allowedSymbols: options.allowedSymbols,
1776
+ dedupeStore: options.dedupeStore,
1777
+ ignoreZeroAmount: options.ignoreZeroAmount,
1778
+ onRouted: async (results, event, plan) => {
1779
+ if (options.onSplit) {
1780
+ await options.onSplit(results, event, plan);
1781
+ }
1782
+ },
1783
+ onError: options.onError
1784
+ });
1785
+ }
1786
+ planIncomingTransferRoutes(transfer, options) {
1787
+ if (!options || !Array.isArray(options.routes) || options.routes.length === 0) {
1788
+ throw new Error('planIncomingTransferRoutes requires at least one route');
1789
+ }
1790
+ const event = this.normalizeTransferPreviewInput(transfer);
1791
+ const allowedSymbols = options.allowedSymbols || ['HIVE', 'HBD'];
1792
+ return this.buildPlannedIncomingTransferRoutes(event, options.routes, allowedSymbols, options.memo);
1793
+ }
1794
+ autoRouteIncomingTransfers(options) {
1795
+ if (!options || !Array.isArray(options.routes) || options.routes.length === 0) {
1796
+ throw new Error('autoRouteIncomingTransfers requires at least one route');
1797
+ }
1798
+ const account = this.resolveAccount(options.account);
1799
+ const normalizedStore = this.normalizeDedupeStore(options.dedupeStore);
1800
+ const allowedSymbols = options.allowedSymbols || ['HIVE', 'HBD'];
1801
+ const ignoreZeroAmount = options.ignoreZeroAmount !== false;
1802
+ const callback = async (op, blockNumber, blockId, prevBlockId, trxId, blockTime) => {
1803
+ const event = this.createTransferEvent(op, blockNumber, blockId, prevBlockId, trxId, blockTime);
1804
+ try {
1805
+ if (await this.dedupeHas(normalizedStore, trxId)) {
1806
+ return;
1807
+ }
1808
+ const plan = this.buildPlannedIncomingTransferRoutes(event, options.routes, allowedSymbols, options.memo);
1809
+ const results = await this.executePlannedFlowRoutes(account, plan.routes, ignoreZeroAmount);
1810
+ await this.dedupeAdd(normalizedStore, trxId);
1811
+ if (options.onRouted) {
1812
+ await options.onRouted(results, event, plan.routes);
1813
+ }
1814
+ }
1815
+ catch (error) {
1816
+ if (ignoreZeroAmount && this.isZeroAmountFlowError(error)) {
1817
+ return;
1818
+ }
1819
+ if (options.onError) {
1820
+ await options.onError(error, event);
1821
+ return;
1822
+ }
1823
+ throw error;
1824
+ }
1825
+ };
1826
+ this.onTransfer(account, callback);
1827
+ return {
1828
+ account,
1829
+ stop: () => {
1830
+ this.removeTransferSubscription(account, callback);
1831
+ }
1832
+ };
1031
1833
  }
1032
1834
  transferHiveTokensMultiple(from, accounts = [], amount = '0', symbol, memo = '') {
1033
- return utils_2.Utils.transferHiveTokensMultiple(this.client, this.config, from, accounts, amount, symbol, memo);
1835
+ return utils_1.Utils.transferHiveTokensMultiple(this.client, this.config, from, accounts, amount, symbol, memo);
1034
1836
  }
1035
1837
  broadcastOperations(operations, signingKeys) {
1036
- return utils_2.Utils.broadcastOperations(this.client, operations, signingKeys || this.config.ACTIVE_KEY);
1838
+ if (signingKeys) {
1839
+ return utils_1.Utils.broadcastOperations(this.client, operations, signingKeys);
1840
+ }
1841
+ // Auto-detect key: use posting key for posting-level ops, active key for active-level ops
1842
+ const activeOps = new Set([
1843
+ 'transfer', 'transfer_to_vesting', 'withdraw_vesting', 'delegate_vesting_shares',
1844
+ 'transfer_to_savings', 'transfer_from_savings', 'cancel_transfer_from_savings',
1845
+ 'convert', 'collateralized_convert', 'limit_order_create', 'limit_order_create2',
1846
+ 'limit_order_cancel', 'escrow_transfer', 'escrow_approve', 'escrow_dispute',
1847
+ 'escrow_release', 'account_update', 'account_witness_vote', 'account_witness_proxy',
1848
+ 'set_withdraw_vesting_route', 'claim_account', 'create_claimed_account',
1849
+ 'create_proposal', 'update_proposal_votes', 'remove_proposal', 'recurrent_transfer',
1850
+ 'feed_publish'
1851
+ ]);
1852
+ const needsActive = operations.some(([opType]) => activeOps.has(opType));
1853
+ const key = needsActive
1854
+ ? (this.config.ACTIVE_KEY || this.config.POSTING_KEY)
1855
+ : (this.config.POSTING_KEY || this.config.ACTIVE_KEY);
1856
+ return utils_1.Utils.broadcastOperations(this.client, operations, key);
1037
1857
  }
1038
1858
  broadcastMultiSigOperations(operations, signingKeys) {
1039
- return utils_2.Utils.broadcastMultiSigOperations(this.client, operations, signingKeys);
1859
+ return utils_1.Utils.broadcastMultiSigOperations(this.client, operations, signingKeys);
1040
1860
  }
1041
1861
  createAuthority(keyAuths = [], accountAuths = [], weightThreshold = 1) {
1042
- return utils_2.Utils.createAuthority(keyAuths, accountAuths, weightThreshold);
1862
+ return utils_1.Utils.createAuthority(keyAuths, accountAuths, weightThreshold);
1043
1863
  }
1044
1864
  updateAccountAuthorities(account, authorityUpdate, signingKeys) {
1045
- return utils_2.Utils.updateAccountAuthorities(this.client, this.config, account, authorityUpdate, signingKeys);
1865
+ return utils_1.Utils.updateAccountAuthorities(this.client, this.config, account, authorityUpdate, signingKeys);
1046
1866
  }
1047
1867
  escrowTransfer(options, signingKeys) {
1048
- return utils_2.Utils.escrowTransfer(this.client, this.config, options, signingKeys);
1868
+ return utils_1.Utils.escrowTransfer(this.client, this.config, options, signingKeys);
1049
1869
  }
1050
1870
  escrowApprove(options, signingKeys) {
1051
- return utils_2.Utils.escrowApprove(this.client, this.config, options, signingKeys);
1871
+ return utils_1.Utils.escrowApprove(this.client, this.config, options, signingKeys);
1052
1872
  }
1053
1873
  escrowDispute(options, signingKeys) {
1054
- return utils_2.Utils.escrowDispute(this.client, this.config, options, signingKeys);
1874
+ return utils_1.Utils.escrowDispute(this.client, this.config, options, signingKeys);
1055
1875
  }
1056
1876
  escrowRelease(options, signingKeys) {
1057
- return utils_2.Utils.escrowRelease(this.client, this.config, options, signingKeys);
1877
+ return utils_1.Utils.escrowRelease(this.client, this.config, options, signingKeys);
1058
1878
  }
1059
1879
  recurrentTransfer(options, signingKeys) {
1060
- return utils_2.Utils.recurrentTransfer(this.client, this.config, options, signingKeys);
1880
+ return utils_1.Utils.recurrentTransfer(this.client, this.config, options, signingKeys);
1061
1881
  }
1062
1882
  createProposal(options, signingKeys) {
1063
- return utils_2.Utils.createProposal(this.client, this.config, options, signingKeys);
1883
+ return utils_1.Utils.createProposal(this.client, this.config, options, signingKeys);
1064
1884
  }
1065
1885
  updateProposalVotes(options, signingKeys) {
1066
- return utils_2.Utils.updateProposalVotes(this.client, this.config, options, signingKeys);
1886
+ return utils_1.Utils.updateProposalVotes(this.client, this.config, options, signingKeys);
1067
1887
  }
1068
1888
  removeProposals(options, signingKeys) {
1069
- return utils_2.Utils.removeProposals(this.client, this.config, options, signingKeys);
1889
+ return utils_1.Utils.removeProposals(this.client, this.config, options, signingKeys);
1070
1890
  }
1071
1891
  transferHiveEngineTokens(from, to, symbol, quantity, memo = '') {
1072
- return utils_2.Utils.transferHiveEngineTokens(this.client, this.config, from, to, quantity, symbol, memo);
1892
+ return utils_1.Utils.transferHiveEngineTokens(this.client, this.config, from, to, quantity, symbol, memo);
1893
+ }
1894
+ burnHiveEngineTokens(from, symbol, quantity, memo = '') {
1895
+ return utils_1.Utils.burnHiveEngineTokens(this.client, this.config, from, symbol, quantity, memo);
1073
1896
  }
1074
1897
  transferHiveEngineTokensMultiple(from, accounts = [], symbol, memo = '', amount = '0') {
1075
- return utils_2.Utils.transferHiveEngineTokensMultiple(this.client, this.config, from, accounts, symbol, memo, amount);
1898
+ return utils_1.Utils.transferHiveEngineTokensMultiple(this.client, this.config, from, accounts, symbol, memo, amount);
1076
1899
  }
1077
1900
  issueHiveEngineTokens(from, to, symbol, quantity, memo = '') {
1078
- return utils_2.Utils.issueHiveEngineTokens(this.client, this.config, from, to, symbol, quantity, memo);
1901
+ return utils_1.Utils.issueHiveEngineTokens(this.client, this.config, from, to, symbol, quantity, memo);
1079
1902
  }
1080
1903
  issueHiveEngineTokensMultiple(from, accounts = [], symbol, memo = '', amount = '0') {
1081
- return utils_2.Utils.issueHiveEngineTokensMultiple(this.client, this.config, from, accounts, symbol, memo, amount);
1904
+ return utils_1.Utils.issueHiveEngineTokensMultiple(this.client, this.config, from, accounts, symbol, memo, amount);
1082
1905
  }
1083
1906
  upvote(votePercentage = '100.0', username, permlink) {
1084
- return utils_2.Utils.upvote(this.client, this.config, this.username, votePercentage, username, permlink);
1907
+ return utils_1.Utils.upvote(this.client, this.config, this.username, votePercentage, username, permlink);
1085
1908
  }
1086
1909
  downvote(votePercentage = '100.0', username, permlink) {
1087
- return utils_2.Utils.downvote(this.client, this.config, this.username, votePercentage, username, permlink);
1910
+ return utils_1.Utils.downvote(this.client, this.config, this.username, votePercentage, username, permlink);
1088
1911
  }
1089
1912
  getTransaction(blockNumber, transactionId) {
1090
- return utils_2.Utils.getTransaction(this.client, blockNumber, transactionId);
1913
+ return utils_1.Utils.getTransaction(this.client, blockNumber, transactionId);
1091
1914
  }
1092
1915
  getStatus() {
1093
1916
  return {
@@ -1101,7 +1924,7 @@ class Streamer {
1101
1924
  };
1102
1925
  }
1103
1926
  verifyTransfer(transaction, from, to, amount) {
1104
- return utils_2.Utils.verifyTransfer(transaction, from, to, amount);
1927
+ return utils_1.Utils.verifyTransfer(transaction, from, to, amount);
1105
1928
  }
1106
1929
  onComment(callback) {
1107
1930
  this.commentSubscriptions.push({
@@ -1122,7 +1945,7 @@ class Streamer {
1122
1945
  onCustomJson(callback) {
1123
1946
  this.customJsonSubscriptions.push({ callback });
1124
1947
  }
1125
- onCustomJsonId(callback, id) {
1948
+ onCustomJsonId(id, callback) {
1126
1949
  this.customJsonIdSubscriptions.push({ callback, id });
1127
1950
  }
1128
1951
  onHiveEngine(callback) {
@@ -1140,6 +1963,189 @@ class Streamer {
1140
1963
  onEscrowRelease(callback) {
1141
1964
  this.escrowSubscriptions.push({ type: 'escrow_release', callback });
1142
1965
  }
1966
+ // ─── New Event Subscriptions ────────────────────────────────────────
1967
+ onVote(callback) {
1968
+ this.voteSubscriptions.push({ callback });
1969
+ }
1970
+ onDelegate(callback) {
1971
+ this.delegateSubscriptions.push({ callback });
1972
+ }
1973
+ onPowerUp(callback) {
1974
+ this.powerUpSubscriptions.push({ callback });
1975
+ }
1976
+ onPowerDown(callback) {
1977
+ this.powerDownSubscriptions.push({ callback });
1978
+ }
1979
+ onClaimRewards(callback) {
1980
+ this.claimRewardsSubscriptions.push({ callback });
1981
+ }
1982
+ onAccountWitnessVote(callback) {
1983
+ this.witnessVoteSubscriptions.push({ callback });
1984
+ }
1985
+ // ─── Social Operations ──────────────────────────────────────────────
1986
+ follow(follower, following) {
1987
+ return utils_1.Utils.follow(this.client, this.config, follower, following);
1988
+ }
1989
+ unfollow(follower, following) {
1990
+ return utils_1.Utils.unfollow(this.client, this.config, follower, following);
1991
+ }
1992
+ mute(follower, following) {
1993
+ return utils_1.Utils.mute(this.client, this.config, follower, following);
1994
+ }
1995
+ reblog(account, author, permlink) {
1996
+ return utils_1.Utils.reblog(this.client, this.config, account, author, permlink);
1997
+ }
1998
+ // ─── Staking Operations ─────────────────────────────────────────────
1999
+ powerUp(from, to, amount) {
2000
+ return utils_1.Utils.powerUp(this.client, this.config, from, to, amount);
2001
+ }
2002
+ powerDown(account, vestingShares) {
2003
+ return utils_1.Utils.powerDown(this.client, this.config, account, vestingShares);
2004
+ }
2005
+ cancelPowerDown(account) {
2006
+ return utils_1.Utils.cancelPowerDown(this.client, this.config, account);
2007
+ }
2008
+ delegateVestingShares(delegator, delegatee, vestingShares) {
2009
+ return utils_1.Utils.delegateVestingShares(this.client, this.config, delegator, delegatee, vestingShares);
2010
+ }
2011
+ undelegateVestingShares(delegator, delegatee) {
2012
+ return utils_1.Utils.undelegateVestingShares(this.client, this.config, delegator, delegatee);
2013
+ }
2014
+ // ─── Account Operations ─────────────────────────────────────────────
2015
+ claimRewards(account, rewardHive, rewardHbd, rewardVests) {
2016
+ return utils_1.Utils.claimRewards(this.client, this.config, account, rewardHive, rewardHbd, rewardVests);
2017
+ }
2018
+ witnessVote(account, witness, approve = true) {
2019
+ return utils_1.Utils.witnessVote(this.client, this.config, account, witness, approve);
2020
+ }
2021
+ setProxy(account, proxy) {
2022
+ return utils_1.Utils.setProxy(this.client, this.config, account, proxy);
2023
+ }
2024
+ clearProxy(account) {
2025
+ return utils_1.Utils.clearProxy(this.client, this.config, account);
2026
+ }
2027
+ updateProfile(account, profile) {
2028
+ return utils_1.Utils.updateProfile(this.client, this.config, account, profile);
2029
+ }
2030
+ getAccount(username) {
2031
+ return utils_1.Utils.getAccount(this.client, username);
2032
+ }
2033
+ getAccounts(usernames) {
2034
+ return utils_1.Utils.getAccounts(this.client, usernames);
2035
+ }
2036
+ // ─── Savings Operations ─────────────────────────────────────────────
2037
+ transferToSavings(from, to, amount, symbol, memo = '') {
2038
+ return utils_1.Utils.transferToSavings(this.client, this.config, from, to, amount, symbol, memo);
2039
+ }
2040
+ transferFromSavings(from, to, amount, symbol, requestId = 0, memo = '') {
2041
+ return utils_1.Utils.transferFromSavings(this.client, this.config, from, to, amount, symbol, requestId, memo);
2042
+ }
2043
+ cancelTransferFromSavings(from, requestId) {
2044
+ return utils_1.Utils.cancelTransferFromSavings(this.client, this.config, from, requestId);
2045
+ }
2046
+ // ─── Convert Operations ─────────────────────────────────────────────
2047
+ convert(owner, amount, requestId = 0) {
2048
+ return utils_1.Utils.convert(this.client, this.config, owner, amount, requestId);
2049
+ }
2050
+ collateralizedConvert(owner, amount, requestId = 0) {
2051
+ return utils_1.Utils.collateralizedConvert(this.client, this.config, owner, amount, requestId);
2052
+ }
2053
+ // ─── Content Operations ─────────────────────────────────────────────
2054
+ deleteComment(author, permlink) {
2055
+ return utils_1.Utils.deleteComment(this.client, this.config, author, permlink);
2056
+ }
2057
+ commentOptions(author, permlink, options) {
2058
+ return utils_1.Utils.commentOptions(this.client, this.config, author, permlink, options);
2059
+ }
2060
+ // ─── Market Operations ──────────────────────────────────────────────
2061
+ limitOrderCreate(owner, orderId, amountToSell, minToReceive, fillOrKill = false, expiration, signingKeys) {
2062
+ return utils_1.Utils.limitOrderCreate(this.client, this.config, owner, orderId, amountToSell, minToReceive, fillOrKill, expiration);
2063
+ }
2064
+ limitOrderCancel(owner, orderId, signingKeys) {
2065
+ return utils_1.Utils.limitOrderCancel(this.client, this.config, owner, orderId);
2066
+ }
2067
+ // ─── Vesting Route ──────────────────────────────────────────────────
2068
+ setWithdrawVestingRoute(fromAccount, toAccount, percent, autoVest = false, signingKeys) {
2069
+ return utils_1.Utils.setWithdrawVestingRoute(this.client, this.config, fromAccount, toAccount, percent, autoVest, signingKeys);
2070
+ }
2071
+ // ─── Account Creation ───────────────────────────────────────────────
2072
+ claimAccount(creator, fee = '0.000 HIVE') {
2073
+ return utils_1.Utils.claimAccount(this.client, this.config, creator, fee);
2074
+ }
2075
+ feedPublish(publisher, baseAmount, quoteAmount = '1.000 HIVE') {
2076
+ return utils_1.Utils.feedPublish(this.client, this.config, publisher, baseAmount, quoteAmount);
2077
+ }
2078
+ // ─── Batch Builder ────────────────────────────────────────────────
2079
+ batch() {
2080
+ return new builders_1.HiveBatchBuilder(this);
2081
+ }
2082
+ // ─── Authority Management ───────────────────────────────────────────
2083
+ hasPostingAuth(account, authAccount) {
2084
+ return utils_1.Utils.hasPostingAuth(this.client, account, authAccount);
2085
+ }
2086
+ grantPostingAuth(account, authAccount, signingKeys) {
2087
+ return utils_1.Utils.grantPostingAuth(this.client, this.config, account, authAccount, signingKeys);
2088
+ }
2089
+ revokePostingAuth(account, authAccount, signingKeys) {
2090
+ return utils_1.Utils.revokePostingAuth(this.client, this.config, account, authAccount, signingKeys);
2091
+ }
2092
+ // ─── Additional Event Subscriptions ─────────────────────────────────
2093
+ onFollow(callback) {
2094
+ this.followSubscriptions.push({ callback });
2095
+ }
2096
+ onReblog(callback) {
2097
+ this.reblogSubscriptions.push({ callback });
2098
+ }
2099
+ onAccountUpdate(callback) {
2100
+ this.accountUpdateSubscriptions.push({ callback });
2101
+ }
2102
+ onDeleteComment(callback) {
2103
+ this.deleteCommentSubscriptions.push({ callback });
2104
+ }
2105
+ onLimitOrder(callback) {
2106
+ this.limitOrderSubscriptions.push({ callback });
2107
+ }
2108
+ onSavingsTransfer(callback) {
2109
+ this.savingsSubscriptions.push({ callback });
2110
+ }
2111
+ onConvert(callback) {
2112
+ this.convertSubscriptions.push({ callback });
2113
+ }
2114
+ onBlock(callback) {
2115
+ this.blockSubscriptions.push({ callback });
2116
+ }
2117
+ onAnyOperation(callback) {
2118
+ this.anyOperationSubscriptions.push({ callback });
2119
+ }
2120
+ // ─── Hive Engine Write Operations ───────────────────────────────────
2121
+ stakeEngineTokens(from, to, symbol, quantity) {
2122
+ return utils_1.Utils.stakeEngineTokens(this.client, this.config, from, to, symbol, quantity);
2123
+ }
2124
+ unstakeEngineTokens(from, symbol, quantity) {
2125
+ return utils_1.Utils.unstakeEngineTokens(this.client, this.config, from, symbol, quantity);
2126
+ }
2127
+ buyEngineTokens(from, symbol, quantity, price) {
2128
+ return utils_1.Utils.buyEngineTokens(this.client, this.config, from, symbol, quantity, price);
2129
+ }
2130
+ sellEngineTokens(from, symbol, quantity, price) {
2131
+ return utils_1.Utils.sellEngineTokens(this.client, this.config, from, symbol, quantity, price);
2132
+ }
2133
+ cancelEngineOrder(from, type, orderId) {
2134
+ return utils_1.Utils.cancelEngineOrder(this.client, this.config, from, type, orderId);
2135
+ }
2136
+ delegateEngineTokens(from, to, symbol, quantity) {
2137
+ return utils_1.Utils.delegateEngineTokens(this.client, this.config, from, to, symbol, quantity);
2138
+ }
2139
+ undelegateEngineTokens(from, to, symbol, quantity) {
2140
+ return utils_1.Utils.undelegateEngineTokens(this.client, this.config, from, to, symbol, quantity);
2141
+ }
2142
+ // ─── Community Operations ───────────────────────────────────────────
2143
+ subscribeCommunity(account, community) {
2144
+ return utils_1.Utils.subscribeCommunity(this.client, this.config, account, community);
2145
+ }
2146
+ unsubscribeCommunity(account, community) {
2147
+ return utils_1.Utils.unsubscribeCommunity(this.client, this.config, account, community);
2148
+ }
1143
2149
  // Memory management: cleanup subscriptions
1144
2150
  cleanupSubscriptions() {
1145
2151
  // Limit subscription arrays to prevent memory leaks
@@ -1171,10 +2177,53 @@ class Streamer {
1171
2177
  this.escrowSubscriptions = this.escrowSubscriptions.slice(-this.maxSubscriptions);
1172
2178
  console.warn(`[Streamer] Trimmed escrowSubscriptions to ${this.maxSubscriptions} items`);
1173
2179
  }
2180
+ if (this.voteSubscriptions.length > this.maxSubscriptions) {
2181
+ this.voteSubscriptions = this.voteSubscriptions.slice(-this.maxSubscriptions);
2182
+ console.warn(`[Streamer] Trimmed voteSubscriptions to ${this.maxSubscriptions} items`);
2183
+ }
2184
+ if (this.delegateSubscriptions.length > this.maxSubscriptions) {
2185
+ this.delegateSubscriptions = this.delegateSubscriptions.slice(-this.maxSubscriptions);
2186
+ console.warn(`[Streamer] Trimmed delegateSubscriptions to ${this.maxSubscriptions} items`);
2187
+ }
2188
+ if (this.powerUpSubscriptions.length > this.maxSubscriptions) {
2189
+ this.powerUpSubscriptions = this.powerUpSubscriptions.slice(-this.maxSubscriptions);
2190
+ console.warn(`[Streamer] Trimmed powerUpSubscriptions to ${this.maxSubscriptions} items`);
2191
+ }
2192
+ if (this.powerDownSubscriptions.length > this.maxSubscriptions) {
2193
+ this.powerDownSubscriptions = this.powerDownSubscriptions.slice(-this.maxSubscriptions);
2194
+ console.warn(`[Streamer] Trimmed powerDownSubscriptions to ${this.maxSubscriptions} items`);
2195
+ }
2196
+ if (this.claimRewardsSubscriptions.length > this.maxSubscriptions) {
2197
+ this.claimRewardsSubscriptions = this.claimRewardsSubscriptions.slice(-this.maxSubscriptions);
2198
+ console.warn(`[Streamer] Trimmed claimRewardsSubscriptions to ${this.maxSubscriptions} items`);
2199
+ }
2200
+ if (this.witnessVoteSubscriptions.length > this.maxSubscriptions) {
2201
+ this.witnessVoteSubscriptions = this.witnessVoteSubscriptions.slice(-this.maxSubscriptions);
2202
+ console.warn(`[Streamer] Trimmed witnessVoteSubscriptions to ${this.maxSubscriptions} items`);
2203
+ }
2204
+ const extraArrays = [
2205
+ 'followSubscriptions', 'reblogSubscriptions', 'accountUpdateSubscriptions',
2206
+ 'deleteCommentSubscriptions', 'limitOrderSubscriptions', 'savingsSubscriptions',
2207
+ 'convertSubscriptions'
2208
+ ];
2209
+ for (const name of extraArrays) {
2210
+ if (this[name].length > this.maxSubscriptions) {
2211
+ this[name] = this[name].slice(-this.maxSubscriptions);
2212
+ console.warn(`[Streamer] Trimmed ${name} to ${this.maxSubscriptions} items`);
2213
+ }
2214
+ }
1174
2215
  }
1175
2216
  // Add method to remove specific subscriptions
1176
- removeTransferSubscription(account) {
1177
- this.transferSubscriptions = this.transferSubscriptions.filter(sub => sub.account !== account);
2217
+ removeTransferSubscription(account, callback) {
2218
+ this.transferSubscriptions = this.transferSubscriptions.filter((sub) => {
2219
+ if (sub.account !== account) {
2220
+ return true;
2221
+ }
2222
+ if (!callback) {
2223
+ return false;
2224
+ }
2225
+ return sub.callback !== callback;
2226
+ });
1178
2227
  }
1179
2228
  removeCustomJsonIdSubscription(id) {
1180
2229
  this.customJsonIdSubscriptions = this.customJsonIdSubscriptions.filter(sub => sub.id !== id);