hive-stream 2.0.6 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +2 -2
  3. package/.travis.yml +11 -11
  4. package/AGENTS.md +35 -0
  5. package/CHANGELOG.md +166 -0
  6. package/CLAUDE.md +75 -0
  7. package/DOCUMENTATION.md +380 -0
  8. package/LICENSE +21 -21
  9. package/README.md +429 -238
  10. package/dist/actions.d.ts +41 -10
  11. package/dist/actions.js +126 -23
  12. package/dist/actions.js.map +1 -1
  13. package/dist/adapters/base.adapter.d.ts +43 -25
  14. package/dist/adapters/base.adapter.js +79 -49
  15. package/dist/adapters/base.adapter.js.map +1 -1
  16. package/dist/adapters/mongodb.adapter.d.ts +44 -37
  17. package/dist/adapters/mongodb.adapter.js +363 -158
  18. package/dist/adapters/mongodb.adapter.js.map +1 -1
  19. package/dist/adapters/postgresql.adapter.d.ts +66 -0
  20. package/dist/adapters/postgresql.adapter.js +598 -0
  21. package/dist/adapters/postgresql.adapter.js.map +1 -0
  22. package/dist/adapters/sqlite.adapter.d.ts +57 -41
  23. package/dist/adapters/sqlite.adapter.js +561 -397
  24. package/dist/adapters/sqlite.adapter.js.map +1 -1
  25. package/dist/api.d.ts +6 -6
  26. package/dist/api.js +181 -55
  27. package/dist/api.js.map +1 -1
  28. package/dist/config.d.ts +19 -16
  29. package/dist/config.js +21 -18
  30. package/dist/config.js.map +1 -1
  31. package/dist/contracts/coinflip.contract.d.ts +9 -14
  32. package/dist/contracts/coinflip.contract.js +232 -94
  33. package/dist/contracts/coinflip.contract.js.map +1 -1
  34. package/dist/contracts/contract.d.ts +3 -0
  35. package/dist/contracts/contract.js +26 -0
  36. package/dist/contracts/contract.js.map +1 -0
  37. package/dist/contracts/dice.contract.d.ts +10 -29
  38. package/dist/contracts/dice.contract.js +217 -155
  39. package/dist/contracts/dice.contract.js.map +1 -1
  40. package/dist/contracts/exchange.contract.d.ts +11 -0
  41. package/dist/contracts/exchange.contract.js +492 -0
  42. package/dist/contracts/exchange.contract.js.map +1 -0
  43. package/dist/contracts/lotto.contract.d.ts +16 -20
  44. package/dist/contracts/lotto.contract.js +238 -246
  45. package/dist/contracts/lotto.contract.js.map +1 -1
  46. package/dist/contracts/nft.contract.d.ts +28 -0
  47. package/dist/contracts/nft.contract.js +598 -0
  48. package/dist/contracts/nft.contract.js.map +1 -0
  49. package/dist/contracts/poll.contract.d.ts +4 -0
  50. package/dist/contracts/poll.contract.js +105 -0
  51. package/dist/contracts/poll.contract.js.map +1 -0
  52. package/dist/contracts/rps.contract.d.ts +9 -0
  53. package/dist/contracts/rps.contract.js +217 -0
  54. package/dist/contracts/rps.contract.js.map +1 -0
  55. package/dist/contracts/tipjar.contract.d.ts +4 -0
  56. package/dist/contracts/tipjar.contract.js +60 -0
  57. package/dist/contracts/tipjar.contract.js.map +1 -0
  58. package/dist/contracts/token.contract.d.ts +4 -0
  59. package/dist/contracts/token.contract.js +311 -0
  60. package/dist/contracts/token.contract.js.map +1 -0
  61. package/dist/exchanges/bittrex.d.ts +6 -6
  62. package/dist/exchanges/bittrex.js +34 -34
  63. package/dist/exchanges/coingecko.d.ts +11 -0
  64. package/dist/exchanges/coingecko.js +57 -0
  65. package/dist/exchanges/coingecko.js.map +1 -0
  66. package/dist/exchanges/exchange.d.ts +16 -9
  67. package/dist/exchanges/exchange.js +80 -26
  68. package/dist/exchanges/exchange.js.map +1 -1
  69. package/dist/hive-rates.d.ts +34 -9
  70. package/dist/hive-rates.js +208 -75
  71. package/dist/hive-rates.js.map +1 -1
  72. package/dist/index.d.ts +19 -11
  73. package/dist/index.js +47 -32
  74. package/dist/index.js.map +1 -1
  75. package/dist/streamer.d.ts +233 -93
  76. package/dist/streamer.js +1063 -545
  77. package/dist/streamer.js.map +1 -1
  78. package/dist/test.d.ts +1 -1
  79. package/dist/test.js +24 -25
  80. package/dist/test.js.map +1 -1
  81. package/dist/types/hive-stream.d.ts +106 -6
  82. package/dist/types/hive-stream.js +2 -2
  83. package/dist/types/rates.d.ts +47 -0
  84. package/dist/types/rates.js +29 -0
  85. package/dist/types/rates.js.map +1 -0
  86. package/dist/utils.d.ts +334 -27
  87. package/dist/utils.js +960 -261
  88. package/dist/utils.js.map +1 -1
  89. package/ecosystem.config.js +17 -17
  90. package/examples/contracts/README.md +8 -0
  91. package/examples/contracts/exchange.ts +38 -0
  92. package/examples/contracts/poll.ts +21 -0
  93. package/examples/contracts/rps.ts +19 -0
  94. package/examples/contracts/tipjar.ts +19 -0
  95. package/jest.config.js +8 -8
  96. package/package.json +54 -48
  97. package/test-contract-block.md +18 -18
  98. package/tests/actions.spec.ts +252 -0
  99. package/tests/adapters/actions-persistence.spec.ts +144 -0
  100. package/tests/adapters/postgresql.adapter.spec.ts +127 -0
  101. package/tests/adapters/sqlite.adapter.spec.ts +180 -42
  102. package/tests/contracts/coinflip.contract.spec.ts +94 -132
  103. package/tests/contracts/dice.contract.spec.ts +87 -160
  104. package/tests/contracts/entrants.json +728 -728
  105. package/tests/contracts/exchange.contract.spec.ts +84 -0
  106. package/tests/contracts/lotto.contract.spec.ts +59 -324
  107. package/tests/contracts/nft.contract.spec.ts +948 -0
  108. package/tests/contracts/token.contract.spec.ts +90 -0
  109. package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
  110. package/tests/exchanges/exchange.base.spec.ts +246 -0
  111. package/tests/helpers/mock-adapter.ts +214 -0
  112. package/tests/helpers/mock-fetch.ts +165 -0
  113. package/tests/hive-chain-features.spec.ts +238 -0
  114. package/tests/hive-rates.spec.ts +443 -0
  115. package/tests/integration/hive-rates.integration.spec.ts +35 -0
  116. package/tests/setup.ts +29 -18
  117. package/tests/streamer-actions.spec.ts +274 -0
  118. package/tests/streamer.spec.ts +342 -152
  119. package/tests/types/rates.spec.ts +216 -0
  120. package/tests/utils.spec.ts +113 -95
  121. package/tsconfig.build.json +3 -22
  122. package/tslint.json +20 -20
  123. package/wallaby.js +26 -26
