hive-stream 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/DOCUMENTATION.md +693 -1
  2. package/README.md +251 -1
  3. package/dist/adapters/mongodb.adapter.js +21 -30
  4. package/dist/adapters/mongodb.adapter.js.map +1 -1
  5. package/dist/adapters/postgresql.adapter.js +16 -16
  6. package/dist/adapters/postgresql.adapter.js.map +1 -1
  7. package/dist/adapters/sqlite.adapter.js +17 -17
  8. package/dist/adapters/sqlite.adapter.js.map +1 -1
  9. package/dist/api.js +2 -0
  10. package/dist/api.js.map +1 -1
  11. package/dist/builders.d.ts +444 -0
  12. package/dist/builders.js +1609 -0
  13. package/dist/builders.js.map +1 -0
  14. package/dist/config.d.ts +10 -1
  15. package/dist/config.js +90 -4
  16. package/dist/config.js.map +1 -1
  17. package/dist/contracts/coinflip.contract.js +18 -21
  18. package/dist/contracts/coinflip.contract.js.map +1 -1
  19. package/dist/contracts/dice.contract.js +18 -21
  20. package/dist/contracts/dice.contract.js.map +1 -1
  21. package/dist/contracts/helpers.d.ts +2 -0
  22. package/dist/contracts/helpers.js +18 -11
  23. package/dist/contracts/helpers.js.map +1 -1
  24. package/dist/contracts/nft.contract.js +5 -10
  25. package/dist/contracts/nft.contract.js.map +1 -1
  26. package/dist/contracts/rps.contract.js +35 -23
  27. package/dist/contracts/rps.contract.js.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/metadata.d.ts +13 -0
  32. package/dist/metadata.js +256 -0
  33. package/dist/metadata.js.map +1 -1
  34. package/dist/streamer.d.ts +148 -12
  35. package/dist/streamer.js +1102 -53
  36. package/dist/streamer.js.map +1 -1
  37. package/dist/types/hive-stream.d.ts +623 -0
  38. package/dist/utils.d.ts +475 -0
  39. package/dist/utils.js +1618 -8
  40. package/dist/utils.js.map +1 -1
  41. package/package.json +4 -5
package/dist/utils.js CHANGED
@@ -11,6 +11,7 @@ const config_1 = require("./config");
11
11
  const seedrandom_1 = __importDefault(require("seedrandom"));
12
12
  const MAX_PAYLOAD_SIZE = 2000;
13
13
  const MAX_ACCOUNTS_CHECK = 999;
14
+ const NULL_ACCOUNT = 'null';
14
15
  /**
15
16
  * Utility functions for Hive blockchain operations and general helpers
16
17
  */
@@ -57,6 +58,168 @@ exports.Utils = {
57
58
  }
58
59
  return JSON.stringify(meta);
59
60
  },
61
+ parseAssetAmount(rawAmount) {
62
+ if (typeof rawAmount !== 'string' || rawAmount.trim().length === 0) {
63
+ throw new Error('Asset amount must be a non-empty string');
64
+ }
65
+ const parts = rawAmount.trim().split(/\s+/);
66
+ if (parts.length !== 2) {
67
+ throw new Error(`Invalid asset amount '${rawAmount}'`);
68
+ }
69
+ const value = new bignumber_js_1.default(parts[0]);
70
+ if (value.isNaN() || !value.isFinite()) {
71
+ throw new Error(`Invalid asset amount '${rawAmount}'`);
72
+ }
73
+ return {
74
+ rawAmount: rawAmount.trim(),
75
+ amount: parts[0],
76
+ asset: parts[1],
77
+ value
78
+ };
79
+ },
80
+ calculateBasisPointsAmount(amount, basisPoints, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
81
+ if (!Number.isInteger(basisPoints) || basisPoints < 0 || basisPoints > 10000) {
82
+ throw new Error('basisPoints must be an integer between 0 and 10000');
83
+ }
84
+ if (!Number.isInteger(precision) || precision < 0) {
85
+ throw new Error('precision must be a non-negative integer');
86
+ }
87
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
88
+ if (value.isNaN() || !value.isFinite()) {
89
+ throw new Error('Invalid amount');
90
+ }
91
+ return value
92
+ .multipliedBy(basisPoints)
93
+ .dividedBy(10000)
94
+ .decimalPlaces(precision, roundingMode)
95
+ .toFixed(precision);
96
+ },
97
+ formatAmount(amount, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
98
+ if (!Number.isInteger(precision) || precision < 0) {
99
+ throw new Error('precision must be a non-negative integer');
100
+ }
101
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
102
+ if (value.isNaN() || !value.isFinite()) {
103
+ throw new Error('Invalid amount');
104
+ }
105
+ return value.decimalPlaces(precision, roundingMode).toFixed(precision);
106
+ },
107
+ formatAssetAmount(amount, symbol, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
108
+ if (typeof symbol !== 'string' || symbol.trim().length === 0) {
109
+ throw new Error('Asset symbol is required');
110
+ }
111
+ return `${this.formatAmount(amount, precision, roundingMode)} ${symbol.trim()}`;
112
+ },
113
+ calculatePercentageAmount(amount, percentage, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
114
+ if (!Number.isInteger(precision) || precision < 0) {
115
+ throw new Error('precision must be a non-negative integer');
116
+ }
117
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
118
+ const percentageValue = bignumber_js_1.default.isBigNumber(percentage) ? percentage : new bignumber_js_1.default(percentage);
119
+ if (value.isNaN() || !value.isFinite()) {
120
+ throw new Error('Invalid amount');
121
+ }
122
+ if (percentageValue.isNaN() || !percentageValue.isFinite()) {
123
+ throw new Error('Invalid percentage');
124
+ }
125
+ if (percentageValue.lt(0) || percentageValue.gt(100)) {
126
+ throw new Error('percentage must be between 0 and 100');
127
+ }
128
+ return value
129
+ .multipliedBy(percentageValue)
130
+ .dividedBy(100)
131
+ .decimalPlaces(precision, roundingMode)
132
+ .toFixed(precision);
133
+ },
134
+ splitAmountByBasisPoints(amount, basisPoints, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
135
+ if (!Array.isArray(basisPoints) || basisPoints.length === 0) {
136
+ throw new Error('basisPoints array cannot be empty');
137
+ }
138
+ if (!Number.isInteger(precision) || precision < 0) {
139
+ throw new Error('precision must be a non-negative integer');
140
+ }
141
+ const totalBps = basisPoints.reduce((sum, value) => sum + value, 0);
142
+ if (totalBps !== 10000) {
143
+ throw new Error('basisPoints allocations must total 10000');
144
+ }
145
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
146
+ if (value.isNaN() || !value.isFinite()) {
147
+ throw new Error('Invalid amount');
148
+ }
149
+ let allocated = new bignumber_js_1.default(0);
150
+ return basisPoints.map((bps, index) => {
151
+ if (!Number.isInteger(bps) || bps < 0 || bps > 10000) {
152
+ throw new Error('basisPoints must be integers between 0 and 10000');
153
+ }
154
+ const share = index === basisPoints.length - 1
155
+ ? value.minus(allocated)
156
+ : value.multipliedBy(bps).dividedBy(10000).decimalPlaces(precision, roundingMode);
157
+ allocated = allocated.plus(share);
158
+ return share.toFixed(precision);
159
+ });
160
+ },
161
+ splitAmountByPercentage(amount, percentages, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
162
+ if (!Array.isArray(percentages) || percentages.length === 0) {
163
+ throw new Error('percentages array cannot be empty');
164
+ }
165
+ const normalized = percentages.map((percentage) => {
166
+ const value = bignumber_js_1.default.isBigNumber(percentage) ? percentage : new bignumber_js_1.default(percentage);
167
+ if (value.isNaN() || !value.isFinite()) {
168
+ throw new Error('Invalid percentage');
169
+ }
170
+ if (value.lt(0) || value.gt(100)) {
171
+ throw new Error('percentage must be between 0 and 100');
172
+ }
173
+ return value;
174
+ });
175
+ const total = normalized.reduce((sum, value) => sum.plus(value), new bignumber_js_1.default(0));
176
+ if (!total.eq(100)) {
177
+ throw new Error('percentages must total 100');
178
+ }
179
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
180
+ if (value.isNaN() || !value.isFinite()) {
181
+ throw new Error('Invalid amount');
182
+ }
183
+ let allocated = new bignumber_js_1.default(0);
184
+ return normalized.map((percentage, index) => {
185
+ const share = index === normalized.length - 1
186
+ ? value.minus(allocated)
187
+ : value.multipliedBy(percentage).dividedBy(100).decimalPlaces(precision, roundingMode);
188
+ allocated = allocated.plus(share);
189
+ return share.toFixed(precision);
190
+ });
191
+ },
192
+ splitAmountByWeights(amount, weights, precision = 3, roundingMode = bignumber_js_1.default.ROUND_DOWN) {
193
+ if (!Array.isArray(weights) || weights.length === 0) {
194
+ throw new Error('weights array cannot be empty');
195
+ }
196
+ if (!Number.isInteger(precision) || precision < 0) {
197
+ throw new Error('precision must be a non-negative integer');
198
+ }
199
+ const normalized = weights.map((weight) => {
200
+ const value = bignumber_js_1.default.isBigNumber(weight) ? weight : new bignumber_js_1.default(weight);
201
+ if (value.isNaN() || !value.isFinite()) {
202
+ throw new Error('Invalid weight');
203
+ }
204
+ if (value.lte(0)) {
205
+ throw new Error('weights must be greater than zero');
206
+ }
207
+ return value;
208
+ });
209
+ const totalWeight = normalized.reduce((sum, value) => sum.plus(value), new bignumber_js_1.default(0));
210
+ const value = bignumber_js_1.default.isBigNumber(amount) ? amount : new bignumber_js_1.default(amount);
211
+ if (value.isNaN() || !value.isFinite()) {
212
+ throw new Error('Invalid amount');
213
+ }
214
+ let allocated = new bignumber_js_1.default(0);
215
+ return normalized.map((weight, index) => {
216
+ const share = index === normalized.length - 1
217
+ ? value.minus(allocated)
218
+ : value.multipliedBy(weight).dividedBy(totalWeight).decimalPlaces(precision, roundingMode);
219
+ allocated = allocated.plus(share);
220
+ return share.toFixed(precision);
221
+ });
222
+ },
60
223
  /**
61
224
  * Shuffles an array in place using the Fisher-Yates algorithm
62
225
  * @param array - The array to shuffle (modified in place)
@@ -260,14 +423,29 @@ exports.Utils = {
260
423
  if (!client || !config.ACTIVE_KEY || !from || !to || !amount || !symbol) {
261
424
  throw new Error('Missing required parameters for Hive token transfer');
262
425
  }
263
- const amountBN = new bignumber_js_1.default(amount);
264
- if (amountBN.isNaN() || !amountBN.isFinite()) {
426
+ let formattedAmount;
427
+ try {
428
+ formattedAmount = this.formatAssetAmount(amount, symbol);
429
+ }
430
+ catch (error) {
265
431
  throw new Error('Invalid transfer amount');
266
432
  }
267
433
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
268
- const formattedAmount = `${amountBN.toFixed(3)} ${symbol}`;
269
434
  return client.broadcast.transfer({ from, to, amount: formattedAmount, memo }, key);
270
435
  },
436
+ /**
437
+ * Burns HIVE or HBD by transferring the amount to the null account.
438
+ * @param client - The Hive client instance
439
+ * @param config - Configuration containing the active key
440
+ * @param from - Sender account name
441
+ * @param amount - Amount to burn
442
+ * @param symbol - Asset symbol (HIVE or HBD)
443
+ * @param memo - Optional memo for the burn transfer
444
+ * @returns Promise resolving to the broadcast result
445
+ */
446
+ burnHiveTokens(client, config, from, amount, symbol, memo = '') {
447
+ return this.transferHiveTokens(client, config, from, NULL_ACCOUNT, amount, symbol, memo);
448
+ },
271
449
  /**
272
450
  * Broadcasts one or more Hive operations signed by one or multiple private keys.
273
451
  */
