@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.js
CHANGED
|
@@ -13838,6 +13838,20 @@ class PublicKey {
|
|
|
13838
13838
|
const secp256k1$1 = new ellipticExports.ec('secp256k1');
|
|
13839
13839
|
const G = secp256k1$1.g;
|
|
13840
13840
|
const n = new BN(secp256k1$1.n.toString());
|
|
13841
|
+
/**
|
|
13842
|
+
* Constant-time buffer comparison to prevent timing attacks.
|
|
13843
|
+
* Returns true only if a.length === b.length and a[i] === b[i] for all i.
|
|
13844
|
+
*/
|
|
13845
|
+
function constantTimeCompare(a, b) {
|
|
13846
|
+
if (a.length !== b.length) {
|
|
13847
|
+
return false;
|
|
13848
|
+
}
|
|
13849
|
+
let result = 0;
|
|
13850
|
+
for (let i = 0; i < a.length; i++) {
|
|
13851
|
+
result |= a[i] ^ b[i];
|
|
13852
|
+
}
|
|
13853
|
+
return result === 0;
|
|
13854
|
+
}
|
|
13841
13855
|
class PrivateKey {
|
|
13842
13856
|
/**
|
|
13843
13857
|
* @private see static functions
|
|
@@ -13850,11 +13864,8 @@ class PrivateKey {
|
|
|
13850
13864
|
if (!Buffer.isBuffer(buf)) {
|
|
13851
13865
|
throw new Error("Expecting parameter to be a Buffer type");
|
|
13852
13866
|
}
|
|
13853
|
-
if (
|
|
13854
|
-
|
|
13855
|
-
}
|
|
13856
|
-
if (buf.length === 0) {
|
|
13857
|
-
throw new Error("Empty buffer");
|
|
13867
|
+
if (buf.length !== 32) {
|
|
13868
|
+
throw new Error(`Invalid private key buffer: expected 32 bytes, got ${buf.length}`);
|
|
13858
13869
|
}
|
|
13859
13870
|
return new PrivateKey(new BN(buf));
|
|
13860
13871
|
}
|
|
@@ -13879,21 +13890,33 @@ class PrivateKey {
|
|
|
13879
13890
|
* @return {string} Wallet Import Format (still a secret, Not encrypted)
|
|
13880
13891
|
*/
|
|
13881
13892
|
static fromWif(private_wif) {
|
|
13882
|
-
|
|
13893
|
+
if (!private_wif || typeof private_wif !== 'string') {
|
|
13894
|
+
throw new Error('Invalid WIF: empty or not a string');
|
|
13895
|
+
}
|
|
13896
|
+
let private_wif_buffer;
|
|
13897
|
+
try {
|
|
13898
|
+
private_wif_buffer = Buffer.from(bs58.decode(private_wif));
|
|
13899
|
+
}
|
|
13900
|
+
catch {
|
|
13901
|
+
throw new Error('Invalid WIF: failed to decode base58');
|
|
13902
|
+
}
|
|
13903
|
+
// Valid WIF: 1 byte version + 32 bytes key + 4 bytes checksum = 37 bytes
|
|
13904
|
+
if (private_wif_buffer.length !== 37) {
|
|
13905
|
+
throw new Error(`Invalid WIF: expected 37 bytes, got ${private_wif_buffer.length}`);
|
|
13906
|
+
}
|
|
13883
13907
|
const version = private_wif_buffer.readUInt8(0);
|
|
13884
|
-
if (version !== 0x80)
|
|
13885
|
-
throw new Error(`
|
|
13886
|
-
|
|
13887
|
-
const private_key = private_wif_buffer.slice(
|
|
13888
|
-
const checksum = private_wif_buffer.slice(
|
|
13889
|
-
let new_checksum = sha256$1(private_key);
|
|
13908
|
+
if (version !== 0x80) {
|
|
13909
|
+
throw new Error(`Invalid WIF: expected version 0x80, got 0x${version.toString(16)}`);
|
|
13910
|
+
}
|
|
13911
|
+
const private_key = private_wif_buffer.slice(1, 33);
|
|
13912
|
+
const checksum = private_wif_buffer.slice(33);
|
|
13913
|
+
let new_checksum = sha256$1(Buffer.concat([Buffer.from([0x80]), private_key]));
|
|
13890
13914
|
new_checksum = sha256$1(new_checksum);
|
|
13891
13915
|
new_checksum = new_checksum.slice(0, 4);
|
|
13892
|
-
if (checksum.
|
|
13916
|
+
if (!constantTimeCompare(checksum, Buffer.from(new_checksum))) {
|
|
13893
13917
|
throw new Error('Invalid WIF key (checksum miss-match)');
|
|
13894
13918
|
}
|
|
13895
|
-
|
|
13896
|
-
return PrivateKey.fromBuffer(private_key.slice(1));
|
|
13919
|
+
return PrivateKey.fromBuffer(private_key);
|
|
13897
13920
|
}
|
|
13898
13921
|
toWif() {
|
|
13899
13922
|
const private_key = this.toBuffer();
|
|
@@ -14272,18 +14295,21 @@ function calcPubKeyRecoveryParam(curve, e, signature, Q) {
|
|
|
14272
14295
|
}
|
|
14273
14296
|
catch (error) {
|
|
14274
14297
|
// try next value
|
|
14275
|
-
|
|
14298
|
+
if (process.env.NODE_ENV === 'development') {
|
|
14299
|
+
console.debug(`Recovery attempt ${i} failed:`, error.message);
|
|
14300
|
+
}
|
|
14276
14301
|
}
|
|
14277
14302
|
}
|
|
14278
|
-
|
|
14279
|
-
|
|
14280
|
-
|
|
14281
|
-
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14285
|
-
|
|
14286
|
-
|
|
14303
|
+
if (process.env.NODE_ENV === 'development') {
|
|
14304
|
+
console.debug('All recovery attempts failed. Signature:', {
|
|
14305
|
+
r: signature.r.toString(16),
|
|
14306
|
+
s: signature.s.toString(16)
|
|
14307
|
+
});
|
|
14308
|
+
console.debug('Expected public key:', {
|
|
14309
|
+
x: Q.getX().toString(16),
|
|
14310
|
+
y: Q.getY().toString(16)
|
|
14311
|
+
});
|
|
14312
|
+
}
|
|
14287
14313
|
throw new Error('Unable to find valid recovery factor');
|
|
14288
14314
|
}
|
|
14289
14315
|
|
|
@@ -14335,9 +14361,10 @@ class Signature {
|
|
|
14335
14361
|
const d = privKey.d;
|
|
14336
14362
|
let ecsignature;
|
|
14337
14363
|
let nonce = 0;
|
|
14364
|
+
const MAX_NONCE_ATTEMPTS = 1000;
|
|
14338
14365
|
// Match old-steem-js behavior: find canonical signature (lenR === 32 && lenS === 32)
|
|
14339
14366
|
// Based on C++ is_fc_canonical logic
|
|
14340
|
-
while (
|
|
14367
|
+
while (nonce < MAX_NONCE_ATTEMPTS) {
|
|
14341
14368
|
ecsignature = sign$3(secp256k1, buf_sha256, d, nonce++);
|
|
14342
14369
|
const rBa = ecsignature.r.toArrayLike(Buffer, 'be', 32);
|
|
14343
14370
|
const sBa = ecsignature.s.toArrayLike(Buffer, 'be', 32);
|
|
@@ -14354,6 +14381,9 @@ class Signature {
|
|
|
14354
14381
|
console.debug("WARN: " + nonce + " attempts to find canonical signature");
|
|
14355
14382
|
}
|
|
14356
14383
|
}
|
|
14384
|
+
if (nonce >= MAX_NONCE_ATTEMPTS || ecsignature === undefined) {
|
|
14385
|
+
throw new Error('Failed to find canonical signature after maximum attempts');
|
|
14386
|
+
}
|
|
14357
14387
|
const i = calcPubKeyRecoveryParam(secp256k1, new BN(buf_sha256), ecsignature, privKey.toPublic().Q);
|
|
14358
14388
|
// Use recovery byte 31-34 (instead of 27-30) to be compatible with dsteem
|
|
14359
14389
|
// dsteem expects: recovery = byte - 31, so byte = recovery + 31
|
|
@@ -14654,7 +14684,11 @@ async function validate(request, verify) {
|
|
|
14654
14684
|
if (Number.isNaN(timestamp)) {
|
|
14655
14685
|
throw new Error('Invalid timestamp');
|
|
14656
14686
|
}
|
|
14657
|
-
|
|
14687
|
+
const now = Date.now();
|
|
14688
|
+
const timeDiff = Math.abs(now - timestamp);
|
|
14689
|
+
const SIGNATURE_VALIDITY_MS = 60 * 1000;
|
|
14690
|
+
const MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;
|
|
14691
|
+
if (timeDiff > SIGNATURE_VALIDITY_MS + MAX_CLOCK_SKEW_MS) {
|
|
14658
14692
|
throw new Error('Signature expired');
|
|
14659
14693
|
}
|
|
14660
14694
|
const message = hashMessage(signed.timestamp, signed.account, request.method, signed.params, nonce);
|
|
@@ -15629,6 +15663,37 @@ class BaseTransport extends EventEmitter {
|
|
|
15629
15663
|
}
|
|
15630
15664
|
|
|
15631
15665
|
// @ts-expect-error: No types for 'retry'
|
|
15666
|
+
/** Detect Node.js for optional undici Agent (custom TLS). */
|
|
15667
|
+
const isNode = typeof process !== 'undefined' &&
|
|
15668
|
+
typeof process.versions === 'object' &&
|
|
15669
|
+
typeof process.versions.node === 'string';
|
|
15670
|
+
/**
|
|
15671
|
+
* Build RequestInit for fetch. In Node when options.httpsOptions is set, inject undici Agent as dispatcher.
|
|
15672
|
+
*/
|
|
15673
|
+
async function buildFetchOptions(body, options) {
|
|
15674
|
+
const init = {
|
|
15675
|
+
method: 'POST',
|
|
15676
|
+
headers: {
|
|
15677
|
+
'Content-Type': 'application/json',
|
|
15678
|
+
'Accept': 'application/json'
|
|
15679
|
+
},
|
|
15680
|
+
body
|
|
15681
|
+
};
|
|
15682
|
+
if (isNode && options.httpsOptions) {
|
|
15683
|
+
// Node 18+ built-in fetch uses undici; custom TLS via node:undici Agent (built-in, no package)
|
|
15684
|
+
// @ts-expect-error - node:undici is Node built-in, not in @types/node
|
|
15685
|
+
const { Agent } = await import('node:undici');
|
|
15686
|
+
const opts = options.httpsOptions;
|
|
15687
|
+
const agent = new Agent({
|
|
15688
|
+
connect: {
|
|
15689
|
+
rejectUnauthorized: opts.rejectUnauthorized,
|
|
15690
|
+
ca: opts.ca
|
|
15691
|
+
}
|
|
15692
|
+
});
|
|
15693
|
+
init.dispatcher = agent;
|
|
15694
|
+
}
|
|
15695
|
+
return init;
|
|
15696
|
+
}
|
|
15632
15697
|
/**
|
|
15633
15698
|
* Extended Error type for JSON-RPC errors
|
|
15634
15699
|
*/
|
|
@@ -15648,29 +15713,22 @@ class JsonRpcError extends Error {
|
|
|
15648
15713
|
* @param request - The JSON-RPC request object
|
|
15649
15714
|
* @param fetchMethod - Optional fetch implementation (defaults to global fetch)
|
|
15650
15715
|
* @param timeoutMs - Request timeout in milliseconds (default: 30000)
|
|
15716
|
+
* @param httpsOptions - Optional TLS options (Node.js only): rejectUnauthorized, ca
|
|
15651
15717
|
* @returns Promise resolving to the JSON-RPC result
|
|
15652
15718
|
*/
|
|
15653
|
-
const jsonRpc = async (url, request, fetchMethod = fetch, timeoutMs = 30000) => {
|
|
15719
|
+
const jsonRpc = async (url, request, fetchMethod = fetch, timeoutMs = 30000, httpsOptions) => {
|
|
15654
15720
|
const payload = {
|
|
15655
15721
|
jsonrpc: '2.0',
|
|
15656
15722
|
...request
|
|
15657
15723
|
};
|
|
15658
15724
|
let timeoutId = null;
|
|
15659
|
-
// Create a promise that will reject after the timeout
|
|
15660
15725
|
const timeoutPromise = new Promise((_, reject) => {
|
|
15661
15726
|
timeoutId = setTimeout(() => {
|
|
15662
15727
|
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
15663
15728
|
}, timeoutMs);
|
|
15664
15729
|
});
|
|
15665
|
-
|
|
15666
|
-
const fetchPromise = fetchMethod(url,
|
|
15667
|
-
method: 'POST',
|
|
15668
|
-
headers: {
|
|
15669
|
-
'Content-Type': 'application/json',
|
|
15670
|
-
'Accept': 'application/json'
|
|
15671
|
-
},
|
|
15672
|
-
body: JSON.stringify(payload)
|
|
15673
|
-
})
|
|
15730
|
+
const fetchOptions = await buildFetchOptions(JSON.stringify(payload), { httpsOptions });
|
|
15731
|
+
const fetchPromise = fetchMethod(url, fetchOptions)
|
|
15674
15732
|
.then(async (res) => {
|
|
15675
15733
|
if (!res.ok) {
|
|
15676
15734
|
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
@@ -15721,71 +15779,56 @@ class HttpTransport extends BaseTransport {
|
|
|
15721
15779
|
const params = [api, data.method, data.params];
|
|
15722
15780
|
const isBroadcast = this.isBroadcastOperation(data.method);
|
|
15723
15781
|
const retryOptions = this.options.retry;
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
|
|
15743
|
-
|
|
15782
|
+
const body = JSON.stringify({
|
|
15783
|
+
jsonrpc: '2.0',
|
|
15784
|
+
method: 'call',
|
|
15785
|
+
params,
|
|
15786
|
+
id
|
|
15787
|
+
});
|
|
15788
|
+
const doRequest = (fetchOpts) => fetchMethod(url, fetchOpts)
|
|
15789
|
+
.then(async (res) => {
|
|
15790
|
+
if (!res.ok) {
|
|
15791
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
15792
|
+
}
|
|
15793
|
+
return res.json();
|
|
15794
|
+
});
|
|
15795
|
+
const runWithOptions = (fetchOpts) => {
|
|
15796
|
+
if (!isBroadcast && retryOptions) {
|
|
15797
|
+
const operation = typeof retryOptions === 'object' ? retry.operation(retryOptions) : retry.operation();
|
|
15798
|
+
operation.attempt((currentAttempt) => {
|
|
15799
|
+
doRequest(fetchOpts)
|
|
15800
|
+
.then((result) => {
|
|
15801
|
+
if (result.error) {
|
|
15802
|
+
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
15803
|
+
callback(error, undefined, currentAttempt);
|
|
15804
|
+
}
|
|
15805
|
+
else {
|
|
15806
|
+
callback(null, result.result, currentAttempt);
|
|
15807
|
+
}
|
|
15808
|
+
}, (error) => {
|
|
15809
|
+
if (operation.retry(error)) {
|
|
15810
|
+
return;
|
|
15811
|
+
}
|
|
15812
|
+
callback(operation.mainError(), undefined, currentAttempt);
|
|
15813
|
+
});
|
|
15814
|
+
});
|
|
15815
|
+
}
|
|
15816
|
+
else {
|
|
15817
|
+
doRequest(fetchOpts)
|
|
15744
15818
|
.then((result) => {
|
|
15745
|
-
// Check for JSON-RPC errors
|
|
15746
15819
|
if (result.error) {
|
|
15747
15820
|
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
15748
|
-
callback(error, undefined,
|
|
15821
|
+
callback(error, undefined, 1);
|
|
15749
15822
|
}
|
|
15750
15823
|
else {
|
|
15751
|
-
callback(null, result.result,
|
|
15824
|
+
callback(null, result.result, 1);
|
|
15752
15825
|
}
|
|
15753
|
-
}, (error) =>
|
|
15754
|
-
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
});
|
|
15760
|
-
}
|
|
15761
|
-
else {
|
|
15762
|
-
fetchMethod(url, {
|
|
15763
|
-
method: 'POST',
|
|
15764
|
-
body: JSON.stringify({
|
|
15765
|
-
jsonrpc: '2.0',
|
|
15766
|
-
method: 'call',
|
|
15767
|
-
params,
|
|
15768
|
-
id
|
|
15769
|
-
}),
|
|
15770
|
-
headers: { 'Content-Type': 'application/json' }
|
|
15771
|
-
})
|
|
15772
|
-
.then(async (res) => {
|
|
15773
|
-
if (!res.ok) {
|
|
15774
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
15775
|
-
}
|
|
15776
|
-
return res.json();
|
|
15777
|
-
})
|
|
15778
|
-
.then((result) => {
|
|
15779
|
-
// Check for JSON-RPC errors
|
|
15780
|
-
if (result.error) {
|
|
15781
|
-
const error = new JsonRpcError(result.error.message || 'JSON-RPC error', result.error.code, result.error.data);
|
|
15782
|
-
callback(error, undefined, 1);
|
|
15783
|
-
}
|
|
15784
|
-
else {
|
|
15785
|
-
callback(null, result.result, 1);
|
|
15786
|
-
}
|
|
15787
|
-
}, (error) => callback(error instanceof Error ? error : new Error(String(error)), undefined, 1));
|
|
15788
|
-
}
|
|
15826
|
+
}, (error) => callback(error instanceof Error ? error : new Error(String(error)), undefined, 1));
|
|
15827
|
+
}
|
|
15828
|
+
};
|
|
15829
|
+
buildFetchOptions(body, this.options)
|
|
15830
|
+
.then(runWithOptions)
|
|
15831
|
+
.catch((err) => callback(err instanceof Error ? err : new Error(String(err)), undefined, 1));
|
|
15789
15832
|
}
|
|
15790
15833
|
}
|
|
15791
15834
|
|
|
@@ -22063,7 +22106,7 @@ if (typeof BigInt === "function") {
|
|
|
22063
22106
|
* Serialize a transaction to binary format for Steem blockchain
|
|
22064
22107
|
* This is a simplified implementation that handles the basic structure
|
|
22065
22108
|
*/
|
|
22066
|
-
function serializeTransaction
|
|
22109
|
+
function serializeTransaction(trx) {
|
|
22067
22110
|
const bb = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
22068
22111
|
const trxObj = trx;
|
|
22069
22112
|
// Write ref_block_num (uint16)
|
|
@@ -22093,7 +22136,7 @@ function serializeTransaction$1(trx) {
|
|
|
22093
22136
|
const operations = (Array.isArray(trxObj.operations) ? trxObj.operations : []);
|
|
22094
22137
|
bb.writeVarint32(operations.length);
|
|
22095
22138
|
for (const op of operations) {
|
|
22096
|
-
serializeOperation
|
|
22139
|
+
serializeOperation(bb, op);
|
|
22097
22140
|
}
|
|
22098
22141
|
// Write extensions (set of future_extensions, which is void/empty)
|
|
22099
22142
|
bb.writeVarint32(0); // Empty set
|
|
@@ -22122,7 +22165,7 @@ function serializeTransaction$1(trx) {
|
|
|
22122
22165
|
/**
|
|
22123
22166
|
* Serialize an operation to binary format
|
|
22124
22167
|
*/
|
|
22125
|
-
function serializeOperation
|
|
22168
|
+
function serializeOperation(bb, op) {
|
|
22126
22169
|
if (!Array.isArray(op) || op.length !== 2) {
|
|
22127
22170
|
throw new Error('Operation must be an array of [operation_type, operation_data]');
|
|
22128
22171
|
}
|
|
@@ -23054,7 +23097,7 @@ function serializeCommentOptions(bb, data) {
|
|
|
23054
23097
|
bb.writeUint16(dataObj.percent_steem_dollars ?? 0);
|
|
23055
23098
|
serializeBool(bb, dataObj.allow_votes);
|
|
23056
23099
|
serializeBool(bb, dataObj.allow_curation_rewards);
|
|
23057
|
-
|
|
23100
|
+
serializeCommentOptionsExtensions(bb, dataObj.extensions);
|
|
23058
23101
|
}
|
|
23059
23102
|
/**
|
|
23060
23103
|
* Serialize custom_json operation
|
|
@@ -23131,7 +23174,7 @@ function serializeAuthority(bb, auth) {
|
|
|
23131
23174
|
*
|
|
23132
23175
|
* Format: int64 amount (little-endian) + uint8 precision + 7-byte symbol (UTF-8, null-padded).
|
|
23133
23176
|
*
|
|
23134
|
-
* This helper is reused across all operations
|
|
23177
|
+
* This helper is reused for asset fields across all operations, e.g.
|
|
23135
23178
|
* - amount / vesting_shares / reward_* / *_pays
|
|
23136
23179
|
*/
|
|
23137
23180
|
function serializeAsset(bb, amount) {
|
|
@@ -23151,7 +23194,7 @@ function serializeAsset(bb, amount) {
|
|
|
23151
23194
|
}
|
|
23152
23195
|
/**
|
|
23153
23196
|
* Write a string using ByteBuffer's writeVString method.
|
|
23154
|
-
*
|
|
23197
|
+
* All string fields are serialized through this helper to avoid calling ByteBuffer API directly everywhere.
|
|
23155
23198
|
*/
|
|
23156
23199
|
function writeString(bb, str) {
|
|
23157
23200
|
bb.writeVString(str);
|
|
@@ -23159,8 +23202,8 @@ function writeString(bb, str) {
|
|
|
23159
23202
|
/**
|
|
23160
23203
|
* Serialize a time_point_sec-style field.
|
|
23161
23204
|
*
|
|
23162
|
-
*
|
|
23163
|
-
*
|
|
23205
|
+
* Accepts ISO string / Date / seconds number; writes uint32 (seconds since epoch).
|
|
23206
|
+
* Used for proposal start/end, escrow_deadline, and similar fields.
|
|
23164
23207
|
*/
|
|
23165
23208
|
function serializeTimePointSec(bb, value) {
|
|
23166
23209
|
let seconds;
|
|
@@ -23173,7 +23216,7 @@ function serializeTimePointSec(bb, value) {
|
|
|
23173
23216
|
seconds = Math.floor(value.getTime() / 1000);
|
|
23174
23217
|
}
|
|
23175
23218
|
else if (typeof value === 'number') {
|
|
23176
|
-
//
|
|
23219
|
+
// Assume value is already in seconds
|
|
23177
23220
|
seconds = value;
|
|
23178
23221
|
}
|
|
23179
23222
|
else {
|
|
@@ -23183,29 +23226,61 @@ function serializeTimePointSec(bb, value) {
|
|
|
23183
23226
|
}
|
|
23184
23227
|
/**
|
|
23185
23228
|
* Serialize a generic bool flag as uint8(0/1).
|
|
23186
|
-
*
|
|
23229
|
+
* Reused for optional / approve / decline and similar fields.
|
|
23187
23230
|
*/
|
|
23188
23231
|
function serializeBool(bb, value) {
|
|
23189
23232
|
bb.writeUint8(value ? 1 : 0);
|
|
23190
23233
|
}
|
|
23191
23234
|
/**
|
|
23192
|
-
* Serialize
|
|
23235
|
+
* Serialize comment_options extensions (flat_set<comment_options_extension>).
|
|
23236
|
+
* Used only for comment_options operation. Supports tag 0 (comment_payout_beneficiaries).
|
|
23237
|
+
* Beneficiaries are sorted alphabetically by account name before encoding to satisfy Steem protocol.
|
|
23238
|
+
* Other extension tags are skipped; only tag 0 is serialized.
|
|
23239
|
+
*/
|
|
23240
|
+
function serializeCommentOptionsExtensions(bb, extensions) {
|
|
23241
|
+
if (!Array.isArray(extensions) || extensions.length === 0) {
|
|
23242
|
+
bb.writeVarint32(0);
|
|
23243
|
+
return;
|
|
23244
|
+
}
|
|
23245
|
+
// Only serialize tag 0 (comment_payout_beneficiaries); skip unknown tags
|
|
23246
|
+
const supported = extensions.filter((ext) => {
|
|
23247
|
+
const tag = Array.isArray(ext) && ext.length >= 1 ? Number(ext[0]) : -1;
|
|
23248
|
+
return tag === 0;
|
|
23249
|
+
});
|
|
23250
|
+
bb.writeVarint32(supported.length);
|
|
23251
|
+
for (const ext of supported) {
|
|
23252
|
+
const tag = ext[0];
|
|
23253
|
+
const value = ext[1];
|
|
23254
|
+
bb.writeVarint32(tag);
|
|
23255
|
+
if (tag === 0) {
|
|
23256
|
+
const beneficiaries = Array.isArray(value?.beneficiaries) ? value.beneficiaries.slice() : [];
|
|
23257
|
+
beneficiaries.sort((a, b) => String(a.account).localeCompare(String(b.account)));
|
|
23258
|
+
bb.writeVarint32(beneficiaries.length);
|
|
23259
|
+
for (const b of beneficiaries) {
|
|
23260
|
+
writeString(bb, String(b.account ?? ''));
|
|
23261
|
+
bb.writeUint16(Number(b.weight) & 0xffff);
|
|
23262
|
+
}
|
|
23263
|
+
}
|
|
23264
|
+
}
|
|
23265
|
+
}
|
|
23266
|
+
/**
|
|
23267
|
+
* Serialize a future_extensions / extensions-style field.
|
|
23193
23268
|
*
|
|
23194
|
-
*
|
|
23269
|
+
* For most on-chain transactions extensions are still an empty set. Protocol format:
|
|
23195
23270
|
* - varint32 length
|
|
23196
|
-
* -
|
|
23271
|
+
* - then each element serialized per convention (current implementation supports empty only).
|
|
23197
23272
|
*
|
|
23198
|
-
*
|
|
23199
|
-
*
|
|
23273
|
+
* To stay compatible with existing usage, we only write length 0 and ignore content here;
|
|
23274
|
+
* when supporting specific extension types, extend this after verification.
|
|
23200
23275
|
*/
|
|
23201
23276
|
function serializeExtensions(bb, extensions) {
|
|
23202
23277
|
if (!Array.isArray(extensions) || extensions.length === 0) {
|
|
23203
23278
|
bb.writeVarint32(0);
|
|
23204
23279
|
return;
|
|
23205
23280
|
}
|
|
23206
|
-
//
|
|
23207
|
-
//
|
|
23208
|
-
//
|
|
23281
|
+
// Protocol-wise extensions are future_extensions; on mainnet they are typically 0.
|
|
23282
|
+
// To avoid serializing data incompatible with C++ nodes, we still write 0 conservatively.
|
|
23283
|
+
// To support non-empty extensions in the future, enable the logic below after tests:
|
|
23209
23284
|
//
|
|
23210
23285
|
// bb.writeVarint32(extensions.length);
|
|
23211
23286
|
// for (const ext of extensions) {
|
|
@@ -23218,6 +23293,10 @@ function serializeExtensions(bb, extensions) {
|
|
|
23218
23293
|
class Serializer {
|
|
23219
23294
|
static fromBuffer(buffer) {
|
|
23220
23295
|
const bb = ByteBuffer.fromBinary(buffer.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
|
|
23296
|
+
// Require at least 66 bytes for two 33-byte public keys before reading
|
|
23297
|
+
if (bb.remaining() < 66) {
|
|
23298
|
+
throw new Error('Invalid memo: insufficient data for public keys');
|
|
23299
|
+
}
|
|
23221
23300
|
// Read public keys
|
|
23222
23301
|
const fromKey = PublicKey.fromBuffer(bb.readBytes(33).toBuffer());
|
|
23223
23302
|
const toKey = PublicKey.fromBuffer(bb.readBytes(33).toBuffer());
|
|
@@ -23241,22 +23320,14 @@ class Serializer {
|
|
|
23241
23320
|
// Write public keys
|
|
23242
23321
|
bb.append(memo.from.toBuffer());
|
|
23243
23322
|
bb.append(memo.to.toBuffer());
|
|
23244
|
-
// Write nonce (uint64) -
|
|
23323
|
+
// Write nonce (uint64) - must be string representing unsigned 64-bit integer
|
|
23245
23324
|
let nonceLong;
|
|
23246
23325
|
if (typeof memo.nonce === 'string') {
|
|
23247
|
-
// Use Long.fromString with unsigned flag for large numbers
|
|
23248
23326
|
try {
|
|
23249
23327
|
nonceLong = Long.fromString(memo.nonce, true, 10); // unsigned, base 10
|
|
23250
23328
|
}
|
|
23251
23329
|
catch {
|
|
23252
|
-
|
|
23253
|
-
const num = Number(memo.nonce);
|
|
23254
|
-
if (!isNaN(num) && isFinite(num)) {
|
|
23255
|
-
nonceLong = Long.fromNumber(num, true); // unsigned
|
|
23256
|
-
}
|
|
23257
|
-
else {
|
|
23258
|
-
throw new Error(`Invalid nonce format: ${memo.nonce}`);
|
|
23259
|
-
}
|
|
23330
|
+
throw new Error(`Invalid nonce format: ${memo.nonce}. Must be a string representing an unsigned 64-bit integer.`);
|
|
23260
23331
|
}
|
|
23261
23332
|
}
|
|
23262
23333
|
else {
|
|
@@ -23278,7 +23349,7 @@ class Serializer {
|
|
|
23278
23349
|
const transaction = {
|
|
23279
23350
|
toBuffer(trx) {
|
|
23280
23351
|
// Use binary serialization for proper signature generation
|
|
23281
|
-
return serializeTransaction
|
|
23352
|
+
return serializeTransaction(trx);
|
|
23282
23353
|
}
|
|
23283
23354
|
};
|
|
23284
23355
|
const signed_transaction = {
|
|
@@ -23290,7 +23361,7 @@ const signed_transaction = {
|
|
|
23290
23361
|
}
|
|
23291
23362
|
};
|
|
23292
23363
|
|
|
23293
|
-
const serializer
|
|
23364
|
+
const serializer = /*#__PURE__*/Object.freeze({
|
|
23294
23365
|
__proto__: null,
|
|
23295
23366
|
Serializer: Serializer,
|
|
23296
23367
|
signed_transaction: signed_transaction,
|
|
@@ -23341,12 +23412,16 @@ const Auth = {
|
|
|
23341
23412
|
let isWif = false;
|
|
23342
23413
|
try {
|
|
23343
23414
|
const bufWif = Buffer.from(bs58.decode(privWif));
|
|
23415
|
+
// Valid WIF: 1 byte version + 32 bytes key + 4 bytes checksum = 37 bytes
|
|
23416
|
+
if (bufWif.length !== 37) {
|
|
23417
|
+
return false;
|
|
23418
|
+
}
|
|
23344
23419
|
const privKey = bufWif.slice(0, -4);
|
|
23345
23420
|
const checksum = bufWif.slice(-4);
|
|
23346
23421
|
let newChecksum = sha256$1(privKey);
|
|
23347
23422
|
newChecksum = sha256$1(newChecksum);
|
|
23348
23423
|
newChecksum = newChecksum.slice(0, 4);
|
|
23349
|
-
if (checksum.
|
|
23424
|
+
if (constantTimeCompare(checksum, Buffer.from(newChecksum))) {
|
|
23350
23425
|
isWif = true;
|
|
23351
23426
|
}
|
|
23352
23427
|
}
|
|
@@ -24515,20 +24590,29 @@ class Broadcast {
|
|
|
24515
24590
|
try {
|
|
24516
24591
|
// Prepare the transaction (fetch global props, block header, etc.)
|
|
24517
24592
|
const transaction = await broadcastMethods._prepareTransaction.call(this, tx);
|
|
24518
|
-
// Debug: Print transaction
|
|
24593
|
+
// Debug: Print transaction info (full details only in development to avoid leaking sensitive data)
|
|
24519
24594
|
const { debug } = await Promise.resolve().then(function () { return debug$1; });
|
|
24520
24595
|
if (debug.isEnabled('transaction')) {
|
|
24521
|
-
const
|
|
24522
|
-
|
|
24523
|
-
|
|
24524
|
-
|
|
24525
|
-
|
|
24526
|
-
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
|
|
24531
|
-
|
|
24596
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
24597
|
+
if (isDev) {
|
|
24598
|
+
const { transaction: transactionSerializer } = await Promise.resolve().then(function () { return serializer; });
|
|
24599
|
+
const { getConfig } = await Promise.resolve().then(function () { return config$1; });
|
|
24600
|
+
const buf = transactionSerializer.toBuffer(transaction);
|
|
24601
|
+
const chainId = Buffer.from(getConfig().get('chain_id') || '', 'hex');
|
|
24602
|
+
const digest = Buffer.from(sha256$2(Buffer.concat([chainId, buf])));
|
|
24603
|
+
debug.transaction('\n=== Transaction Debug Info (before signing) ===');
|
|
24604
|
+
debug.transaction('Transaction:', JSON.stringify(transaction, null, 2));
|
|
24605
|
+
debug.transaction('Transaction.toHex():', buf.toString('hex'));
|
|
24606
|
+
debug.transaction('Digest (sha256(chain_id + transaction)):', digest.toString('hex'));
|
|
24607
|
+
debug.transaction('===============================================\n');
|
|
24608
|
+
}
|
|
24609
|
+
else {
|
|
24610
|
+
const tx = transaction;
|
|
24611
|
+
debug.transaction('Transaction signed:', {
|
|
24612
|
+
operations: tx.operations?.length ?? 0,
|
|
24613
|
+
ref_block_num: tx.ref_block_num
|
|
24614
|
+
});
|
|
24615
|
+
}
|
|
24532
24616
|
}
|
|
24533
24617
|
// Ensure privKeys is always an array for signTransaction
|
|
24534
24618
|
const keysArray = Array.isArray(privKeys)
|
|
@@ -25917,19 +26001,15 @@ const cbc = /* @__PURE__ */ wrapCipher({ blockSize: 16, nonceLength: 16 }, funct
|
|
|
25917
26001
|
};
|
|
25918
26002
|
});
|
|
25919
26003
|
|
|
25920
|
-
let uniqueNonceEntropy = null;
|
|
25921
26004
|
function sha512Buffer(data) {
|
|
25922
26005
|
const result = sha512(data);
|
|
25923
26006
|
return Buffer.isBuffer(result) ? result : Buffer.from(result, 'hex');
|
|
25924
26007
|
}
|
|
25925
26008
|
class Aes {
|
|
25926
26009
|
static uniqueNonce() {
|
|
25927
|
-
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
let long = Long.fromNumber(Date.now());
|
|
25931
|
-
const entropy = ++uniqueNonceEntropy % 0xFFFF;
|
|
25932
|
-
long = long.shiftLeft(16).or(Long.fromNumber(entropy));
|
|
26010
|
+
const now = Date.now() >>> 0; // low 32 bits of ms
|
|
26011
|
+
const randomPart = randomBytes(4).readUInt32BE(0);
|
|
26012
|
+
const long = Long.fromNumber(now, true).shiftLeft(32).or(Long.fromNumber(randomPart, true));
|
|
25933
26013
|
return long.toString();
|
|
25934
26014
|
}
|
|
25935
26015
|
static encrypt(private_key, public_key, message, nonce = Aes.uniqueNonce()) {
|
|
@@ -26083,7 +26163,14 @@ function encode(private_key, public_key, memo, testNonce) {
|
|
|
26083
26163
|
const serialized = Serializer.toBuffer(memoData);
|
|
26084
26164
|
return '#' + bs58.encode(serialized);
|
|
26085
26165
|
}
|
|
26086
|
-
|
|
26166
|
+
/**
|
|
26167
|
+
* Decode an encrypted memo.
|
|
26168
|
+
* @param private_key - Our private key (WIF or PrivateKey)
|
|
26169
|
+
* @param memo - Encrypted memo string (leading #)
|
|
26170
|
+
* @param expectedRecipientPubKey - If we are the sender, optionally verify the memo's 'to' matches this (prevents wrong-recipient decryption)
|
|
26171
|
+
* @returns Decrypted memo with leading #, or original string on failure
|
|
26172
|
+
*/
|
|
26173
|
+
function decode(private_key, memo, expectedRecipientPubKey) {
|
|
26087
26174
|
if (!memo || typeof memo !== 'string') {
|
|
26088
26175
|
return memo;
|
|
26089
26176
|
}
|
|
@@ -26103,7 +26190,24 @@ function decode(private_key, memo) {
|
|
|
26103
26190
|
const memoData = Serializer.fromBuffer(Buffer.from(decoded));
|
|
26104
26191
|
const { from, to, nonce, check, encrypted } = memoData;
|
|
26105
26192
|
const pubkey = privateKey.toPublicKey().toString();
|
|
26106
|
-
|
|
26193
|
+
let otherpub;
|
|
26194
|
+
if (pubkey === from.toString()) {
|
|
26195
|
+
otherpub = to;
|
|
26196
|
+
if (expectedRecipientPubKey !== undefined) {
|
|
26197
|
+
const expected = typeof expectedRecipientPubKey === 'string'
|
|
26198
|
+
? PublicKey.fromString(expectedRecipientPubKey)
|
|
26199
|
+
: expectedRecipientPubKey;
|
|
26200
|
+
if (!expected || otherpub.toString() !== expected.toString()) {
|
|
26201
|
+
throw new Error('Memo encrypted for unexpected recipient');
|
|
26202
|
+
}
|
|
26203
|
+
}
|
|
26204
|
+
}
|
|
26205
|
+
else if (pubkey === to.toString()) {
|
|
26206
|
+
otherpub = from;
|
|
26207
|
+
}
|
|
26208
|
+
else {
|
|
26209
|
+
throw new Error('Memo not encrypted for this key');
|
|
26210
|
+
}
|
|
26107
26211
|
const decrypted = Aes.decrypt(privateKey, otherpub, nonce, encrypted, check);
|
|
26108
26212
|
const mbuf = ByteBuffer.fromBinary(decrypted.toString('binary'), ByteBuffer.LITTLE_ENDIAN);
|
|
26109
26213
|
try {
|
|
@@ -26177,439 +26281,6 @@ const operations = /*#__PURE__*/Object.freeze({
|
|
|
26177
26281
|
createVote: createVote
|
|
26178
26282
|
});
|
|
26179
26283
|
|
|
26180
|
-
/**
|
|
26181
|
-
* Convert implementation to support serializing types.
|
|
26182
|
-
*/
|
|
26183
|
-
class Convert {
|
|
26184
|
-
constructor(type) {
|
|
26185
|
-
this.type = type;
|
|
26186
|
-
}
|
|
26187
|
-
toHex(value) {
|
|
26188
|
-
if (!this.type || typeof this.type.toHex !== 'function') {
|
|
26189
|
-
throw new Error(`Type ${this.type} does not implement toHex method`);
|
|
26190
|
-
}
|
|
26191
|
-
return this.type.toHex(value);
|
|
26192
|
-
}
|
|
26193
|
-
fromHex(hex) {
|
|
26194
|
-
if (!this.type || typeof this.type.fromHex !== 'function') {
|
|
26195
|
-
throw new Error(`Type ${this.type} does not implement fromHex method`);
|
|
26196
|
-
}
|
|
26197
|
-
return this.type.fromHex(hex);
|
|
26198
|
-
}
|
|
26199
|
-
fromObject(obj) {
|
|
26200
|
-
if (!this.type || typeof this.type.fromObject !== 'function') {
|
|
26201
|
-
throw new Error(`Type ${this.type} does not implement fromObject method`);
|
|
26202
|
-
}
|
|
26203
|
-
return this.type.fromObject(obj);
|
|
26204
|
-
}
|
|
26205
|
-
toObject(obj) {
|
|
26206
|
-
if (!this.type || typeof this.type.toObject !== 'function') {
|
|
26207
|
-
throw new Error(`Type ${this.type} does not implement toObject method`);
|
|
26208
|
-
}
|
|
26209
|
-
return this.type.toObject(obj);
|
|
26210
|
-
}
|
|
26211
|
-
}
|
|
26212
|
-
// Export a factory function to create Convert instances
|
|
26213
|
-
function convert (type) {
|
|
26214
|
-
return new Convert(type);
|
|
26215
|
-
}
|
|
26216
|
-
|
|
26217
|
-
// Minimal implementation for types to satisfy test imports
|
|
26218
|
-
const vote_id = {
|
|
26219
|
-
fromObject: (id) => {
|
|
26220
|
-
if (typeof id !== 'string') {
|
|
26221
|
-
throw new Error('Expected string representing vote_id');
|
|
26222
|
-
}
|
|
26223
|
-
// Handle out of range test cases
|
|
26224
|
-
if (id === '256:0' || id === '0:16777216') {
|
|
26225
|
-
throw new Error('out of range');
|
|
26226
|
-
}
|
|
26227
|
-
const parts = id.split(':');
|
|
26228
|
-
if (parts.length !== 2) {
|
|
26229
|
-
throw new Error('vote_id should be in the form of type:id');
|
|
26230
|
-
}
|
|
26231
|
-
const typeNum = parseInt(parts[0], 10);
|
|
26232
|
-
const idNum = parseInt(parts[1], 10);
|
|
26233
|
-
if (isNaN(typeNum) || isNaN(idNum)) {
|
|
26234
|
-
throw new Error('Invalid vote_id format');
|
|
26235
|
-
}
|
|
26236
|
-
// Check range for proper implementation
|
|
26237
|
-
if (typeNum < 0 || typeNum > 255 || idNum < 0 || idNum > 16777215) {
|
|
26238
|
-
throw new Error('out of range');
|
|
26239
|
-
}
|
|
26240
|
-
return id; // Return the original string for further processing
|
|
26241
|
-
},
|
|
26242
|
-
toHex: (id) => {
|
|
26243
|
-
// Explicit test cases
|
|
26244
|
-
if (id === '255:0')
|
|
26245
|
-
return 'ff000000';
|
|
26246
|
-
if (id === '0:16777215')
|
|
26247
|
-
return '00ffffff';
|
|
26248
|
-
// If id is already in the right format, use it directly for tests
|
|
26249
|
-
if (/^[0-9a-f]{8}$/.test(id)) {
|
|
26250
|
-
return id;
|
|
26251
|
-
}
|
|
26252
|
-
// Otherwise, parse the colon format
|
|
26253
|
-
try {
|
|
26254
|
-
const parts = id.split(':');
|
|
26255
|
-
if (parts.length !== 2) {
|
|
26256
|
-
throw new Error('vote_id should be in the form of type:id');
|
|
26257
|
-
}
|
|
26258
|
-
const typeNum = parseInt(parts[0], 10);
|
|
26259
|
-
const idNum = parseInt(parts[1], 10);
|
|
26260
|
-
if (isNaN(typeNum) || isNaN(idNum)) {
|
|
26261
|
-
throw new Error('Invalid vote_id format');
|
|
26262
|
-
}
|
|
26263
|
-
// Check range
|
|
26264
|
-
if (typeNum < 0 || typeNum > 255 || idNum < 0 || idNum > 16777215) {
|
|
26265
|
-
throw new Error('out of range');
|
|
26266
|
-
}
|
|
26267
|
-
// Format as 8-character hex string
|
|
26268
|
-
return typeNum.toString(16).padStart(2, '0') + idNum.toString(16).padStart(6, '0');
|
|
26269
|
-
}
|
|
26270
|
-
catch (e) {
|
|
26271
|
-
// For test cases, rethrow specific errors
|
|
26272
|
-
if (e instanceof Error && e.message.includes('out of range')) {
|
|
26273
|
-
throw e;
|
|
26274
|
-
}
|
|
26275
|
-
// For other errors in test cases, don't break tests
|
|
26276
|
-
console.error('Error in vote_id.toHex:', e);
|
|
26277
|
-
return ''; // Return empty string which will fail the test explicitly
|
|
26278
|
-
}
|
|
26279
|
-
}
|
|
26280
|
-
};
|
|
26281
|
-
const set = (_type) => ({
|
|
26282
|
-
fromObject: (arr) => {
|
|
26283
|
-
if (!Array.isArray(arr)) {
|
|
26284
|
-
throw new Error('Expected array for set type');
|
|
26285
|
-
}
|
|
26286
|
-
// Only check for duplicates for 'string' and 'number' types using a JS object as a map
|
|
26287
|
-
const dup_map = {};
|
|
26288
|
-
for (let i = 0; i < arr.length; i++) {
|
|
26289
|
-
const o = arr[i];
|
|
26290
|
-
const ref = typeof o;
|
|
26291
|
-
if (ref === 'string' || ref === 'number') {
|
|
26292
|
-
const key = o;
|
|
26293
|
-
if (dup_map[key] !== undefined) {
|
|
26294
|
-
throw new Error('duplicate (set)');
|
|
26295
|
-
}
|
|
26296
|
-
dup_map[key] = true;
|
|
26297
|
-
}
|
|
26298
|
-
}
|
|
26299
|
-
// Sort using the original logic
|
|
26300
|
-
return [...arr].sort((a, b) => {
|
|
26301
|
-
if (typeof a === 'number' && typeof b === 'number')
|
|
26302
|
-
return a - b;
|
|
26303
|
-
if (Buffer.isBuffer(a) && Buffer.isBuffer(b))
|
|
26304
|
-
return a.toString('hex').localeCompare(b.toString('hex'));
|
|
26305
|
-
if (typeof a === 'string' && typeof b === 'string')
|
|
26306
|
-
return a.localeCompare(b);
|
|
26307
|
-
const aStr = a != null ? String(a) : '';
|
|
26308
|
-
const bStr = b != null ? String(b) : '';
|
|
26309
|
-
return aStr.localeCompare(bStr);
|
|
26310
|
-
});
|
|
26311
|
-
},
|
|
26312
|
-
toObject: (set) => [...set].sort((a, b) => {
|
|
26313
|
-
if (typeof a === 'number' && typeof b === 'number')
|
|
26314
|
-
return a - b;
|
|
26315
|
-
if (Buffer.isBuffer(a) && Buffer.isBuffer(b))
|
|
26316
|
-
return a.toString('hex').localeCompare(b.toString('hex'));
|
|
26317
|
-
if (typeof a === 'string' && typeof b === 'string')
|
|
26318
|
-
return a.localeCompare(b);
|
|
26319
|
-
const aStr = a != null ? String(a) : '';
|
|
26320
|
-
const bStr = b != null ? String(b) : '';
|
|
26321
|
-
return aStr.localeCompare(bStr);
|
|
26322
|
-
}),
|
|
26323
|
-
toHex: (arr) => {
|
|
26324
|
-
// Explicit test case handling
|
|
26325
|
-
if (JSON.stringify(arr) === JSON.stringify([1, 0])) {
|
|
26326
|
-
return '020001';
|
|
26327
|
-
}
|
|
26328
|
-
// Fallback implementation
|
|
26329
|
-
const buffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
26330
|
-
buffer.writeUint8(arr.length);
|
|
26331
|
-
for (const item of arr) {
|
|
26332
|
-
buffer.writeUint8(item ? 1 : 0); // For bool types
|
|
26333
|
-
}
|
|
26334
|
-
buffer.flip();
|
|
26335
|
-
return buffer.toHex();
|
|
26336
|
-
}
|
|
26337
|
-
});
|
|
26338
|
-
const map = (_keyType, _valueType) => ({
|
|
26339
|
-
fromObject: (arr) => {
|
|
26340
|
-
if (!Array.isArray(arr)) {
|
|
26341
|
-
throw new Error('Expected array for map type');
|
|
26342
|
-
}
|
|
26343
|
-
// Only check for duplicate primitive keys ('string' and 'number') using a JS object as a map
|
|
26344
|
-
const dup_map = {};
|
|
26345
|
-
for (let i = 0; i < arr.length; i++) {
|
|
26346
|
-
const o = arr[i][0];
|
|
26347
|
-
const ref = typeof o;
|
|
26348
|
-
if (ref === 'string' || ref === 'number') {
|
|
26349
|
-
const key = o;
|
|
26350
|
-
if (dup_map[key] !== undefined) {
|
|
26351
|
-
throw new Error('duplicate (map)');
|
|
26352
|
-
}
|
|
26353
|
-
dup_map[key] = true;
|
|
26354
|
-
}
|
|
26355
|
-
}
|
|
26356
|
-
// Sort by key using the original logic
|
|
26357
|
-
return [...arr].sort((a, b) => {
|
|
26358
|
-
const ka = a[0];
|
|
26359
|
-
const kb = b[0];
|
|
26360
|
-
if (typeof ka === 'number' && typeof kb === 'number')
|
|
26361
|
-
return ka - kb;
|
|
26362
|
-
if (Buffer.isBuffer(ka) && Buffer.isBuffer(kb))
|
|
26363
|
-
return ka.toString('hex').localeCompare(kb.toString('hex'));
|
|
26364
|
-
if (typeof ka === 'string' && typeof kb === 'string')
|
|
26365
|
-
return ka.localeCompare(kb);
|
|
26366
|
-
return String(ka).localeCompare(String(kb));
|
|
26367
|
-
});
|
|
26368
|
-
},
|
|
26369
|
-
toObject: (map) => [...map].sort((a, b) => {
|
|
26370
|
-
const ka = a[0];
|
|
26371
|
-
const kb = b[0];
|
|
26372
|
-
if (typeof ka === 'number' && typeof kb === 'number')
|
|
26373
|
-
return ka - kb;
|
|
26374
|
-
if (Buffer.isBuffer(ka) && Buffer.isBuffer(kb))
|
|
26375
|
-
return ka.toString('hex').localeCompare(kb.toString('hex'));
|
|
26376
|
-
if (typeof ka === 'string' && typeof kb === 'string')
|
|
26377
|
-
return ka.localeCompare(kb);
|
|
26378
|
-
return String(ka).localeCompare(String(kb));
|
|
26379
|
-
}),
|
|
26380
|
-
toHex: (arr) => {
|
|
26381
|
-
// Explicit test case
|
|
26382
|
-
if (JSON.stringify(arr) === JSON.stringify([[1, 1], [0, 0]])) {
|
|
26383
|
-
return '0200000101';
|
|
26384
|
-
}
|
|
26385
|
-
// Fallback implementation
|
|
26386
|
-
const buffer = new ByteBuffer(ByteBuffer.DEFAULT_CAPACITY, ByteBuffer.LITTLE_ENDIAN);
|
|
26387
|
-
buffer.writeUint8(arr.length);
|
|
26388
|
-
for (const [key, value] of arr) {
|
|
26389
|
-
buffer.writeUint8(key ? 1 : 0); // For bool keys
|
|
26390
|
-
buffer.writeUint8(value ? 1 : 0); // For bool values
|
|
26391
|
-
}
|
|
26392
|
-
buffer.flip();
|
|
26393
|
-
return buffer.toHex();
|
|
26394
|
-
}
|
|
26395
|
-
});
|
|
26396
|
-
const bool = {
|
|
26397
|
-
toHex: (value) => {
|
|
26398
|
-
return value ? '01' : '00';
|
|
26399
|
-
}
|
|
26400
|
-
};
|
|
26401
|
-
const string = {
|
|
26402
|
-
toHex: (value) => {
|
|
26403
|
-
return Buffer.from(value, 'utf8').toString('hex');
|
|
26404
|
-
}
|
|
26405
|
-
};
|
|
26406
|
-
const public_key = {
|
|
26407
|
-
toHex: (key) => {
|
|
26408
|
-
return Buffer.from(key, 'utf8').toString('hex');
|
|
26409
|
-
}
|
|
26410
|
-
};
|
|
26411
|
-
const uint16 = {
|
|
26412
|
-
toHex: (value) => {
|
|
26413
|
-
const buffer = new ByteBuffer(2, ByteBuffer.LITTLE_ENDIAN);
|
|
26414
|
-
buffer.writeUint16(value);
|
|
26415
|
-
buffer.flip();
|
|
26416
|
-
return buffer.toHex();
|
|
26417
|
-
}
|
|
26418
|
-
};
|
|
26419
|
-
// For precision_number, which is challenging to implement fully
|
|
26420
|
-
const _internal$1 = {
|
|
26421
|
-
decimal_precision_string: (value, precision) => {
|
|
26422
|
-
// Remove leading/trailing whitespace
|
|
26423
|
-
let number_string = (value || '').trim();
|
|
26424
|
-
// Handle empty or dash
|
|
26425
|
-
if (!number_string || number_string === '-') {
|
|
26426
|
-
return precision === 0 ? '0' : '0'.padEnd(precision + 1, '0');
|
|
26427
|
-
}
|
|
26428
|
-
// Handle sign
|
|
26429
|
-
let sign = '';
|
|
26430
|
-
if (number_string[0] === '-') {
|
|
26431
|
-
sign = '-';
|
|
26432
|
-
number_string = number_string.slice(1);
|
|
26433
|
-
}
|
|
26434
|
-
// Validate format
|
|
26435
|
-
const match = number_string.match(/^([0-9]*)(?:\.([0-9]*))?$/);
|
|
26436
|
-
if (!match) {
|
|
26437
|
-
throw new Error('Invalid number');
|
|
26438
|
-
}
|
|
26439
|
-
let int_part = match[1] || '';
|
|
26440
|
-
let dec_part = match[2] || '';
|
|
26441
|
-
// Remove leading zeros from int_part
|
|
26442
|
-
int_part = int_part.replace(/^0+/, '');
|
|
26443
|
-
if (!int_part)
|
|
26444
|
-
int_part = '0';
|
|
26445
|
-
// Check for overflow
|
|
26446
|
-
if (dec_part.length > precision) {
|
|
26447
|
-
throw new Error('overflow');
|
|
26448
|
-
}
|
|
26449
|
-
// Pad dec_part with zeros
|
|
26450
|
-
while (dec_part.length < precision) {
|
|
26451
|
-
dec_part += '0';
|
|
26452
|
-
}
|
|
26453
|
-
// Truncate dec_part to precision
|
|
26454
|
-
dec_part = dec_part.substring(0, precision);
|
|
26455
|
-
// If sign is negative and all digits are zero, remove sign
|
|
26456
|
-
if (sign && /^0+$/.test(int_part + dec_part)) {
|
|
26457
|
-
sign = '';
|
|
26458
|
-
}
|
|
26459
|
-
// If all digits are zero, return '0' (or '-0' if negative)
|
|
26460
|
-
if (/^0+$/.test(int_part + dec_part)) {
|
|
26461
|
-
return sign + '0';
|
|
26462
|
-
}
|
|
26463
|
-
// Always concatenate int_part and dec_part (remove decimal point)
|
|
26464
|
-
return sign + int_part + dec_part;
|
|
26465
|
-
},
|
|
26466
|
-
precision_number_long: (value, precision) => {
|
|
26467
|
-
// Throw overflow for the specific test case and for precision > 15
|
|
26468
|
-
if (value === '92233720368547758075' || precision > 15) {
|
|
26469
|
-
throw new Error('overflow');
|
|
26470
|
-
}
|
|
26471
|
-
}
|
|
26472
|
-
};
|
|
26473
|
-
const type_id = {
|
|
26474
|
-
toHex: (value) => {
|
|
26475
|
-
return Buffer.from(value, 'utf8').toString('hex');
|
|
26476
|
-
}
|
|
26477
|
-
};
|
|
26478
|
-
const protocol_id_type = (_name) => ({
|
|
26479
|
-
toHex: (value) => {
|
|
26480
|
-
const buffer = new ByteBuffer(8, ByteBuffer.LITTLE_ENDIAN);
|
|
26481
|
-
buffer.writeUint64(value);
|
|
26482
|
-
buffer.flip();
|
|
26483
|
-
return buffer.toHex();
|
|
26484
|
-
}
|
|
26485
|
-
});
|
|
26486
|
-
|
|
26487
|
-
const types = /*#__PURE__*/Object.freeze({
|
|
26488
|
-
__proto__: null,
|
|
26489
|
-
_internal: _internal$1,
|
|
26490
|
-
bool: bool,
|
|
26491
|
-
map: map,
|
|
26492
|
-
protocol_id_type: protocol_id_type,
|
|
26493
|
-
public_key: public_key,
|
|
26494
|
-
set: set,
|
|
26495
|
-
string: string,
|
|
26496
|
-
type_id: type_id,
|
|
26497
|
-
uint16: uint16,
|
|
26498
|
-
vote_id: vote_id
|
|
26499
|
-
});
|
|
26500
|
-
|
|
26501
|
-
// Ported logic from original steem-js
|
|
26502
|
-
// Helper: 64-bit signed integer range
|
|
26503
|
-
const MAX_INT64 = BigInt('9223372036854775807');
|
|
26504
|
-
const MIN_INT64 = BigInt('-9223372036854775808');
|
|
26505
|
-
const _internal = {
|
|
26506
|
-
decimal_precision_string: (number, precision) => {
|
|
26507
|
-
if (number === undefined || number === null)
|
|
26508
|
-
throw new Error('number required');
|
|
26509
|
-
if (precision === undefined || precision === null)
|
|
26510
|
-
throw new Error('precision required');
|
|
26511
|
-
const number_string = String(number).trim();
|
|
26512
|
-
precision = Number(precision);
|
|
26513
|
-
// remove leading zeros (not suffixing)
|
|
26514
|
-
const number_parts = number_string.match(/^-?0*([0-9]*)\.?([0-9]*)$/);
|
|
26515
|
-
if (!number_parts) {
|
|
26516
|
-
throw new Error(`Invalid number: ${number_string}`);
|
|
26517
|
-
}
|
|
26518
|
-
let sign = number_string.charAt(0) === '-' ? '-' : '';
|
|
26519
|
-
let int_part = number_parts[1];
|
|
26520
|
-
let decimal_part = number_parts[2] || '';
|
|
26521
|
-
// remove trailing zeros
|
|
26522
|
-
while (/0$/.test(decimal_part)) {
|
|
26523
|
-
decimal_part = decimal_part.substring(0, decimal_part.length - 1);
|
|
26524
|
-
}
|
|
26525
|
-
const zero_pad_count = precision - decimal_part.length;
|
|
26526
|
-
if (zero_pad_count < 0) {
|
|
26527
|
-
throw new Error(`overflow, up to ${precision} decimals may be used`);
|
|
26528
|
-
}
|
|
26529
|
-
if (sign === '-' && !/[1-9]/.test(int_part + decimal_part)) {
|
|
26530
|
-
sign = '';
|
|
26531
|
-
}
|
|
26532
|
-
if (int_part === '') {
|
|
26533
|
-
int_part = '0';
|
|
26534
|
-
}
|
|
26535
|
-
for (let i = 0; i < zero_pad_count; i++) {
|
|
26536
|
-
decimal_part += '0';
|
|
26537
|
-
}
|
|
26538
|
-
return sign + int_part + decimal_part;
|
|
26539
|
-
}
|
|
26540
|
-
};
|
|
26541
|
-
const to_bigint64 = (number_or_string, precision) => {
|
|
26542
|
-
// Convert to implied decimal string
|
|
26543
|
-
const implied = _internal.decimal_precision_string(number_or_string, precision);
|
|
26544
|
-
// Convert to BigInt
|
|
26545
|
-
const value = BigInt(implied);
|
|
26546
|
-
// Check 64-bit signed integer range
|
|
26547
|
-
if (value > MAX_INT64 || value < MIN_INT64) {
|
|
26548
|
-
throw new Error('overflow');
|
|
26549
|
-
}
|
|
26550
|
-
return value;
|
|
26551
|
-
};
|
|
26552
|
-
const to_string64 = (input, precision) => {
|
|
26553
|
-
// Convert to string with implied decimal
|
|
26554
|
-
return _internal.decimal_precision_string(String(input), precision);
|
|
26555
|
-
};
|
|
26556
|
-
|
|
26557
|
-
const precision = /*#__PURE__*/Object.freeze({
|
|
26558
|
-
__proto__: null,
|
|
26559
|
-
_internal: _internal,
|
|
26560
|
-
to_bigint64: to_bigint64,
|
|
26561
|
-
to_string64: to_string64
|
|
26562
|
-
});
|
|
26563
|
-
|
|
26564
|
-
const serializeTransaction = (transaction) => {
|
|
26565
|
-
return Buffer.from(JSON.stringify(transaction));
|
|
26566
|
-
};
|
|
26567
|
-
const serializeOperation = (operation) => {
|
|
26568
|
-
return Buffer.from(JSON.stringify(operation));
|
|
26569
|
-
};
|
|
26570
|
-
const getTransactionDigest = (transaction) => {
|
|
26571
|
-
const serialized = serializeTransaction(transaction);
|
|
26572
|
-
const serializedBuf = Buffer.isBuffer(serialized) ? serialized : Buffer.from(serialized);
|
|
26573
|
-
return Buffer.from(sha256$2(serializedBuf));
|
|
26574
|
-
};
|
|
26575
|
-
const getTransactionId = (transaction) => {
|
|
26576
|
-
const digest = getTransactionDigest(transaction);
|
|
26577
|
-
return digest.toString('hex');
|
|
26578
|
-
};
|
|
26579
|
-
const serialize = (operation) => {
|
|
26580
|
-
return Buffer.from(JSON.stringify(operation));
|
|
26581
|
-
};
|
|
26582
|
-
const deserialize = (buffer) => {
|
|
26583
|
-
if (!buffer || buffer.length === 0)
|
|
26584
|
-
return {};
|
|
26585
|
-
return JSON.parse(buffer.toString());
|
|
26586
|
-
};
|
|
26587
|
-
const deserializeTransaction = (buffer) => {
|
|
26588
|
-
if (!buffer || buffer.length === 0) {
|
|
26589
|
-
return {
|
|
26590
|
-
ref_block_num: 0,
|
|
26591
|
-
ref_block_prefix: 0,
|
|
26592
|
-
expiration: '',
|
|
26593
|
-
operations: []
|
|
26594
|
-
};
|
|
26595
|
-
}
|
|
26596
|
-
return JSON.parse(buffer.toString());
|
|
26597
|
-
};
|
|
26598
|
-
|
|
26599
|
-
const serializer = /*#__PURE__*/Object.freeze({
|
|
26600
|
-
__proto__: null,
|
|
26601
|
-
convert: convert,
|
|
26602
|
-
deserialize: deserialize,
|
|
26603
|
-
deserializeTransaction: deserializeTransaction,
|
|
26604
|
-
getTransactionDigest: getTransactionDigest,
|
|
26605
|
-
getTransactionId: getTransactionId,
|
|
26606
|
-
precision: precision,
|
|
26607
|
-
serialize: serialize,
|
|
26608
|
-
serializeOperation: serializeOperation,
|
|
26609
|
-
serializeTransaction: serializeTransaction,
|
|
26610
|
-
types: types
|
|
26611
|
-
});
|
|
26612
|
-
|
|
26613
26284
|
const sha256 = (data) => {
|
|
26614
26285
|
const input = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
26615
26286
|
return Buffer.from(sha256$2(input));
|
|
@@ -26684,9 +26355,8 @@ const steem = {
|
|
|
26684
26355
|
formatter,
|
|
26685
26356
|
memo,
|
|
26686
26357
|
operations,
|
|
26687
|
-
serializer,
|
|
26688
26358
|
utils: utils$3,
|
|
26689
|
-
version: '1.0.
|
|
26359
|
+
version: '1.0.15',
|
|
26690
26360
|
config: {
|
|
26691
26361
|
set: (options) => {
|
|
26692
26362
|
// If nodes is provided, extract the first node as url for API
|