hive-stream 3.0.0 → 3.0.2

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 (107) hide show
  1. package/AGENTS.md +35 -0
  2. package/DOCUMENTATION.md +396 -0
  3. package/README.md +160 -39
  4. package/dist/actions.d.ts +3 -3
  5. package/dist/actions.js +7 -7
  6. package/dist/actions.js.map +1 -1
  7. package/dist/adapters/base.adapter.d.ts +19 -1
  8. package/dist/adapters/base.adapter.js +16 -0
  9. package/dist/adapters/base.adapter.js.map +1 -1
  10. package/dist/adapters/mongodb.adapter.d.ts +5 -11
  11. package/dist/adapters/mongodb.adapter.js +10 -10
  12. package/dist/adapters/mongodb.adapter.js.map +1 -1
  13. package/dist/adapters/postgresql.adapter.d.ts +17 -0
  14. package/dist/adapters/postgresql.adapter.js +99 -8
  15. package/dist/adapters/postgresql.adapter.js.map +1 -1
  16. package/dist/adapters/sqlite.adapter.d.ts +17 -0
  17. package/dist/adapters/sqlite.adapter.js +99 -8
  18. package/dist/adapters/sqlite.adapter.js.map +1 -1
  19. package/dist/api.js +86 -0
  20. package/dist/api.js.map +1 -1
  21. package/dist/config.d.ts +26 -0
  22. package/dist/config.js +76 -4
  23. package/dist/config.js.map +1 -1
  24. package/dist/contracts/coinflip.contract.d.ts +8 -26
  25. package/dist/contracts/coinflip.contract.js +123 -144
  26. package/dist/contracts/coinflip.contract.js.map +1 -1
  27. package/dist/contracts/contract.d.ts +3 -0
  28. package/dist/contracts/contract.js +26 -0
  29. package/dist/contracts/contract.js.map +1 -0
  30. package/dist/contracts/dice.contract.d.ts +9 -36
  31. package/dist/contracts/dice.contract.js +135 -200
  32. package/dist/contracts/dice.contract.js.map +1 -1
  33. package/dist/contracts/exchange.contract.d.ts +11 -0
  34. package/dist/contracts/exchange.contract.js +492 -0
  35. package/dist/contracts/exchange.contract.js.map +1 -0
  36. package/dist/contracts/lotto.contract.d.ts +15 -19
  37. package/dist/contracts/lotto.contract.js +154 -162
  38. package/dist/contracts/lotto.contract.js.map +1 -1
  39. package/dist/contracts/nft.contract.d.ts +4 -0
  40. package/dist/contracts/nft.contract.js +65 -0
  41. package/dist/contracts/nft.contract.js.map +1 -1
  42. package/dist/contracts/poll.contract.d.ts +4 -0
  43. package/dist/contracts/poll.contract.js +105 -0
  44. package/dist/contracts/poll.contract.js.map +1 -0
  45. package/dist/contracts/rps.contract.d.ts +9 -0
  46. package/dist/contracts/rps.contract.js +217 -0
  47. package/dist/contracts/rps.contract.js.map +1 -0
  48. package/dist/contracts/tipjar.contract.d.ts +4 -0
  49. package/dist/contracts/tipjar.contract.js +60 -0
  50. package/dist/contracts/tipjar.contract.js.map +1 -0
  51. package/dist/contracts/token.contract.d.ts +3 -17
  52. package/dist/contracts/token.contract.js +128 -80
  53. package/dist/contracts/token.contract.js.map +1 -1
  54. package/dist/exchanges/coingecko.d.ts +7 -1
  55. package/dist/exchanges/coingecko.js +38 -21
  56. package/dist/exchanges/coingecko.js.map +1 -1
  57. package/dist/exchanges/exchange.d.ts +15 -8
  58. package/dist/exchanges/exchange.js +65 -11
  59. package/dist/exchanges/exchange.js.map +1 -1
  60. package/dist/hive-rates.d.ts +29 -4
  61. package/dist/hive-rates.js +179 -92
  62. package/dist/hive-rates.js.map +1 -1
  63. package/dist/index.d.ts +11 -3
  64. package/dist/index.js +19 -4
  65. package/dist/index.js.map +1 -1
  66. package/dist/metadata.d.ts +63 -0
  67. package/dist/metadata.js +407 -0
  68. package/dist/metadata.js.map +1 -0
  69. package/dist/streamer.d.ts +104 -11
  70. package/dist/streamer.js +415 -143
  71. package/dist/streamer.js.map +1 -1
  72. package/dist/test.js +11 -12
  73. package/dist/test.js.map +1 -1
  74. package/dist/types/hive-stream.d.ts +85 -14
  75. package/dist/types/rates.d.ts +47 -0
  76. package/dist/types/rates.js +29 -0
  77. package/dist/types/rates.js.map +1 -0
  78. package/dist/utils.d.ts +318 -11
  79. package/dist/utils.js +804 -115
  80. package/dist/utils.js.map +1 -1
  81. package/examples/contracts/README.md +8 -0
  82. package/examples/contracts/exchange.ts +38 -0
  83. package/examples/contracts/poll.ts +21 -0
  84. package/examples/contracts/rps.ts +19 -0
  85. package/examples/contracts/tipjar.ts +19 -0
  86. package/package.json +20 -19
  87. package/test-contract-block.md +3 -3
  88. package/tests/actions.spec.ts +7 -7
  89. package/tests/adapters/actions-persistence.spec.ts +4 -4
  90. package/tests/adapters/sqlite.adapter.spec.ts +2 -2
  91. package/tests/config-input.spec.ts +90 -0
  92. package/tests/contracts/coinflip.contract.spec.ts +26 -154
  93. package/tests/contracts/dice.contract.spec.ts +24 -140
  94. package/tests/contracts/exchange.contract.spec.ts +84 -0
  95. package/tests/contracts/lotto.contract.spec.ts +30 -295
  96. package/tests/contracts/token.contract.spec.ts +72 -316
  97. package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
  98. package/tests/exchanges/exchange.base.spec.ts +246 -0
  99. package/tests/helpers/mock-fetch.ts +165 -0
  100. package/tests/hive-chain-features.spec.ts +319 -0
  101. package/tests/hive-rates.spec.ts +443 -0
  102. package/tests/integration/hive-rates.integration.spec.ts +35 -0
  103. package/tests/metadata.spec.ts +63 -0
  104. package/tests/streamer-actions.spec.ts +29 -18
  105. package/tests/streamer.spec.ts +142 -49
  106. package/tests/types/rates.spec.ts +216 -0
  107. package/tests/utils.spec.ts +27 -6
