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.
- package/.claude/settings.local.json +12 -0
- package/.env.example +2 -2
- package/.travis.yml +11 -11
- package/AGENTS.md +35 -0
- package/CHANGELOG.md +166 -0
- package/CLAUDE.md +75 -0
- package/DOCUMENTATION.md +380 -0
- package/LICENSE +21 -21
- package/README.md +429 -238
- package/dist/actions.d.ts +41 -10
- package/dist/actions.js +126 -23
- package/dist/actions.js.map +1 -1
- package/dist/adapters/base.adapter.d.ts +43 -25
- package/dist/adapters/base.adapter.js +79 -49
- package/dist/adapters/base.adapter.js.map +1 -1
- package/dist/adapters/mongodb.adapter.d.ts +44 -37
- package/dist/adapters/mongodb.adapter.js +363 -158
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.d.ts +66 -0
- package/dist/adapters/postgresql.adapter.js +598 -0
- package/dist/adapters/postgresql.adapter.js.map +1 -0
- package/dist/adapters/sqlite.adapter.d.ts +57 -41
- package/dist/adapters/sqlite.adapter.js +561 -397
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.d.ts +6 -6
- package/dist/api.js +181 -55
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +19 -16
- package/dist/config.js +21 -18
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.d.ts +9 -14
- package/dist/contracts/coinflip.contract.js +232 -94
- 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 +10 -29
- package/dist/contracts/dice.contract.js +217 -155
- 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 +16 -20
- package/dist/contracts/lotto.contract.js +238 -246
- package/dist/contracts/lotto.contract.js.map +1 -1
- package/dist/contracts/nft.contract.d.ts +28 -0
- package/dist/contracts/nft.contract.js +598 -0
- package/dist/contracts/nft.contract.js.map +1 -0
- 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 +4 -0
- package/dist/contracts/token.contract.js +311 -0
- package/dist/contracts/token.contract.js.map +1 -0
- package/dist/exchanges/bittrex.d.ts +6 -6
- package/dist/exchanges/bittrex.js +34 -34
- package/dist/exchanges/coingecko.d.ts +11 -0
- package/dist/exchanges/coingecko.js +57 -0
- package/dist/exchanges/coingecko.js.map +1 -0
- package/dist/exchanges/exchange.d.ts +16 -9
- package/dist/exchanges/exchange.js +80 -26
- package/dist/exchanges/exchange.js.map +1 -1
- package/dist/hive-rates.d.ts +34 -9
- package/dist/hive-rates.js +208 -75
- package/dist/hive-rates.js.map +1 -1
- package/dist/index.d.ts +19 -11
- package/dist/index.js +47 -32
- package/dist/index.js.map +1 -1
- package/dist/streamer.d.ts +233 -93
- package/dist/streamer.js +1063 -545
- package/dist/streamer.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/dist/test.js +24 -25
- package/dist/test.js.map +1 -1
- package/dist/types/hive-stream.d.ts +106 -6
- package/dist/types/hive-stream.js +2 -2
- 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 +334 -27
- package/dist/utils.js +960 -261
- package/dist/utils.js.map +1 -1
- package/ecosystem.config.js +17 -17
- 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/jest.config.js +8 -8
- package/package.json +54 -48
- package/test-contract-block.md +18 -18
- package/tests/actions.spec.ts +252 -0
- package/tests/adapters/actions-persistence.spec.ts +144 -0
- package/tests/adapters/postgresql.adapter.spec.ts +127 -0
- package/tests/adapters/sqlite.adapter.spec.ts +180 -42
- package/tests/contracts/coinflip.contract.spec.ts +94 -132
- package/tests/contracts/dice.contract.spec.ts +87 -160
- package/tests/contracts/entrants.json +728 -728
- package/tests/contracts/exchange.contract.spec.ts +84 -0
- package/tests/contracts/lotto.contract.spec.ts +59 -324
- package/tests/contracts/nft.contract.spec.ts +948 -0
- package/tests/contracts/token.contract.spec.ts +90 -0
- package/tests/exchanges/coingecko.exchange.spec.ts +169 -0
- package/tests/exchanges/exchange.base.spec.ts +246 -0
- package/tests/helpers/mock-adapter.ts +214 -0
- package/tests/helpers/mock-fetch.ts +165 -0
- package/tests/hive-chain-features.spec.ts +238 -0
- package/tests/hive-rates.spec.ts +443 -0
- package/tests/integration/hive-rates.integration.spec.ts +35 -0
- package/tests/setup.ts +29 -18
- package/tests/streamer-actions.spec.ts +274 -0
- package/tests/streamer.spec.ts +342 -152
- package/tests/types/rates.spec.ts +216 -0
- package/tests/utils.spec.ts +113 -95
- package/tsconfig.build.json +3 -22
- package/tslint.json +20 -20
- 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
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|