@steemit/steem-js 1.0.17 → 1.0.19

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.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Chain-safe JSON normalization for `account_update` operations.
3
+ *
4
+ * Protocol types (steem/libraries/protocol):
5
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
6
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
7
+ *
8
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
9
+ * - `authority` is a variant_object, not a bare key_auths array.
10
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
11
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
12
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
13
+ *
14
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
15
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
16
+ */
17
+ /** One entry in an fc::flat_map after JSON (de)serialization: [key, weight]. */
18
+ export type AuthorityWeightPair = [string, number];
19
+ /**
20
+ * `steem::protocol::authority` as returned by condenser / required for broadcast JSON.
21
+ * account_auths: flat_map<account_name_type, uint16>
22
+ * key_auths: flat_map<public_key_type, uint16> (keys are "STM..." strings in JSON)
23
+ */
24
+ export type ChainAuthority = {
25
+ weight_threshold: number;
26
+ /** FC flat_map JSON: `[["account", weight], ...]` */
27
+ account_auths: AuthorityWeightPair[];
28
+ /** FC flat_map JSON: `[["STM...", weight], ...]` */
29
+ key_auths: AuthorityWeightPair[];
30
+ };
31
+ /**
32
+ * `steem::protocol::account_update_operation` payload for JSON broadcast and signing.
33
+ * Operation tuple form: `["account_update", AccountUpdatePayload]`.
34
+ */
35
+ export type AccountUpdatePayload = {
36
+ account: string;
37
+ owner: ChainAuthority;
38
+ active: ChainAuthority;
39
+ posting: ChainAuthority;
40
+ memo_key: string;
41
+ json_metadata: string;
42
+ };
43
+ export type OperationTuple = [string, Record<string, unknown>];
44
+ /**
45
+ * Coerce `json_metadata` to protocol `string` (FC string field).
46
+ * Node rejects object/array variants with bad_cast when broadcasting.
47
+ */
48
+ export declare function normalizeChainJsonMetadata(value: unknown): string;
49
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
50
+ export declare function normalizeAuthoritySource(source: unknown): ChainAuthority;
51
+ /**
52
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
53
+ * Emits fc::flat_map fields as pair arrays, not object maps.
54
+ */
55
+ export declare function sanitizeAccountUpdatePayload(payload: Record<string, unknown>): AccountUpdatePayload;
56
+ /**
57
+ * Normalize an operation tuple before signing or JSON broadcast.
58
+ * Only account_update is rewritten; other operations pass through unchanged.
59
+ */
60
+ export declare function normalizeOperationForBroadcast(operation: unknown): unknown;
61
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
62
+ export declare function normalizeTransactionForBroadcast(trx: Record<string, unknown>): Record<string, unknown>;
63
+ /**
64
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
65
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
66
+ */
67
+ export declare function resolveAuthorityForSerialize(auth: unknown, field: string): Record<string, unknown>;
@@ -1,3 +1,5 @@
1
+ export { normalizeOperationForBroadcast, normalizeTransactionForBroadcast, normalizeChainJsonMetadata, sanitizeAccountUpdatePayload, normalizeAuthoritySource, resolveAuthorityForSerialize, } from './account-update-chain';
2
+ export type { AuthorityWeightPair, ChainAuthority, AccountUpdatePayload, OperationTuple, } from './account-update-chain';
1
3
  export interface KeyPair {
2
4
  privateKey: string;
3
5
  publicKey: string;
@@ -25300,6 +25300,153 @@ if (typeof BigInt === "function") {
25300
25300
  };
25301
25301
  }
25302
25302
 
