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.
- package/AGENTS.md +35 -0
- package/DOCUMENTATION.md +396 -0
- package/README.md +160 -39
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +7 -7
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +19 -1
- package/dist/adapters/base.adapter.js +16 -0
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +5 -11
- package/dist/adapters/mongodb.adapter.js +10 -10
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +17 -0
- package/dist/adapters/postgresql.adapter.js +99 -8
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.d.ts +17 -0
- package/dist/adapters/sqlite.adapter.js +99 -8
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.js +86 -0
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +26 -0
- package/dist/config.js +76 -4
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +8 -26
- package/dist/contracts/coinflip.contract.js +123 -144
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/contract.d.ts +3 -0
- package/dist/contracts/contract.js +26 -0
- package/dist/contracts/contract.js.map +1 -0
- package/dist/contracts/dice.contract.d.ts +9 -36
- package/dist/contracts/dice.contract.js +135 -200
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/exchange.contract.d.ts +11 -0
- package/dist/contracts/exchange.contract.js +492 -0
- package/dist/contracts/exchange.contract.js.map +1 -0
- package/dist/contracts/lotto.contract.d.ts +15 -19
- package/dist/contracts/lotto.contract.js +154 -162
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/nft.contract.d.ts +4 -0
- package/dist/contracts/nft.contract.js +65 -0
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/poll.contract.d.ts +4 -0
- package/dist/contracts/poll.contract.js +105 -0
- package/dist/contracts/poll.contract.js.map +1 -0
- package/dist/contracts/rps.contract.d.ts +9 -0
- package/dist/contracts/rps.contract.js +217 -0
- package/dist/contracts/rps.contract.js.map +1 -0
- package/dist/contracts/tipjar.contract.d.ts +4 -0
- package/dist/contracts/tipjar.contract.js +60 -0
- package/dist/contracts/tipjar.contract.js.map +1 -0
- package/dist/contracts/token.contract.d.ts +3 -17
- package/dist/contracts/token.contract.js +128 -80
- package/dist/contracts/token.contract.js.map +1 -1
- package/dist/exchanges/coingecko.d.ts +7 -1
- package/dist/exchanges/coingecko.js +38 -21
- package/dist/exchanges/coingecko.js.map +1 -1
- package/dist/exchanges/exchange.d.ts +15 -8
- package/dist/exchanges/exchange.js +65 -11
- package/dist/exchanges/exchange.js.map +1 -1
- package/dist/hive-rates.d.ts +29 -4
- package/dist/hive-rates.js +179 -92
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +19 -4
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +63 -0
- package/dist/metadata.js +407 -0
- package/dist/metadata.js.map +1 -0
- package/dist/streamer.d.ts +104 -11
- package/dist/streamer.js +415 -143
- package/dist/streamer.js.map +1 -1
- package/dist/test.js +11 -12
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +85 -14
- package/dist/types/rates.d.ts +47 -0
- package/dist/types/rates.js +29 -0
- package/dist/types/rates.js.map +1 -0
- package/dist/utils.d.ts +318 -11
- package/dist/utils.js +804 -115
- package/dist/utils.js.map +1 -1
- package/examples/contracts/README.md +8 -0
- package/examples/contracts/exchange.ts +38 -0
- package/examples/contracts/poll.ts +21 -0
- package/examples/contracts/rps.ts +19 -0
- package/examples/contracts/tipjar.ts +19 -0
- package/package.json +20 -19
- package/test-contract-block.md +3 -3
- package/tests/actions.spec.ts +7 -7
- package/tests/adapters/actions-persistence.spec.ts +4 -4
- package/tests/adapters/sqlite.adapter.spec.ts +2 -2
- package/tests/config-input.spec.ts +90 -0
- package/tests/contracts/coinflip.contract.spec.ts +26 -154
- package/tests/contracts/dice.contract.spec.ts +24 -140
- package/tests/contracts/exchange.contract.spec.ts +84 -0
- package/tests/contracts/lotto.contract.spec.ts +30 -295
- package/tests/contracts/token.contract.spec.ts +72 -316
- package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
- package/tests/exchanges/exchange.base.spec.ts +246 -0
- package/tests/helpers/mock-fetch.ts +165 -0
- package/tests/hive-chain-features.spec.ts +319 -0
- package/tests/hive-rates.spec.ts +443 -0
- package/tests/integration/hive-rates.integration.spec.ts +35 -0
- package/tests/metadata.spec.ts +63 -0
- package/tests/streamer-actions.spec.ts +29 -18
- package/tests/streamer.spec.ts +142 -49
- package/tests/types/rates.spec.ts +216 -0
- 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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (!
|
|
83
|
-
throw new Error(
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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({
|
|
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
|
|
152
|
-
const quantity = user.amount
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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 (
|
|
198
|
-
console.log(
|
|
687
|
+
if (config_1.Config.DEBUG_MODE) {
|
|
688
|
+
console.log('[Utils] Issuing Hive Engine Token:', json);
|
|
199
689
|
}
|
|
200
|
-
return client.broadcast.json({
|
|
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
|
|
208
|
-
const quantity = user.amount
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
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
|