package/dist/utils.js CHANGED
@@ -1,262 +1,961 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Utils = void 0;
7
- const hive_rates_1 = require("./hive-rates");
8
- const dhive_1 = require("@hiveio/dhive");
9
- const seedrandom_1 = __importDefault(require("seedrandom"));
10
- const MAX_PAYLOAD_SIZE = 2000;
11
- const MAX_ACCOUNTS_CHECK = 999;
12
- exports.Utils = {
13
- // https://flaviocopes.com/javascript-sleep/
14
- sleep(milliseconds) {
15
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
16
- },
17
- // Fisher Yates shuffle
18
- shuffle(array) {
19
- let currentIndex = array.length;
20
- let temporaryValue;
21
- let randomIndex;
22
- while (0 !== currentIndex) {
23
- // Pick a remaining element...
24
- randomIndex = Math.floor(Math.random() * currentIndex);
25
- currentIndex -= 1;
26
- temporaryValue = array[currentIndex];
27
- array[currentIndex] = array[randomIndex];
28
- array[randomIndex] = temporaryValue;
29
- }
30
- return array;
31
- },
32
- roundPrecision(value, precision) {
33
- const NUMBER_SIGN = value >= 0 ? 1 : -1;
34
- return parseFloat((Math.round((value * Math.pow(10, precision)) + (NUMBER_SIGN * 0.0001)) / Math.pow(10, precision)).toFixed(precision));
35
- },
36
- randomRange(min = 0, max = 2000) {
37
- return (!isNaN(min) && !isNaN(max) ? Math.floor(Math.random() * (max - min + 1)) + min : NaN);
38
- },
39
- randomString(length = 12) {
40
- let str = '';
41
- const characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
42
- const max = characters.length - 1;
43
- for (let i = 0; i < length; i++) {
44
- str += characters[exports.Utils.randomRange(0, max)];
45
- }
46
- return str;
47
- },
48
- async convertHiveAmount(amount, fiatSymbol, hiveSymbol) {
49
- if (fiatSymbol === hiveSymbol) {
50
- return amount;
51
- }
52
- const rates = new hive_rates_1.HiveRates();
53
- await rates.fetchRates();
54
- const rate = rates.fiatToHiveRate(fiatSymbol, hiveSymbol);
55
- const total = amount / rate;
56
- return rate > 0 ? exports.Utils.roundPrecision(total, 3) : 0;
57
- },
58
- jsonParse(str) {
59
- let obj = null;
60
- try {
61
- obj = JSON.parse(str);
62
- }
63
- catch {
64
- // We don't do anything
65
- }
66
- return obj;
67
- },
68
- async getTransaction(client, blockNumber, transactionId) {
69
- const block = await client.database.getBlock(blockNumber);
70
- const exists = block.transaction_ids.includes(transactionId);
71
- const index = block.transaction_ids.indexOf(transactionId);
72
- if (!exists) {
73
- throw new Error(`Unable to find transaction ${transactionId} in block ${blockNumber}`);
74
- }
75
- return block.transactions[index];
76
- },
77
- async verifyTransfer(transaction, from, to, amount) {
78
- const operation = transaction.operations[0][1];
79
- return (operation.from === from && operation.to === to && operation.amount === amount);
80
- },
81
- transferHiveTokens(client, config, from, to, amount, symbol, memo = '') {
82
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
83
- return client.broadcast.transfer({ from, to, amount: `${parseFloat(amount).toFixed(3)} ${symbol}`, memo }, key);
84
- },
85
- async transferHiveTokensMultiple(client, config, from, accounts, amount = '0', symbol, memo) {
86
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
87
- let completed = 0;
88
- for (const user of accounts) {
89
- const to = user.replace('@', '');
90
- await client.broadcast.transfer({ from, to, amount: `${parseFloat(amount).toFixed(3)} ${symbol}`, memo }, key);
91
- completed++;
92
- await this.sleep(3000);
93
- }
94
- if (completed === accounts.length) {
95
- return true;
96
- }
97
- },
98
- async getAccountTransfers(client, account, from = -1, max = 100) {
99
- const history = await client.call('condenser_api', 'get_account_history', [account, from, max]);
100
- const transfers = history.filter(tx => tx[1].op[0] === 'transfer');
101
- const actualTransfers = transfers.reduce((arr, tx) => {
102
- const transaction = tx[1].op[1];
103
- const date = new Date(`${tx[1].timestamp}Z`);
104
- transaction.date = date;
105
- arr.push(transaction);
106
- return arr;
107
- }, []);
108
- return actualTransfers;
109
- },
110
- async getApiJson(client, from = -1, limit = 500) {
111
- const history = await client.call('condenser_api', 'get_account_history', ['hiveapi', from, limit]);
112
- const customJson = history.filter(tx => tx[1].op[0] === 'custom_json');
113
- const actualJson = customJson.reduce((arr, tx) => {
114
- const transaction = tx[1].op[1];
115
- const date = new Date(`${tx[1].timestamp}Z`);
116
- transaction.date = date;
117
- arr.push(transaction);
118
- return arr;
119
- }, []);
120
- return actualJson;
121
- },
122
- transferHiveEngineTokens(client, config, from, to, quantity, symbol, memo = '') {
123
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
124
- const json = {
125
- contractName: 'tokens',
126
- contractAction: 'transfer',
127
- contractPayload: {
128
- symbol: symbol.toUpperCase(),
129
- to,
130
- quantity,
131
- memo,
132
- }
133
- };
134
- return client.broadcast.json({ required_auths: [from], required_posting_auths: [], id: config.HIVE_ENGINE_ID, json: JSON.stringify(json) }, key);
135
- },
136
- async transferHiveEngineTokensMultiple(client, config, from, accounts, symbol, memo, amount = '0') {
137
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
138
- const payloads = [[]];
139
- let completed = 0;
140
- for (const user of accounts) {
141
- const account = user.account.replace('@', '');
142
- const quantity = user.amount ? parseFloat(user.amount.replace(',', '.')).toString() : parseFloat(amount).toString();
143
- // 0 means no quantity supplied (either in accounts or default)
144
- if (parseFloat(quantity) > 0) {
145
- const json = {
146
- contractName: 'tokens',
147
- contractAction: 'transfer',
148
- contractPayload: {
149
- symbol: symbol.toUpperCase(),
150
- to: account,
151
- quantity,
152
- memo,
153
- },
154
- };
155
- const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
156
- const payloadSize = JSON.stringify(json).length;
157
- if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
158
- payloads.push([json]);
159
- }
160
- else {
161
- payloads[payloads.length - 1].push(json);
162
- }
163
- }
164
- }
165
- for (const payload of payloads) {
166
- const requiredAuths = [from];
167
- const requiredPostingAuths = [];
168
- await client.broadcast.json({ required_auths: requiredAuths, required_posting_auths: requiredPostingAuths, id: config.HIVE_ENGINE_ID, json: JSON.stringify(payload) }, key);
169
- completed++;
170
- if (completed !== (payloads.length) && completed !== 0) {
171
- await this.sleep(3000);
172
- }
173
- }
174
- },
175
- issueHiveEngineTokens(client, config, from, to, symbol, quantity, memo = '') {
176
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
177
- const json = {
178
- contractName: 'tokens',
179
- contractAction: 'issue',
180
- contractPayload: {
181
- symbol,
182
- to,
183
- quantity,
184
- memo,
185
- },
186
- };
187
- if (config.DEBUG_MODE) {
188
- console.log(`Issuing Hive Engine Token: `, json, JSON.stringify(json));
189
- }
190
- return client.broadcast.json({ required_auths: [from], required_posting_auths: [], id: config.HIVE_ENGINE_ID, json: JSON.stringify(json) }, key);
191
- },
192
- async issueHiveEngineTokensMultiple(client, config, from, accounts, symbol, memo, amount = '0') {
193
- const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
194
- const payloads = [[]];
195
- let completed = 0;
196
- for (const user of accounts) {
197
- const to = user.account.replace('@', '');
198
- const quantity = user.amount ? parseFloat(user.amount.replace(',', '.')).toString() : parseFloat(amount).toString();
199
- // 0 means no quantity supplied (either in accounts or default)
200
- if (parseFloat(quantity) > 0) {
201
- const json = {
202
- contractName: 'tokens',
203
- contractAction: 'issue',
204
- contractPayload: {
205
- symbol: symbol.toUpperCase(),
206
- to,
207
- quantity,
208
- memo,
209
- },
210
- };
211
- const lastPayloadSize = JSON.stringify(payloads[payloads.length - 1]).length;
212
- const payloadSize = JSON.stringify(json).length;
213
- if (payloadSize + lastPayloadSize > MAX_PAYLOAD_SIZE) {
214
- payloads.push([json]);
215
- }
216
- else {
217
- payloads[payloads.length - 1].push(json);
218
- }
219
- }
220
- }
221
- for (const payload of payloads) {
222
- const requiredAuths = [from];
223
- const requiredPostingAuths = null;
224
- await client.broadcast.json({ required_auths: requiredAuths, required_posting_auths: requiredPostingAuths, id: config.HIVE_ENGINE_ID, json: JSON.stringify(payload) }, key);
225
- completed++;
226
- if (completed !== (payloads.length) && completed !== 0) {
227
- await this.sleep(3000);
228
- }
229
- }
230
- },
231
- randomNumber(previousBlockId, blockId, transactionId) {
232
- const random = (0, seedrandom_1.default)(`${previousBlockId}${blockId}${transactionId}`).double();
233
- const randomRoll = Math.floor(random * 100) + 1;
234
- return randomRoll;
235
- },
236
- upvote(client, config, voter, votePercentage = '100.0', author, permlink) {
237
- const percentage = parseFloat(votePercentage);
238
- const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
239
- if (percentage < 0) {
240
- throw new Error('Negative voting values are for downvotes, not upvotes');
241
- }
242
- const weight = this.votingWeight(percentage);
243
- return client.broadcast.vote({ voter, author, permlink, weight }, key);
244
- },
245
- downvote(client, config, voter, votePercentage = '100.0', author, permlink) {
246
- const weight = this.votingWeight(parseFloat(votePercentage)) * -1;
247
- const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
248
- return client.broadcast.vote({ voter, author, permlink, weight }, key);
249
- },
250
- votingWeight(votePercentage) {
251
- return Math.min(Math.floor(parseFloat(votePercentage.toFixed(2)) * 100), 10000);
252
- },
253
- async asyncForEach(array, callback) {
254
- for (let index = 0; index < array.length; index++) {
255
- await callback(array[index], index, array);
256
- }
257
- },
258
- getTransferUrl(to, memo, amount, redirectUri) {
259
- return `https://hivesigner.com/sign/transfer?to=${to}&memo=${memo}&amount=${amount}&redirect_uri=${redirectUri}`;
260
- }
261
- };
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Utils = void 0;
7
+ const hive_rates_1 = require("./hive-rates");
8
+ const dhive_1 = require("@hiveio/dhive");
9
+ const config_1 = require("./config");
10
+ const seedrandom_1 = __importDefault(require("seedrandom"));
11
+ const MAX_PAYLOAD_SIZE = 2000;
12
+ const MAX_ACCOUNTS_CHECK = 999;
13
+ /**
14
+ * Utility functions for Hive blockchain operations and general helpers
15
+ */
16
+ exports.Utils = {
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
+ */
22
+ sleep(milliseconds) {
23
+ if (milliseconds < 0) {
24
+ throw new Error('Sleep duration cannot be negative');
25
+ }
26
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
27
+ },
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
+ */
64
+ shuffle(array) {
65
+ if (!Array.isArray(array)) {
66
+ throw new Error('Input must be an array');
67
+ }
68
+ let currentIndex = array.length;
69
+ let temporaryValue;
70
+ let randomIndex;
71
+ while (currentIndex !== 0) {
72
+ // Pick a remaining element
73
+ randomIndex = Math.floor(Math.random() * currentIndex);
74
+ currentIndex -= 1;
75
+ // Swap with the current element
76
+ temporaryValue = array[currentIndex];
77
+ array[currentIndex] = array[randomIndex];
78
+ array[randomIndex] = temporaryValue;
79
+ }
80
+ return array;
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
+ */
88
+ roundPrecision(value, 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));
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
+ */
105
+ randomRange(min = 0, max = 2000) {
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;
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
+ */
119
+ randomString(length = 12) {
120
+ if (length < 0) {
121
+ throw new Error('Length cannot be negative');
122
+ }
123
+ if (length === 0) {
124
+ return '';
125
+ }
126
+ let str = '';
127
+ const characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
128
+ const max = characters.length - 1;
129
+ for (let i = 0; i < length; i++) {
130
+ str += characters[exports.Utils.randomRange(0, max)];
131
+ }
132
+ return str;
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
+ */
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
+ }
148
+ if (fiatSymbol === hiveSymbol) {
149
+ return amount;
150
+ }
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
+ }
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
+ */
173
+ jsonParse(str) {
174
+ if (!str || typeof str !== 'string') {
175
+ return null;
176
+ }
177
+ try {
178
+ return JSON.parse(str);
179
+ }
180
+ catch (e) {
181
+ if (config_1.Config.DEBUG_MODE) {
182
+ const error = e instanceof Error ? e : new Error(String(e));
183
+ console.warn(`[Utils] JSON parse failed: ${error.message}`, {
184
+ input: str.substring(0, 100) + (str.length > 100 ? '...' : ''),
185
+ stack: error.stack
186
+ });
187
+ }
188
+ return null;
189
+ }
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
+ */
199
+ async getTransaction(client, blockNumber, transactionId) {
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;
222
+ }
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
+ */
232
+ async verifyTransfer(transaction, from, to, 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
+ }
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
+ */
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
+ }
262
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_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);
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
+ */
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
+ }
458
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
459
+ let completed = 0;
460
+ for (const user of accounts) {
461
+ const to = user.replace('@', '');
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
+ }
477
+ }
478
+ return completed === accounts.length;
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
+ */
488
+ async getAccountTransfers(client, account, from = -1, max = 100) {
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
+ }
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
+ */
527
+ async getApiJson(client, from = -1, limit = 500) {
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
+ }
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
+ */
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
+ }
571
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
572
+ const json = {
573
+ contractName: 'tokens',
574
+ contractAction: 'transfer',
575
+ contractPayload: {
576
+ symbol: symbol.toUpperCase(),
577
+ to,
578
+ quantity,
579
+ memo,
580
+ }
581
+ };
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);
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
+ */
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
+ }
604
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
605
+ const payloads = [[]];
606
+ let completed = 0;
607
+ // Build payloads in batches to respect size limits
608
+ for (const user of accounts) {
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);
634
+ }
635
+ }
636
+ // Execute batches with delays
637
+ for (const payload of payloads) {
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;
658
+ }
659
+ }
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
+ */
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
+ }
676
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
677
+ const json = {
678
+ contractName: 'tokens',
679
+ contractAction: 'issue',
680
+ contractPayload: {
681
+ symbol,
682
+ to,
683
+ quantity,
684
+ memo,
685
+ },
686
+ };
687
+ if (config_1.Config.DEBUG_MODE) {
688
+ console.log('[Utils] Issuing Hive Engine Token:', json);
689
+ }
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);
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
+ */
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
+ }
712
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
713
+ const payloads = [[]];
714
+ let completed = 0;
715
+ // Build payloads in batches to respect size limits
716
+ for (const user of accounts) {
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);
742
+ }
743
+ }
744
+ // Execute batches with delays
745
+ for (const payload of payloads) {
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
+ }
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');
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);
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
+ */
864
+ randomNumber(previousBlockId, blockId, transactionId) {
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;
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
+ */
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
+ }
887
+ const percentage = parseFloat(votePercentage);
888
+ if (percentage < 0) {
889
+ throw new Error('Negative voting values are for downvotes, not upvotes');
890
+ }
891
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
892
+ const weight = this.votingWeight(percentage);
893
+ return client.broadcast.vote({ voter, author, permlink, weight }, key);
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
+ */
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
+ }
909
+ const weight = this.votingWeight(parseFloat(votePercentage)) * -1;
910
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
911
+ return client.broadcast.vote({ voter, author, permlink, weight }, key);
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
+ */
918
+ votingWeight(votePercentage) {
919
+ if (typeof votePercentage !== 'number' || votePercentage < 0) {
920
+ throw new Error('Vote percentage must be a non-negative number');
921
+ }
922
+ return Math.min(Math.floor(parseFloat(votePercentage.toFixed(2)) * 100), 10000);
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
+ */
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
+ }
937
+ for (let index = 0; index < array.length; index++) {
938
+ await callback(array[index], index, array);
939
+ }
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
+ */
949
+ getTransferUrl(to, memo, amount, 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}`;
959
+ }
960
+ };
262
961
  //# sourceMappingURL=utils.js.map