25303
+ /**
25304
+ * Chain-safe JSON normalization for `account_update` operations.
25305
+ *
25306
+ * Protocol types (steem/libraries/protocol):
25307
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
25308
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
25309
+ *
25310
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
25311
+ * - `authority` is a variant_object, not a bare key_auths array.
25312
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
25313
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
25314
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
25315
+ *
25316
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
25317
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
25318
+ */
25319
+ /**
25320
+ * Coerce `json_metadata` to protocol `string` (FC string field).
25321
+ * Node rejects object/array variants with bad_cast when broadcasting.
25322
+ */
25323
+ function normalizeChainJsonMetadata(value) {
25324
+ if (typeof value === 'string')
25325
+ return value;
25326
+ if (value == null)
25327
+ return '';
25328
+ if (Array.isArray(value))
25329
+ return value.length === 0 ? '' : JSON.stringify(value);
25330
+ if (typeof value === 'object')
25331
+ return JSON.stringify(value);
25332
+ return String(value);
25333
+ }
25334
+ /**
25335
+ * Parse fc::flat_map JSON (pair array) or mistaken object-map input into pair arrays.
25336
+ * Broadcast output always uses pair arrays per fc::from_variant(flat_map).
25337
+ */
25338
+ function toAuthPairs(raw) {
25339
+ if (Array.isArray(raw)) {
25340
+ return raw
25341
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25342
+ .map(([key, weight]) => [String(key), Number(weight)])
25343
+ .filter(([, weight]) => Number.isFinite(weight));
25344
+ }
25345
+ if (raw && typeof raw === 'object') {
25346
+ return Object.entries(raw)
25347
+ .map(([key, weight]) => [String(key), Number(weight)])
25348
+ .filter(([, weight]) => Number.isFinite(weight));
25349
+ }
25350
+ return [];
25351
+ }
25352
+ function toKeyAuthPairs(raw) {
25353
+ if (Array.isArray(raw)) {
25354
+ return raw
25355
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25356
+ .map(([key, weight]) => [String(key), Number(weight)])
25357
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25358
+ }
25359
+ if (raw && typeof raw === 'object') {
25360
+ return Object.entries(raw)
25361
+ .map(([key, weight]) => [String(key), Number(weight)])
25362
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25363
+ }
25364
+ return [];
25365
+ }
25366
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
25367
+ function normalizeAuthoritySource(source) {
25368
+ if (Array.isArray(source) || !source || typeof source !== 'object') {
25369
+ return { weight_threshold: 1, account_auths: [], key_auths: [] };
25370
+ }
25371
+ const obj = source;
25372
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25373
+ return {
25374
+ weight_threshold,
25375
+ account_auths: toAuthPairs(obj.account_auths),
25376
+ key_auths: toKeyAuthPairs(obj.key_auths),
25377
+ };
25378
+ }
25379
+ function sanitizeAuthority(value, field) {
25380
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
25381
+ throw new Error(`Invalid ${field} authority: expected object with weight_threshold, account_auths, key_auths`);
25382
+ }
25383
+ const obj = value;
25384
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25385
+ const account_auths = toAuthPairs(obj.account_auths);
25386
+ const key_auths = toKeyAuthPairs(obj.key_auths);
25387
+ if (key_auths.length === 0) {
25388
+ throw new Error(`Invalid ${field} authority: key_auths is empty or malformed`);
25389
+ }
25390
+ return { weight_threshold, account_auths, key_auths };
25391
+ }
25392
+ /**
25393
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
25394
+ * Emits fc::flat_map fields as pair arrays, not object maps.
25395
+ */
25396
+ function sanitizeAccountUpdatePayload(payload) {
25397
+ return {
25398
+ account: String(payload.account || ''),
25399
+ owner: sanitizeAuthority(payload.owner, 'owner'),
25400
+ active: sanitizeAuthority(payload.active, 'active'),
25401
+ posting: sanitizeAuthority(payload.posting, 'posting'),
25402
+ memo_key: String(payload.memo_key || ''),
25403
+ json_metadata: normalizeChainJsonMetadata(payload.json_metadata),
25404
+ };
25405
+ }
25406
+ /**
25407
+ * Normalize an operation tuple before signing or JSON broadcast.
25408
+ * Only account_update is rewritten; other operations pass through unchanged.
25409
+ */
25410
+ function normalizeOperationForBroadcast(operation) {
25411
+ if (!Array.isArray(operation) || operation.length !== 2) {
25412
+ return operation;
25413
+ }
25414
+ const [opType, opData] = operation;
25415
+ if (opType !== 'account_update') {
25416
+ return operation;
25417
+ }
25418
+ if (!opData || typeof opData !== 'object' || Array.isArray(opData)) {
25419
+ throw new Error('account_update payload must be an object');
25420
+ }
25421
+ return ['account_update', sanitizeAccountUpdatePayload(opData)];
25422
+ }
25423
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
25424
+ function normalizeTransactionForBroadcast(trx) {
25425
+ const operations = Array.isArray(trx.operations) ? trx.operations : [];
25426
+ const extensions = Array.isArray(trx.extensions) ? trx.extensions : [];
25427
+ return {
25428
+ ...trx,
25429
+ operations: operations.map((op) => normalizeOperationForBroadcast(op)),
25430
+ extensions,
25431
+ };
25432
+ }
25433
+ /**
25434
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
25435
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
25436
+ */
25437
+ function resolveAuthorityForSerialize(auth, field) {
25438
+ if (auth == null || auth === '') {
25439
+ throw new Error(`Invalid ${field} authority: value is required`);
25440
+ }
25441
+ if (Array.isArray(auth)) {
25442
+ throw new Error(`Invalid ${field} authority: expected object, got array (wrap key_auths inside an authority object)`);
25443
+ }
25444
+ if (typeof auth !== 'object') {
25445
+ throw new Error(`Invalid ${field} authority: expected object`);
25446
+ }
25447
+ return auth;
25448
+ }
25449
+
25303
25450
  /**
25304
25451
  * Serialize a transaction to binary format for Steem blockchain
25305
25452
  * This is a simplified implementation that handles the basic structure
@@ -25657,8 +25804,9 @@ function serializeAccountCreate(bb, data) {
25657
25804
  writeString(bb, String(dataObj.json_metadata || ''));
25658
25805
  }
25659
25806
  /**
25660
- * Serialize account_update operation.
25661
- * Format: account, optional owner (1 byte + authority?), optional active, optional posting, memo_key, json_metadata.
25807
+ * Serialize account_update_operation (steem_operations.hpp).
25808
+ * JSON fields: account, owner/active/posting (authority objects), memo_key, json_metadata (string).
25809
+ * Optional authorities use a presence byte before serializeAuthority.
25662
25810
  */