@@ -462,13 +640,9 @@ exports.Utils = {
462
640
  }
463
641
  const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
464
642
  let completed = 0;
465
- const amountBN = new bignumber_js_1.default(amount);
466
- if (amountBN.isNaN() || !amountBN.isFinite()) {
467
- throw new Error('Invalid transfer amount');
468
- }
469
643
  for (const user of accounts) {
470
644
  const to = user.replace('@', '');
471
- const formattedAmount = `${amountBN.toFixed(3)} ${symbol}`;
645
+ const formattedAmount = this.formatAssetAmount(amount, symbol);
472
646
  try {
473
647
  await client.broadcast.transfer({ from, to, amount: formattedAmount, memo }, key);
474
648
  completed++;
@@ -595,6 +769,19 @@ exports.Utils = {
595
769
  json: JSON.stringify(json)
596
770
  }, key);
597
771
  },
772
+ /**
773
+ * Burns Hive Engine tokens by transferring them to the null account.
774
+ * @param client - The Hive client instance
775
+ * @param config - Configuration containing the active key and Hive Engine ID
776
+ * @param from - Sender account name
777
+ * @param symbol - Token symbol
778
+ * @param quantity - Token quantity to burn
779
+ * @param memo - Optional memo for the burn transfer
780
+ * @returns Promise resolving to the broadcast result
781
+ */
782
+ burnHiveEngineTokens(client, config, from, symbol, quantity, memo = '') {
783
+ return this.transferHiveEngineTokens(client, config, from, NULL_ACCOUNT, quantity, symbol, memo);
784
+ },
598
785
  /**
599
786
  * Transfers Hive Engine tokens to multiple accounts in batches
600
787
  * @param client - The Hive client instance
@@ -965,6 +1152,1429 @@ exports.Utils = {
965
1152
  const encodedAmount = encodeURIComponent(amount);
966
1153
  const encodedRedirectUri = encodeURIComponent(redirectUri);
967
1154
  return `https://hivesigner.com/sign/transfer?to=${encodedTo}&memo=${encodedMemo}&amount=${encodedAmount}&redirect_uri=${encodedRedirectUri}`;
1155
+ },
1156
+ // ─── Social Operations ───────────────────────────────────────────────
1157
+ /**
1158
+ * Follows a Hive user by broadcasting a custom_json 'follow' operation
1159
+ * @param client - The Hive client instance
1160
+ * @param config - Configuration containing the posting key
1161
+ * @param follower - The account doing the following
1162
+ * @param following - The account to follow
1163
+ * @returns Promise resolving to the broadcast result
1164
+ */
1165
+ follow(client, config, follower, following) {
1166
+ if (!client || !config.POSTING_KEY || !follower || !following) {
1167
+ throw new Error('Missing required parameters for follow operation');
1168
+ }
1169
+ const json = JSON.stringify(['follow', {
1170
+ follower,
1171
+ following,
1172
+ what: ['blog']
1173
+ }]);
1174
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1175
+ return client.broadcast.json({
1176
+ required_auths: [],
1177
+ required_posting_auths: [follower],
1178
+ id: 'follow',
1179
+ json
1180
+ }, key);
1181
+ },
1182
+ /**
1183
+ * Unfollows a Hive user by broadcasting a custom_json 'follow' operation with empty what
1184
+ * @param client - The Hive client instance
1185
+ * @param config - Configuration containing the posting key
1186
+ * @param follower - The account doing the unfollowing
1187
+ * @param following - The account to unfollow
1188
+ * @returns Promise resolving to the broadcast result
1189
+ */
1190
+ unfollow(client, config, follower, following) {
1191
+ if (!client || !config.POSTING_KEY || !follower || !following) {
1192
+ throw new Error('Missing required parameters for unfollow operation');
1193
+ }
1194
+ const json = JSON.stringify(['follow', {
1195
+ follower,
1196
+ following,
1197
+ what: []
1198
+ }]);
1199
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1200
+ return client.broadcast.json({
1201
+ required_auths: [],
1202
+ required_posting_auths: [follower],
1203
+ id: 'follow',
1204
+ json
1205
+ }, key);
1206
+ },
1207
+ /**
1208
+ * Mutes a Hive user by broadcasting a custom_json 'follow' operation with 'ignore'
1209
+ * @param client - The Hive client instance
1210
+ * @param config - Configuration containing the posting key
1211
+ * @param follower - The account doing the muting
1212
+ * @param following - The account to mute
1213
+ * @returns Promise resolving to the broadcast result
1214
+ */
1215
+ mute(client, config, follower, following) {
1216
+ if (!client || !config.POSTING_KEY || !follower || !following) {
1217
+ throw new Error('Missing required parameters for mute operation');
1218
+ }
1219
+ const json = JSON.stringify(['follow', {
1220
+ follower,
1221
+ following,
1222
+ what: ['ignore']
1223
+ }]);
1224
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1225
+ return client.broadcast.json({
1226
+ required_auths: [],
1227
+ required_posting_auths: [follower],
1228
+ id: 'follow',
1229
+ json
1230
+ }, key);
1231
+ },
1232
+ /**
1233
+ * Reblogs (resteems) a post on Hive
1234
+ * @param client - The Hive client instance
1235
+ * @param config - Configuration containing the posting key
1236
+ * @param account - The account performing the reblog
1237
+ * @param author - The original post author
1238
+ * @param permlink - The permlink of the post to reblog
1239
+ * @returns Promise resolving to the broadcast result
1240
+ */
1241
+ reblog(client, config, account, author, permlink) {
1242
+ if (!client || !config.POSTING_KEY || !account || !author || !permlink) {
1243
+ throw new Error('Missing required parameters for reblog operation');
1244
+ }
1245
+ const json = JSON.stringify(['reblog', {
1246
+ account,
1247
+ author,
1248
+ permlink
1249
+ }]);
1250
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1251
+ return client.broadcast.json({
1252
+ required_auths: [],
1253
+ required_posting_auths: [account],
1254
+ id: 'follow',
1255
+ json
1256
+ }, key);
1257
+ },
1258
+ // ─── Staking Operations ─────────────────────────────────────────────
1259
+ /**
1260
+ * Powers up HIVE to Hive Power (VESTS) for an account
1261
+ * @param client - The Hive client instance
1262
+ * @param config - Configuration containing the active key
1263
+ * @param from - The account providing the HIVE
1264
+ * @param to - The account receiving the Hive Power (can be same as from)
1265
+ * @param amount - The amount of HIVE to power up
1266
+ * @returns Promise resolving to the broadcast result
1267
+ */
1268
+ powerUp(client, config, from, to, amount) {
1269
+ if (!client || !config.ACTIVE_KEY || !from || !to || !amount) {
1270
+ throw new Error('Missing required parameters for power up operation');
1271
+ }
1272
+ const formattedAmount = this.formatAssetAmount(amount, 'HIVE');
1273
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1274
+ const operation = ['transfer_to_vesting', {
1275
+ from,
1276
+ to,
1277
+ amount: formattedAmount
1278
+ }];
1279
+ return this.broadcastOperations(client, [operation], key);
1280
+ },
1281
+ /**
1282
+ * Initiates a power down (withdrawal of vesting shares) for an account
1283
+ * @param client - The Hive client instance
1284
+ * @param config - Configuration containing the active key
1285
+ * @param account - The account to power down
1286
+ * @param vestingShares - The amount of VESTS to power down (e.g. '1000.000000 VESTS')
1287
+ * @returns Promise resolving to the broadcast result
1288
+ */
1289
+ powerDown(client, config, account, vestingShares) {
1290
+ if (!client || !config.ACTIVE_KEY || !account || !vestingShares) {
1291
+ throw new Error('Missing required parameters for power down operation');
1292
+ }
1293
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1294
+ const operation = ['withdraw_vesting', {
1295
+ account,
1296
+ vesting_shares: vestingShares.includes('VESTS') ? vestingShares : `${vestingShares} VESTS`
1297
+ }];
1298
+ return this.broadcastOperations(client, [operation], key);
1299
+ },
1300
+ /**
1301
+ * Cancels an active power down by setting vesting shares to 0
1302
+ * @param client - The Hive client instance
1303
+ * @param config - Configuration containing the active key
1304
+ * @param account - The account to cancel power down for
1305
+ * @returns Promise resolving to the broadcast result
1306
+ */
1307
+ cancelPowerDown(client, config, account) {
1308
+ if (!client || !config.ACTIVE_KEY || !account) {
1309
+ throw new Error('Missing required parameters for cancel power down operation');
1310
+ }
1311
+ return this.powerDown(client, config, account, '0.000000 VESTS');
1312
+ },
1313
+ /**
1314
+ * Delegates vesting shares (Hive Power) to another account
1315
+ * @param client - The Hive client instance
1316
+ * @param config - Configuration containing the active key
1317
+ * @param delegator - The account delegating HP
1318
+ * @param delegatee - The account receiving the delegation
1319
+ * @param vestingShares - The amount of VESTS to delegate (e.g. '1000.000000 VESTS')
1320
+ * @returns Promise resolving to the broadcast result
1321
+ */
1322
+ delegateVestingShares(client, config, delegator, delegatee, vestingShares) {
1323
+ if (!client || !config.ACTIVE_KEY || !delegator || !delegatee || !vestingShares) {
1324
+ throw new Error('Missing required parameters for delegate vesting shares operation');
1325
+ }
1326
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1327
+ const operation = ['delegate_vesting_shares', {
1328
+ delegator,
1329
+ delegatee,
1330
+ vesting_shares: vestingShares.includes('VESTS') ? vestingShares : `${vestingShares} VESTS`
1331
+ }];
1332
+ return this.broadcastOperations(client, [operation], key);
1333
+ },
1334
+ /**
1335
+ * Removes a vesting shares delegation by delegating 0 VESTS
1336
+ * @param client - The Hive client instance
1337
+ * @param config - Configuration containing the active key
1338
+ * @param delegator - The account removing the delegation
1339
+ * @param delegatee - The account to remove delegation from
1340
+ * @returns Promise resolving to the broadcast result
1341
+ */
1342
+ undelegateVestingShares(client, config, delegator, delegatee) {
1343
+ if (!client || !config.ACTIVE_KEY || !delegator || !delegatee) {
1344
+ throw new Error('Missing required parameters for undelegate vesting shares operation');
1345
+ }
1346
+ return this.delegateVestingShares(client, config, delegator, delegatee, '0.000000 VESTS');
1347
+ },
1348
+ // ─── Account Operations ─────────────────────────────────────────────
1349
+ /**
1350
+ * Claims pending rewards for an account
1351
+ * @param client - The Hive client instance
1352
+ * @param config - Configuration containing the posting key
1353
+ * @param account - The account to claim rewards for
1354
+ * @param rewardHive - HIVE rewards to claim (e.g. '0.000 HIVE')
1355
+ * @param rewardHbd - HBD rewards to claim (e.g. '0.000 HBD')
1356
+ * @param rewardVests - VESTS rewards to claim (e.g. '0.000000 VESTS')
1357
+ * @returns Promise resolving to the broadcast result
1358
+ */
1359
+ claimRewards(client, config, account, rewardHive, rewardHbd, rewardVests) {
1360
+ if (!client || !config.POSTING_KEY || !account) {
1361
+ throw new Error('Missing required parameters for claim rewards operation');
1362
+ }
1363
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1364
+ const operation = ['claim_reward_balance', {
1365
+ account,
1366
+ reward_hive: rewardHive || '0.000 HIVE',
1367
+ reward_hbd: rewardHbd || '0.000 HBD',
1368
+ reward_vests: rewardVests || '0.000000 VESTS'
1369
+ }];
1370
+ return this.broadcastOperations(client, [operation], key);
1371
+ },
1372
+ /**
1373
+ * Votes for or unvotes a witness
1374
+ * @param client - The Hive client instance
1375
+ * @param config - Configuration containing the active key
1376
+ * @param account - The account casting the witness vote
1377
+ * @param witness - The witness account to vote for
1378
+ * @param approve - true to vote, false to remove vote
1379
+ * @returns Promise resolving to the broadcast result
1380
+ */
1381
+ witnessVote(client, config, account, witness, approve = true) {
1382
+ if (!client || !config.ACTIVE_KEY || !account || !witness) {
1383
+ throw new Error('Missing required parameters for witness vote operation');
1384
+ }
1385
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1386
+ const operation = ['account_witness_vote', {
1387
+ account,
1388
+ witness,
1389
+ approve
1390
+ }];
1391
+ return this.broadcastOperations(client, [operation], key);
1392
+ },
1393
+ /**
1394
+ * Sets a governance proxy for an account (delegates witness voting to another account)
1395
+ * @param client - The Hive client instance
1396
+ * @param config - Configuration containing the active key
1397
+ * @param account - The account setting the proxy
1398
+ * @param proxy - The account to proxy votes to
1399
+ * @returns Promise resolving to the broadcast result
1400
+ */
1401
+ setProxy(client, config, account, proxy) {
1402
+ if (!client || !config.ACTIVE_KEY || !account || proxy === undefined || proxy === null) {
1403
+ throw new Error('Missing required parameters for set proxy operation');
1404
+ }
1405
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1406
+ const operation = ['account_witness_proxy', {
1407
+ account,
1408
+ proxy
1409
+ }];
1410
+ return this.broadcastOperations(client, [operation], key);
1411
+ },
1412
+ /**
1413
+ * Clears the governance proxy for an account
1414
+ * @param client - The Hive client instance
1415
+ * @param config - Configuration containing the active key
1416
+ * @param account - The account clearing the proxy
1417
+ * @returns Promise resolving to the broadcast result
1418
+ */
1419
+ clearProxy(client, config, account) {
1420
+ if (!client || !config.ACTIVE_KEY || !account) {
1421
+ throw new Error('Missing required parameters for clear proxy operation');
1422
+ }
1423
+ return this.setProxy(client, config, account, '');
1424
+ },
1425
+ /**
1426
+ * Updates an account's profile metadata using account_update2
1427
+ * @param client - The Hive client instance
1428
+ * @param config - Configuration containing the posting key
1429
+ * @param account - The account to update
1430
+ * @param profile - Profile data (name, about, location, website, profile_image, cover_image)
1431
+ * @returns Promise resolving to the broadcast result
1432
+ */
1433
+ async updateProfile(client, config, account, profile) {
1434
+ if (!client || !config.POSTING_KEY || !account) {
1435
+ throw new Error('Missing required parameters for update profile operation');
1436
+ }
1437
+ const accounts = await client.database.getAccounts([account]);
1438
+ const existingAccount = Array.isArray(accounts) ? accounts[0] : null;
1439
+ if (!existingAccount) {
1440
+ throw new Error(`Unable to load account '${account}' for profile update`);
1441
+ }
1442
+ let existingMeta = {};
1443
+ try {
1444
+ existingMeta = JSON.parse(existingAccount.posting_json_metadata || '{}');
1445
+ }
1446
+ catch {
1447
+ existingMeta = {};
1448
+ }
1449
+ const updatedMeta = {
1450
+ ...existingMeta,
1451
+ profile: {
1452
+ ...(existingMeta.profile || {}),
1453
+ ...profile
1454
+ }
1455
+ };
1456
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1457
+ const operation = ['account_update2', {
1458
+ account,
1459
+ json_metadata: '',
1460
+ posting_json_metadata: JSON.stringify(updatedMeta),
1461
+ extensions: []
1462
+ }];
1463
+ return this.broadcastOperations(client, [operation], key);
1464
+ },
1465
+ /**
1466
+ * Fetches account data for a single account
1467
+ * @param client - The Hive client instance
1468
+ * @param username - The account name to look up
1469
+ * @returns Promise resolving to the account data, or null if not found
1470
+ */
1471
+ async getAccount(client, username) {
1472
+ if (!client || !username) {
1473
+ throw new Error('Client and username are required');
1474
+ }
1475
+ const accounts = await client.database.getAccounts([username]);
1476
+ return Array.isArray(accounts) && accounts.length > 0 ? accounts[0] : null;
1477
+ },
1478
+ /**
1479
+ * Fetches account data for multiple accounts
1480
+ * @param client - The Hive client instance
1481
+ * @param usernames - Array of account names to look up
1482
+ * @returns Promise resolving to array of account data
1483
+ */
1484
+ async getAccounts(client, usernames) {
1485
+ if (!client || !Array.isArray(usernames) || usernames.length === 0) {
1486
+ throw new Error('Client and at least one username are required');
1487
+ }
1488
+ if (usernames.length > MAX_ACCOUNTS_CHECK) {
1489
+ throw new Error(`Cannot look up more than ${MAX_ACCOUNTS_CHECK} accounts at once`);
1490
+ }
1491
+ const accounts = await client.database.getAccounts(usernames);
1492
+ return Array.isArray(accounts) ? accounts : [];
1493
+ },
1494
+ // ─── Blockchain Helpers ─────────────────────────────────────────────
1495
+ /**
1496
+ * Converts raw Hive reputation value to a human-readable score (roughly 25-75 range)
1497
+ * @param rawReputation - The raw reputation value from the blockchain (can be string or number)
1498
+ * @returns The human-readable reputation score
1499
+ */
1500
+ calculateReputation(rawReputation) {
1501
+ const rep = new bignumber_js_1.default(rawReputation);
1502
+ if (rep.isZero()) {
1503
+ return 25;
1504
+ }
1505
+ const negative = rep.isNegative();
1506
+ const absRep = rep.abs();
1507
+ let score = absRep.toNumber();
1508
+ score = Math.log10(score);
1509
+ score = (score - 9) * 9 + 25;
1510
+ if (negative) {
1511
+ score = 50 - (score - 50);
1512
+ }
1513
+ return Math.floor(score * 100) / 100;
1514
+ },
1515
+ /**
1516
+ * Converts VESTS to Hive Power (HP)
1517
+ * @param vests - The amount of VESTS (as string or number)
1518
+ * @param totalVestingFundHive - Total vesting fund in HIVE from dynamic global properties
1519
+ * @param totalVestingShares - Total vesting shares from dynamic global properties
1520
+ * @returns The equivalent Hive Power amount as a string with 3 decimal places
1521
+ */
1522
+ vestToHP(vests, totalVestingFundHive, totalVestingShares) {
1523
+ const vestsValue = new bignumber_js_1.default(String(vests).replace(' VESTS', ''));
1524
+ const fundValue = new bignumber_js_1.default(String(totalVestingFundHive).replace(' HIVE', ''));
1525
+ const sharesValue = new bignumber_js_1.default(String(totalVestingShares).replace(' VESTS', ''));
1526
+ if (vestsValue.isNaN() || fundValue.isNaN() || sharesValue.isNaN()) {
1527
+ throw new Error('Invalid numeric input for VESTS to HP conversion');
1528
+ }
1529
+ if (sharesValue.isZero()) {
1530
+ throw new Error('Total vesting shares cannot be zero');
1531
+ }
1532
+ return vestsValue.multipliedBy(fundValue).dividedBy(sharesValue).toFixed(3);
1533
+ },
1534
+ /**
1535
+ * Converts Hive Power (HP) to VESTS
1536
+ * @param hp - The amount of HP (as string or number)
1537
+ * @param totalVestingFundHive - Total vesting fund in HIVE from dynamic global properties
1538
+ * @param totalVestingShares - Total vesting shares from dynamic global properties
1539
+ * @returns The equivalent VESTS amount as a string with 6 decimal places
1540
+ */
1541
+ hpToVest(hp, totalVestingFundHive, totalVestingShares) {
1542
+ const hpValue = new bignumber_js_1.default(String(hp).replace(' HIVE', ''));
1543
+ const fundValue = new bignumber_js_1.default(String(totalVestingFundHive).replace(' HIVE', ''));
1544
+ const sharesValue = new bignumber_js_1.default(String(totalVestingShares).replace(' VESTS', ''));
1545
+ if (hpValue.isNaN() || fundValue.isNaN() || sharesValue.isNaN()) {
1546
+ throw new Error('Invalid numeric input for HP to VESTS conversion');
1547
+ }
1548
+ if (fundValue.isZero()) {
1549
+ throw new Error('Total vesting fund cannot be zero');
1550
+ }
1551
+ return hpValue.multipliedBy(sharesValue).dividedBy(fundValue).toFixed(6);
1552
+ },
1553
+ /**
1554
+ * Parses Hive account profile metadata from json_metadata or posting_json_metadata
1555
+ * @param jsonMetadata - The raw JSON metadata string from the account
1556
+ * @returns Parsed profile object, or empty object if parsing fails
1557
+ */
1558
+ parseProfileMetadata(jsonMetadata) {
1559
+ if (!jsonMetadata || typeof jsonMetadata !== 'string') {
1560
+ return {};
1561
+ }
1562
+ try {
1563
+ const parsed = JSON.parse(jsonMetadata);
1564
+ if (parsed && typeof parsed === 'object' && parsed.profile && typeof parsed.profile === 'object') {
1565
+ return parsed.profile;
1566
+ }
1567
+ return {};
1568
+ }
1569
+ catch {
1570
+ return {};
1571
+ }
1572
+ },
1573
+ /**
1574
+ * Converts a Hive Power (HP) amount to a formatted VESTS string suitable for delegation/power down
1575
+ * @param hp - The amount of HP
1576
+ * @param totalVestingFundHive - Total vesting fund in HIVE
1577
+ * @param totalVestingShares - Total vesting shares
1578
+ * @returns Formatted VESTS string (e.g. '1234.567890 VESTS')
1579
+ */
1580
+ hpToVestString(hp, totalVestingFundHive, totalVestingShares) {
1581
+ const vests = this.hpToVest(hp, totalVestingFundHive, totalVestingShares);
1582
+ return `${vests} VESTS`;
1583
+ },
1584
+ // ─── Savings Operations ─────────────────────────────────────────────
1585
+ transferToSavings(client, config, from, to, amount, symbol, memo = '') {
1586
+ if (!client || !config.ACTIVE_KEY || !from || !to || !amount || !symbol) {
1587
+ throw new Error('Missing required parameters for transfer to savings');
1588
+ }
1589
+ const formattedAmount = this.formatAssetAmount(amount, symbol);
1590
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1591
+ const operation = ['transfer_to_savings', {
1592
+ from,
1593
+ to,
1594
+ amount: formattedAmount,
1595
+ memo
1596
+ }];
1597
+ return this.broadcastOperations(client, [operation], key);
1598
+ },
1599
+ transferFromSavings(client, config, from, to, amount, symbol, requestId, memo = '') {
1600
+ if (!client || !config.ACTIVE_KEY || !from || !to || !amount || !symbol) {
1601
+ throw new Error('Missing required parameters for transfer from savings');
1602
+ }
1603
+ const formattedAmount = this.formatAssetAmount(amount, symbol);
1604
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1605
+ const operation = ['transfer_from_savings', {
1606
+ from,
1607
+ to,
1608
+ amount: formattedAmount,
1609
+ memo,
1610
+ request_id: requestId || 0
1611
+ }];
1612
+ return this.broadcastOperations(client, [operation], key);
1613
+ },
1614
+ cancelTransferFromSavings(client, config, from, requestId) {
1615
+ if (!client || !config.ACTIVE_KEY || !from || typeof requestId !== 'number') {
1616
+ throw new Error('Missing required parameters for cancel transfer from savings');
1617
+ }
1618
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1619
+ const operation = ['cancel_transfer_from_savings', {
1620
+ from,
1621
+ request_id: requestId
1622
+ }];
1623
+ return this.broadcastOperations(client, [operation], key);
1624
+ },
1625
+ // ─── Convert Operations ─────────────────────────────────────────────
1626
+ convert(client, config, owner, amount, requestId = 0) {
1627
+ if (!client || !config.ACTIVE_KEY || !owner || !amount) {
1628
+ throw new Error('Missing required parameters for convert operation');
1629
+ }
1630
+ const formattedAmount = amount.includes('HBD') ? amount : this.formatAssetAmount(amount, 'HBD');
1631
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1632
+ const operation = ['convert', {
1633
+ owner,
1634
+ requestid: requestId,
1635
+ amount: formattedAmount
1636
+ }];
1637
+ return this.broadcastOperations(client, [operation], key);
1638
+ },
1639
+ collateralizedConvert(client, config, owner, amount, requestId = 0) {
1640
+ if (!client || !config.ACTIVE_KEY || !owner || !amount) {
1641
+ throw new Error('Missing required parameters for collateralized convert operation');
1642
+ }
1643
+ const formattedAmount = amount.includes('HIVE') ? amount : this.formatAssetAmount(amount, 'HIVE');
1644
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1645
+ const operation = ['collateralized_convert', {
1646
+ owner,
1647
+ requestid: requestId,
1648
+ amount: formattedAmount
1649
+ }];
1650
+ return this.broadcastOperations(client, [operation], key);
1651
+ },
1652
+ // ─── Content Operations ─────────────────────────────────────────────
1653
+ deleteComment(client, config, author, permlink) {
1654
+ if (!client || !config.POSTING_KEY || !author || !permlink) {
1655
+ throw new Error('Missing required parameters for delete comment');
1656
+ }
1657
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1658
+ const operation = ['delete_comment', {
1659
+ author,
1660
+ permlink
1661
+ }];
1662
+ return this.broadcastOperations(client, [operation], key);
1663
+ },
1664
+ commentOptions(client, config, author, permlink, options) {
1665
+ if (!client || !config.POSTING_KEY || !author || !permlink) {
1666
+ throw new Error('Missing required parameters for comment options');
1667
+ }
1668
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
1669
+ const operation = ['comment_options', {
1670
+ author,
1671
+ permlink,
1672
+ max_accepted_payout: options.max_accepted_payout || '1000000.000 HBD',
1673
+ percent_hbd: options.percent_hbd !== undefined ? options.percent_hbd : 10000,
1674
+ allow_votes: options.allow_votes !== false,
1675
+ allow_curation_rewards: options.allow_curation_rewards !== false,
1676
+ extensions: options.extensions || []
1677
+ }];
1678
+ return this.broadcastOperations(client, [operation], key);
1679
+ },
1680
+ // ─── Market Operations ──────────────────────────────────────────────
1681
+ limitOrderCreate(client, config, owner, orderId, amountToSell, minToReceive, fillOrKill = false, expiration) {
1682
+ if (!client || !config.ACTIVE_KEY || !owner || !amountToSell || !minToReceive) {
1683
+ throw new Error('Missing required parameters for limit order create');
1684
+ }
1685
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1686
+ const expirationDate = expiration
1687
+ ? this.toHiveTimestamp(expiration)
1688
+ : this.toHiveTimestamp(new Date(Date.now() + 28 * 24 * 60 * 60 * 1000));
1689
+ const operation = ['limit_order_create', {
1690
+ owner,
1691
+ orderid: orderId,
1692
+ amount_to_sell: amountToSell,
1693
+ min_to_receive: minToReceive,
1694
+ fill_or_kill: fillOrKill,
1695
+ expiration: expirationDate
1696
+ }];
1697
+ return this.broadcastOperations(client, [operation], key);
1698
+ },
1699
+ limitOrderCancel(client, config, owner, orderId) {
1700
+ if (!client || !config.ACTIVE_KEY || !owner || typeof orderId !== 'number') {
1701
+ throw new Error('Missing required parameters for limit order cancel');
1702
+ }
1703
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1704
+ const operation = ['limit_order_cancel', {
1705
+ owner,
1706
+ orderid: orderId
1707
+ }];
1708
+ return this.broadcastOperations(client, [operation], key);
1709
+ },
1710
+ // ─── Vesting Route ──────────────────────────────────────────────────
1711
+ setWithdrawVestingRoute(client, config, fromAccount, toAccount, percent, autoVest = false, signingKeys) {
1712
+ if (!client || !fromAccount || !toAccount) {
1713
+ throw new Error('Missing required parameters for set withdraw vesting route');
1714
+ }
1715
+ if (percent < 0 || percent > 10000) {
1716
+ throw new Error('Percent must be between 0 and 10000');
1717
+ }
1718
+ const keys = signingKeys || config.ACTIVE_KEY;
1719
+ if (!keys) {
1720
+ throw new Error('Active key or explicit signing keys are required');
1721
+ }
1722
+ const operation = ['set_withdraw_vesting_route', {
1723
+ from_account: fromAccount,
1724
+ to_account: toAccount,
1725
+ percent,
1726
+ auto_vest: autoVest
1727
+ }];
1728
+ return this.broadcastOperations(client, [operation], keys);
1729
+ },
1730
+ // ─── Account Creation ───────────────────────────────────────────────
1731
+ claimAccount(client, config, creator, fee = '0.000 HIVE') {
1732
+ if (!client || !config.ACTIVE_KEY || !creator) {
1733
+ throw new Error('Missing required parameters for claim account');
1734
+ }
1735
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1736
+ const operation = ['claim_account', {
1737
+ creator,
1738
+ fee,
1739
+ extensions: []
1740
+ }];
1741
+ return this.broadcastOperations(client, [operation], key);
1742
+ },
1743
+ createClaimedAccount(client, config, creator, newAccountName, owner, active, posting, memoKey, jsonMetadata = '') {
1744
+ if (!client || !config.ACTIVE_KEY || !creator || !newAccountName || !owner || !active || !posting || !memoKey) {
1745
+ throw new Error('Missing required parameters for create claimed account');
1746
+ }
1747
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1748
+ const operation = ['create_claimed_account', {
1749
+ creator,
1750
+ new_account_name: newAccountName,
1751
+ owner,
1752
+ active,
1753
+ posting,
1754
+ memo_key: memoKey,
1755
+ json_metadata: jsonMetadata,
1756
+ extensions: []
1757
+ }];
1758
+ return this.broadcastOperations(client, [operation], key);
1759
+ },
1760
+ // ─── Witness Operations ─────────────────────────────────────────────
1761
+ feedPublish(client, config, publisher, baseAmount, quoteAmount = '1.000 HIVE') {
1762
+ if (!client || !config.ACTIVE_KEY || !publisher || !baseAmount) {
1763
+ throw new Error('Missing required parameters for feed publish');
1764
+ }
1765
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
1766
+ const operation = ['feed_publish', {
1767
+ publisher,
1768
+ exchange_rate: {
1769
+ base: baseAmount,
1770
+ quote: quoteAmount
1771
+ }
1772
+ }];
1773
+ return this.broadcastOperations(client, [operation], key);
1774
+ },
1775
+ // ─── Comprehensive Utility Helpers ──────────────────────────────────
1776
+ /**
1777
+ * Calculates current voting mana percentage for an account
1778
+ * @param account - Extended account object from getAccounts()
1779
+ * @returns Voting mana as a percentage (0-100)
1780
+ */
1781
+ calculateVotingMana(account) {
1782
+ if (!account || !account.voting_manabar) {
1783
+ throw new Error('Invalid account object');
1784
+ }
1785
+ const lastMana = parseInt(account.voting_manabar.current_mana, 10);
1786
+ const lastUpdate = parseInt(account.voting_manabar.last_update_time, 10);
1787
+ const now = Math.floor(Date.now() / 1000);
1788
+ const maxMana = this.getEffectiveVestingShares(account);
1789
+ if (maxMana === 0) {
1790
+ return 0;
1791
+ }
1792
+ const elapsed = now - lastUpdate;
1793
+ const regenerated = (maxMana * elapsed) / (5 * 24 * 60 * 60);
1794
+ const currentMana = Math.min(lastMana + regenerated, maxMana);
1795
+ return Math.min(100, (currentMana / maxMana) * 100);
1796
+ },
1797
+ /**
1798
+ * Gets the effective vesting shares for an account (own + received - delegated)
1799
+ * @param account - Extended account object
1800
+ * @returns Effective vesting shares as a number
1801
+ */
1802
+ getEffectiveVestingShares(account) {
1803
+ const own = parseFloat(String(account.vesting_shares || '0').replace(' VESTS', ''));
1804
+ const received = parseFloat(String(account.received_vesting_shares || '0').replace(' VESTS', ''));
1805
+ const delegated = parseFloat(String(account.delegated_vesting_shares || '0').replace(' VESTS', ''));
1806
+ return own + received - delegated;
1807
+ },
1808
+ /**
1809
+ * Estimates the value of a vote in USD
1810
+ * @param votingPower - Current voting power percentage (0-100)
1811
+ * @param weight - Vote weight percentage (0-100)
1812
+ * @param effectiveVests - Effective vesting shares as number
1813
+ * @param rewardFund - Reward fund object from condenser_api.get_reward_fund
1814
+ * @param medianPrice - Median price object from getDynamicGlobalProperties
1815
+ * @returns Estimated vote value in USD
1816
+ */
1817
+ estimateVoteValue(votingPower, weight, effectiveVests, rewardFund, medianPrice) {
1818
+ if (!rewardFund || !medianPrice) {
1819
+ throw new Error('Reward fund and median price are required');
1820
+ }
1821
+ const voteWeight = Math.floor(votingPower * 100 * weight);
1822
+ const rshares = effectiveVests * 1e6 * (voteWeight / 10000);
1823
+ const rewardBalance = parseFloat(rewardFund.reward_balance.replace(' HIVE', ''));
1824
+ const recentClaims = parseFloat(rewardFund.recent_claims);
1825
+ const base = parseFloat(medianPrice.base.replace(' HBD', ''));
1826
+ const quote = parseFloat(medianPrice.quote.replace(' HIVE', ''));
1827
+ if (recentClaims === 0 || quote === 0) {
1828
+ return 0;
1829
+ }
1830
+ const hivePerRshare = rewardBalance / recentClaims;
1831
+ const voteValue = rshares * hivePerRshare * (base / quote);
1832
+ return Math.max(0, Math.round(voteValue * 1000) / 1000);
1833
+ },
1834
+ /**
1835
+ * Generates a URL-safe permlink from a title
1836
+ * @param title - The post title
1837
+ * @returns A valid Hive permlink string
1838
+ */
1839
+ generatePermlink(title) {
1840
+ if (!title || typeof title !== 'string') {
1841
+ throw new Error('Title is required');
1842
+ }
1843
+ return title
1844
+ .toLowerCase()
1845
+ .replace(/[^\w\s-]/g, '')
1846
+ .replace(/\s+/g, '-')
1847
+ .replace(/-+/g, '-')
1848
+ .replace(/^-|-$/g, '')
1849
+ .substring(0, 255) || 'untitled';
1850
+ },
1851
+ /**
1852
+ * Validates a Hive account name according to blockchain rules
1853
+ * @param name - The account name to validate
1854
+ * @returns null if valid, or an error message string
1855
+ */
1856
+ validateAccountName(name) {
1857
+ if (!name) {
1858
+ return 'Account name is required';
1859
+ }
1860
+ if (typeof name !== 'string') {
1861
+ return 'Account name must be a string';
1862
+ }
1863
+ const length = name.length;
1864
+ if (length < 3) {
1865
+ return 'Account name must be at least 3 characters';
1866
+ }
1867
+ if (length > 16) {
1868
+ return 'Account name must be at most 16 characters';
1869
+ }
1870
+ if (!/^[a-z]/.test(name)) {
1871
+ return 'Account name must start with a letter';
1872
+ }
1873
+ if (!/^[a-z0-9.-]+$/.test(name)) {
1874
+ return 'Account name may only contain lowercase letters, digits, dots, and hyphens';
1875
+ }
1876
+ if (/\.\./.test(name) || /--/.test(name)) {
1877
+ return 'Account name may not contain consecutive dots or hyphens';
1878
+ }
1879
+ if (/\.$/.test(name) || /-$/.test(name)) {
1880
+ return 'Account name may not end with a dot or hyphen';
1881
+ }
1882
+ const segments = name.split('.');
1883
+ for (const segment of segments) {
1884
+ if (segment.length < 3) {
1885
+ return 'Each account name segment must be at least 3 characters';
1886
+ }
1887
+ if (!/^[a-z]/.test(segment)) {
1888
+ return 'Each account name segment must start with a letter';
1889
+ }
1890
+ }
1891
+ return null;
1892
+ },
1893
+ /**
1894
+ * Returns true if the given string is a valid Hive account name
1895
+ * @param name - The account name to check
1896
+ * @returns true if valid, false otherwise
1897
+ */
1898
+ isValidAccountName(name) {
1899
+ return this.validateAccountName(name) === null;
1900
+ },
1901
+ /**
1902
+ * Checks if an account exists on the blockchain
1903
+ * @param client - The Hive client instance
1904
+ * @param username - Account name to check
1905
+ * @returns Promise resolving to true if account exists
1906
+ */
1907
+ async accountExists(client, username) {
1908
+ const account = await this.getAccount(client, username);
1909
+ return account !== null;
1910
+ },
1911
+ /**
1912
+ * Parses a Hive URL or link into its components
1913
+ * @param url - URL like https://hive.blog/@author/permlink or @author/permlink
1914
+ * @returns Parsed components { author, permlink, category? } or null if invalid
1915
+ */
1916
+ parseHiveUrl(url) {
1917
+ if (!url || typeof url !== 'string') {
1918
+ return null;
1919
+ }
1920
+ const cleaned = url.trim();
1921
+ // Handle @author/permlink format
1922
+ const atMatch = cleaned.match(/^@([a-z0-9.-]+)\/([a-z0-9-]+)$/);
1923
+ if (atMatch) {
1924
+ return { author: atMatch[1], permlink: atMatch[2] };
1925
+ }
1926
+ // Handle full URLs: https://hive.blog/category/@author/permlink
1927
+ const urlMatch = cleaned.match(/(?:https?:\/\/[^\/]+)?\/(?:([^\/]+)\/)?@([a-z0-9.-]+)\/([a-z0-9-]+)/);
1928
+ if (urlMatch) {
1929
+ return {
1930
+ author: urlMatch[2],
1931
+ permlink: urlMatch[3],
1932
+ category: urlMatch[1] || undefined
1933
+ };
1934
+ }
1935
+ return null;
1936
+ },
1937
+ /**
1938
+ * Generates a unique permlink with timestamp for replies
1939
+ * @param parentPermlink - The parent post's permlink (optional)
1940
+ * @returns A unique permlink string
1941
+ */
1942
+ generateReplyPermlink(parentPermlink) {
1943
+ const timestamp = new Date().toISOString().replace(/[^a-z0-9]/gi, '').toLowerCase();
1944
+ const prefix = parentPermlink ? `re-${parentPermlink.substring(0, 100)}` : 're';
1945
+ return `${prefix}-${timestamp}`;
1946
+ },
1947
+ /**
1948
+ * Creates the json_metadata string for a Hive post
1949
+ * @param options - Post metadata options
1950
+ * @returns JSON string for json_metadata field
1951
+ */
1952
+ createPostMetadata(options = {}) {
1953
+ const metadata = {
1954
+ tags: options.tags || [],
1955
+ image: options.image || [],
1956
+ links: options.links || [],
1957
+ app: options.app || 'hive-stream',
1958
+ format: options.format || 'markdown'
1959
+ };
1960
+ if (options.description) {
1961
+ metadata.description = options.description;
1962
+ }
1963
+ // Include any additional custom fields
1964
+ for (const [key, value] of Object.entries(options)) {
1965
+ if (!['tags', 'image', 'links', 'app', 'format', 'description'].includes(key)) {
1966
+ metadata[key] = value;
1967
+ }
1968
+ }
1969
+ return JSON.stringify(metadata);
1970
+ },
1971
+ /**
1972
+ * Encodes a memo with a shared secret (requires sender private memo key and receiver public memo key)
1973
+ * Note: Requires @hiveio/dhive Memo support
1974
+ */
1975
+ encodeMemo(privateKey, publicKey, message) {
1976
+ // Dynamic import to avoid hard dependency on Memo
1977
+ try {
1978
+ const { Memo } = require('@hiveio/dhive');
1979
+ return Memo.encode(dhive_1.PrivateKey.fromString(privateKey), publicKey, `#${message}`);
1980
+ }
1981
+ catch {
1982
+ throw new Error('Memo encoding requires @hiveio/dhive with Memo support');
1983
+ }
1984
+ },
1985
+ /**
1986
+ * Decodes an encrypted memo
1987
+ * Note: Requires @hiveio/dhive Memo support
1988
+ */
1989
+ decodeMemo(privateKey, encodedMemo) {
1990
+ try {
1991
+ const { Memo } = require('@hiveio/dhive');
1992
+ return Memo.decode(dhive_1.PrivateKey.fromString(privateKey), encodedMemo);
1993
+ }
1994
+ catch {
1995
+ throw new Error('Memo decoding requires @hiveio/dhive with Memo support');
1996
+ }
1997
+ },
1998
+ // ─── Authority Management ───────────────────────────────────────────
1999
+ /**
2000
+ * Checks if an account has granted posting authority to an app/account
2001
+ */
2002
+ async hasPostingAuth(client, account, authAccount) {
2003
+ const acc = await this.getAccount(client, account);
2004
+ if (!acc || !acc.posting || !Array.isArray(acc.posting.account_auths)) {
2005
+ return false;
2006
+ }
2007
+ return acc.posting.account_auths.some(([name]) => name === authAccount);
2008
+ },
2009
+ /**
2010
+ * Grants posting authority to an app/account
2011
+ */
2012
+ async grantPostingAuth(client, config, account, authAccount, signingKeys) {
2013
+ const acc = await this.getAccount(client, account);
2014
+ if (!acc) {
2015
+ throw new Error(`Account '${account}' not found`);
2016
+ }
2017
+ const existing = acc.posting.account_auths || [];
2018
+ if (existing.some(([name]) => name === authAccount)) {
2019
+ return; // Already authorized
2020
+ }
2021
+ const newAuths = [...existing, [authAccount, 1]].sort((a, b) => a[0].localeCompare(b[0]));
2022
+ const keys = signingKeys || config.ACTIVE_KEY;
2023
+ if (!keys) {
2024
+ throw new Error('Active key is required to grant posting authority');
2025
+ }
2026
+ const operation = ['account_update', {
2027
+ account,
2028
+ memo_key: acc.memo_key,
2029
+ json_metadata: acc.json_metadata || '',
2030
+ posting: {
2031
+ ...acc.posting,
2032
+ account_auths: newAuths
2033
+ }
2034
+ }];
2035
+ return this.broadcastOperations(client, [operation], keys);
2036
+ },
2037
+ /**
2038
+ * Revokes posting authority from an app/account
2039
+ */
2040
+ async revokePostingAuth(client, config, account, authAccount, signingKeys) {
2041
+ const acc = await this.getAccount(client, account);
2042
+ if (!acc) {
2043
+ throw new Error(`Account '${account}' not found`);
2044
+ }
2045
+ const existing = acc.posting.account_auths || [];
2046
+ const filtered = existing.filter(([name]) => name !== authAccount);
2047
+ if (filtered.length === existing.length) {
2048
+ return; // Was not authorized
2049
+ }
2050
+ const keys = signingKeys || config.ACTIVE_KEY;
2051
+ if (!keys) {
2052
+ throw new Error('Active key is required to revoke posting authority');
2053
+ }
2054
+ const operation = ['account_update', {
2055
+ account,
2056
+ memo_key: acc.memo_key,
2057
+ json_metadata: acc.json_metadata || '',
2058
+ posting: {
2059
+ ...acc.posting,
2060
+ account_auths: filtered
2061
+ }
2062
+ }];
2063
+ return this.broadcastOperations(client, [operation], keys);
2064
+ },
2065
+ // ─── Power Down Schedule ────────────────────────────────────────────
2066
+ /**
2067
+ * Calculates the remaining power down schedule for an account
2068
+ * @param account - Extended account object from getAccounts()
2069
+ * @returns Array of weekly payment entries with dates and amounts
2070
+ */
2071
+ calculatePowerDownSchedule(account) {
2072
+ const withdrawRate = parseFloat(String(account.vesting_withdraw_rate || '0').replace(' VESTS', ''));
2073
+ const nextDate = new Date(account.next_vesting_withdrawal + 'Z');
2074
+ const toWithdraw = parseFloat(String(account.to_withdraw || '0'));
2075
+ const withdrawn = parseFloat(String(account.withdrawn || '0'));
2076
+ if (withdrawRate <= 0 || toWithdraw <= withdrawn) {
2077
+ return [];
2078
+ }
2079
+ const remaining = toWithdraw - withdrawn;
2080
+ const weeksLeft = Math.ceil(remaining / (withdrawRate * 1e6));
2081
+ const schedule = [];
2082
+ let totalPaid = 0;
2083
+ for (let i = 0; i < Math.min(weeksLeft, 13); i++) {
2084
+ const isLast = i === weeksLeft - 1;
2085
+ const weekAmount = isLast
2086
+ ? (remaining / 1e6) - totalPaid
2087
+ : withdrawRate;
2088
+ const date = new Date(nextDate.getTime() + i * 7 * 24 * 60 * 60 * 1000);
2089
+ totalPaid += weekAmount;
2090
+ schedule.push({
2091
+ week: i + 1,
2092
+ date,
2093
+ amount: weekAmount.toFixed(6),
2094
+ vestingShares: `${weekAmount.toFixed(6)} VESTS`
2095
+ });
2096
+ }
2097
+ return schedule;
2098
+ },
2099
+ // ─── HBD Interest Calculator ────────────────────────────────────────
2100
+ /**
2101
+ * Calculates pending HBD savings interest
2102
+ * @param hbdBalance - HBD savings balance (e.g. '100.000 HBD')
2103
+ * @param lastInterestPayment - Date of last interest payment
2104
+ * @param annualRate - Annual interest rate as percentage (default: 15)
2105
+ * @returns Pending interest amount as a string
2106
+ */
2107
+ calculateHbdInterest(hbdBalance, lastInterestPayment, annualRate = 15) {
2108
+ const balance = new bignumber_js_1.default(String(hbdBalance).replace(' HBD', ''));
2109
+ if (balance.isZero() || balance.isNaN()) {
2110
+ return '0.000';
2111
+ }
2112
+ const lastPayment = new Date(typeof lastInterestPayment === 'string'
2113
+ ? lastInterestPayment + (lastInterestPayment.endsWith('Z') ? '' : 'Z')
2114
+ : lastInterestPayment);
2115
+ const now = new Date();
2116
+ const daysSincePayment = (now.getTime() - lastPayment.getTime()) / (1000 * 60 * 60 * 24);
2117
+ if (daysSincePayment <= 0) {
2118
+ return '0.000';
2119
+ }
2120
+ const dailyRate = annualRate / 365 / 100;
2121
+ const interest = balance.multipliedBy(dailyRate).multipliedBy(daysSincePayment);
2122
+ return interest.decimalPlaces(3, bignumber_js_1.default.ROUND_DOWN).toFixed(3);
2123
+ },
2124
+ // ─── Payout Helpers ─────────────────────────────────────────────────
2125
+ /**
2126
+ * Checks if a post is still within its payout window (7 days)
2127
+ */
2128
+ isInPayoutWindow(post) {
2129
+ if (!post || !post.cashout_time) {
2130
+ return false;
2131
+ }
2132
+ const cashout = new Date(post.cashout_time + (post.cashout_time.endsWith('Z') ? '' : 'Z'));
2133
+ const epoch = new Date('1969-12-31T23:59:59Z');
2134
+ // Posts past payout have cashout_time set to epoch
2135
+ if (cashout.getTime() <= epoch.getTime()) {
2136
+ return false;
2137
+ }
2138
+ return cashout.getTime() > Date.now();
2139
+ },
2140
+ /**
2141
+ * Returns milliseconds until payout, or 0 if already paid out
2142
+ */
2143
+ timeUntilPayout(post) {
2144
+ if (!this.isInPayoutWindow(post)) {
2145
+ return 0;
2146
+ }
2147
+ const cashout = new Date(post.cashout_time + (post.cashout_time.endsWith('Z') ? '' : 'Z'));
2148
+ return Math.max(0, cashout.getTime() - Date.now());
2149
+ },
2150
+ /**
2151
+ * Calculates the pending payout value of a post in HBD
2152
+ */
2153
+ getPendingPayout(post) {
2154
+ if (!post) {
2155
+ return '0.000';
2156
+ }
2157
+ const pending = parseFloat(String(post.pending_payout_value || '0').replace(' HBD', ''));
2158
+ const total = parseFloat(String(post.total_payout_value || '0').replace(' HBD', ''));
2159
+ const curator = parseFloat(String(post.curator_payout_value || '0').replace(' HBD', ''));
2160
+ if (pending > 0) {
2161
+ return pending.toFixed(3);
2162
+ }
2163
+ return (total + curator).toFixed(3);
2164
+ },
2165
+ // ─── Account Value Calculator ───────────────────────────────────────
2166
+ /**
2167
+ * Calculates total account value breakdown
2168
+ * @param account - Extended account object
2169
+ * @param hivePrice - Current HIVE price in USD
2170
+ * @param hbdPrice - Current HBD price in USD (usually ~1.00)
2171
+ * @param totalVestingFundHive - From dynamic global properties
2172
+ * @param totalVestingShares - From dynamic global properties
2173
+ * @returns Breakdown of account value
2174
+ */
2175
+ calculateAccountValue(account, hivePrice, hbdPrice = 1.0, totalVestingFundHive = '0', totalVestingShares = '1') {
2176
+ const hive = parseFloat(String(account.balance || '0').replace(' HIVE', ''));
2177
+ const hbd = parseFloat(String(account.hbd_balance || '0').replace(' HBD', ''));
2178
+ const savingsHive = parseFloat(String(account.savings_balance || '0').replace(' HIVE', ''));
2179
+ const savingsHbd = parseFloat(String(account.savings_hbd_balance || '0').replace(' HBD', ''));
2180
+ const effectiveVests = this.getEffectiveVestingShares(account);
2181
+ const hp = parseFloat(this.vestToHP(String(effectiveVests), totalVestingFundHive, totalVestingShares));
2182
+ const totalHive = hive + savingsHive + hp;
2183
+ const totalHbd = hbd + savingsHbd;
2184
+ const totalUsd = (totalHive * hivePrice) + (totalHbd * hbdPrice);
2185
+ return {
2186
+ hive,
2187
+ hbd,
2188
+ savings_hive: savingsHive,
2189
+ savings_hbd: savingsHbd,
2190
+ hp,
2191
+ total_hive: totalHive,
2192
+ total_usd: Math.round(totalUsd * 1000) / 1000
2193
+ };
2194
+ },
2195
+ // ─── Content Helpers ────────────────────────────────────────────────
2196
+ /**
2197
+ * Extracts all image URLs from a post body (markdown/HTML)
2198
+ */
2199
+ extractImagesFromBody(body) {
2200
+ if (!body || typeof body !== 'string') {
2201
+ return [];
2202
+ }
2203
+ const images = [];
2204
+ const seen = new Set();
2205
+ // Markdown images: ![alt](url)
2206
+ const mdRegex = /!\[.*?\]\((https?:\/\/[^)\s]+)\)/g;
2207
+ let match;
2208
+ while ((match = mdRegex.exec(body)) !== null) {
2209
+ if (!seen.has(match[1])) {
2210
+ seen.add(match[1]);
2211
+ images.push(match[1]);
2212
+ }
2213
+ }
2214
+ // HTML images: <img src="url">
2215
+ const htmlRegex = /<img[^>]+src=["'](https?:\/\/[^"']+)["']/gi;
2216
+ while ((match = htmlRegex.exec(body)) !== null) {
2217
+ if (!seen.has(match[1])) {
2218
+ seen.add(match[1]);
2219
+ images.push(match[1]);
2220
+ }
2221
+ }
2222
+ return images;
2223
+ },
2224
+ /**
2225
+ * Extracts all hyperlinks from a post body
2226
+ */
2227
+ extractLinksFromBody(body) {
2228
+ if (!body || typeof body !== 'string') {
2229
+ return [];
2230
+ }
2231
+ const links = [];
2232
+ const seen = new Set();
2233
+ // Markdown links: [text](url) but NOT images
2234
+ const mdRegex = /(?<!!)\[.*?\]\((https?:\/\/[^)\s]+)\)/g;
2235
+ let match;
2236
+ while ((match = mdRegex.exec(body)) !== null) {
2237
+ if (!seen.has(match[1])) {
2238
+ seen.add(match[1]);
2239
+ links.push(match[1]);
2240
+ }
2241
+ }
2242
+ // HTML links: <a href="url">
2243
+ const htmlRegex = /<a[^>]+href=["'](https?:\/\/[^"']+)["']/gi;
2244
+ while ((match = htmlRegex.exec(body)) !== null) {
2245
+ if (!seen.has(match[1])) {
2246
+ seen.add(match[1]);
2247
+ links.push(match[1]);
2248
+ }
2249
+ }
2250
+ return links;
2251
+ },
2252
+ /**
2253
+ * Generates a plain-text summary from a post body by stripping markdown/HTML
2254
+ */
2255
+ generatePostSummary(body, maxLength = 200) {
2256
+ if (!body || typeof body !== 'string') {
2257
+ return '';
2258
+ }
2259
+ let text = body
2260
+ // Remove images
2261
+ .replace(/!\[.*?\]\(.*?\)/g, '')
2262
+ // Convert links to text
2263
+ .replace(/\[([^\]]+)\]\(.*?\)/g, '$1')
2264
+ // Remove HTML tags
2265
+ .replace(/<[^>]+>/g, '')
2266
+ // Remove headers
2267
+ .replace(/^#{1,6}\s+/gm, '')
2268
+ // Remove bold/italic
2269
+ .replace(/[*_]{1,3}([^*_]+)[*_]{1,3}/g, '$1')
2270
+ // Remove strikethrough
2271
+ .replace(/~~([^~]+)~~/g, '$1')
2272
+ // Remove blockquotes
2273
+ .replace(/^>\s+/gm, '')
2274
+ // Remove horizontal rules
2275
+ .replace(/^[-*_]{3,}$/gm, '')
2276
+ // Remove code blocks
2277
+ .replace(/```[\s\S]*?```/g, '')
2278
+ .replace(/`([^`]+)`/g, '$1')
2279
+ // Collapse whitespace
2280
+ .replace(/\n+/g, ' ')
2281
+ .replace(/\s+/g, ' ')
2282
+ .trim();
2283
+ if (text.length > maxLength) {
2284
+ text = text.substring(0, maxLength).replace(/\s+\S*$/, '') + '...';
2285
+ }
2286
+ return text;
2287
+ },
2288
+ // ─── Hivesigner URL Generators ──────────────────────────────────────
2289
+ /**
2290
+ * Generates a Hivesigner signing URL for any operation
2291
+ */
2292
+ getHivesignerSignUrl(operationType, params, redirectUri) {
2293
+ const queryParams = Object.entries(params)
2294
+ .filter(([, v]) => v !== undefined && v !== null)
2295
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
2296
+ .join('&');
2297
+ let url = `https://hivesigner.com/sign/${operationType}?${queryParams}`;
2298
+ if (redirectUri) {
2299
+ url += `&redirect_uri=${encodeURIComponent(redirectUri)}`;
2300
+ }
2301
+ return url;
2302
+ },
2303
+ /**
2304
+ * Generates a Hivesigner vote URL
2305
+ */
2306
+ getVoteUrl(voter, author, permlink, weight, redirectUri) {
2307
+ return this.getHivesignerSignUrl('vote', { voter, author, permlink, weight }, redirectUri);
2308
+ },
2309
+ /**
2310
+ * Generates a Hivesigner delegation URL
2311
+ */
2312
+ getDelegateUrl(delegator, delegatee, vestingShares, redirectUri) {
2313
+ return this.getHivesignerSignUrl('delegate-vesting-shares', {
2314
+ delegator, delegatee, vesting_shares: vestingShares
2315
+ }, redirectUri);
2316
+ },
2317
+ /**
2318
+ * Generates a Hivesigner follow URL
2319
+ */
2320
+ getFollowUrl(follower, following, redirectUri) {
2321
+ return this.getHivesignerSignUrl('follow', { follower, following }, redirectUri);
2322
+ },
2323
+ // ─── Transfer Memo Helpers ──────────────────────────────────────────
2324
+ /**
2325
+ * Checks if a memo is encrypted (starts with #)
2326
+ */
2327
+ isEncryptedMemo(memo) {
2328
+ return typeof memo === 'string' && memo.startsWith('#');
2329
+ },
2330
+ /**
2331
+ * Creates a JSON-encoded memo string for structured data in transfers
2332
+ */
2333
+ createJsonMemo(data) {
2334
+ return JSON.stringify(data);
2335
+ },
2336
+ /**
2337
+ * Attempts to parse a transfer memo as JSON, returns null if not valid JSON
2338
+ */
2339
+ parseJsonMemo(memo) {
2340
+ if (!memo || typeof memo !== 'string') {
2341
+ return null;
2342
+ }
2343
+ const trimmed = memo.trim();
2344
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
2345
+ return null;
2346
+ }
2347
+ try {
2348
+ return JSON.parse(trimmed);
2349
+ }
2350
+ catch {
2351
+ return null;
2352
+ }
2353
+ },
2354
+ // ─── Key Derivation ─────────────────────────────────────────────────
2355
+ /**
2356
+ * Derives all private and public keys from a Hive master password
2357
+ * @param account - The Hive account name
2358
+ * @param password - The master password
2359
+ * @returns Object with owner, active, posting, memo keys (private and public)
2360
+ */
2361
+ deriveKeys(account, password) {
2362
+ if (!account || !password) {
2363
+ throw new Error('Account and password are required for key derivation');
2364
+ }
2365
+ const roles = ['owner', 'active', 'posting', 'memo'];
2366
+ const result = {};
2367
+ for (const role of roles) {
2368
+ const privateKey = dhive_1.PrivateKey.fromLogin(account, password, role);
2369
+ result[role] = privateKey.toString();
2370
+ result[`${role}Public`] = privateKey.createPublic().toString();
2371
+ }
2372
+ return result;
2373
+ },
2374
+ /**
2375
+ * Gets the public key from a private key string
2376
+ */
2377
+ getPublicKey(privateKey) {
2378
+ return dhive_1.PrivateKey.fromString(privateKey).createPublic().toString();
2379
+ },
2380
+ // ─── Hive Engine Write Operations ───────────────────────────────────
2381
+ stakeEngineTokens(client, config, from, to, symbol, quantity) {
2382
+ if (!client || !config.ACTIVE_KEY || !from || !symbol || !quantity) {
2383
+ throw new Error('Missing required parameters for stake engine tokens');
2384
+ }
2385
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2386
+ const json = JSON.stringify({
2387
+ contractName: 'tokens',
2388
+ contractAction: 'stake',
2389
+ contractPayload: { to: to || from, symbol, quantity: String(quantity) }
2390
+ });
2391
+ return client.broadcast.json({
2392
+ required_auths: [from],
2393
+ required_posting_auths: [],
2394
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2395
+ json
2396
+ }, key);
2397
+ },
2398
+ unstakeEngineTokens(client, config, from, symbol, quantity) {
2399
+ if (!client || !config.ACTIVE_KEY || !from || !symbol || !quantity) {
2400
+ throw new Error('Missing required parameters for unstake engine tokens');
2401
+ }
2402
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2403
+ const json = JSON.stringify({
2404
+ contractName: 'tokens',
2405
+ contractAction: 'unstake',
2406
+ contractPayload: { symbol, quantity: String(quantity) }
2407
+ });
2408
+ return client.broadcast.json({
2409
+ required_auths: [from],
2410
+ required_posting_auths: [],
2411
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2412
+ json
2413
+ }, key);
2414
+ },
2415
+ buyEngineTokens(client, config, from, symbol, quantity, price) {
2416
+ if (!client || !config.ACTIVE_KEY || !from || !symbol || !quantity || !price) {
2417
+ throw new Error('Missing required parameters for buy engine tokens');
2418
+ }
2419
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2420
+ const json = JSON.stringify({
2421
+ contractName: 'market',
2422
+ contractAction: 'buy',
2423
+ contractPayload: { symbol, quantity: String(quantity), price: String(price) }
2424
+ });
2425
+ return client.broadcast.json({
2426
+ required_auths: [from],
2427
+ required_posting_auths: [],
2428
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2429
+ json
2430
+ }, key);
2431
+ },
2432
+ sellEngineTokens(client, config, from, symbol, quantity, price) {
2433
+ if (!client || !config.ACTIVE_KEY || !from || !symbol || !quantity || !price) {
2434
+ throw new Error('Missing required parameters for sell engine tokens');
2435
+ }
2436
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2437
+ const json = JSON.stringify({
2438
+ contractName: 'market',
2439
+ contractAction: 'sell',
2440
+ contractPayload: { symbol, quantity: String(quantity), price: String(price) }
2441
+ });
2442
+ return client.broadcast.json({
2443
+ required_auths: [from],
2444
+ required_posting_auths: [],
2445
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2446
+ json
2447
+ }, key);
2448
+ },
2449
+ cancelEngineOrder(client, config, from, type, orderId) {
2450
+ if (!client || !config.ACTIVE_KEY || !from || !type || !orderId) {
2451
+ throw new Error('Missing required parameters for cancel engine order');
2452
+ }
2453
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2454
+ const json = JSON.stringify({
2455
+ contractName: 'market',
2456
+ contractAction: 'cancel',
2457
+ contractPayload: { type, id: orderId }
2458
+ });
2459
+ return client.broadcast.json({
2460
+ required_auths: [from],
2461
+ required_posting_auths: [],
2462
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2463
+ json
2464
+ }, key);
2465
+ },
2466
+ delegateEngineTokens(client, config, from, to, symbol, quantity) {
2467
+ if (!client || !config.ACTIVE_KEY || !from || !to || !symbol || !quantity) {
2468
+ throw new Error('Missing required parameters for delegate engine tokens');
2469
+ }
2470
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2471
+ const json = JSON.stringify({
2472
+ contractName: 'tokens',
2473
+ contractAction: 'delegate',
2474
+ contractPayload: { to, symbol, quantity: String(quantity) }
2475
+ });
2476
+ return client.broadcast.json({
2477
+ required_auths: [from],
2478
+ required_posting_auths: [],
2479
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2480
+ json
2481
+ }, key);
2482
+ },
2483
+ undelegateEngineTokens(client, config, from, to, symbol, quantity) {
2484
+ if (!client || !config.ACTIVE_KEY || !from || !to || !symbol || !quantity) {
2485
+ throw new Error('Missing required parameters for undelegate engine tokens');
2486
+ }
2487
+ const key = dhive_1.PrivateKey.fromString(config.ACTIVE_KEY);
2488
+ const json = JSON.stringify({
2489
+ contractName: 'tokens',
2490
+ contractAction: 'undelegate',
2491
+ contractPayload: { from: to, symbol, quantity: String(quantity) }
2492
+ });
2493
+ return client.broadcast.json({
2494
+ required_auths: [from],
2495
+ required_posting_auths: [],
2496
+ id: config.HIVE_ENGINE_ID || 'ssc-mainnet-hive',
2497
+ json
2498
+ }, key);
2499
+ },
2500
+ // ─── Community Operations ───────────────────────────────────────────
2501
+ subscribeCommunity(client, config, account, community) {
2502
+ if (!client || !config.POSTING_KEY || !account || !community) {
2503
+ throw new Error('Missing required parameters for subscribe to community');
2504
+ }
2505
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2506
+ const json = JSON.stringify(['subscribe', { community }]);
2507
+ return client.broadcast.json({
2508
+ required_auths: [],
2509
+ required_posting_auths: [account],
2510
+ id: 'community',
2511
+ json
2512
+ }, key);
2513
+ },
2514
+ unsubscribeCommunity(client, config, account, community) {
2515
+ if (!client || !config.POSTING_KEY || !account || !community) {
2516
+ throw new Error('Missing required parameters for unsubscribe from community');
2517
+ }
2518
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2519
+ const json = JSON.stringify(['unsubscribe', { community }]);
2520
+ return client.broadcast.json({
2521
+ required_auths: [],
2522
+ required_posting_auths: [account],
2523
+ id: 'community',
2524
+ json
2525
+ }, key);
2526
+ },
2527
+ pinCommunityPost(client, config, account, community, author, permlink) {
2528
+ if (!client || !config.POSTING_KEY || !account || !community || !author || !permlink) {
2529
+ throw new Error('Missing required parameters for pin community post');
2530
+ }
2531
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2532
+ const json = JSON.stringify(['pinPost', { community, account: author, permlink }]);
2533
+ return client.broadcast.json({
2534
+ required_auths: [],
2535
+ required_posting_auths: [account],
2536
+ id: 'community',
2537
+ json
2538
+ }, key);
2539
+ },
2540
+ unpinCommunityPost(client, config, account, community, author, permlink) {
2541
+ if (!client || !config.POSTING_KEY || !account || !community || !author || !permlink) {
2542
+ throw new Error('Missing required parameters for unpin community post');
2543
+ }
2544
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2545
+ const json = JSON.stringify(['unpinPost', { community, account: author, permlink }]);
2546
+ return client.broadcast.json({
2547
+ required_auths: [],
2548
+ required_posting_auths: [account],
2549
+ id: 'community',
2550
+ json
2551
+ }, key);
2552
+ },
2553
+ muteCommunityUser(client, config, account, community, targetAccount, notes = '') {
2554
+ if (!client || !config.POSTING_KEY || !account || !community || !targetAccount) {
2555
+ throw new Error('Missing required parameters for mute community user');
2556
+ }
2557
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2558
+ const json = JSON.stringify(['mutePost', { community, account: targetAccount, notes }]);
2559
+ return client.broadcast.json({
2560
+ required_auths: [],
2561
+ required_posting_auths: [account],
2562
+ id: 'community',
2563
+ json
2564
+ }, key);
2565
+ },
2566
+ unmuteCommunityUser(client, config, account, community, targetAccount, notes = '') {
2567
+ if (!client || !config.POSTING_KEY || !account || !community || !targetAccount) {
2568
+ throw new Error('Missing required parameters for unmute community user');
2569
+ }
2570
+ const key = dhive_1.PrivateKey.fromString(config.POSTING_KEY);
2571
+ const json = JSON.stringify(['unmutePost', { community, account: targetAccount, notes }]);
2572
+ return client.broadcast.json({
2573
+ required_auths: [],
2574
+ required_posting_auths: [account],
2575
+ id: 'community',
2576
+ json
2577
+ }, key);
968
2578
  }
969
2579
  };
970
2580
  //# sourceMappingURL=utils.js.map