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.
- package/DOCUMENTATION.md +693 -1
- package/README.md +251 -1
- package/dist/adapters/mongodb.adapter.js +21 -30
- package/dist/adapters/mongodb.adapter.js.map +1 -1
- package/dist/adapters/postgresql.adapter.js +16 -16
- package/dist/adapters/postgresql.adapter.js.map +1 -1
- package/dist/adapters/sqlite.adapter.js +17 -17
- package/dist/adapters/sqlite.adapter.js.map +1 -1
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -1
- package/dist/builders.d.ts +444 -0
- package/dist/builders.js +1609 -0
- package/dist/builders.js.map +1 -0
- package/dist/config.d.ts +10 -1
- package/dist/config.js +90 -4
- package/dist/config.js.map +1 -1
- package/dist/contracts/coinflip.contract.js +18 -21
- package/dist/contracts/coinflip.contract.js.map +1 -1
- package/dist/contracts/dice.contract.js +18 -21
- package/dist/contracts/dice.contract.js.map +1 -1
- package/dist/contracts/helpers.d.ts +2 -0
- package/dist/contracts/helpers.js +18 -11
- package/dist/contracts/helpers.js.map +1 -1
- package/dist/contracts/nft.contract.js +5 -10
- package/dist/contracts/nft.contract.js.map +1 -1
- package/dist/contracts/rps.contract.js +35 -23
- package/dist/contracts/rps.contract.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/metadata.d.ts +13 -0
- package/dist/metadata.js +256 -0
- package/dist/metadata.js.map +1 -1
- package/dist/streamer.d.ts +148 -12
- package/dist/streamer.js +1102 -53
- package/dist/streamer.js.map +1 -1
- package/dist/types/hive-stream.d.ts +623 -0
- package/dist/utils.d.ts +475 -0
- package/dist/utils.js +1618 -8
- package/dist/utils.js.map +1 -1
- 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
|
-
|
|
264
|
-
|
|
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 =
|
|
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: 
|
|
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
|