@steemit/steem-js 1.0.17 → 1.0.18

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/index.umd.js CHANGED
@@ -24986,6 +24986,132 @@
24986
24986
  };
24987
24987
  }
24988
24988
 
24989
+ /**
24990
+ * Chain-safe JSON normalization for account_update operations.
24991
+ * Matches steem::protocol::account_update_operation and authority (FC_REFLECT).
24992
+ * Broadcast via JSON-RPC uses fc::from_variant; malformed shapes cause bad_cast_exception.
24993
+ */
24994
+ /** Steem broadcast JSON requires json_metadata to be a string (not object/array). */
24995
+ function normalizeChainJsonMetadata(value) {
24996
+ if (typeof value === 'string')
24997
+ return value;
24998
+ if (value == null)
24999
+ return '';
25000
+ if (Array.isArray(value))
25001
+ return value.length === 0 ? '' : JSON.stringify(value);
25002
+ if (typeof value === 'object')
25003
+ return JSON.stringify(value);
25004
+ return String(value);
25005
+ }
25006
+ function toAuthPairs(raw) {
25007
+ if (Array.isArray(raw)) {
25008
+ return raw
25009
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25010
+ .map(([key, weight]) => [String(key), Number(weight)])
25011
+ .filter(([, weight]) => Number.isFinite(weight));
25012
+ }
25013
+ if (raw && typeof raw === 'object') {
25014
+ return Object.entries(raw)
25015
+ .map(([key, weight]) => [String(key), Number(weight)])
25016
+ .filter(([, weight]) => Number.isFinite(weight));
25017
+ }
25018
+ return [];
25019
+ }
25020
+ function toKeyAuthPairs(raw) {
25021
+ if (Array.isArray(raw)) {
25022
+ return raw
25023
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25024
+ .map(([key, weight]) => [String(key), Number(weight)])
25025
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25026
+ }
25027
+ if (raw && typeof raw === 'object') {
25028
+ return Object.entries(raw)
25029
+ .map(([key, weight]) => [String(key), Number(weight)])
25030
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25031
+ }
25032
+ return [];
25033
+ }
25034
+ /** Normalize API authority data (handles object-maps and malformed arrays). */
25035
+ function normalizeAuthoritySource(source) {
25036
+ if (Array.isArray(source) || !source || typeof source !== 'object') {
25037
+ return { weight_threshold: 1, account_auths: [], key_auths: [] };
25038
+ }
25039
+ const obj = source;
25040
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25041
+ return {
25042
+ weight_threshold,
25043
+ account_auths: toAuthPairs(obj.account_auths),
25044
+ key_auths: toKeyAuthPairs(obj.key_auths),
25045
+ };
25046
+ }
25047
+ function sanitizeAuthority(value, field) {
25048
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
25049
+ throw new Error(`Invalid ${field} authority: expected object with weight_threshold, account_auths, key_auths`);
25050
+ }
25051
+ const obj = value;
25052
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25053
+ const account_auths = toAuthPairs(obj.account_auths);
25054
+ const key_auths = toKeyAuthPairs(obj.key_auths);
25055
+ if (key_auths.length === 0) {
25056
+ throw new Error(`Invalid ${field} authority: key_auths is empty or malformed`);
25057
+ }
25058
+ return { weight_threshold, account_auths, key_auths };
25059
+ }
25060
+ /** Coerce account_update operation payload to chain-safe JSON (for signing and broadcast). */
25061
+ function sanitizeAccountUpdatePayload(payload) {
25062
+ return {
25063
+ account: String(payload.account || ''),
25064
+ owner: sanitizeAuthority(payload.owner, 'owner'),
25065
+ active: sanitizeAuthority(payload.active, 'active'),
25066
+ posting: sanitizeAuthority(payload.posting, 'posting'),
25067
+ memo_key: String(payload.memo_key || ''),
25068
+ json_metadata: normalizeChainJsonMetadata(payload.json_metadata),
25069
+ };
25070
+ }
25071
+ /**
25072
+ * Normalize an operation tuple before signing or JSON broadcast.
25073
+ * Only account_update is rewritten; other operations pass through unchanged.
25074
+ */
25075
+ function normalizeOperationForBroadcast(operation) {
25076
+ if (!Array.isArray(operation) || operation.length !== 2) {
25077
+ return operation;
25078
+ }
25079
+ const [opType, opData] = operation;
25080
+ if (opType !== 'account_update') {
25081
+ return operation;
25082
+ }
25083
+ if (!opData || typeof opData !== 'object' || Array.isArray(opData)) {
25084
+ throw new Error('account_update payload must be an object');
25085
+ }
25086
+ return ['account_update', sanitizeAccountUpdatePayload(opData)];
25087
+ }
25088
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
25089
+ function normalizeTransactionForBroadcast(trx) {
25090
+ const operations = Array.isArray(trx.operations) ? trx.operations : [];
25091
+ const extensions = Array.isArray(trx.extensions) ? trx.extensions : [];
25092
+ return {
25093
+ ...trx,
25094
+ operations: operations.map((op) => normalizeOperationForBroadcast(op)),
25095
+ extensions,
25096
+ };
25097
+ }
25098
+ /**
25099
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
25100
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
25101
+ */
25102
+ function resolveAuthorityForSerialize(auth, field) {
25103
+ if (auth == null || auth === '') {
25104
+ throw new Error(`Invalid ${field} authority: value is required`);
25105
+ }
25106
+ if (Array.isArray(auth)) {
25107
+ throw new Error(`Invalid ${field} authority: expected object, got array (wrap key_auths inside an authority object)`);
25108
+ }
25109
+ if (typeof auth !== 'object') {
25110
+ throw new Error(`Invalid ${field} authority: expected object`);
25111
+ }
25112
+ return auth;
25113
+ }
25114
+
24989
25115
  /**
24990
25116
  * Serialize a transaction to binary format for Steem blockchain
24991
25117
  * This is a simplified implementation that handles the basic structure
@@ -25352,21 +25478,21 @@
25352
25478
  // Optional authorities: 0 = not present, 1 = present then serialize authority
25353
25479
  if (dataObj.owner != null && dataObj.owner !== '') {
25354
25480
  bb.writeUint8(1);
25355
- serializeAuthority(bb, typeof dataObj.owner === 'object' ? dataObj.owner : { weight_threshold: 1, account_auths: [], key_auths: [] });
25481
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.owner, 'owner'));
25356
25482
  }
25357
25483
  else {
25358
25484
  bb.writeUint8(0);
25359
25485
  }
25360
25486
  if (dataObj.active != null && dataObj.active !== '') {
25361
25487
  bb.writeUint8(1);
25362
- serializeAuthority(bb, typeof dataObj.active === 'object' ? dataObj.active : { weight_threshold: 1, account_auths: [], key_auths: [] });
25488
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.active, 'active'));
25363
25489
  }
25364
25490
  else {
25365
25491
  bb.writeUint8(0);
25366
25492
  }
25367
25493
  if (dataObj.posting != null && dataObj.posting !== '') {
25368
25494
  bb.writeUint8(1);
25369
- serializeAuthority(bb, typeof dataObj.posting === 'object' ? dataObj.posting : { weight_threshold: 1, account_auths: [], key_auths: [] });
25495
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.posting, 'posting'));
25370
25496
  }
25371
25497
  else {
25372
25498
  bb.writeUint8(0);
@@ -26014,6 +26140,12 @@
26014
26140
  * Serialize Authority
26015
26141
  */
