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.
- package/DOCUMENTATION.md +693 -1
- package/README.md +251 -1
- package/dist/adapters/mongodb.adapter.js +21 -30
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.js +16 -16
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.js +17 -17
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -1
- package/dist/builders.d.ts +444 -0
- package/dist/builders.js +1609 -0
- package/dist/builders.js.map +1 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.js +90 -4
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.js +18 -21
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/dice.contract.js +18 -21
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/helpers.d.ts +2 -0
- package/dist/contracts/helpers.js +18 -11
- package/dist/contracts/helpers.js.map +1 -1
- package/dist/contracts/nft.contract.js +5 -10
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/rps.contract.js +35 -23
- package/dist/contracts/rps.contract.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +13 -0
- package/dist/metadata.js +256 -0
- package/dist/metadata.js.map +1 -1
- package/dist/streamer.d.ts +148 -12
- package/dist/streamer.js +1102 -53
- package/dist/streamer.js.map +1 -1
- package/dist/types/hive-stream.d.ts +623 -0
- package/dist/utils.d.ts +475 -0
- package/dist/utils.js +1618 -8
- package/dist/utils.js.map +1 -1
- 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
|
|
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
|
-
|
|
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
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
|
648
|
-
asset
|
|
1145
|
+
amount,
|
|
1146
|
+
asset,
|
|
649
1147
|
memo: operationData?.memo
|
|
650
1148
|
};
|
|
651
|
-
const json =
|
|
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
|
|
672
|
-
|
|
673
|
-
|
|
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 =
|
|
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 ?
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
1606
|
+
return utils_1.Utils.getAccountTransfers(this.client, account, from, limit);
|
|
1028
1607
|
}
|
|
1029
1608
|
transferHiveTokens(from, to, amount, symbol, memo = '') {
|
|
1030
|
-
return
|
|
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
|
|
1835
|
+
return utils_1.Utils.transferHiveTokensMultiple(this.client, this.config, from, accounts, amount, symbol, memo);
|
|
1034
1836
|
}
|
|
1035
1837
|
broadcastOperations(operations, signingKeys) {
|
|
1036
|
-
|
|
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
|
|
1859
|
+
return utils_1.Utils.broadcastMultiSigOperations(this.client, operations, signingKeys);
|
|
1040
1860
|
}
|
|
1041
1861
|
createAuthority(keyAuths = [], accountAuths = [], weightThreshold = 1) {
|
|
1042
|
-
return
|
|
1862
|
+
return utils_1.Utils.createAuthority(keyAuths, accountAuths, weightThreshold);
|
|
1043
1863
|
}
|
|
1044
1864
|
updateAccountAuthorities(account, authorityUpdate, signingKeys) {
|
|
1045
|
-
return
|
|
1865
|
+
return utils_1.Utils.updateAccountAuthorities(this.client, this.config, account, authorityUpdate, signingKeys);
|
|
1046
1866
|
}
|
|
1047
1867
|
escrowTransfer(options, signingKeys) {
|
|
1048
|
-
return
|
|
1868
|
+
return utils_1.Utils.escrowTransfer(this.client, this.config, options, signingKeys);
|
|
1049
1869
|
}
|
|
1050
1870
|
escrowApprove(options, signingKeys) {
|
|
1051
|
-
return
|
|
1871
|
+
return utils_1.Utils.escrowApprove(this.client, this.config, options, signingKeys);
|
|
1052
1872
|
}
|
|
1053
1873
|
escrowDispute(options, signingKeys) {
|
|
1054
|
-
return
|
|
1874
|
+
return utils_1.Utils.escrowDispute(this.client, this.config, options, signingKeys);
|
|
1055
1875
|
}
|
|
1056
1876
|
escrowRelease(options, signingKeys) {
|
|
1057
|
-
return
|
|
1877
|
+
return utils_1.Utils.escrowRelease(this.client, this.config, options, signingKeys);
|
|
1058
1878
|
}
|
|
1059
1879
|
recurrentTransfer(options, signingKeys) {
|
|
1060
|
-
return
|
|
1880
|
+
return utils_1.Utils.recurrentTransfer(this.client, this.config, options, signingKeys);
|
|
1061
1881
|
}
|
|
1062
1882
|
createProposal(options, signingKeys) {
|
|
1063
|
-
return
|
|
1883
|
+
return utils_1.Utils.createProposal(this.client, this.config, options, signingKeys);
|
|
1064
1884
|
}
|
|
1065
1885
|
updateProposalVotes(options, signingKeys) {
|
|
1066
|
-
return
|
|
1886
|
+
return utils_1.Utils.updateProposalVotes(this.client, this.config, options, signingKeys);
|
|
1067
1887
|
}
|
|
1068
1888
|
removeProposals(options, signingKeys) {
|
|
1069
|
-
return
|
|
1889
|
+
return utils_1.Utils.removeProposals(this.client, this.config, options, signingKeys);
|
|
1070
1890
|
}
|
|
1071
1891
|
transferHiveEngineTokens(from, to, symbol, quantity, memo = '') {
|
|
1072
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1910
|
+
return utils_1.Utils.downvote(this.client, this.config, this.username, votePercentage, username, permlink);
|
|
1088
1911
|
}
|
|
1089
1912
|
getTransaction(blockNumber, transactionId) {
|
|
1090
|
-
return
|
|
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
|
|
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(
|
|
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 =>
|
|
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);
|