25663
25811
  function serializeAccountUpdate(bb, data) {
25664
25812
  const dataObj = data;
@@ -25666,21 +25814,21 @@ function serializeAccountUpdate(bb, data) {
25666
25814
  // Optional authorities: 0 = not present, 1 = present then serialize authority
25667
25815
  if (dataObj.owner != null && dataObj.owner !== '') {
25668
25816
  bb.writeUint8(1);
25669
- serializeAuthority(bb, typeof dataObj.owner === 'object' ? dataObj.owner : { weight_threshold: 1, account_auths: [], key_auths: [] });
25817
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.owner, 'owner'));
25670
25818
  }
25671
25819
  else {
25672
25820
  bb.writeUint8(0);
25673
25821
  }
25674
25822
  if (dataObj.active != null && dataObj.active !== '') {
25675
25823
  bb.writeUint8(1);
25676
- serializeAuthority(bb, typeof dataObj.active === 'object' ? dataObj.active : { weight_threshold: 1, account_auths: [], key_auths: [] });
25824
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.active, 'active'));
25677
25825
  }
25678
25826
  else {
25679
25827
  bb.writeUint8(0);
25680
25828
  }
25681
25829
  if (dataObj.posting != null && dataObj.posting !== '') {
25682
25830
  bb.writeUint8(1);
25683
- serializeAuthority(bb, typeof dataObj.posting === 'object' ? dataObj.posting : { weight_threshold: 1, account_auths: [], key_auths: [] });
25831
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.posting, 'posting'));
25684
25832
  }