package/dist/utils.js CHANGED
@@ -10,34 +10,119 @@ const config_1 = require("./config");
10
10
  const seedrandom_1 = __importDefault(require("seedrandom"));
11
11
  const MAX_PAYLOAD_SIZE = 2000;
12
12
  const MAX_ACCOUNTS_CHECK = 999;
13
+ /**
14
+ * Utility functions for Hive blockchain operations and general helpers
15
+ */
13
16
  exports.Utils = {
14
- // https://flaviocopes.com/javascript-sleep/
17
+ /**
18
+ * Pauses execution for the specified number of milliseconds
19
+ * @param milliseconds - The number of milliseconds to sleep
20
+ * @returns Promise that resolves after the specified time
21
+ */
15
22
  sleep(milliseconds) {
23
+ if (milliseconds < 0) {
24
+ throw new Error('Sleep duration cannot be negative');
25
+ }
16
26
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
17
27
  },
18
- // Fisher Yates shuffle
28
+ normalizePrivateKeys(keys) {
29
+ const input = Array.isArray(keys) ? keys : [keys];
30
+ if (input.length === 0) {
31
+ throw new Error('At least one private key is required');
32
+ }
33
+ return input.map((key) => {
34
+ if (key instanceof dhive_1.PrivateKey) {
35
+ return key;
36
+ }
37
+ if (typeof key === 'string' && key.trim().length > 0) {
38
+ return dhive_1.PrivateKey.fromString(key.trim());
39
+ }
40
+ throw new Error('Invalid private key input');
41
+ });
42
+ },
43
+ toHiveTimestamp(value) {
44
+ const date = value instanceof Date ? value : new Date(value);
45
+ if (isNaN(date.getTime())) {
46
+ throw new Error('Invalid date supplied for Hive operation');
47
+ }
48
+ return date.toISOString().replace(/\.\d{3}Z$/, '');
49
+ },
50
+ normalizeJsonMeta(meta) {
51
+ if (meta === undefined || meta === null) {
52
+ return '{}';
53
+ }
54
+ if (typeof meta === 'string') {
55
+ return meta;
56
+ }
57
+ return JSON.stringify(meta);
58
+ },
59
+ /**
60
+ * Shuffles an array in place using the Fisher-Yates algorithm
61
+ * @param array - The array to shuffle (modified in place)
62
+ * @returns The shuffled array
63
+ */
19
64
  shuffle(array) {
65
+ if (!Array.isArray(array)) {
66
+ throw new Error('Input must be an array');
67
+ }
20
68
  let currentIndex = array.length;
21
69
  let temporaryValue;
22
70
  let randomIndex;
23
- while (0 !== currentIndex) {
24
- // Pick a remaining element...
71
+ while (currentIndex !== 0) {
72
+ // Pick a remaining element
25
73
  randomIndex = Math.floor(Math.random() * currentIndex);
26
74
  currentIndex -= 1;
75
+ // Swap with the current element
27
76
  temporaryValue = array[currentIndex];
28
77
  array[currentIndex] = array[randomIndex];
29
78
  array[randomIndex] = temporaryValue;
30
79
  }
31
80
  return array;
32
81
  },
82
+ /**
83
+ * Rounds a number to the specified precision
84
+ * @param value - The number to round
85
+ * @param precision - The number of decimal places
86
+ * @returns The rounded number
87
+ */
33
88
  roundPrecision(value, precision) {
34
- const NUMBER_SIGN = value >= 0 ? 1 : -1;
35
- return parseFloat((Math.round((value * Math.pow(10, precision)) + (NUMBER_SIGN * 0.0001)) / Math.pow(10, precision)).toFixed(precision));
89
+ if (typeof value !== 'number' || isNaN(value)) {
90
+ return NaN;
91
+ }
92
+ if (typeof precision !== 'number' || precision < 0) {
93
+ throw new Error('Precision must be a non-negative number');
94
+ }
95
+ const numberSign = value >= 0 ? 1 : -1;
96
+ const factor = Math.pow(10, precision);
97
+ return parseFloat((Math.round((value * factor) + (numberSign * 0.0001)) / factor).toFixed(precision));
36
98
  },
99
+ /**
100
+ * Generates a random number within the specified range (inclusive)
101
+ * @param min - The minimum value (default: 0)
102
+ * @param max - The maximum value (default: 2000)
103
+ * @returns A random number between min and max, or NaN if inputs are invalid
104
+ */
37
105
  randomRange(min = 0, max = 2000) {
38
- return (!isNaN(min) && !isNaN(max) ? Math.floor(Math.random() * (max - min + 1)) + min : NaN);
106
+ if (isNaN(min) || isNaN(max)) {
107
+ return NaN;
108
+ }
109
+ if (min > max) {
110
+ throw new Error('Minimum value cannot be greater than maximum value');
111
+ }
112
+ return Math.floor(Math.random() * (max - min + 1)) + min;
39
113
  },
114
+ /**
115
+ * Generates a random alphanumeric string of the specified length
116
+ * @param length - The desired length of the string (default: 12)
117
+ * @returns A random string containing numbers and letters
118
+ */
40
119
  randomString(length = 12) {
120
+ if (length < 0) {
121
+ throw new Error('Length cannot be negative');
122
+ }
123
+ if (length === 0) {
124
+ return '';
125
+ }
41
126
  let str = '';
42
127
  const characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
43
128
  const max = characters.length - 1;
@@ -46,23 +131,51 @@ exports.Utils = {
46
131
  }
47
132
  return str;
48
133
  },
134
+ /**
135
+ * Converts a Hive amount from one currency to another using current exchange rates
136
+ * @param amount - The amount to convert
137
+ * @param fiatSymbol - The source currency symbol
138
+ * @param hiveSymbol - The target Hive currency symbol
139
+ * @returns The converted amount rounded to 3 decimal places
140
+ */
49
141
  async convertHiveAmount(amount, fiatSymbol, hiveSymbol) {
142
+ if (typeof amount !== 'number' || amount < 0) {
143
+ throw new Error('Amount must be a non-negative number');
144
+ }
145
+ if (!fiatSymbol || !hiveSymbol) {
146
+ throw new Error('Currency symbols cannot be empty');
147
+ }
50
148
  if (fiatSymbol === hiveSymbol) {
51
149
  return amount;
52
150
  }
53
- const rates = new hive_rates_1.HiveRates();
54
- await rates.fetchRates();
55
- const rate = rates.fiatToHiveRate(fiatSymbol, hiveSymbol);
56
- const total = amount / rate;
57
- return rate > 0 ? exports.Utils.roundPrecision(total, 3) : 0;
151
+ try {
152
+ const rates = new hive_rates_1.HiveRates();
153
+ await rates.fetchRates();
154
+ const rate = rates.fiatToHiveRate(fiatSymbol, hiveSymbol);
155
+ if (rate <= 0) {
156
+ return 0;
157
+ }
158
+ const total = amount / rate;
159
+ return exports.Utils.roundPrecision(total, 3);
160
+ }
161
+ catch (error) {
162
+ if (config_1.Config.DEBUG_MODE) {
163
+ console.error('[Utils] Failed to convert Hive amount:', error);
164
+ }
165
+ return 0;
166
+ }
58
167
  },
168
+ /**
169
+ * Safely parses a JSON string
170
+ * @param str - The string to parse
171
+ * @returns The parsed object or null if parsing fails
172
+ */
59
173
  jsonParse(str) {
60
174
  if (!str || typeof str !== 'string') {
61
175
  return null;
62
176
  }
63
- let obj = null;
64
177
  try {
65
- obj = JSON.parse(str);
178
+ return JSON.parse(str);
66
179
  }
67
180
  catch (e) {
68
181
  if (config_1.Config.DEBUG_MODE) {
@@ -72,64 +185,389 @@ exports.Utils = {
72
185
  stack: error.stack
73
186
  });
74
187
  }
188
+ return null;
75
189
  }
76
- return obj;
77
190
  },
191
+ /**
192
+ * Retrieves a transaction from a specific block
193
+ * @param client - The Hive client instance
194
+ * @param blockNumber - The block number containing the transaction
195
+ * @param transactionId - The transaction ID to retrieve
196
+ * @returns The signed transaction
197
+ * @throws Error if transaction is not found in the block
198
+ */
78
199
  async getTransaction(client, blockNumber, transactionId) {
79
- const block = await client.database.getBlock(blockNumber);
80
- const exists = block.transaction_ids.includes(transactionId);
81
- const index = block.transaction_ids.indexOf(transactionId);
82
- if (!exists) {
83
- throw new Error(`Unable to find transaction ${transactionId} in block ${blockNumber}`);
200
+ if (!client) {
201
+ throw new Error('Client instance is required');
202
+ }
203
+ if (!transactionId) {
204
+ throw new Error('Transaction ID is required');
205
+ }
206
+ try {
207
+ const block = await client.database.getBlock(blockNumber);
208
+ if (!block || !block.transaction_ids) {
209
+ throw new Error(`Block ${blockNumber} not found or invalid`);
210
+ }
211
+ const index = block.transaction_ids.indexOf(transactionId);
212
+ if (index === -1) {
213
+ throw new Error(`Unable to find transaction ${transactionId} in block ${blockNumber}`);
214
+ }
215
+ return block.transactions[index];
216
+ }
217
+ catch (error) {
218
+ if (config_1.Config.DEBUG_MODE) {
219
+ console.error('[Utils] Failed to get transaction:', error);
220
+ }
221
+ throw error;
84
222
  }
85
- return block.transactions[index];
86
223
  },
224
+ /**
225
+ * Verifies that a transfer transaction matches the expected parameters
226
+ * @param transaction - The signed transaction to verify
227
+ * @param from - Expected sender account
228
+ * @param to - Expected recipient account
229
+ * @param amount - Expected transfer amount
230
+ * @returns True if the transfer matches all parameters
231
+ */
87
232
  async verifyTransfer(transaction, from, to, amount) {
88
- const operation = transaction.operations[0][1];
89
- return (operation.from === from && operation.to === to && operation.amount === amount);
233
+ if (!transaction || !transaction.operations || transaction.operations.length === 0) {
234
+ return false;
235
+ }
236
+ try {
237
+ const operation = transaction.operations[0][1];
238
+ return (operation.from === from && operation.to === to && operation.amount === amount);
239
+ }
240
+ catch (error) {
241
+ if (config_1.Config.DEBUG_MODE) {
242
+ console.error('[Utils] Failed to verify transfer:', error);
243
+ }
244
+ return false;
245
+ }
90
246
  },
247
+ /**
248
+ * Transfers Hive tokens between accounts
249
+ * @param client - The Hive client instance
250
+ * @param config - Configuration containing the active key
251
+ * @param from - Sender account name
252
+ * @param to - Recipient account name
253
+ * @param amount - Amount to transfer (will be formatted to 3 decimal places)
254
+ * @param symbol - Token symbol (e.g., 'HIVE', 'HBD')
255
+ * @param memo - Optional memo for the transfer
256
+ * @returns Promise resolving to the broadcast result
257
+ */
91
258
  transferHiveTokens(client, config, from, to, amount, symbol, memo = '') {
259
+ if (!client || !config.ACTIVE_KEY || !from || !to || !amount || !symbol) {
260
+ throw new Error('Missing required parameters for Hive token transfer');
261
+ }
92
262
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
93
- return client.broadcast.transfer({ from, to, amount: `${parseFloat(amount).toFixed(3)} ${symbol}`, memo }, key);
263
+ const formattedAmount = `${parseFloat(amount).toFixed(3)} ${symbol}`;
264
+ return client.broadcast.transfer({ from, to, amount: formattedAmount, memo }, key);
265
+ },
266
+ /**
267
+ * Broadcasts one or more Hive operations signed by one or multiple private keys.
268
+ */
269
+ broadcastOperations(client, operations, signingKeys) {
270
+ if (!client || !Array.isArray(operations) || operations.length === 0) {
271
+ throw new Error('Client and at least one operation are required');
272
+ }
273
+ const keys = this.normalizePrivateKeys(signingKeys);
274
+ return client.broadcast.sendOperations(operations, keys.length === 1 ? keys[0] : keys);
275
+ },
276
+ /**
277
+ * Alias for explicitly broadcasting with multiple signatures.
278
+ */
279
+ broadcastMultiSigOperations(client, operations, signingKeys) {
280
+ if (!Array.isArray(signingKeys) || signingKeys.length < 2) {
281
+ throw new Error('Multi-sign broadcast requires at least two keys');
282
+ }
283
+ return this.broadcastOperations(client, operations, signingKeys);
284
+ },
285
+ /**
286
+ * Builds a Hive authority object for account_update/account_update2 operations.
287
+ */
288
+ createAuthority(keyAuths = [], accountAuths = [], weightThreshold = 1) {
289
+ if (weightThreshold <= 0) {
290
+ throw new Error('Authority weight threshold must be greater than zero');
291
+ }
292
+ if (!Array.isArray(keyAuths) || !Array.isArray(accountAuths)) {
293
+ throw new Error('Authority auths must be arrays');
294
+ }
295
+ return {
296
+ weight_threshold: weightThreshold,
297
+ account_auths: accountAuths,
298
+ key_auths: keyAuths
299
+ };
300
+ },
301
+ /**
302
+ * Updates account authorities, enabling native Hive multisig thresholds.
303
+ */
304
+ async updateAccountAuthorities(client, config, account, authorityUpdate, signingKeys) {
305
+ if (!client || !account || !authorityUpdate) {
306
+ throw new Error('Client, account, and authority update data are required');
307
+ }
308
+ const keys = signingKeys || config.ACTIVE_KEY;
309
+ if (!keys) {
310
+ throw new Error('Active key or explicit signing keys are required for account authority updates');
311
+ }
312
+ const accounts = await client.database.getAccounts([account]);
313
+ const existingAccount = Array.isArray(accounts) ? accounts[0] : null;
314
+ if (!existingAccount) {
315
+ throw new Error(`Unable to load account '${account}' for authority update`);
316
+ }
317
+ const memoKey = authorityUpdate.memo_key || existingAccount.memo_key;
318
+ const jsonMetadata = authorityUpdate.json_metadata !== undefined
319
+ ? authorityUpdate.json_metadata
320
+ : (existingAccount.json_metadata || '');
321
+ const postingJsonMetadata = authorityUpdate.posting_json_metadata !== undefined
322
+ ? authorityUpdate.posting_json_metadata
323
+ : (existingAccount.posting_json_metadata || '');
324
+ const useUpdate2 = Boolean(authorityUpdate.useAccountUpdate2 || authorityUpdate.posting_json_metadata !== undefined);
325
+ if (useUpdate2) {
326
+ const operation = ['account_update2', {
327
+ account,
328
+ owner: authorityUpdate.owner,
329
+ active: authorityUpdate.active,
330
+ posting: authorityUpdate.posting,
331
+ memo_key: memoKey,
332
+ json_metadata: jsonMetadata,
333
+ posting_json_metadata: postingJsonMetadata,
334
+ extensions: []
335
+ }];
336
+ return this.broadcastOperations(client, [operation], keys);
337
+ }
338
+ const operation = ['account_update', {
339
+ account,
340
+ owner: authorityUpdate.owner,
341
+ active: authorityUpdate.active,
342
+ posting: authorityUpdate.posting,
343
+ memo_key: memoKey,
344
+ json_metadata: jsonMetadata
345
+ }];
346
+ return this.broadcastOperations(client, [operation], keys);
347
+ },
348
+ /**
349
+ * Creates an escrow transfer on Hive.
350
+ */
351
+ escrowTransfer(client, config, options, signingKeys) {
352
+ if (!client || !options?.from || !options?.to || !options?.agent) {
353
+ throw new Error('Escrow transfer requires client, from, to, and agent');
354
+ }
355
+ if (typeof options.escrow_id !== 'number') {
356
+ throw new Error('Escrow transfer requires a numeric escrow_id');
357
+ }
358
+ if (!options.fee) {
359
+ throw new Error('Escrow transfer requires an escrow fee');
360
+ }
361
+ const keys = signingKeys || config.ACTIVE_KEY;
362
+ if (!keys) {
363
+ throw new Error('Active key or explicit signing keys are required for escrow transfer');
364
+ }
365
+ const operation = ['escrow_transfer', {
366
+ from: options.from,
367
+ to: options.to,
368
+ agent: options.agent,
369
+ escrow_id: options.escrow_id,
370
+ hive_amount: options.hive_amount || '0.000 HIVE',
371
+ hbd_amount: options.hbd_amount || '0.000 HBD',
372
+ fee: options.fee,
373
+ ratification_deadline: this.toHiveTimestamp(options.ratification_deadline),
374
+ escrow_expiration: this.toHiveTimestamp(options.escrow_expiration),
375
+ json_meta: this.normalizeJsonMeta(options.json_meta)
376
+ }];
377
+ return this.broadcastOperations(client, [operation], keys);
378
+ },
379
+ /**
380
+ * Approves or rejects an escrow transfer.
381
+ */
382
+ escrowApprove(client, config, options, signingKeys) {
383
+ if (!client || !options?.from || !options?.to || !options?.agent || !options?.who) {
384
+ throw new Error('Escrow approve requires client, from, to, agent, and who');
385
+ }
386
+ const keys = signingKeys || config.ACTIVE_KEY;
387
+ if (!keys) {
388
+ throw new Error('Active key or explicit signing keys are required for escrow approval');
389
+ }
390
+ const operation = ['escrow_approve', {
391
+ from: options.from,
392
+ to: options.to,
393
+ agent: options.agent,
394
+ who: options.who,
395
+ escrow_id: options.escrow_id,
396
+ approve: options.approve
397
+ }];
398
+ return this.broadcastOperations(client, [operation], keys);
399
+ },
400
+ /**
401
+ * Opens an escrow dispute.
402
+ */
403
+ escrowDispute(client, config, options, signingKeys) {
404
+ if (!client || !options?.from || !options?.to || !options?.agent || !options?.who) {
405
+ throw new Error('Escrow dispute requires client, from, to, agent, and who');
406
+ }
407
+ const keys = signingKeys || config.ACTIVE_KEY;
408
+ if (!keys) {
409
+ throw new Error('Active key or explicit signing keys are required for escrow dispute');
410
+ }
411
+ const operation = ['escrow_dispute', {
412
+ from: options.from,
413
+ to: options.to,
414
+ agent: options.agent,
415
+ who: options.who,
416
+ escrow_id: options.escrow_id
417
+ }];
418
+ return this.broadcastOperations(client, [operation], keys);
419
+ },
420
+ /**
421
+ * Releases escrow funds.
422
+ */
423
+ escrowRelease(client, config, options, signingKeys) {
424
+ if (!client || !options?.from || !options?.to || !options?.agent || !options?.who || !options?.receiver) {
425
+ throw new Error('Escrow release requires client, from, to, agent, who, and receiver');
426
+ }
427
+ const keys = signingKeys || config.ACTIVE_KEY;
428
+ if (!keys) {
429
+ throw new Error('Active key or explicit signing keys are required for escrow release');
430
+ }
431
+ const operation = ['escrow_release', {
432
+ from: options.from,
433
+ to: options.to,
434
+ agent: options.agent,
435
+ who: options.who,
436
+ receiver: options.receiver,
437
+ escrow_id: options.escrow_id,
438
+ hive_amount: options.hive_amount || '0.000 HIVE',
439
+ hbd_amount: options.hbd_amount || '0.000 HBD'
440
+ }];
441
+ return this.broadcastOperations(client, [operation], keys);
94
442
  },
443
+ /**
444
+ * Transfers Hive tokens to multiple accounts with a delay between transfers
445
+ * @param client - The Hive client instance
446
+ * @param config - Configuration containing the active key
447
+ * @param from - Sender account name
448
+ * @param accounts - Array of recipient account names
449
+ * @param amount - Amount to transfer to each account
450
+ * @param symbol - Token symbol
451
+ * @param memo - Memo for all transfers
452
+ * @returns True if all transfers completed successfully
453
+ */
95
454
  async transferHiveTokensMultiple(client, config, from, accounts, amount = '0', symbol, memo) {
455
+ if (!Array.isArray(accounts) || accounts.length === 0) {
456
+ throw new Error('Accounts array cannot be empty');
457
+ }
96
458
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
97
459
  let completed = 0;
98
460
  for (const user of accounts) {
99
461
  const to = user.replace('@', '');
100
- await client.broadcast.transfer({ from, to, amount: `${parseFloat(amount).toFixed(3)} ${symbol}`, memo }, key);
101
- completed++;
102
- await this.sleep(3000);
103
- }
104
- if (completed === accounts.length) {
105
- return true;
462
+ const formattedAmount = `${parseFloat(amount).toFixed(3)} ${symbol}`;
463
+ try {
464
+ await client.broadcast.transfer({ from, to, amount: formattedAmount, memo }, key);
465
+ completed++;
466
+ // Add delay between transfers to avoid rate limiting
467
+ if (completed < accounts.length) {
468
+ await this.sleep(3000);
469
+ }
470
+ }
471
+ catch (error) {
472
+ if (config_1.Config.DEBUG_MODE) {
473
+ console.error(`[Utils] Failed to transfer to ${to}:`, error);
474
+ }
475
+ // Continue with other transfers even if one fails
476
+ }
106
477
  }
478
+ return completed === accounts.length;
107
479
  },
480
+ /**
481
+ * Retrieves account transfer history
482
+ * @param client - The Hive client instance
483
+ * @param account - Account name to get transfers for
484
+ * @param from - Starting index (default: -1 for most recent)
485
+ * @param max - Maximum number of transfers to retrieve
486
+ * @returns Array of transfer operations with date information
487
+ */
108
488
  async getAccountTransfers(client, account, from = -1, max = 100) {
109
- const history = await client.call('condenser_api', 'get_account_history', [account, from, max]);
110
- const transfers = history.filter(tx => tx[1].op[0] === 'transfer');
111
- const actualTransfers = transfers.reduce((arr, tx) => {
112
- const transaction = tx[1].op[1];
113
- const date = new Date(`${tx[1].timestamp}Z`);
114
- transaction.date = date;
115
- arr.push(transaction);
116
- return arr;
117
- }, []);
118
- return actualTransfers;
489
+ if (!account) {
490
+ throw new Error('Account name is required');
491
+ }
492
+ try {
493
+ const history = await client.call('condenser_api', 'get_account_history', [account, from, max]);
494
+ if (!Array.isArray(history)) {
495
+ return [];
496
+ }
497
+ const transfers = history.filter(tx => tx[1]?.op?.[0] === 'transfer');
498
+ return transfers.reduce((arr, tx) => {
499
+ try {
500
+ const transaction = { ...tx[1].op[1] };
501
+ const date = new Date(`${tx[1].timestamp}Z`);
502
+ transaction.date = date;
503
+ arr.push(transaction);
504
+ }
505
+ catch (error) {
506
+ if (config_1.Config.DEBUG_MODE) {
507
+ console.warn('[Utils] Failed to process transfer:', error);
508
+ }
509
+ }
510
+ return arr;
511
+ }, []);
512
+ }
513
+ catch (error) {
514
+ if (config_1.Config.DEBUG_MODE) {
515
+ console.error('[Utils] Failed to get account transfers:', error);
516
+ }
517
+ return [];
518
+ }
119
519
  },
520
+ /**
521
+ * Retrieves custom JSON operations from the hiveapi account
522
+ * @param client - The Hive client instance
523
+ * @param from - Starting index (default: -1)
524
+ * @param limit - Maximum number of operations to retrieve
525
+ * @returns Array of custom JSON operations with date information
526
+ */
120
527
  async getApiJson(client, from = -1, limit = 500) {
121
- const history = await client.call('condenser_api', 'get_account_history', ['hiveapi', from, limit]);
122
- const customJson = history.filter(tx => tx[1].op[0] === 'custom_json');
123
- const actualJson = customJson.reduce((arr, tx) => {
124
- const transaction = tx[1].op[1];
125
- const date = new Date(`${tx[1].timestamp}Z`);
126
- transaction.date = date;
127
- arr.push(transaction);
128
- return arr;
129
- }, []);
130
- return actualJson;
528
+ try {
529
+ const history = await client.call('condenser_api', 'get_account_history', ['hiveapi', from, limit]);
530
+ if (!Array.isArray(history)) {
531
+ return [];
532
+ }
533
+ const customJson = history.filter(tx => tx[1]?.op?.[0] === 'custom_json');
534
+ return customJson.reduce((arr, tx) => {
535
+ try {
536
+ const transaction = { ...tx[1].op[1] };
537
+ const date = new Date(`${tx[1].timestamp}Z`);
538
+ transaction.date = date;
539
+ arr.push(transaction);
540
+ }
541
+ catch (error) {
542
+ if (config_1.Config.DEBUG_MODE) {
543
+ console.warn('[Utils] Failed to process custom JSON:', error);
544
+ }
545
+ }
546
+ return arr;
547
+ }, []);
548
+ }
549
+ catch (error) {
550
+ if (config_1.Config.DEBUG_MODE) {
551
+ console.error('[Utils] Failed to get API JSON:', error);
552
+ }
553
+ return [];
554
+ }
131
555
  },
556
+ /**
557
+ * Transfers Hive Engine tokens between accounts
558
+ * @param client - The Hive client instance
559
+ * @param config - Configuration containing the active key and Hive Engine ID
560
+ * @param from - Sender account name
561
+ * @param to - Recipient account name
562
+ * @param quantity - Token quantity to transfer
563
+ * @param symbol - Token symbol (will be converted to uppercase)
564
+ * @param memo - Optional memo for the transfer
565
+ * @returns Promise resolving to the broadcast result
566
+ */
132
567
  transferHiveEngineTokens(client, config, from, to, quantity, symbol, memo = '') {
568
+ if (!client || !config.ACTIVE_KEY || !from || !to || !quantity || !symbol) {
569
+ throw new Error('Missing required parameters for Hive Engine token transfer');
570
+ }
133
571
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
134
572
  const json = {
135
573
  contractName: 'tokens',
@@ -141,48 +579,100 @@ exports.Utils = {
141
579
  memo,
142
580
  }
143
581
  };
144
- return client.broadcast.json({ required_auths: [from], required_posting_auths: [], id: config.HIVE_ENGINE_ID, json: JSON.stringify(json) }, key);
582
+ return client.broadcast.json({
583
+ required_auths: [from],
584
+ required_posting_auths: [],
585
+ id: config.HIVE_ENGINE_ID,
586
+ json: JSON.stringify(json)
587
+ }, key);
145
588
  },
589
+ /**
590
+ * Transfers Hive Engine tokens to multiple accounts in batches
591
+ * @param client - The Hive client instance
592
+ * @param config - Configuration containing the active key and Hive Engine ID
593
+ * @param from - Sender account name
594
+ * @param accounts - Array of account objects with 'account' and optional 'amount' properties
595
+ * @param symbol - Token symbol
596
+ * @param memo - Memo for all transfers
597
+ * @param amount - Default amount if not specified per account
598
+ * @returns Promise that resolves when all batches are completed
599
+ */
146
600
  async transferHiveEngineTokensMultiple(client, config, from, accounts, symbol, memo, amount = '0') {
601
+ if (!Array.isArray(accounts) || accounts.length === 0) {
602
+ throw new Error('Accounts array cannot be empty');
603
+ }
147
604
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
148
605
  const payloads = [[]];
149
606
  let completed = 0;
607
+ // Build payloads in batches to respect size limits
150
608
  for (const user of accounts) {
151
- const account = user.account.replace('@', '');
152
- const quantity = user.amount ? parseFloat(user.amount.replace(',', '.')).toString() : parseFloat(amount).toString();
153
- // 0 means no quantity supplied (either in accounts or default)
154
- if (parseFloat(quantity) > 0) {
155
- const json = {
156
- contractName: 'tokens',
157
- contractAction: 'transfer',
158
- contractPayload: {
159
- symbol: symbol.toUpperCase(),
160
- to: account,
161
- quantity,
162
- memo,
163
- },
164
- };
165
- const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
166
- const payloadSize = JSON.stringify(json).length;
167
- if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
168
- payloads.push([json]);
169
- }
170
- else {
171
- payloads[payloads.length - 1].push(json);
172
- }
609
+ const account = user.account?.replace('@', '') || '';
610
+ const quantity = user.amount
611
+ ? parseFloat(user.amount.replace(',', '.')).toString()
612
+ : parseFloat(amount).toString();
613
+ // Skip if no valid quantity
614
+ if (parseFloat(quantity) <= 0 || !account) {
615
+ continue;
616
+ }
617
+ const json = {
618
+ contractName: 'tokens',
619
+ contractAction: 'transfer',
620
+ contractPayload: {
621
+ symbol: symbol.toUpperCase(),
622
+ to: account,
623
+ quantity,
624
+ memo,
625
+ },
626
+ };
627
+ const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
628
+ const payloadSize = JSON.stringify(json).length;
629
+ if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
630
+ payloads.push([json]);
631
+ }
632
+ else {
633
+ payloads[payloads.length - 1].push(json);
173
634
  }
174
635
  }
636
+ // Execute batches with delays
175
637
  for (const payload of payloads) {
176
- const requiredAuths = [from];
177
- const requiredPostingAuths = [];
178
- await client.broadcast.json({ required_auths: requiredAuths, required_posting_auths: requiredPostingAuths, id: config.HIVE_ENGINE_ID, json: JSON.stringify(payload) }, key);
179
- completed++;
180
- if (completed !== (payloads.length) && completed !== 0) {
181
- await this.sleep(3000);
638
+ if (payload.length === 0)
639
+ continue;
640
+ try {
641
+ await client.broadcast.json({
642
+ required_auths: [from],
643
+ required_posting_auths: [],
644
+ id: config.HIVE_ENGINE_ID,
645
+ json: JSON.stringify(payload)
646
+ }, key);
647
+ completed++;
648
+ // Add delay between batches
649
+ if (completed < payloads.length) {
650
+ await this.sleep(3000);
651
+ }
652
+ }
653
+ catch (error) {
654
+ if (config_1.Config.DEBUG_MODE) {
655
+ console.error('[Utils] Failed to transfer Hive Engine tokens batch:', error);
656
+ }
657
+ throw error;
182
658
  }
183
659
  }
184
660
  },
661
+ /**
662
+ * Issues new Hive Engine tokens to an account
663
+ * @param client - The Hive client instance
664
+ * @param config - Configuration containing the active key and Hive Engine ID
665
+ * @param from - Issuer account name
666
+ * @param to - Recipient account name
667
+ * @param symbol - Token symbol
668
+ * @param quantity - Quantity of tokens to issue
669
+ * @param memo - Optional memo for the issuance
670
+ * @returns Promise resolving to the broadcast result
671
+ */
185
672
  issueHiveEngineTokens(client, config, from, to, symbol, quantity, memo = '') {
673
+ if (!client || !config.ACTIVE_KEY || !from || !to || !symbol || !quantity) {
674
+ throw new Error('Missing required parameters for Hive Engine token issuance');
675
+ }
186
676
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
187
677
  const json = {
188
678
  contractName: 'tokens',
@@ -194,79 +684,278 @@ exports.Utils = {
194
684
  memo,
195
685
  },
196
686
  };
197
- if (config.DEBUG_MODE) {
198
- console.log(`Issuing Hive Engine Token: `, json, JSON.stringify(json));
687
+ if (config_1.Config.DEBUG_MODE) {
688
+ console.log('[Utils] Issuing Hive Engine Token:', json);
199
689
  }
200
- return client.broadcast.json({ required_auths: [from], required_posting_auths: [], id: config.HIVE_ENGINE_ID, json: JSON.stringify(json) }, key);
690
+ return client.broadcast.json({
691
+ required_auths: [from],
692
+ required_posting_auths: [],
693
+ id: config.HIVE_ENGINE_ID,
694
+ json: JSON.stringify(json)
695
+ }, key);
201
696
  },
697
+ /**
698
+ * Issues Hive Engine tokens to multiple accounts in batches
699
+ * @param client - The Hive client instance
700
+ * @param config - Configuration containing the active key and Hive Engine ID
701
+ * @param from - Issuer account name
702
+ * @param accounts - Array of account objects with 'account' and optional 'amount' properties
703
+ * @param symbol - Token symbol
704
+ * @param memo - Memo for all issuances
705
+ * @param amount - Default amount if not specified per account
706
+ * @returns Promise that resolves when all batches are completed
707
+ */
202
708
  async issueHiveEngineTokensMultiple(client, config, from, accounts, symbol, memo, amount = '0') {
709
+ if (!Array.isArray(accounts) || accounts.length === 0) {
710
+ throw new Error('Accounts array cannot be empty');
711
+ }
203
712
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
204
713
  const payloads = [[]];
205
714
  let completed = 0;
715
+ // Build payloads in batches to respect size limits
206
716
  for (const user of accounts) {
207
- const to = user.account.replace('@', '');
208
- const quantity = user.amount ? parseFloat(user.amount.replace(',', '.')).toString() : parseFloat(amount).toString();
209
- // 0 means no quantity supplied (either in accounts or default)
210
- if (parseFloat(quantity) > 0) {
211
- const json = {
212
- contractName: 'tokens',
213
- contractAction: 'issue',
214
- contractPayload: {
215
- symbol: symbol.toUpperCase(),
216
- to,
217
- quantity,
218
- memo,
219
- },
220
- };
221
- const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
222
- const payloadSize = JSON.stringify(json).length;
223
- if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
224
- payloads.push([json]);
225
- }
226
- else {
227
- payloads[payloads.length - 1].push(json);
228
- }
717
+ const to = user.account?.replace('@', '') || '';
718
+ const quantity = user.amount
719
+ ? parseFloat(user.amount.replace(',', '.')).toString()
720
+ : parseFloat(amount).toString();
721
+ // Skip if no valid quantity
722
+ if (parseFloat(quantity) <= 0 || !to) {
723
+ continue;
724
+ }
725
+ const json = {
726
+ contractName: 'tokens',
727
+ contractAction: 'issue',
728
+ contractPayload: {
729
+ symbol: symbol.toUpperCase(),
730
+ to,
731
+ quantity,
732
+ memo,
733
+ },
734
+ };
735
+ const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
736
+ const payloadSize = JSON.stringify(json).length;
737
+ if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
738
+ payloads.push([json]);
739
+ }
740
+ else {
741
+ payloads[payloads.length - 1].push(json);
229
742
  }
230
743
  }
744
+ // Execute batches with delays
231
745
  for (const payload of payloads) {
232
- const requiredAuths = [from];
233
- const requiredPostingAuths = null;
234
- await client.broadcast.json({ required_auths: requiredAuths, required_posting_auths: requiredPostingAuths, id: config.HIVE_ENGINE_ID, json: JSON.stringify(payload) }, key);
235
- completed++;
236
- if (completed !== (payloads.length) && completed !== 0) {
237
- await this.sleep(3000);
746
+ if (payload.length === 0)
747
+ continue;
748
+ try {
749
+ await client.broadcast.json({
750
+ required_auths: [from],
751
+ required_posting_auths: [],
752
+ id: config.HIVE_ENGINE_ID,
753
+ json: JSON.stringify(payload)
754
+ }, key);
755
+ completed++;
756
+ // Add delay between batches
757
+ if (completed < payloads.length) {
758
+ await this.sleep(3000);
759
+ }
238
760
  }
761
+ catch (error) {
762
+ if (config_1.Config.DEBUG_MODE) {
763
+ console.error('[Utils] Failed to issue Hive Engine tokens batch:', error);
764
+ }
765
+ throw error;
766
+ }
767
+ }
768
+ },
769
+ /**
770
+ * Schedules a recurrent transfer on Hive.
771
+ */
772
+ recurrentTransfer(client, config, options, signingKeys) {
773
+ if (!client || !options?.from || !options?.to || !options?.amount) {
774
+ throw new Error('Recurrent transfer requires client, from, to, and amount');
775
+ }
776
+ if (!Number.isInteger(options.recurrence) || options.recurrence <= 0) {
777
+ throw new Error('Recurrent transfer requires a positive integer recurrence');
239
778
  }
779
+ if (!Number.isInteger(options.executions) || options.executions <= 0) {
780
+ throw new Error('Recurrent transfer requires a positive integer executions value');
781
+ }
782
+ const keys = signingKeys || config.ACTIVE_KEY;
783
+ if (!keys) {
784
+ throw new Error('Active key or explicit signing keys are required for recurrent transfer');
785
+ }
786
+ const operation = ['recurrent_transfer', {
787
+ from: options.from,
788
+ to: options.to,
789
+ amount: options.amount,
790
+ memo: options.memo || '',
791
+ recurrence: options.recurrence,
792
+ executions: options.executions,
793
+ extensions: []
794
+ }];
795
+ return this.broadcastOperations(client, [operation], keys);
240
796
  },
797
+ /**
798
+ * Creates a DHF proposal.
799
+ */
800
+ createProposal(client, config, options, signingKeys) {
801
+ if (!client || !options?.creator || !options?.receiver || !options?.daily_pay || !options?.subject || !options?.permlink) {
802
+ throw new Error('Create proposal requires creator, receiver, daily_pay, subject, and permlink');
803
+ }
804
+ const keys = signingKeys || config.ACTIVE_KEY;
805
+ if (!keys) {
806
+ throw new Error('Active key or explicit signing keys are required for proposal creation');
807
+ }
808
+ const operation = ['create_proposal', {
809
+ creator: options.creator,
810
+ receiver: options.receiver,
811
+ start_date: this.toHiveTimestamp(options.start_date),
812
+ end_date: this.toHiveTimestamp(options.end_date),
813
+ daily_pay: options.daily_pay,
814
+ subject: options.subject,
815
+ permlink: options.permlink,
816
+ extensions: []
817
+ }];
818
+ return this.broadcastOperations(client, [operation], keys);
819
+ },
820
+ /**
821
+ * Votes for/against one or more DHF proposals.
822
+ */
823
+ updateProposalVotes(client, config, options, signingKeys) {
824
+ if (!client || !options?.voter || !Array.isArray(options.proposal_ids) || options.proposal_ids.length === 0) {
825
+ throw new Error('Proposal votes require voter and proposal_ids');
826
+ }
827
+ const keys = signingKeys || config.ACTIVE_KEY;
828
+ if (!keys) {
829
+ throw new Error('Active key or explicit signing keys are required for proposal voting');
830
+ }
831
+ const operation = ['update_proposal_votes', {
832
+ voter: options.voter,
833
+ proposal_ids: options.proposal_ids,
834
+ approve: options.approve,
835
+ extensions: []
836
+ }];
837
+ return this.broadcastOperations(client, [operation], keys);
838
+ },
839
+ /**
840
+ * Removes one or more DHF proposals.
841
+ */
842
+ removeProposals(client, config, options, signingKeys) {
843
+ if (!client || !options?.proposal_owner || !Array.isArray(options.proposal_ids) || options.proposal_ids.length === 0) {
844
+ throw new Error('Remove proposals requires proposal_owner and proposal_ids');
845
+ }
846
+ const keys = signingKeys || config.ACTIVE_KEY;
847
+ if (!keys) {
848
+ throw new Error('Active key or explicit signing keys are required for proposal removal');
849
+ }
850
+ const operation = ['remove_proposal', {
851
+ proposal_owner: options.proposal_owner,
852
+ proposal_ids: options.proposal_ids,
853
+ extensions: []
854
+ }];
855
+ return this.broadcastOperations(client, [operation], keys);
856
+ },
857
+ /**
858
+ * Generates a deterministic random number based on blockchain data
859
+ * @param previousBlockId - The previous block ID
860
+ * @param blockId - The current block ID
861
+ * @param transactionId - The transaction ID
862
+ * @returns A deterministic random number between 1 and 100
863
+ */
241
864
  randomNumber(previousBlockId, blockId, transactionId) {
242
- const random = (0, seedrandom_1.default)(`${previousBlockId}${blockId}${transactionId}`).double();
243
- const randomRoll = Math.floor(random * 100) + 1;
244
- return randomRoll;
865
+ if (!previousBlockId || !blockId || !transactionId) {
866
+ throw new Error('All block and transaction IDs are required for deterministic random number generation');
867
+ }
868
+ const seed = `${previousBlockId}${blockId}${transactionId}`;
869
+ const random = (0, seedrandom_1.default)(seed).double();
870
+ return Math.floor(random * 100) + 1;
245
871
  },
872
+ /**
873
+ * Upvotes a post or comment
874
+ * @param client - The Hive client instance
875
+ * @param config - Configuration containing the posting key
876
+ * @param voter - The account name doing the voting
877
+ * @param votePercentage - Vote percentage as a string (default: '100.0')
878
+ * @param author - The author of the post/comment
879
+ * @param permlink - The permlink of the post/comment
880
+ * @returns Promise resolving to the broadcast result
881
+ * @throws Error if negative voting values are provided
882
+ */
246
883
  upvote(client, config, voter, votePercentage = '100.0', author, permlink) {
884
+ if (!client || !config.POSTING_KEY || !voter || !author || !permlink) {
885
+ throw new Error('Missing required parameters for upvote');
886
+ }
247
887
  const percentage = parseFloat(votePercentage);
248
- const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
249
888
  if (percentage < 0) {
250
889
  throw new Error('Negative voting values are for downvotes, not upvotes');
251
890
  }
891
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
252
892
  const weight = this.votingWeight(percentage);
253
893
  return client.broadcast.vote({ voter, author, permlink, weight }, key);
254
894
  },
895
+ /**
896
+ * Downvotes a post or comment
897
+ * @param client - The Hive client instance
898
+ * @param config - Configuration containing the posting key
899
+ * @param voter - The account name doing the voting
900
+ * @param votePercentage - Vote percentage as a string (default: '100.0')
901
+ * @param author - The author of the post/comment
902
+ * @param permlink - The permlink of the post/comment
903
+ * @returns Promise resolving to the broadcast result
904
+ */
255
905
  downvote(client, config, voter, votePercentage = '100.0', author, permlink) {
906
+ if (!client || !config.POSTING_KEY || !voter || !author || !permlink) {
907
+ throw new Error('Missing required parameters for downvote');
908
+ }
256
909
  const weight = this.votingWeight(parseFloat(votePercentage)) * -1;
257
910
  const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
258
911
  return client.broadcast.vote({ voter, author, permlink, weight }, key);
259
912
  },
913
+ /**
914
+ * Converts vote percentage to voting weight for Hive blockchain
915
+ * @param votePercentage - Vote percentage (0-100)
916
+ * @returns Voting weight (0-10000)
917
+ */
260
918
  votingWeight(votePercentage) {
919
+ if (typeof votePercentage !== 'number' || votePercentage < 0) {
920
+ throw new Error('Vote percentage must be a non-negative number');
921
+ }
261
922
  return Math.min(Math.floor(parseFloat(votePercentage.toFixed(2)) * 100), 10000);
262
923
  },
924
+ /**
925
+ * Executes an async function for each element in an array sequentially
926
+ * @param array - The array to iterate over
927
+ * @param callback - The async callback function to execute for each element
928
+ * @returns Promise that resolves when all iterations are complete
929
+ */
263
930
  async asyncForEach(array, callback) {
931
+ if (!Array.isArray(array)) {
932
+ throw new Error('First argument must be an array');
933
+ }
934
+ if (typeof callback !== 'function') {
935
+ throw new Error('Second argument must be a function');
936
+ }
264
937
  for (let index = 0; index < array.length; index++) {
265
938
  await callback(array[index], index, array);
266
939
  }
267
940
  },
941
+ /**
942
+ * Generates a Hivesigner transfer URL
943
+ * @param to - Recipient account name
944
+ * @param memo - Transfer memo
945
+ * @param amount - Transfer amount with currency symbol
946
+ * @param redirectUri - URI to redirect to after signing
947
+ * @returns The complete Hivesigner transfer URL
948
+ */
268
949
  getTransferUrl(to, memo, amount, redirectUri) {
269
- return `https://hivesigner.com/sign/transfer?to=${to}&memo=${memo}&amount=${amount}&redirect_uri=${redirectUri}`;
950
+ if (!to || !memo || !amount || !redirectUri) {
951
+ throw new Error('All parameters are required for transfer URL generation');
952
+ }
953
+ // URL encode the parameters to handle special characters
954
+ const encodedTo = encodeURIComponent(to);
955
+ const encodedMemo = encodeURIComponent(memo);
956
+ const encodedAmount = encodeURIComponent(amount);
957
+ const encodedRedirectUri = encodeURIComponent(redirectUri);
958
+ return `https://hivesigner.com/sign/transfer?to=${encodedTo}&memo=${encodedMemo}&amount=${encodedAmount}&redirect_uri=${encodedRedirectUri}`;
270
959
  }
271
960
  };
272
961
  //# sourceMappingURL=utils.js.map