@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.
package/dist/index.umd.js CHANGED
@@ -24986,6 +24986,153 @@
24986
24986
  };
24987
24987
  }
24988
24988
 
24989
+ /**
24990
+ * Chain-safe JSON normalization for `account_update` operations.
24991
+ *
24992
+ * Protocol types (steem/libraries/protocol):
24993
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
24994
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
24995
+ *
24996
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
24997
+ * - `authority` is a variant_object, not a bare key_auths array.
24998
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
24999
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
25000
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
25001
+ *
25002
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
25003
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
25004
+ */
25005
+ /**
25006
+ * Coerce `json_metadata` to protocol `string` (FC string field).
25007
+ * Node rejects object/array variants with bad_cast when broadcasting.
25008
+ */
25009
+ function normalizeChainJsonMetadata(value) {
25010
+ if (typeof value === 'string')
25011
+ return value;
25012
+ if (value == null)
25013
+ return '';
25014
+ if (Array.isArray(value))
25015
+ return value.length === 0 ? '' : JSON.stringify(value);
25016
+ if (typeof value === 'object')
25017
+ return JSON.stringify(value);
25018
+ return String(value);
25019
+ }
25020
+ /**
25021
+ * Parse fc::flat_map JSON (pair array) or mistaken object-map input into pair arrays.
25022
+ * Broadcast output always uses pair arrays per fc::from_variant(flat_map).
25023
+ */
25024
+ function toAuthPairs(raw) {
25025
+ if (Array.isArray(raw)) {
25026
+ return raw
25027
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25028
+ .map(([key, weight]) => [String(key), Number(weight)])
25029
+ .filter(([, weight]) => Number.isFinite(weight));
25030
+ }
25031
+ if (raw && typeof raw === 'object') {
25032
+ return Object.entries(raw)
25033
+ .map(([key, weight]) => [String(key), Number(weight)])
25034
+ .filter(([, weight]) => Number.isFinite(weight));
25035
+ }
25036
+ return [];
25037
+ }
25038
+ function toKeyAuthPairs(raw) {
25039
+ if (Array.isArray(raw)) {
25040
+ return raw
25041
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
25042
+ .map(([key, weight]) => [String(key), Number(weight)])
25043
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25044
+ }
25045
+ if (raw && typeof raw === 'object') {
25046
+ return Object.entries(raw)
25047
+ .map(([key, weight]) => [String(key), Number(weight)])
25048
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
25049
+ }
25050
+ return [];
25051
+ }
25052
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
25053
+ function normalizeAuthoritySource(source) {
25054
+ if (Array.isArray(source) || !source || typeof source !== 'object') {
25055
+ return { weight_threshold: 1, account_auths: [], key_auths: [] };
25056
+ }
25057
+ const obj = source;
25058
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25059
+ return {
25060
+ weight_threshold,
25061
+ account_auths: toAuthPairs(obj.account_auths),
25062
+ key_auths: toKeyAuthPairs(obj.key_auths),
25063
+ };
25064
+ }
25065
+ function sanitizeAuthority(value, field) {
25066
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
25067
+ throw new Error(`Invalid ${field} authority: expected object with weight_threshold, account_auths, key_auths`);
25068
+ }
25069
+ const obj = value;
25070
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
25071
+ const account_auths = toAuthPairs(obj.account_auths);
25072
+ const key_auths = toKeyAuthPairs(obj.key_auths);
25073
+ if (key_auths.length === 0) {
25074
+ throw new Error(`Invalid ${field} authority: key_auths is empty or malformed`);
25075
+ }
25076
+ return { weight_threshold, account_auths, key_auths };
25077
+ }
25078
+ /**
25079
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
25080
+ * Emits fc::flat_map fields as pair arrays, not object maps.
25081
+ */
25082
+ function sanitizeAccountUpdatePayload(payload) {
25083
+ return {
25084
+ account: String(payload.account || ''),
25085
+ owner: sanitizeAuthority(payload.owner, 'owner'),
25086
+ active: sanitizeAuthority(payload.active, 'active'),
25087
+ posting: sanitizeAuthority(payload.posting, 'posting'),
25088
+ memo_key: String(payload.memo_key || ''),
25089
+ json_metadata: normalizeChainJsonMetadata(payload.json_metadata),
25090
+ };
25091
+ }
25092
+ /**
25093
+ * Normalize an operation tuple before signing or JSON broadcast.
25094
+ * Only account_update is rewritten; other operations pass through unchanged.
25095
+ */
25096
+ function normalizeOperationForBroadcast(operation) {
25097
+ if (!Array.isArray(operation) || operation.length !== 2) {
25098
+ return operation;
25099
+ }
25100
+ const [opType, opData] = operation;
25101
+ if (opType !== 'account_update') {
25102
+ return operation;
25103
+ }
25104
+ if (!opData || typeof opData !== 'object' || Array.isArray(opData)) {
25105
+ throw new Error('account_update payload must be an object');
25106
+ }
25107
+ return ['account_update', sanitizeAccountUpdatePayload(opData)];
25108
+ }
25109
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
25110
+ function normalizeTransactionForBroadcast(trx) {
25111
+ const operations = Array.isArray(trx.operations) ? trx.operations : [];
25112
+ const extensions = Array.isArray(trx.extensions) ? trx.extensions : [];
25113
+ return {
25114
+ ...trx,
25115
+ operations: operations.map((op) => normalizeOperationForBroadcast(op)),
25116
+ extensions,
25117
+ };
25118
+ }
25119
+ /**
25120
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
25121
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
25122
+ */
25123
+ function resolveAuthorityForSerialize(auth, field) {
25124
+ if (auth == null || auth === '') {
25125
+ throw new Error(`Invalid ${field} authority: value is required`);
25126
+ }
25127
+ if (Array.isArray(auth)) {
25128
+ throw new Error(`Invalid ${field} authority: expected object, got array (wrap key_auths inside an authority object)`);
25129
+ }
25130
+ if (typeof auth !== 'object') {
25131
+ throw new Error(`Invalid ${field} authority: expected object`);
25132
+ }
25133
+ return auth;
25134
+ }
25135
+
24989
25136
  /**
24990
25137
  * Serialize a transaction to binary format for Steem blockchain
24991
25138
  * This is a simplified implementation that handles the basic structure
@@ -25343,8 +25490,9 @@
25343
25490
  writeString(bb, String(dataObj.json_metadata || ''));
25344
25491
  }
25345
25492
  /**
25346
- * Serialize account_update operation.
25347
- * Format: account, optional owner (1 byte + authority?), optional active, optional posting, memo_key, json_metadata.
25493
+ * Serialize account_update_operation (steem_operations.hpp).
25494
+ * JSON fields: account, owner/active/posting (authority objects), memo_key, json_metadata (string).
25495
+ * Optional authorities use a presence byte before serializeAuthority.
25348
25496
  */