25685
25833
  else {
25686
25834
  bb.writeUint8(0);
@@ -26325,46 +26473,50 @@ function serializeCustomJson(bb, data) {
26325
26473
  writeString(bb, String(dataObj.json || '{}'));
26326
26474
  }
26327
26475
  /**
26328
- * Serialize Authority
26476
+ * Read authority map fields for binary packing (on-wire sorted flat_map).
26477
+ * JSON-RPC uses fc::flat_map → array of [key, weight] pairs; object maps are accepted
26478
+ * here only so callers that forgot to normalize still sign the intended keys.
26479
+ */
26480
+ function authorityMapEntries(raw) {
26481
+ if (Array.isArray(raw)) {
26482
+ return raw
26483
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
26484
+ .map(([key, weight]) => [String(key), Number(weight)])
26485
+ .filter(([, weight]) => Number.isFinite(weight));
26486
+ }
26487
+ if (raw && typeof raw === 'object') {
26488
+ return Object.entries(raw)
26489
+ .map(([key, weight]) => [String(key), Number(weight)])
26490
+ .filter(([, weight]) => Number.isFinite(weight));
26491
+ }
26492
+ return [];
26493
+ }
26494
+ /**
26495
+ * Serialize steem::protocol::authority (weight_threshold + sorted account/key flat_maps).
26329
26496
  */
26330
26497
  function serializeAuthority(bb, auth) {
26498
+ if (Array.isArray(auth)) {
26499
+ throw new Error('Invalid authority: expected object, got array');
26500
+ }
26501
+ if (auth == null || typeof auth !== 'object') {
26502
+ throw new Error('Invalid authority: expected object');
26503
+ }
26331
26504
  const authObj = auth;
26332
26505
  bb.writeUint32(authObj.weight_threshold || 1);
26333
26506
  // Account auths (map<string, uint16>)
26334
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
26335
- // Maps in Steem serialization are sorted by key
26336
- const accountAuthsArray = accountAuths;
26337
- accountAuthsArray.sort((a, b) => {
26338
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26339
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26340
- return aKey.localeCompare(bKey);
26341
- });
26507
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
26342
26508
  bb.writeVarint32(accountAuths.length);
26343
- for (const authEntry of accountAuths) {
26344
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
26345
- writeString(bb, String(authEntry[0]));
26346
- bb.writeUint16(authEntry[1]);
26347
- }
26509
+ for (const [account, weight] of accountAuths) {
26510
+ writeString(bb, account);
26511
+ bb.writeUint16(weight);
26348
26512
  }
26349
26513
  // Key auths (map<public_key, uint16>)
26350
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
26351
- // Maps in Steem serialization are sorted by key (public key string)
26352
- // But serialized as bytes. Usually sorting by string representation of public key works.
26353
- const keyAuthsArray = keyAuths;
26354
- keyAuthsArray.sort((a, b) => {
26355
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26356
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26357
- return aKey.localeCompare(bKey);
26358
- });
26514
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
26359
26515
  bb.writeVarint32(keyAuths.length);
26360
- for (const keyAuth of keyAuths) {
26361
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
26362
- const keyStr = String(keyAuth[0]);
26363
- const weight = keyAuth[1];
26364
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
26365
- bb.append(pubKey.toBuffer());
26366
- bb.writeUint16(weight);
26367
- }
26516
+ for (const [keyStr, weight] of keyAuths) {
26517
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
26518
+ bb.append(pubKey.toBuffer());
26519
+ bb.writeUint16(weight);
26368
26520
  }
26369
26521
  }
26370
26522
  /**
@@ -26663,17 +26815,18 @@ const Auth = {
26663
26815
  if (Array.isArray(trxObjWithSigs.signatures)) {
26664
26816
  signatures.push(...trxObjWithSigs.signatures.map((sig) => bufferExports.Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig)));
26665
26817
  }
26818
+ const trxObj = trx;
26819
+ const normalizedTrx = normalizeTransactionForBroadcast(trxObj);
26666
26820
  const chainId = getConfig().get('chain_id') || '';
26667
26821
  const cid = bufferExports.Buffer.from(chainId, 'hex');
26668
- const buf = transaction.toBuffer(trx);
26822
+ const buf = transaction.toBuffer(normalizedTrx);
26669
26823
  for (const key of keys) {
26670
26824
  const sig = Signature.signBuffer(bufferExports.Buffer.concat([cid, buf]), key);
26671
26825
  // Use toBuffer() to match old-steem-js behavior
26672
26826
  // The serializer will convert Buffer to hex string when needed
26673
26827
  signatures.push(sig.toBuffer().toString('hex'));
26674
26828
  }
26675
- const trxObj = trx;
26676
- return signed_transaction.toObject(Object.assign(trxObj, { signatures }));
26829
+ return signed_transaction.toObject(Object.assign({}, normalizedTrx, { signatures }));
26677
26830
  }
26678
26831
  };
26679
26832
  // Export individual functions
@@ -26753,6 +26906,12 @@ const auth = /*#__PURE__*/Object.freeze({
26753
26906
  getPublicKey: getPublicKey,
26754
26907
  isPubkey: isPubkey,
26755
26908
  isWif: isWif,
26909
+ normalizeAuthoritySource: normalizeAuthoritySource,
26910
+ normalizeChainJsonMetadata: normalizeChainJsonMetadata,
26911
+ normalizeOperationForBroadcast: normalizeOperationForBroadcast,
26912
+ normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
26913
+ resolveAuthorityForSerialize: resolveAuthorityForSerialize,
26914
+ sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
26756
26915
  sign: sign$1,
26757
26916
  signTransaction: signTransaction,
26758
26917
  toWif: toWif,
@@ -29561,7 +29720,7 @@ const steem = {
29561
29720
  memo,
29562
29721
  operations,
29563
29722
  utils: utils$3,
29564
- version: '1.0.17',
29723
+ version: '1.0.19',
29565
29724
  config: {
29566
29725
  set: (options) => {
29567
29726
  // If nodes is provided, extract the first node as url for API