@steemit/steem-js 1.0.13 → 1.0.15
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/dist/api/transports/http.d.ts +5 -1
- package/dist/api/transports/types.d.ts +8 -0
- package/dist/auth/ecc/src/key_private.d.ts +5 -0
- package/dist/auth/ecc/src/key_utils.d.ts +3 -2
- package/dist/browser.esm.js +254 -584
- package/dist/browser.esm.js.map +1 -1
- package/dist/index.cjs +254 -584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +254 -584
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +254 -584
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/dist/memo/index.d.ts +8 -1
- package/dist/umd.d.ts +0 -2
- package/package.json +5 -3
- package/dist/serializer/convert.d.ts +0 -21
- package/dist/serializer/index.d.ts +0 -11
- package/dist/serializer/number_utils.d.ts +0 -8
- package/dist/serializer/precision.d.ts +0 -5
- package/dist/serializer/types.d.ts +0 -36
package/dist/index.umd.js
CHANGED
|
@@ -16593,6 +16593,20 @@
|
|
|
16593
16593
|
const secp256k1$1 = new elliptic.ec('secp256k1');
|
|
16594
16594
|
const G = secp256k1$1.g;
|
|
16595
16595
|
const n = new BN(secp256k1$1.n.toString());
|
|
16596
|
+
/**
|
|
16597
|
+
* Constant-time buffer comparison to prevent timing attacks.
|
|
16598
|
+
* Returns true only if a.length === b.length and a[i] === b[i] for all i.
|
|
16599
|
+
*/
|
|
16600
|
+
function constantTimeCompare(a, b) {
|
|
16601
|
+
if (a.length !== b.length) {
|
|
16602
|
+
return false;
|
|
16603
|
+
}
|
|
16604
|
+
let result = 0;
|
|
16605
|
+
for (let i = 0; i < a.length; i++) {
|
|
16606
|
+
result |= a[i] ^ b[i];
|
|
16607
|
+
}
|
|
16608
|
+
return result === 0;
|
|
16609
|
+
}
|
|
16596
16610
|
class PrivateKey {
|
|
16597
16611
|
/**
|
|
16598
16612
|
* @private see static functions
|
|
@@ -16605,11 +16619,8 @@
|
|
|
16605
16619
|
if (!buffer.Buffer.isBuffer(buf)) {
|
|
16606
16620
|
throw new Error("Expecting parameter to be a Buffer type");
|
|
16607
16621
|
}
|
|
16608
|
-
if (
|
|
16609
|
-
|
|
16610
|
-
}
|
|
16611
|
-
if (buf.length === 0) {
|
|
16612
|
-
throw new Error("Empty buffer");
|
|
16622
|
+
if (buf.length !== 32) {
|
|
16623
|
+
throw new Error(`Invalid private key buffer: expected 32 bytes, got ${buf.length}`);
|
|
16613
16624
|
}
|
|
16614
16625
|
return new PrivateKey(new BN(buf));
|
|
16615
16626
|
}
|
|
@@ -16634,21 +16645,33 @@
|
|
|
16634
16645
|
* @return {string} Wallet Import Format (still a secret, Not encrypted)
|
|
16635
16646
|
*/
|
|
16636
16647
|
static fromWif(private_wif) {
|
|
16637
|
-
|
|
16648
|
+
if (!private_wif || typeof private_wif !== 'string') {
|
|
16649
|
+
throw new Error('Invalid WIF: empty or not a string');
|
|
16650
|
+
}
|
|
16651
|
+
let private_wif_buffer;
|
|
16652
|
+
try {
|
|
16653
|
+
private_wif_buffer = buffer.Buffer.from(bs58.decode(private_wif));
|
|
16654
|
+
}
|
|
16655
|
+
catch {
|
|
16656
|
+
throw new Error('Invalid WIF: failed to decode base58');
|
|
16657
|
+
}
|
|
16658
|
+
// Valid WIF: 1 byte version + 32 bytes key + 4 bytes checksum = 37 bytes
|
|
16659
|
+
if (private_wif_buffer.length !== 37) {
|
|
16660
|
+
throw new Error(`Invalid WIF: expected 37 bytes, got ${private_wif_buffer.length}`);
|
|
16661
|
+
}
|
|
16638
16662
|
const version = private_wif_buffer.readUInt8(0);
|
|
16639
|
-
if (version !== 0x80)
|
|
16640
|
-
throw new Error(`
|
|
16641
|
-
|
|
16642
|
-
const private_key = private_wif_buffer.slice(
|
|
16643
|
-
const checksum = private_wif_buffer.slice(
|
|
16644
|
-
let new_checksum = sha256$1(private_key);
|
|
16663
|
+
if (version !== 0x80) {
|
|
16664
|
+
throw new Error(`Invalid WIF: expected version 0x80, got 0x${version.toString(16)}`);
|
|
16665
|
+
}
|
|
16666
|
+
const private_key = private_wif_buffer.slice(1, 33);
|
|
16667
|
+
const checksum = private_wif_buffer.slice(33);
|
|
16668
|
+
let new_checksum = sha256$1(buffer.Buffer.concat([buffer.Buffer.from([0x80]), private_key]));
|
|
16645
16669
|
new_checksum = sha256$1(new_checksum);
|
|
16646
16670
|
new_checksum = new_checksum.slice(0, 4);
|
|
16647
|
-
if (checksum.
|
|
16671
|
+
if (!constantTimeCompare(checksum, buffer.Buffer.from(new_checksum))) {
|
|
16648
16672
|
throw new Error('Invalid WIF key (checksum miss-match)');
|
|
16649
16673
|
}
|
|
16650
|
-
|
|
16651
|
-
return PrivateKey.fromBuffer(private_key.slice(1));
|
|
16674
|
+
return PrivateKey.fromBuffer(private_key);
|
|
16652
16675
|
}
|
|
16653
16676
|
toWif() {
|
|
16654
16677
|
const private_key = this.toBuffer();
|
|
@@ -17027,18 +17050,21 @@
|
|
|
17027
17050
|
}
|
|
17028
17051
|
catch (error) {
|
|
17029
17052
|
// try next value
|
|
17030
|
-
|
|
17053
|
+
if (process$1.env.NODE_ENV === 'development') {
|
|
17054
|
+
console.debug(`Recovery attempt ${i} failed:`, error.message);
|
|
17055
|
+
}
|
|
17031
17056
|
}
|
|
17032
17057
|
}
|
|
17033
|
-
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17058
|
+
if (process$1.env.NODE_ENV === 'development') {
|
|
17059
|
+
console.debug('All recovery attempts failed. Signature:', {
|
|
17060
|
+
r: signature.r.toString(16),
|
|
17061
|
+
s: signature.s.toString(16)
|
|
17062
|
+
});
|
|
17063
|
+
console.debug('Expected public key:', {
|
|
17064
|
+
x: Q.getX().toString(16),
|
|
17065
|
+
y: Q.getY().toString(16)
|
|
17066
|
+
});
|
|
17067
|
+
}
|
|
17042
17068
|
throw new Error('Unable to find valid recovery factor');
|
|
17043
17069
|
}
|
|
17044
17070
|
|
|
@@ -17090,9 +17116,10 @@
|
|
|
17090
17116
|
const d = privKey.d;
|
|
17091
17117
|
let ecsignature;
|
|
17092
17118
|
let nonce = 0;
|
|
17119
|
+
const MAX_NONCE_ATTEMPTS = 1000;
|
|
17093
17120
|
// Match old-steem-js behavior: find canonical signature (lenR === 32 && lenS === 32)
|
|
17094
17121
|
// Based on C++ is_fc_canonical logic
|
|
17095
|
-
while (
|
|
17122
|
+
while (nonce < MAX_NONCE_ATTEMPTS) {
|
|
17096
17123
|
ecsignature = sign$3(secp256k1, buf_sha256, d, nonce++);
|
|
17097
17124
|
const rBa = ecsignature.r.toArrayLike(buffer.Buffer, 'be', 32);
|
|
17098
17125
|
const sBa = ecsignature.s.toArrayLike(buffer.Buffer, 'be', 32);
|
|
@@ -17109,6 +17136,9 @@
|
|
|
17109
17136
|
console.debug("WARN: " + nonce + " attempts to find canonical signature");
|
|
17110
17137
|
}
|
|
17111
17138
|
}
|
|
17139
|
+
if (nonce >= MAX_NONCE_ATTEMPTS || ecsignature === undefined) {
|
|
17140
|
+
throw new Error('Failed to find canonical signature after maximum attempts');
|
|
17141
|
+
}
|
|
17112
17142
|
const i = calcPubKeyRecoveryParam(secp256k1, new BN(buf_sha256), ecsignature, privKey.toPublic().Q);
|
|
17113
17143
|
// Use recovery byte 31-34 (instead of 27-30) to be compatible with dsteem
|
|
17114
17144
|
// dsteem expects: recovery = byte - 31, so byte = recovery + 31
|
|
@@ -17409,7 +17439,11 @@
|
|
|
17409
17439
|
if (Number.isNaN(timestamp)) {
|
|
17410
17440
|
throw new Error('Invalid timestamp');
|
|
17411
17441
|
}
|
|
17412
|
-
|
|
17442
|
+
const now = Date.now();
|
|
17443
|
+
const timeDiff = Math.abs(now - timestamp);
|
|
17444
|
+
const SIGNATURE_VALIDITY_MS = 60 * 1000;
|
|
17445
|
+
const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;
|
|
17446
|
+
if (timeDiff > SIGNATURE_VALIDITY_MS + MAX_CLOCK_SKEW_MS) {
|
|
17413
17447
|
throw new Error('Signature expired');
|
|
17414
17448
|
}
|
|
17415
17449
|
const message = hashMessage(signed.timestamp, signed.account, request.method, signed.params, nonce);
|
|
@@ -18359,7 +18393,37 @@
|
|
|
18359
18393
|
}
|
|
18360
18394
|
}
|
|
18361
18395
|
|
|
18362
|
-
|
|
18396
|
+
/** Detect Node.js for optional undici Agent (custom TLS). */
|
|
18397
|
+
const isNode = typeof process$1 !== 'undefined' &&
|
|
18398
|
+
typeof process$1.versions === 'object' &&
|
|
18399
|
+
typeof process$1.versions.node === 'string';
|
|
18400
|
+
/**
|
|
18401
|
+
* Build RequestInit for fetch. In Node when options.httpsOptions is set, inject undici Agent as dispatcher.
|
|
18402
|
+
*/
|
|
18403
|
+
async function buildFetchOptions(body, options) {
|
|
18404
|
+
const init = {
|
|
18405
|
+
method: 'POST',
|
|
18406
|
+
headers: {
|
|
18407
|
+
'Content-Type': 'application/json',
|
|
18408
|
+
'Accept': 'application/json'
|
|
18409
|
+
},
|
|
18410
|
+
body
|
|
18411
|
+
};
|
|
18412
|
+
if (isNode && options.httpsOptions) {
|
|
18413
|
+
// Node 18+ built-in fetch uses undici; custom TLS via node:undici Agent (built-in, no package)
|
|
18414
|
+
// @ts-expect-error - node:undici is Node built-in, not in @types/node
|
|
18415
|
+
const { Agent } = await import('node:undici');
|
|
18416
|
+
const opts = options.httpsOptions;
|
|
18417
|
+
const agent = new Agent({
|
|
18418
|
+
connect: {
|
|
18419
|
+
rejectUnauthorized: opts.rejectUnauthorized,
|
|
18420
|
+
ca: opts.ca
|
|
18421
|
+
}
|
|
18422
|
+
});
|
|
18423
|
+
init.dispatcher = agent;
|
|
18424
|
+
}
|
|
18425
|
+
return init;
|
|
18426
|
+
}
|
|
18363
18427
|
/**
|
|
18364
18428
|
* Extended Error type for JSON-RPC errors
|
|
18365
18429
|
*/
|
|
@@ -18379,29 +18443,22 @@
|
|
|
18379
18443
|
* @param request - The JSON-RPC request object
|
|
18380
18444
|
* @param fetchMethod - Optional fetch implementation (defaults to global fetch)
|
|
18381
18445
|
* @param timeoutMs - Request timeout in milliseconds (default: 30000)
|
|
18446
|
+
* @param httpsOptions - Optional TLS options (Node.js only): rejectUnauthorized, ca
|
|
18382
18447
|
* @returns Promise resolving to the JSON-RPC result
|
|
18383
18448
|
*/
|
|
18384
|
-
const jsonRpc = async (url, request, fetchMethod = fetch, timeoutMs = 30000) => {
|
|
18449
|
+
const jsonRpc = async (url, request, fetchMethod = fetch, timeoutMs = 30000, httpsOptions) => {
|
|
18385
18450
|
const payload = {
|
|
18386
18451
|
jsonrpc: '2.0',
|
|
18387
18452
|
...request
|
|
18388
18453
|
};
|
|
18389
18454
|
let timeoutId = null;
|
|
18390
|
-
// Create a promise that will reject after the timeout
|
|
18391
18455
|
const timeoutPromise = new Promise((_, reject) => {
|
|
18392
18456
|
timeoutId = setTimeout(() => {
|
|
18393
18457
|
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
18394
18458
|
}, timeoutMs);
|
|
18395
18459
|
});
|
|
18396
|
-
|
|
18397
|
-
const fetchPromise = fetchMethod(url,
|
|
18398
|
-
method: 'POST',
|
|
18399
|
-
headers: {
|
|
18400
|
-
'Content-Type': 'application/json',
|
|
18401
|
-
'Accept': 'application/json'
|
|
18402
|
-
},
|
|
18403
|
-
body: JSON.stringify(payload)
|
|
18404
|
-
})
|
|
18460
|
+
const fetchOptions = await buildFetchOptions(JSON.stringify(payload), { httpsOptions });
|
|
18461
|
+
const fetchPromise = fetchMethod(url, fetchOptions)
|
|
18405
18462
|
.then(async (res) => {
|
|
18406
18463
|
if (!res.ok) {
|
|
18407
18464
|
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
@@ -18452,71 +18509,56 @@
|
|
|
18452
18509
|
const params = [api, data.method, data.params];
|
|
18453
18510
|
const isBroadcast = this.isBroadcastOperation(data.method);
|
|
18454
18511
|
const retryOptions = this.options.retry;
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18512
|
+
const body = JSON.stringify({
|
|
18513
|
+
jsonrpc: '2.0',
|
|
18514
|
+
method: 'call',
|
|
18515
|
+
params,
|
|
18516
|
+
id
|
|
18517
|
+
});
|
|
18518
|
+
const doRequest = (fetchOpts) => fetchMethod(url, fetchOpts)
|
|
18519
|
+
.then(async (res) => {
|
|
18520
|
+
if (!res.ok) {
|
|
18521
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
18522
|
+
}
|
|
18523
|
+
return res.json();
|
|
18524
|
+
});
|
|
18525
|
+
const runWithOptions = (fetchOpts) => {
|
|
18526
|
+
if (!isBroadcast && retryOptions) {
|
|
18527
|
+
const operation = typeof retryOptions === 'object' ? retry$1.operation(retryOptions) : retry$1.operation();
|
|
18528
|
+
operation.attempt((currentAttempt) => {
|
|
18529
|
+
doRequest(fetchOpts)
|
|
18530
|
+
.then((result) => {
|
|
18531
|
+
if (result.error) {
|
|
18532
|
+
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
18533
|
+
callback(error, undefined, currentAttempt);
|
|
18534
|
+
}
|
|
18535
|
+
else {
|
|
18536
|
+
callback(null, result.result, currentAttempt);
|
|
18537
|
+
}
|
|
18538
|
+
}, (error) => {
|
|
18539
|
+
if (operation.retry(error)) {
|
|
18540
|
+
return;
|
|
18541
|
+
}
|
|
18542
|
+
callback(operation.mainError(), undefined, currentAttempt);
|
|
18543
|
+
});
|
|
18544
|
+
});
|
|
18545
|
+
}
|
|
18546
|
+
else {
|
|
18547
|
+
doRequest(fetchOpts)
|
|
18475
18548
|
.then((result) => {
|
|
18476
|
-
// Check for JSON-RPC errors
|
|
18477
18549
|
if (result.error) {
|
|
18478
18550
|
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
18479
|
-
callback(error, undefined,
|
|
18551
|
+
callback(error, undefined, 1);
|
|
18480
18552
|
}
|
|
18481
18553
|
else {
|
|
18482
|
-
callback(null, result.result,
|
|
18554
|
+
callback(null, result.result, 1);
|
|
18483
18555
|
}
|
|
18484
|
-
}, (error) =>
|
|
18485
|
-
|
|
18486
|
-
|
|
18487
|
-
|
|
18488
|
-
|
|
18489
|
-
|
|
18490
|
-
});
|
|
18491
|
-
}
|
|
18492
|
-
else {
|
|
18493
|
-
fetchMethod(url, {
|
|
18494
|
-
method: 'POST',
|
|
18495
|
-
body: JSON.stringify({
|
|
18496
|
-
jsonrpc: '2.0',
|
|
18497
|
-
method: 'call',
|
|
18498
|
-
params,
|
|
18499
|
-
id
|
|
18500
|
-
}),
|
|
18501
|
-
headers: { 'Content-Type': 'application/json' }
|
|
18502
|
-
})
|
|
18503
|
-
.then(async (res) => {
|
|
18504
|
-
if (!res.ok) {
|
|
18505
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
18506
|
-
}
|
|
18507
|
-
return res.json();
|
|
18508
|
-
})
|
|
18509
|
-
.then((result) => {
|
|
18510
|
-
// Check for JSON-RPC errors
|
|
18511
|
-
if (result.error) {
|
|
18512
|
-
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
18513
|
-
callback(error, undefined, 1);
|
|
18514
|
-
}
|
|
18515
|
-
else {
|
|
18516
|
-
callback(null, result.result, 1);
|
|
18517
|
-
}
|
|
18518
|
-
}, (error) => callback(error instanceof Error ? error : new Error(String(error)), undefined, 1));
|
|
18519
|
-
}
|
|
18556
|
+
}, (error) => callback(error instanceof Error ? error : new Error(String(error)), undefined, 1));
|
|
18557
|
+
}
|
|
18558
|
+
};
|
|
18559
|
+
buildFetchOptions(body, this.options)
|
|
18560
|
+
.then(runWithOptions)
|
|
18561
|
+
.catch((err) => callback(err instanceof Error ? err : new Error(String(err)), undefined, 1));
|
|
18520
18562
|
}
|
|
18521
18563
|
}
|
|
18522
18564
|
|
|
@@ -25078,7 +25120,7 @@
|
|
|
25078
25120
|
* Serialize a transaction to binary format for Steem blockchain
|
|
25079
25121
|
* This is a simplified implementation that handles the basic structure
|
|
25080
25122
|
*/
|
|
25081
|
-
function serializeTransaction
|
|
25123
|
+
function serializeTransaction(trx) {
|
|
25082
25124
|
const bb = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
25083
25125
|
const trxObj = trx;
|
|
25084
25126
|
// Write ref_block_num (uint16)
|
|
@@ -25108,7 +25150,7 @@
|
|
|
25108
25150
|
const operations = (Array.isArray(trxObj.operations) ? trxObj.operations : []);
|
|
25109
25151
|
bb.writeVarint32(operations.length);
|
|
25110
25152
|
for (const op of operations) {
|
|
25111
|
-
serializeOperation
|
|
25153
|
+
serializeOperation(bb, op);
|
|
25112
25154
|
}
|
|
25113
25155
|
// Write extensions (set of future_extensions, which is void/empty)
|
|
25114
25156
|
bb.writeVarint32(0); // Empty set
|
|
@@ -25137,7 +25179,7 @@
|
|
|
25137
25179
|
/**
|
|
25138
25180
|
* Serialize an operation to binary format
|
|
25139
25181
|
*/
|
|
25140
|
-
function serializeOperation
|
|
25182
|
+
function serializeOperation(bb, op) {
|
|
25141
25183
|
if (!Array.isArray(op) || op.length !== 2) {
|
|
25142
25184
|
throw new Error('Operation must be an array of [operation_type, operation_data]');
|
|
25143
25185
|
}
|
|
@@ -26069,7 +26111,7 @@
|
|
|
26069
26111
|
bb.writeUint16(dataObj.percent_steem_dollars ?? 0);
|
|
26070
26112
|
serializeBool(bb, dataObj.allow_votes);
|
|
26071
26113
|
serializeBool(bb, dataObj.allow_curation_rewards);
|
|
26072
|
-
|
|
26114
|
+
serializeCommentOptionsExtensions(bb, dataObj.extensions);
|
|
26073
26115
|
}
|
|
26074
26116
|
/**
|
|
26075
26117
|
* Serialize custom_json operation
|
|
@@ -26146,7 +26188,7 @@
|
|
|
26146
26188
|
*
|
|
26147
26189
|
* Format: int64 amount (little-endian) + uint8 precision + 7-byte symbol (UTF-8, null-padded).
|
|
26148
26190
|
*
|
|
26149
|
-
* This helper is reused across all operations
|
|
26191
|
+
* This helper is reused for asset fields across all operations, e.g.
|
|
26150
26192
|
* - amount / vesting_shares / reward_* / *_pays
|
|
26151
26193
|
*/
|
|
26152
26194
|
function serializeAsset(bb, amount) {
|
|
@@ -26166,7 +26208,7 @@
|
|
|
26166
26208
|
}
|
|
26167
26209
|
/**
|
|
26168
26210
|
* Write a string using ByteBuffer's writeVString method.
|
|
26169
|
-
*
|
|
26211
|
+
* All string fields are serialized through this helper to avoid calling ByteBuffer API directly everywhere.
|
|
26170
26212
|
*/
|
|
26171
26213
|
function writeString(bb, str) {
|
|
26172
26214
|
bb.writeVString(str);
|
|
@@ -26174,8 +26216,8 @@
|
|
|
26174
26216
|
/**
|
|
26175
26217
|
* Serialize a time_point_sec-style field.
|
|
26176
26218
|
*
|
|
26177
|
-
*
|
|
26178
|
-
*
|
|
26219
|
+
* Accepts ISO string / Date / seconds number; writes uint32 (seconds since epoch).
|
|
26220
|
+
* Used for proposal start/end, escrow_deadline, and similar fields.
|
|
26179
26221
|
*/
|
|
26180
26222
|
function serializeTimePointSec(bb, value) {
|
|
26181
26223
|
let seconds;
|
|
@@ -26188,7 +26230,7 @@
|
|
|
26188
26230
|
seconds = Math.floor(value.getTime() / 1000);
|
|
26189
26231
|
}
|
|
26190
26232
|
else if (typeof value === 'number') {
|
|
26191
|
-
//
|
|
26233
|
+
// Assume value is already in seconds
|
|
26192
26234
|
seconds = value;
|
|
26193
26235
|
}
|
|
26194
26236
|
else {
|
|
@@ -26198,29 +26240,61 @@
|
|
|
26198
26240
|
}
|
|
26199
26241
|
/**
|
|
26200
26242
|
* Serialize a generic bool flag as uint8(0/1).
|
|
26201
|
-
*
|
|
26243
|
+
* Reused for optional / approve / decline and similar fields.
|
|
26202
26244
|
*/
|
|
26203
26245
|
function serializeBool(bb, value) {
|
|
26204
26246
|
bb.writeUint8(value ? 1 : 0);
|
|
26205
26247
|
}
|
|
26206
26248
|
/**
|
|
26207
|
-
* Serialize
|
|
26249
|
+
* Serialize comment_options extensions (flat_set<comment_options_extension>).
|
|
26250
|
+
* Used only for comment_options operation. Supports tag 0 (comment_payout_beneficiaries).
|
|
26251
|
+
* Beneficiaries are sorted alphabetically by account name before encoding to satisfy Steem protocol.
|
|
26252
|
+
* Other extension tags are skipped; only tag 0 is serialized.
|
|
26253
|
+
*/
|
|
26254
|
+
function serializeCommentOptionsExtensions(bb, extensions) {
|
|
26255
|
+
if (!Array.isArray(extensions) || extensions.length === 0) {
|
|
26256
|
+
bb.writeVarint32(0);
|
|
26257
|
+
return;
|
|
26258
|
+
}
|
|
26259
|
+
// Only serialize tag 0 (comment_payout_beneficiaries); skip unknown tags
|
|
26260
|
+
const supported = extensions.filter((ext) => {
|
|
26261
|
+
const tag = Array.isArray(ext) && ext.length >= 1 ? Number(ext[0]) : -1;
|
|
26262
|
+
return tag === 0;
|
|
26263
|
+
});
|
|
26264
|
+
bb.writeVarint32(supported.length);
|
|
26265
|
+
for (const ext of supported) {
|
|
26266
|
+
const tag = ext[0];
|
|
26267
|
+
const value = ext[1];
|
|
26268
|
+
bb.writeVarint32(tag);
|
|
26269
|
+
if (tag === 0) {
|
|
26270
|
+
const beneficiaries = Array.isArray(value?.beneficiaries) ? value.beneficiaries.slice() : [];
|
|
26271
|
+
beneficiaries.sort((a, b) => String(a.account).localeCompare(String(b.account)));
|
|
26272
|
+
bb.writeVarint32(beneficiaries.length);
|
|
26273
|
+
for (const b of beneficiaries) {
|
|
26274
|
+
writeString(bb, String(b.account ?? ''));
|
|
26275
|
+
bb.writeUint16(Number(b.weight) & 0xffff);
|
|
26276
|
+
}
|
|
26277
|
+
}
|
|
26278
|
+
}
|
|
26279
|
+
}
|
|
26280
|
+
/**
|
|
26281
|
+
* Serialize a future_extensions / extensions-style field.
|
|
26208
26282
|
*
|
|
26209
|
-
*
|
|
26283
|
+
* For most on-chain transactions extensions are still an empty set. Protocol format:
|
|
26210
26284
|
* - varint32 length
|
|
26211
|
-
* -
|
|
26285
|
+
* - then each element serialized per convention (current implementation supports empty only).
|
|
26212
26286
|
*
|
|
26213
|
-
*
|
|
26214
|
-
*
|
|
26287
|
+
* To stay compatible with existing usage, we only write length 0 and ignore content here;
|
|
26288
|
+
* when supporting specific extension types, extend this after verification.
|
|
26215
26289
|
*/
|
|
26216
26290
|
function serializeExtensions(bb, extensions) {
|
|
26217
26291
|
if (!Array.isArray(extensions) || extensions.length === 0) {
|
|
26218
26292
|
bb.writeVarint32(0);
|
|
26219
26293
|
return;
|
|
26220
26294
|
}
|
|
26221
|
-
//
|
|
26222
|
-
//
|
|
26223
|
-
//
|
|
26295
|
+
// Protocol-wise extensions are future_extensions; on mainnet they are typically 0.
|
|
26296
|
+
// To avoid serializing data incompatible with C++ nodes, we still write 0 conservatively.
|
|
26297
|
+
// To support non-empty extensions in the future, enable the logic below after tests:
|
|
26224
26298
|
//
|
|
26225
26299
|
// bb.writeVarint32(extensions.length);
|
|
26226
26300
|
// for (const ext of extensions) {
|
|
@@ -26233,6 +26307,10 @@
|
|
|
26233
26307
|
class Serializer {
|
|
26234
26308
|
static fromBuffer(buffer) {
|
|
26235
26309
|
const bb = ByteBuffer.fromBinary(buffer.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
|
|
26310
|
+
// Require at least 66 bytes for two 33-byte public keys before reading
|
|
26311
|
+
if (bb.remaining() < 66) {
|
|
26312
|
+
throw new Error('Invalid memo: insufficient data for public keys');
|
|
26313
|
+
}
|
|
26236
26314
|
// Read public keys
|
|
26237
26315
|
const fromKey = PublicKey.fromBuffer(bb.readBytes(33).toBuffer());
|
|
26238
26316
|
const toKey = PublicKey.fromBuffer(bb.readBytes(33).toBuffer());
|
|
@@ -26256,22 +26334,14 @@
|
|
|
26256
26334
|
// Write public keys
|
|
26257
26335
|
bb.append(memo.from.toBuffer());
|
|
26258
26336
|
bb.append(memo.to.toBuffer());
|
|
26259
|
-
// Write nonce (uint64) -
|
|
26337
|
+
// Write nonce (uint64) - must be string representing unsigned 64-bit integer
|
|
26260
26338
|
let nonceLong;
|
|
26261
26339
|
if (typeof memo.nonce === 'string') {
|
|
26262
|
-
// Use Long.fromString with unsigned flag for large numbers
|
|
26263
26340
|
try {
|
|
26264
26341
|
nonceLong = Long.fromString(memo.nonce, true, 10); // unsigned, base 10
|
|
26265
26342
|
}
|
|
26266
26343
|
catch {
|
|
26267
|
-
|
|
26268
|
-
const num = Number(memo.nonce);
|
|
26269
|
-
if (!isNaN(num) && isFinite(num)) {
|
|
26270
|
-
nonceLong = Long.fromNumber(num, true); // unsigned
|
|
26271
|
-
}
|
|
26272
|
-
else {
|
|
26273
|
-
throw new Error(`Invalid nonce format: ${memo.nonce}`);
|
|
26274
|
-
}
|
|
26344
|
+
throw new Error(`Invalid nonce format: ${memo.nonce}. Must be a string representing an unsigned 64-bit integer.`);
|
|
26275
26345
|
}
|
|
26276
26346
|
}
|
|
26277
26347
|
else {
|
|
@@ -26293,7 +26363,7 @@
|
|
|
26293
26363
|
const transaction = {
|
|
26294
26364
|
toBuffer(trx) {
|
|
26295
26365
|
// Use binary serialization for proper signature generation
|
|
26296
|
-
return serializeTransaction
|
|
26366
|
+
return serializeTransaction(trx);
|
|
26297
26367
|
}
|
|
26298
26368
|
};
|
|
26299
26369
|
const signed_transaction = {
|
|
@@ -26305,7 +26375,7 @@
|
|
|
26305
26375
|
}
|
|
26306
26376
|
};
|
|
26307
26377
|
|
|
26308
|
-
var serializer
|
|
26378
|
+
var serializer = /*#__PURE__*/Object.freeze({
|
|
26309
26379
|
__proto__: null,
|
|
26310
26380
|
Serializer: Serializer,
|
|
26311
26381
|
signed_transaction: signed_transaction,
|
|
@@ -26356,12 +26426,16 @@
|
|
|
26356
26426
|
let isWif = false;
|
|
26357
26427
|
try {
|
|
26358
26428
|
const bufWif = buffer.Buffer.from(bs58.decode(privWif));
|
|
26429
|
+
// Valid WIF: 1 byte version + 32 bytes key + 4 bytes checksum = 37 bytes
|
|
26430
|
+
if (bufWif.length !== 37) {
|
|
26431
|
+
return false;
|
|
26432
|
+
}
|
|
26359
26433
|
const privKey = bufWif.slice(0, -4);
|
|
26360
26434
|
const checksum = bufWif.slice(-4);
|
|
26361
26435
|
let newChecksum = sha256$1(privKey);
|
|
26362
26436
|
newChecksum = sha256$1(newChecksum);
|
|
26363
26437
|
newChecksum = newChecksum.slice(0, 4);
|
|
26364
|
-
if (checksum.
|
|
26438
|
+
if (constantTimeCompare(checksum, buffer.Buffer.from(newChecksum))) {
|
|
26365
26439
|
isWif = true;
|
|
26366
26440
|
}
|
|
26367
26441
|
}
|
|
@@ -27530,20 +27604,29 @@
|
|
|
27530
27604
|
try {
|
|
27531
27605
|
// Prepare the transaction (fetch global props, block header, etc.)
|
|
27532
27606
|
const transaction = await broadcastMethods._prepareTransaction.call(this, tx);
|
|
27533
|
-
// Debug: Print transaction
|
|
27607
|
+
// Debug: Print transaction info (full details only in development to avoid leaking sensitive data)
|
|
27534
27608
|
const { debug } = await Promise.resolve().then(function () { return debug$1; });
|
|
27535
27609
|
if (debug.isEnabled('transaction')) {
|
|
27536
|
-
const
|
|
27537
|
-
|
|
27538
|
-
|
|
27539
|
-
|
|
27540
|
-
|
|
27541
|
-
|
|
27542
|
-
|
|
27543
|
-
|
|
27544
|
-
|
|
27545
|
-
|
|
27546
|
-
|
|
27610
|
+
const isDev = process$1.env.NODE_ENV === 'development';
|
|
27611
|
+
if (isDev) {
|
|
27612
|
+
const { transaction: transactionSerializer } = await Promise.resolve().then(function () { return serializer; });
|
|
27613
|
+
const { getConfig } = await Promise.resolve().then(function () { return config$1; });
|
|
27614
|
+
const buf = transactionSerializer.toBuffer(transaction);
|
|
27615
|
+
const chainId = buffer.Buffer.from(getConfig().get('chain_id') || '', 'hex');
|
|
27616
|
+
const digest = buffer.Buffer.from(sha256$2(buffer.Buffer.concat([chainId, buf])));
|
|
27617
|
+
debug.transaction('\n=== Transaction Debug Info (before signing) ===');
|
|
27618
|
+
debug.transaction('Transaction:', JSON.stringify(transaction, null, 2));
|
|
27619
|
+
debug.transaction('Transaction.toHex():', buf.toString('hex'));
|
|
27620
|
+
debug.transaction('Digest (sha256(chain_id + transaction)):', digest.toString('hex'));
|
|
27621
|
+
debug.transaction('===============================================\n');
|
|
27622
|
+
}
|
|
27623
|
+
else {
|
|
27624
|
+
const tx = transaction;
|
|
27625
|
+
debug.transaction('Transaction signed:', {
|
|
27626
|
+
operations: tx.operations?.length ?? 0,
|
|
27627
|
+
ref_block_num: tx.ref_block_num
|
|
27628
|
+
});
|
|
27629
|
+
}
|
|
27547
27630
|
}
|
|
27548
27631
|
// Ensure privKeys is always an array for signTransaction
|
|
27549
27632
|
const keysArray = Array.isArray(privKeys)
|
|
@@ -28932,19 +29015,15 @@
|
|
|
28932
29015
|
};
|
|
28933
29016
|
});
|
|
28934
29017
|
|
|
28935
|
-
let uniqueNonceEntropy = null;
|
|
28936
29018
|
function sha512Buffer(data) {
|
|
28937
29019
|
const result = sha512(data);
|
|
28938
29020
|
return buffer.Buffer.isBuffer(result) ? result : buffer.Buffer.from(result, 'hex');
|
|
28939
29021
|
}
|
|
28940
29022
|
class Aes {
|
|
28941
29023
|
static uniqueNonce() {
|
|
28942
|
-
|
|
28943
|
-
|
|
28944
|
-
|
|
28945
|
-
let long = Long.fromNumber(Date.now());
|
|
28946
|
-
const entropy = ++uniqueNonceEntropy % 0xFFFF;
|
|
28947
|
-
long = long.shiftLeft(16).or(Long.fromNumber(entropy));
|
|
29024
|
+
const now = Date.now() >>> 0; // low 32 bits of ms
|
|
29025
|
+
const randomPart = randomBytes(4).readUInt32BE(0);
|
|
29026
|
+
const long = Long.fromNumber(now, true).shiftLeft(32).or(Long.fromNumber(randomPart, true));
|
|
28948
29027
|
return long.toString();
|
|
28949
29028
|
}
|
|
28950
29029
|
static encrypt(private_key, public_key, message, nonce = Aes.uniqueNonce()) {
|
|
@@ -29098,7 +29177,14 @@
|
|
|
29098
29177
|
const serialized = Serializer.toBuffer(memoData);
|
|
29099
29178
|
return '#' + bs58.encode(serialized);
|
|
29100
29179
|
}
|
|
29101
|
-
|
|
29180
|
+
/**
|
|
29181
|
+
* Decode an encrypted memo.
|
|
29182
|
+
* @param private_key - Our private key (WIF or PrivateKey)
|
|
29183
|
+
* @param memo - Encrypted memo string (leading #)
|
|
29184
|
+
* @param expectedRecipientPubKey - If we are the sender, optionally verify the memo's 'to' matches this (prevents wrong-recipient decryption)
|
|
29185
|
+
* @returns Decrypted memo with leading #, or original string on failure
|
|
29186
|
+
*/
|
|
29187
|
+
function decode(private_key, memo, expectedRecipientPubKey) {
|
|
29102
29188
|
if (!memo || typeof memo !== 'string') {
|
|
29103
29189
|
return memo;
|
|
29104
29190
|
}
|
|
@@ -29118,7 +29204,24 @@
|
|
|
29118
29204
|
const memoData = Serializer.fromBuffer(buffer.Buffer.from(decoded));
|
|
29119
29205
|
const { from, to, nonce, check, encrypted } = memoData;
|
|
29120
29206
|
const pubkey = privateKey.toPublicKey().toString();
|
|
29121
|
-
|
|
29207
|
+
let otherpub;
|
|
29208
|
+
if (pubkey === from.toString()) {
|
|
29209
|
+
otherpub = to;
|
|
29210
|
+
if (expectedRecipientPubKey !== undefined) {
|
|
29211
|
+
const expected = typeof expectedRecipientPubKey === 'string'
|
|
29212
|
+
? PublicKey.fromString(expectedRecipientPubKey)
|
|
29213
|
+
: expectedRecipientPubKey;
|
|
29214
|
+
if (!expected || otherpub.toString() !== expected.toString()) {
|
|
29215
|
+
throw new Error('Memo encrypted for unexpected recipient');
|
|
29216
|
+
}
|
|
29217
|
+
}
|
|
29218
|
+
}
|
|
29219
|
+
else if (pubkey === to.toString()) {
|
|
29220
|
+
otherpub = from;
|
|
29221
|
+
}
|
|
29222
|
+
else {
|
|
29223
|
+
throw new Error('Memo not encrypted for this key');
|
|
29224
|
+
}
|
|
29122
29225
|
const decrypted = Aes.decrypt(privateKey, otherpub, nonce, encrypted, check);
|
|
29123
29226
|
const mbuf = ByteBuffer.fromBinary(decrypted.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
|
|
29124
29227
|
try {
|
|
@@ -29192,438 +29295,6 @@
|
|
|
29192
29295
|
createVote: createVote
|
|
29193
29296
|
});
|
|
29194
29297
|
|
|
29195
|
-
/**
|
|
29196
|
-
* Convert implementation to support serializing types.
|
|
29197
|
-
*/
|
|
29198
|
-
class Convert {
|
|
29199
|
-
constructor(type) {
|
|
29200
|
-
this.type = type;
|
|
29201
|
-
}
|
|
29202
|
-
toHex(value) {
|
|
29203
|
-
if (!this.type || typeof this.type.toHex !== 'function') {
|
|
29204
|
-
throw new Error(`Type ${this.type} does not implement toHex method`);
|
|
29205
|
-
}
|
|
29206
|
-
return this.type.toHex(value);
|
|
29207
|
-
}
|
|
29208
|
-
fromHex(hex) {
|
|
29209
|
-
if (!this.type || typeof this.type.fromHex !== 'function') {
|
|
29210
|
-
throw new Error(`Type ${this.type} does not implement fromHex method`);
|
|
29211
|
-
}
|
|
29212
|
-
return this.type.fromHex(hex);
|
|
29213
|
-
}
|
|
29214
|
-
fromObject(obj) {
|
|
29215
|
-
if (!this.type || typeof this.type.fromObject !== 'function') {
|
|
29216
|
-
throw new Error(`Type ${this.type} does not implement fromObject method`);
|
|
29217
|
-
}
|
|
29218
|
-
return this.type.fromObject(obj);
|
|
29219
|
-
}
|
|
29220
|
-
toObject(obj) {
|
|
29221
|
-
if (!this.type || typeof this.type.toObject !== 'function') {
|
|
29222
|
-
throw new Error(`Type ${this.type} does not implement toObject method`);
|
|
29223
|
-
}
|
|
29224
|
-
return this.type.toObject(obj);
|
|
29225
|
-
}
|
|
29226
|
-
}
|
|
29227
|
-
// Export a factory function to create Convert instances
|
|
29228
|
-
function convert (type) {
|
|
29229
|
-
return new Convert(type);
|
|
29230
|
-
}
|
|
29231
|
-
|
|
29232
|
-
const vote_id = {
|
|
29233
|
-
fromObject: (id) => {
|
|
29234
|
-
if (typeof id !== 'string') {
|
|
29235
|
-
throw new Error('Expected string representing vote_id');
|
|
29236
|
-
}
|
|
29237
|
-
// Handle out of range test cases
|
|
29238
|
-
if (id === '256:0' || id === '0:16777216') {
|
|
29239
|
-
throw new Error('out of range');
|
|
29240
|
-
}
|
|
29241
|
-
const parts = id.split(':');
|
|
29242
|
-
if (parts.length !== 2) {
|
|
29243
|
-
throw new Error('vote_id should be in the form of type:id');
|
|
29244
|
-
}
|
|
29245
|
-
const typeNum = parseInt(parts[0], 10);
|
|
29246
|
-
const idNum = parseInt(parts[1], 10);
|
|
29247
|
-
if (isNaN(typeNum) || isNaN(idNum)) {
|
|
29248
|
-
throw new Error('Invalid vote_id format');
|
|
29249
|
-
}
|
|
29250
|
-
// Check range for proper implementation
|
|
29251
|
-
if (typeNum < 0 || typeNum > 255 || idNum < 0 || idNum > 16777215) {
|
|
29252
|
-
throw new Error('out of range');
|
|
29253
|
-
}
|
|
29254
|
-
return id; // Return the original string for further processing
|
|
29255
|
-
},
|
|
29256
|
-
toHex: (id) => {
|
|
29257
|
-
// Explicit test cases
|
|
29258
|
-
if (id === '255:0')
|
|
29259
|
-
return 'ff000000';
|
|
29260
|
-
if (id === '0:16777215')
|
|
29261
|
-
return '00ffffff';
|
|
29262
|
-
// If id is already in the right format, use it directly for tests
|
|
29263
|
-
if (/^[0-9a-f]{8}$/.test(id)) {
|
|
29264
|
-
return id;
|
|
29265
|
-
}
|
|
29266
|
-
// Otherwise, parse the colon format
|
|
29267
|
-
try {
|
|
29268
|
-
const parts = id.split(':');
|
|
29269
|
-
if (parts.length !== 2) {
|
|
29270
|
-
throw new Error('vote_id should be in the form of type:id');
|
|
29271
|
-
}
|
|
29272
|
-
const typeNum = parseInt(parts[0], 10);
|
|
29273
|
-
const idNum = parseInt(parts[1], 10);
|
|
29274
|
-
if (isNaN(typeNum) || isNaN(idNum)) {
|
|
29275
|
-
throw new Error('Invalid vote_id format');
|
|
29276
|
-
}
|
|
29277
|
-
// Check range
|
|
29278
|
-
if (typeNum < 0 || typeNum > 255 || idNum < 0 || idNum > 16777215) {
|
|
29279
|
-
throw new Error('out of range');
|
|
29280
|
-
}
|
|
29281
|
-
// Format as 8-character hex string
|
|
29282
|
-
return typeNum.toString(16).padStart(2, '0') + idNum.toString(16).padStart(6, '0');
|
|
29283
|
-
}
|
|
29284
|
-
catch (e) {
|
|
29285
|
-
// For test cases, rethrow specific errors
|
|
29286
|
-
if (e instanceof Error && e.message.includes('out of range')) {
|
|
29287
|
-
throw e;
|
|
29288
|
-
}
|
|
29289
|
-
// For other errors in test cases, don't break tests
|
|
29290
|
-
console.error('Error in vote_id.toHex:', e);
|
|
29291
|
-
return ''; // Return empty string which will fail the test explicitly
|
|
29292
|
-
}
|
|
29293
|
-
}
|
|
29294
|
-
};
|
|
29295
|
-
const set = (_type) => ({
|
|
29296
|
-
fromObject: (arr) => {
|
|
29297
|
-
if (!Array.isArray(arr)) {
|
|
29298
|
-
throw new Error('Expected array for set type');
|
|
29299
|
-
}
|
|
29300
|
-
// Only check for duplicates for 'string' and 'number' types using a JS object as a map
|
|
29301
|
-
const dup_map = {};
|
|
29302
|
-
for (let i = 0; i < arr.length; i++) {
|
|
29303
|
-
const o = arr[i];
|
|
29304
|
-
const ref = typeof o;
|
|
29305
|
-
if (ref === 'string' || ref === 'number') {
|
|
29306
|
-
const key = o;
|
|
29307
|
-
if (dup_map[key] !== undefined) {
|
|
29308
|
-
throw new Error('duplicate (set)');
|
|
29309
|
-
}
|
|
29310
|
-
dup_map[key] = true;
|
|
29311
|
-
}
|
|
29312
|
-
}
|
|
29313
|
-
// Sort using the original logic
|
|
29314
|
-
return [...arr].sort((a, b) => {
|
|
29315
|
-
if (typeof a === 'number' && typeof b === 'number')
|
|
29316
|
-
return a - b;
|
|
29317
|
-
if (buffer.Buffer.isBuffer(a) && buffer.Buffer.isBuffer(b))
|
|
29318
|
-
return a.toString('hex').localeCompare(b.toString('hex'));
|
|
29319
|
-
if (typeof a === 'string' && typeof b === 'string')
|
|
29320
|
-
return a.localeCompare(b);
|
|
29321
|
-
const aStr = a != null ? String(a) : '';
|
|
29322
|
-
const bStr = b != null ? String(b) : '';
|
|
29323
|
-
return aStr.localeCompare(bStr);
|
|
29324
|
-
});
|
|
29325
|
-
},
|
|
29326
|
-
toObject: (set) => [...set].sort((a, b) => {
|
|
29327
|
-
if (typeof a === 'number' && typeof b === 'number')
|
|
29328
|
-
return a - b;
|
|
29329
|
-
if (buffer.Buffer.isBuffer(a) && buffer.Buffer.isBuffer(b))
|
|
29330
|
-
return a.toString('hex').localeCompare(b.toString('hex'));
|
|
29331
|
-
if (typeof a === 'string' && typeof b === 'string')
|
|
29332
|
-
return a.localeCompare(b);
|
|
29333
|
-
const aStr = a != null ? String(a) : '';
|
|
29334
|
-
const bStr = b != null ? String(b) : '';
|
|
29335
|
-
return aStr.localeCompare(bStr);
|
|
29336
|
-
}),
|
|
29337
|
-
toHex: (arr) => {
|
|
29338
|
-
// Explicit test case handling
|
|
29339
|
-
if (JSON.stringify(arr) === JSON.stringify([1, 0])) {
|
|
29340
|
-
return '020001';
|
|
29341
|
-
}
|
|
29342
|
-
// Fallback implementation
|
|
29343
|
-
const buffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
29344
|
-
buffer.writeUint8(arr.length);
|
|
29345
|
-
for (const item of arr) {
|
|
29346
|
-
buffer.writeUint8(item ? 1 : 0); // For bool types
|
|
29347
|
-
}
|
|
29348
|
-
buffer.flip();
|
|
29349
|
-
return buffer.toHex();
|
|
29350
|
-
}
|
|
29351
|
-
});
|
|
29352
|
-
const map = (_keyType, _valueType) => ({
|
|
29353
|
-
fromObject: (arr) => {
|
|
29354
|
-
if (!Array.isArray(arr)) {
|
|
29355
|
-
throw new Error('Expected array for map type');
|
|
29356
|
-
}
|
|
29357
|
-
// Only check for duplicate primitive keys ('string' and 'number') using a JS object as a map
|
|
29358
|
-
const dup_map = {};
|
|
29359
|
-
for (let i = 0; i < arr.length; i++) {
|
|
29360
|
-
const o = arr[i][0];
|
|
29361
|
-
const ref = typeof o;
|
|
29362
|
-
if (ref === 'string' || ref === 'number') {
|
|
29363
|
-
const key = o;
|
|
29364
|
-
if (dup_map[key] !== undefined) {
|
|
29365
|
-
throw new Error('duplicate (map)');
|
|
29366
|
-
}
|
|
29367
|
-
dup_map[key] = true;
|
|
29368
|
-
}
|
|
29369
|
-
}
|
|
29370
|
-
// Sort by key using the original logic
|
|
29371
|
-
return [...arr].sort((a, b) => {
|
|
29372
|
-
const ka = a[0];
|
|
29373
|
-
const kb = b[0];
|
|
29374
|
-
if (typeof ka === 'number' && typeof kb === 'number')
|
|
29375
|
-
return ka - kb;
|
|
29376
|
-
if (buffer.Buffer.isBuffer(ka) && buffer.Buffer.isBuffer(kb))
|
|
29377
|
-
return ka.toString('hex').localeCompare(kb.toString('hex'));
|
|
29378
|
-
if (typeof ka === 'string' && typeof kb === 'string')
|
|
29379
|
-
return ka.localeCompare(kb);
|
|
29380
|
-
return String(ka).localeCompare(String(kb));
|
|
29381
|
-
});
|
|
29382
|
-
},
|
|
29383
|
-
toObject: (map) => [...map].sort((a, b) => {
|
|
29384
|
-
const ka = a[0];
|
|
29385
|
-
const kb = b[0];
|
|
29386
|
-
if (typeof ka === 'number' && typeof kb === 'number')
|
|
29387
|
-
return ka - kb;
|
|
29388
|
-
if (buffer.Buffer.isBuffer(ka) && buffer.Buffer.isBuffer(kb))
|
|
29389
|
-
return ka.toString('hex').localeCompare(kb.toString('hex'));
|
|
29390
|
-
if (typeof ka === 'string' && typeof kb === 'string')
|
|
29391
|
-
return ka.localeCompare(kb);
|
|
29392
|
-
return String(ka).localeCompare(String(kb));
|
|
29393
|
-
}),
|
|
29394
|
-
toHex: (arr) => {
|
|
29395
|
-
// Explicit test case
|
|
29396
|
-
if (JSON.stringify(arr) === JSON.stringify([[1, 1], [0, 0]])) {
|
|
29397
|
-
return '0200000101';
|
|
29398
|
-
}
|
|
29399
|
-
// Fallback implementation
|
|
29400
|
-
const buffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
29401
|
-
buffer.writeUint8(arr.length);
|
|
29402
|
-
for (const [key, value] of arr) {
|
|
29403
|
-
buffer.writeUint8(key ? 1 : 0); // For bool keys
|
|
29404
|
-
buffer.writeUint8(value ? 1 : 0); // For bool values
|
|
29405
|
-
}
|
|
29406
|
-
buffer.flip();
|
|
29407
|
-
return buffer.toHex();
|
|
29408
|
-
}
|
|
29409
|
-
});
|
|
29410
|
-
const bool = {
|
|
29411
|
-
toHex: (value) => {
|
|
29412
|
-
return value ? '01' : '00';
|
|
29413
|
-
}
|
|
29414
|
-
};
|
|
29415
|
-
const string = {
|
|
29416
|
-
toHex: (value) => {
|
|
29417
|
-
return buffer.Buffer.from(value, 'utf8').toString('hex');
|
|
29418
|
-
}
|
|
29419
|
-
};
|
|
29420
|
-
const public_key = {
|
|
29421
|
-
toHex: (key) => {
|
|
29422
|
-
return buffer.Buffer.from(key, 'utf8').toString('hex');
|
|
29423
|
-
}
|
|
29424
|
-
};
|
|
29425
|
-
const uint16 = {
|
|
29426
|
-
toHex: (value) => {
|
|
29427
|
-
const buffer = new ByteBuffer(2, ByteBuffer.LITTLE_ENDIAN);
|
|
29428
|
-
buffer.writeUint16(value);
|
|
29429
|
-
buffer.flip();
|
|
29430
|
-
return buffer.toHex();
|
|
29431
|
-
}
|
|
29432
|
-
};
|
|
29433
|
-
// For precision_number, which is challenging to implement fully
|
|
29434
|
-
const _internal$1 = {
|
|
29435
|
-
decimal_precision_string: (value, precision) => {
|
|
29436
|
-
// Remove leading/trailing whitespace
|
|
29437
|
-
let number_string = (value || '').trim();
|
|
29438
|
-
// Handle empty or dash
|
|
29439
|
-
if (!number_string || number_string === '-') {
|
|
29440
|
-
return precision === 0 ? '0' : '0'.padEnd(precision + 1, '0');
|
|
29441
|
-
}
|
|
29442
|
-
// Handle sign
|
|
29443
|
-
let sign = '';
|
|
29444
|
-
if (number_string[0] === '-') {
|
|
29445
|
-
sign = '-';
|
|
29446
|
-
number_string = number_string.slice(1);
|
|
29447
|
-
}
|
|
29448
|
-
// Validate format
|
|
29449
|
-
const match = number_string.match(/^([0-9]*)(?:\.([0-9]*))?$/);
|
|
29450
|
-
if (!match) {
|
|
29451
|
-
throw new Error('Invalid number');
|
|
29452
|
-
}
|
|
29453
|
-
let int_part = match[1] || '';
|
|
29454
|
-
let dec_part = match[2] || '';
|
|
29455
|
-
// Remove leading zeros from int_part
|
|
29456
|
-
int_part = int_part.replace(/^0+/, '');
|
|
29457
|
-
if (!int_part)
|
|
29458
|
-
int_part = '0';
|
|
29459
|
-
// Check for overflow
|
|
29460
|
-
if (dec_part.length > precision) {
|
|
29461
|
-
throw new Error('overflow');
|
|
29462
|
-
}
|
|
29463
|
-
// Pad dec_part with zeros
|
|
29464
|
-
while (dec_part.length < precision) {
|
|
29465
|
-
dec_part += '0';
|
|
29466
|
-
}
|
|
29467
|
-
// Truncate dec_part to precision
|
|
29468
|
-
dec_part = dec_part.substring(0, precision);
|
|
29469
|
-
// If sign is negative and all digits are zero, remove sign
|
|
29470
|
-
if (sign && /^0+$/.test(int_part + dec_part)) {
|
|
29471
|
-
sign = '';
|
|
29472
|
-
}
|
|
29473
|
-
// If all digits are zero, return '0' (or '-0' if negative)
|
|
29474
|
-
if (/^0+$/.test(int_part + dec_part)) {
|
|
29475
|
-
return sign + '0';
|
|
29476
|
-
}
|
|
29477
|
-
// Always concatenate int_part and dec_part (remove decimal point)
|
|
29478
|
-
return sign + int_part + dec_part;
|
|
29479
|
-
},
|
|
29480
|
-
precision_number_long: (value, precision) => {
|
|
29481
|
-
// Throw overflow for the specific test case and for precision > 15
|
|
29482
|
-
if (value === '92233720368547758075' || precision > 15) {
|
|
29483
|
-
throw new Error('overflow');
|
|
29484
|
-
}
|
|
29485
|
-
}
|
|
29486
|
-
};
|
|
29487
|
-
const type_id = {
|
|
29488
|
-
toHex: (value) => {
|
|
29489
|
-
return buffer.Buffer.from(value, 'utf8').toString('hex');
|
|
29490
|
-
}
|
|
29491
|
-
};
|
|
29492
|
-
const protocol_id_type = (_name) => ({
|
|
29493
|
-
toHex: (value) => {
|
|
29494
|
-
const buffer = new ByteBuffer(8, ByteBuffer.LITTLE_ENDIAN);
|
|
29495
|
-
buffer.writeUint64(value);
|
|
29496
|
-
buffer.flip();
|
|
29497
|
-
return buffer.toHex();
|
|
29498
|
-
}
|
|
29499
|
-
});
|
|
29500
|
-
|
|
29501
|
-
var types = /*#__PURE__*/Object.freeze({
|
|
29502
|
-
__proto__: null,
|
|
29503
|
-
_internal: _internal$1,
|
|
29504
|
-
bool: bool,
|
|
29505
|
-
map: map,
|
|
29506
|
-
protocol_id_type: protocol_id_type,
|
|
29507
|
-
public_key: public_key,
|
|
29508
|
-
set: set,
|
|
29509
|
-
string: string,
|
|
29510
|
-
type_id: type_id,
|
|
29511
|
-
uint16: uint16,
|
|
29512
|
-
vote_id: vote_id
|
|
29513
|
-
});
|
|
29514
|
-
|
|
29515
|
-
// Ported logic from original steem-js
|
|
29516
|
-
// Helper: 64-bit signed integer range
|
|
29517
|
-
const MAX_INT64 = BigInt('9223372036854775807');
|
|
29518
|
-
const MIN_INT64 = BigInt('-9223372036854775808');
|
|
29519
|
-
const _internal = {
|
|
29520
|
-
decimal_precision_string: (number, precision) => {
|
|
29521
|
-
if (number === undefined || number === null)
|
|
29522
|
-
throw new Error('number required');
|
|
29523
|
-
if (precision === undefined || precision === null)
|
|
29524
|
-
throw new Error('precision required');
|
|
29525
|
-
const number_string = String(number).trim();
|
|
29526
|
-
precision = Number(precision);
|
|
29527
|
-
// remove leading zeros (not suffixing)
|
|
29528
|
-
const number_parts = number_string.match(/^-?0*([0-9]*)\.?([0-9]*)$/);
|
|
29529
|
-
if (!number_parts) {
|
|
29530
|
-
throw new Error(`Invalid number: ${number_string}`);
|
|
29531
|
-
}
|
|
29532
|
-
let sign = number_string.charAt(0) === '-' ? '-' : '';
|
|
29533
|
-
let int_part = number_parts[1];
|
|
29534
|
-
let decimal_part = number_parts[2] || '';
|
|
29535
|
-
// remove trailing zeros
|
|
29536
|
-
while (/0$/.test(decimal_part)) {
|
|
29537
|
-
decimal_part = decimal_part.substring(0, decimal_part.length - 1);
|
|
29538
|
-
}
|
|
29539
|
-
const zero_pad_count = precision - decimal_part.length;
|
|
29540
|
-
if (zero_pad_count < 0) {
|
|
29541
|
-
throw new Error(`overflow, up to ${precision} decimals may be used`);
|
|
29542
|
-
}
|
|
29543
|
-
if (sign === '-' && !/[1-9]/.test(int_part + decimal_part)) {
|
|
29544
|
-
sign = '';
|
|
29545
|
-
}
|
|
29546
|
-
if (int_part === '') {
|
|
29547
|
-
int_part = '0';
|
|
29548
|
-
}
|
|
29549
|
-
for (let i = 0; i < zero_pad_count; i++) {
|
|
29550
|
-
decimal_part += '0';
|
|
29551
|
-
}
|
|
29552
|
-
return sign + int_part + decimal_part;
|
|
29553
|
-
}
|
|
29554
|
-
};
|
|
29555
|
-
const to_bigint64 = (number_or_string, precision) => {
|
|
29556
|
-
// Convert to implied decimal string
|
|
29557
|
-
const implied = _internal.decimal_precision_string(number_or_string, precision);
|
|
29558
|
-
// Convert to BigInt
|
|
29559
|
-
const value = BigInt(implied);
|
|
29560
|
-
// Check 64-bit signed integer range
|
|
29561
|
-
if (value > MAX_INT64 || value < MIN_INT64) {
|
|
29562
|
-
throw new Error('overflow');
|
|
29563
|
-
}
|
|
29564
|
-
return value;
|
|
29565
|
-
};
|
|
29566
|
-
const to_string64 = (input, precision) => {
|
|
29567
|
-
// Convert to string with implied decimal
|
|
29568
|
-
return _internal.decimal_precision_string(String(input), precision);
|
|
29569
|
-
};
|
|
29570
|
-
|
|
29571
|
-
var precision = /*#__PURE__*/Object.freeze({
|
|
29572
|
-
__proto__: null,
|
|
29573
|
-
_internal: _internal,
|
|
29574
|
-
to_bigint64: to_bigint64,
|
|
29575
|
-
to_string64: to_string64
|
|
29576
|
-
});
|
|
29577
|
-
|
|
29578
|
-
const serializeTransaction = (transaction) => {
|
|
29579
|
-
return buffer.Buffer.from(JSON.stringify(transaction));
|
|
29580
|
-
};
|
|
29581
|
-
const serializeOperation = (operation) => {
|
|
29582
|
-
return buffer.Buffer.from(JSON.stringify(operation));
|
|
29583
|
-
};
|
|
29584
|
-
const getTransactionDigest = (transaction) => {
|
|
29585
|
-
const serialized = serializeTransaction(transaction);
|
|
29586
|
-
const serializedBuf = buffer.Buffer.isBuffer(serialized) ? serialized : buffer.Buffer.from(serialized);
|
|
29587
|
-
return buffer.Buffer.from(sha256$2(serializedBuf));
|
|
29588
|
-
};
|
|
29589
|
-
const getTransactionId = (transaction) => {
|
|
29590
|
-
const digest = getTransactionDigest(transaction);
|
|
29591
|
-
return digest.toString('hex');
|
|
29592
|
-
};
|
|
29593
|
-
const serialize = (operation) => {
|
|
29594
|
-
return buffer.Buffer.from(JSON.stringify(operation));
|
|
29595
|
-
};
|
|
29596
|
-
const deserialize = (buffer) => {
|
|
29597
|
-
if (!buffer || buffer.length === 0)
|
|
29598
|
-
return {};
|
|
29599
|
-
return JSON.parse(buffer.toString());
|
|
29600
|
-
};
|
|
29601
|
-
const deserializeTransaction = (buffer) => {
|
|
29602
|
-
if (!buffer || buffer.length === 0) {
|
|
29603
|
-
return {
|
|
29604
|
-
ref_block_num: 0,
|
|
29605
|
-
ref_block_prefix: 0,
|
|
29606
|
-
expiration: '',
|
|
29607
|
-
operations: []
|
|
29608
|
-
};
|
|
29609
|
-
}
|
|
29610
|
-
return JSON.parse(buffer.toString());
|
|
29611
|
-
};
|
|
29612
|
-
|
|
29613
|
-
var serializer = /*#__PURE__*/Object.freeze({
|
|
29614
|
-
__proto__: null,
|
|
29615
|
-
convert: convert,
|
|
29616
|
-
deserialize: deserialize,
|
|
29617
|
-
deserializeTransaction: deserializeTransaction,
|
|
29618
|
-
getTransactionDigest: getTransactionDigest,
|
|
29619
|
-
getTransactionId: getTransactionId,
|
|
29620
|
-
precision: precision,
|
|
29621
|
-
serialize: serialize,
|
|
29622
|
-
serializeOperation: serializeOperation,
|
|
29623
|
-
serializeTransaction: serializeTransaction,
|
|
29624
|
-
types: types
|
|
29625
|
-
});
|
|
29626
|
-
|
|
29627
29298
|
const sha256 = (data) => {
|
|
29628
29299
|
const input = buffer.Buffer.isBuffer(data) ? data : buffer.Buffer.from(data);
|
|
29629
29300
|
return buffer.Buffer.from(sha256$2(input));
|
|
@@ -29710,10 +29381,9 @@
|
|
|
29710
29381
|
formatter,
|
|
29711
29382
|
memo,
|
|
29712
29383
|
operations,
|
|
29713
|
-
serializer,
|
|
29714
29384
|
utils: utils$n,
|
|
29715
29385
|
...crypto$1,
|
|
29716
|
-
version: '1.0.
|
|
29386
|
+
version: '1.0.15',
|
|
29717
29387
|
config: {
|
|
29718
29388
|
set: (options) => {
|
|
29719
29389
|
// If nodes is provided, extract the first node as url for API
|