26016
26142
  function serializeAuthority(bb, auth) {
26143
+ if (Array.isArray(auth)) {
26144
+ throw new Error('Invalid authority: expected object, got array');
26145
+ }
26146
+ if (auth == null || typeof auth !== 'object') {
26147
+ throw new Error('Invalid authority: expected object');
26148
+ }
26017
26149
  const authObj = auth;
26018
26150
  bb.writeUint32(authObj.weight_threshold || 1);
26019
26151
  // Account auths (map<string, uint16>)
@@ -26349,17 +26481,18 @@
26349
26481
  if (Array.isArray(trxObjWithSigs.signatures)) {
26350
26482
  signatures.push(...trxObjWithSigs.signatures.map((sig) => buffer.Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig)));
26351
26483
  }
26484
+ const trxObj = trx;
26485
+ const normalizedTrx = normalizeTransactionForBroadcast(trxObj);
26352
26486
  const chainId = getConfig().get('chain_id') || '';
26353
26487
  const cid = buffer.Buffer.from(chainId, 'hex');
26354
- const buf = transaction.toBuffer(trx);
26488
+ const buf = transaction.toBuffer(normalizedTrx);
26355
26489
  for (const key of keys) {
26356
26490
  const sig = Signature.signBuffer(buffer.Buffer.concat([cid, buf]), key);
26357
26491
  // Use toBuffer() to match old-steem-js behavior
26358
26492
  // The serializer will convert Buffer to hex string when needed
26359
26493
  signatures.push(sig.toBuffer().toString('hex'));
26360
26494
  }
26361
- const trxObj = trx;
26362
- return signed_transaction.toObject(Object.assign(trxObj, { signatures }));
26495
+ return signed_transaction.toObject(Object.assign({}, normalizedTrx, { signatures }));
26363
26496
  }
26364
26497
  };
26365
26498
  // Export individual functions
@@ -26439,6 +26572,12 @@
26439
26572
  getPublicKey: getPublicKey,
26440
26573
  isPubkey: isPubkey,
26441
26574
  isWif: isWif,
26575
+ normalizeAuthoritySource: normalizeAuthoritySource,
26576
+ normalizeChainJsonMetadata: normalizeChainJsonMetadata,
26577
+ normalizeOperationForBroadcast: normalizeOperationForBroadcast,
26578
+ normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
26579
+ resolveAuthorityForSerialize: resolveAuthorityForSerialize,
26580
+ sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
26442
26581
  sign: sign$1,
26443
26582
  signTransaction: signTransaction,
26444
26583
  toWif: toWif,
@@ -29260,7 +29399,7 @@
29260
29399
  operations,
29261
29400
  utils: utils$n,
29262
29401
  ...crypto$1,
29263
- version: '1.0.17',
29402
+ version: '1.0.18',
29264
29403
  config: {
29265
29404
  set: (options) => {
29266
29405
  // If nodes is provided, extract the first node as url for API