25349
25497
  function serializeAccountUpdate(bb, data) {
25350
25498
  const dataObj = data;
@@ -25352,21 +25500,21 @@
25352
25500
  // Optional authorities: 0 = not present, 1 = present then serialize authority
25353
25501
  if (dataObj.owner != null && dataObj.owner !== '') {
25354
25502
  bb.writeUint8(1);
25355
- serializeAuthority(bb, typeof dataObj.owner === 'object' ? dataObj.owner : { weight_threshold: 1, account_auths: [], key_auths: [] });
25503
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.owner, 'owner'));
25356
25504
  }
25357
25505
  else {
25358
25506
  bb.writeUint8(0);
25359
25507
  }
25360
25508
  if (dataObj.active != null && dataObj.active !== '') {
25361
25509
  bb.writeUint8(1);
25362
- serializeAuthority(bb, typeof dataObj.active === 'object' ? dataObj.active : { weight_threshold: 1, account_auths: [], key_auths: [] });
25510
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.active, 'active'));
25363
25511
  }
25364
25512
  else {
25365
25513
  bb.writeUint8(0);
25366
25514
  }
25367
25515
  if (dataObj.posting != null && dataObj.posting !== '') {
25368
25516
  bb.writeUint8(1);
25369
- serializeAuthority(bb, typeof dataObj.posting === 'object' ? dataObj.posting : { weight_threshold: 1, account_auths: [], key_auths: [] });
25517
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.posting, 'posting'));
25370
25518
  }
25371
25519
  else {
25372
25520
  bb.writeUint8(0);
@@ -26011,46 +26159,50 @@
26011
26159
  writeString(bb, String(dataObj.json || '{}'));
26012
26160
  }
26013
26161
  /**
26014
- * Serialize Authority
26162
+ * Read authority map fields for binary packing (on-wire sorted flat_map).
26163
+ * JSON-RPC uses fc::flat_map → array of [key, weight] pairs; object maps are accepted
26164
+ * here only so callers that forgot to normalize still sign the intended keys.
26165
+ */
26166
+ function authorityMapEntries(raw) {
26167
+ if (Array.isArray(raw)) {
26168
+ return raw
26169
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
26170
+ .map(([key, weight]) => [String(key), Number(weight)])
26171
+ .filter(([, weight]) => Number.isFinite(weight));
26172
+ }
26173
+ if (raw && typeof raw === 'object') {
26174
+ return Object.entries(raw)
26175
+ .map(([key, weight]) => [String(key), Number(weight)])
26176
+ .filter(([, weight]) => Number.isFinite(weight));
26177
+ }
26178
+ return [];
26179
+ }
26180
+ /**
26181
+ * Serialize steem::protocol::authority (weight_threshold + sorted account/key flat_maps).
26015
26182
  */
26016
26183
  function serializeAuthority(bb, auth) {
26184
+ if (Array.isArray(auth)) {
26185
+ throw new Error('Invalid authority: expected object, got array');
26186
+ }
26187
+ if (auth == null || typeof auth !== 'object') {
26188
+ throw new Error('Invalid authority: expected object');
26189
+ }
26017
26190
  const authObj = auth;
26018
26191
  bb.writeUint32(authObj.weight_threshold || 1);
26019
26192
  // Account auths (map<string, uint16>)
26020
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
26021
- // Maps in Steem serialization are sorted by key
26022
- const accountAuthsArray = accountAuths;
26023
- accountAuthsArray.sort((a, b) => {
26024
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26025
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26026
- return aKey.localeCompare(bKey);
26027
- });
26193
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
26028
26194
  bb.writeVarint32(accountAuths.length);
26029
- for (const authEntry of accountAuths) {
26030
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
26031
- writeString(bb, String(authEntry[0]));
26032
- bb.writeUint16(authEntry[1]);
26033
- }
26195
+ for (const [account, weight] of accountAuths) {
26196
+ writeString(bb, account);
26197
+ bb.writeUint16(weight);
26034
26198
  }
26035
26199
  // Key auths (map<public_key, uint16>)
26036
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
26037
- // Maps in Steem serialization are sorted by key (public key string)
26038
- // But serialized as bytes. Usually sorting by string representation of public key works.
26039
- const keyAuthsArray = keyAuths;
26040
- keyAuthsArray.sort((a, b) => {
26041
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26042
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26043
- return aKey.localeCompare(bKey);
26044
- });
26200
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
26045
26201
  bb.writeVarint32(keyAuths.length);
26046
- for (const keyAuth of keyAuths) {
26047
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
26048
- const keyStr = String(keyAuth[0]);
26049
- const weight = keyAuth[1];
26050
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
26051
- bb.append(pubKey.toBuffer());
26052
- bb.writeUint16(weight);
26053
- }
26202
+ for (const [keyStr, weight] of keyAuths) {
26203
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
26204
+ bb.append(pubKey.toBuffer());
26205
+ bb.writeUint16(weight);
26054
26206
  }
26055
26207
  }
26056
26208
  /**
@@ -26349,17 +26501,18 @@
26349
26501
  if (Array.isArray(trxObjWithSigs.signatures)) {
26350
26502
  signatures.push(...trxObjWithSigs.signatures.map((sig) => buffer.Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig)));
26351
26503
  }
26504
+ const trxObj = trx;
26505
+ const normalizedTrx = normalizeTransactionForBroadcast(trxObj);
26352
26506
  const chainId = getConfig().get('chain_id') || '';
26353
26507
  const cid = buffer.Buffer.from(chainId, 'hex');
26354
- const buf = transaction.toBuffer(trx);
26508
+ const buf = transaction.toBuffer(normalizedTrx);
26355
26509
  for (const key of keys) {
26356
26510
  const sig = Signature.signBuffer(buffer.Buffer.concat([cid, buf]), key);
26357
26511
  // Use toBuffer() to match old-steem-js behavior
26358
26512
  // The serializer will convert Buffer to hex string when needed
26359
26513
  signatures.push(sig.toBuffer().toString('hex'));
26360
26514
  }
26361
- const trxObj = trx;
26362
- return signed_transaction.toObject(Object.assign(trxObj, { signatures }));
26515
+ return signed_transaction.toObject(Object.assign({}, normalizedTrx, { signatures }));
26363
26516
  }
26364
26517
  };
26365
26518
  // Export individual functions
@@ -26439,6 +26592,12 @@
26439
26592
  getPublicKey: getPublicKey,
26440
26593
  isPubkey: isPubkey,
26441
26594
  isWif: isWif,
26595
+ normalizeAuthoritySource: normalizeAuthoritySource,
26596
+ normalizeChainJsonMetadata: normalizeChainJsonMetadata,
26597
+ normalizeOperationForBroadcast: normalizeOperationForBroadcast,
26598
+ normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
26599
+ resolveAuthorityForSerialize: resolveAuthorityForSerialize,
26600
+ sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
26442
26601
  sign: sign$1,
26443
26602
  signTransaction: signTransaction,
26444
26603
  toWif: toWif,
@@ -29260,7 +29419,7 @@
29260
29419
  operations,
29261
29420
  utils: utils$n,
29262
29421
  ...crypto$1,
29263
- version: '1.0.17',
29422
+ version: '1.0.19',
29264
29423
  config: {
29265
29424
  set: (options) => {
29266
29425
  // If nodes is provided, extract the first node as url for API