@steemit/steem-js 1.0.18 → 1.0.20

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.cjs CHANGED
@@ -21993,11 +21993,25 @@ if (typeof BigInt === "function") {
21993
21993
  }
21994
21994
 
21995
21995
  /**
21996
- * Chain-safe JSON normalization for account_update operations.
21997
- * Matches steem::protocol::account_update_operation and authority (FC_REFLECT).
21998
- * Broadcast via JSON-RPC uses fc::from_variant; malformed shapes cause bad_cast_exception.
21996
+ * Chain-safe JSON normalization for `account_update` operations.
21997
+ *
21998
+ * Protocol types (steem/libraries/protocol):
21999
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
22000
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
22001
+ *
22002
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
22003
+ * - `authority` is a variant_object, not a bare key_auths array.
22004
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
22005
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
22006
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
22007
+ *
22008
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
22009
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
22010
+ */
22011
+ /**
22012
+ * Coerce `json_metadata` to protocol `string` (FC string field).
22013
+ * Node rejects object/array variants with bad_cast when broadcasting.
21999
22014
  */
22000
- /** Steem broadcast JSON requires json_metadata to be a string (not object/array). */
22001
22015
  function normalizeChainJsonMetadata(value) {
22002
22016
  if (typeof value === 'string')
22003
22017
  return value;
@@ -22009,6 +22023,10 @@ function normalizeChainJsonMetadata(value) {
22009
22023
  return JSON.stringify(value);
22010
22024
  return String(value);
22011
22025
  }
22026
+ /**
22027
+ * Parse fc::flat_map JSON (pair array) or mistaken object-map input into pair arrays.
22028
+ * Broadcast output always uses pair arrays per fc::from_variant(flat_map).
22029
+ */
22012
22030
  function toAuthPairs(raw) {
22013
22031
  if (Array.isArray(raw)) {
22014
22032
  return raw
@@ -22037,7 +22055,7 @@ function toKeyAuthPairs(raw) {
22037
22055
  }
22038
22056
  return [];
22039
22057
  }
22040
- /** Normalize API authority data (handles object-maps and malformed arrays). */
22058
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
22041
22059
  function normalizeAuthoritySource(source) {
22042
22060
  if (Array.isArray(source) || !source || typeof source !== 'object') {
22043
22061
  return { weight_threshold: 1, account_auths: [], key_auths: [] };
@@ -22063,7 +22081,10 @@ function sanitizeAuthority(value, field) {
22063
22081
  }
22064
22082
  return { weight_threshold, account_auths, key_auths };
22065
22083
  }
22066
- /** Coerce account_update operation payload to chain-safe JSON (for signing and broadcast). */
22084
+ /**
22085
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
22086
+ * Emits fc::flat_map fields as pair arrays, not object maps.
22087
+ */
22067
22088
  function sanitizeAccountUpdatePayload(payload) {
22068
22089
  return {
22069
22090
  account: String(payload.account || ''),
@@ -22475,8 +22496,9 @@ function serializeAccountCreate(bb, data) {
22475
22496
  writeString(bb, String(dataObj.json_metadata || ''));
22476
22497
  }
22477
22498
  /**
22478
- * Serialize account_update operation.
22479
- * Format: account, optional owner (1 byte + authority?), optional active, optional posting, memo_key, json_metadata.
22499
+ * Serialize account_update_operation (steem_operations.hpp).
22500
+ * JSON fields: account, owner/active/posting (authority objects), memo_key, json_metadata (string).
22501
+ * Optional authorities use a presence byte before serializeAuthority.
22480
22502
  */
22481
22503
  function serializeAccountUpdate(bb, data) {
22482
22504
  const dataObj = data;
@@ -23143,7 +23165,26 @@ function serializeCustomJson(bb, data) {
23143
23165
  writeString(bb, String(dataObj.json || '{}'));
23144
23166
  }
23145
23167
  /**
23146
- * Serialize Authority
23168
+ * Read authority map fields for binary packing (on-wire sorted flat_map).
23169
+ * JSON-RPC uses fc::flat_map → array of [key, weight] pairs; object maps are accepted
23170
+ * here only so callers that forgot to normalize still sign the intended keys.
23171
+ */
23172
+ function authorityMapEntries(raw) {
23173
+ if (Array.isArray(raw)) {
23174
+ return raw
23175
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
23176
+ .map(([key, weight]) => [String(key), Number(weight)])
23177
+ .filter(([, weight]) => Number.isFinite(weight));
23178
+ }
23179
+ if (raw && typeof raw === 'object') {
23180
+ return Object.entries(raw)
23181
+ .map(([key, weight]) => [String(key), Number(weight)])
23182
+ .filter(([, weight]) => Number.isFinite(weight));
23183
+ }
23184
+ return [];
23185
+ }
23186
+ /**
23187
+ * Serialize steem::protocol::authority (weight_threshold + sorted account/key flat_maps).
23147
23188
  */
23148
23189
  function serializeAuthority(bb, auth) {
23149
23190
  if (Array.isArray(auth)) {
@@ -23155,40 +23196,19 @@ function serializeAuthority(bb, auth) {
23155
23196
  const authObj = auth;
23156
23197
  bb.writeUint32(authObj.weight_threshold || 1);
23157
23198
  // Account auths (map<string, uint16>)
23158
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
23159
- // Maps in Steem serialization are sorted by key
23160
- const accountAuthsArray = accountAuths;
23161
- accountAuthsArray.sort((a, b) => {
23162
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23163
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23164
- return aKey.localeCompare(bKey);
23165
- });
23199
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
23166
23200
  bb.writeVarint32(accountAuths.length);
23167
- for (const authEntry of accountAuths) {
23168
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
23169
- writeString(bb, String(authEntry[0]));
23170
- bb.writeUint16(authEntry[1]);
23171
- }
23201
+ for (const [account, weight] of accountAuths) {
23202
+ writeString(bb, account);
23203
+ bb.writeUint16(weight);
23172
23204
  }
23173
23205
  // Key auths (map<public_key, uint16>)
23174
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
23175
- // Maps in Steem serialization are sorted by key (public key string)
23176
- // But serialized as bytes. Usually sorting by string representation of public key works.
23177
- const keyAuthsArray = keyAuths;
23178
- keyAuthsArray.sort((a, b) => {
23179
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23180
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23181
- return aKey.localeCompare(bKey);
23182
- });
23206
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
23183
23207
  bb.writeVarint32(keyAuths.length);
23184
- for (const keyAuth of keyAuths) {
23185
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
23186
- const keyStr = String(keyAuth[0]);
23187
- const weight = keyAuth[1];
23188
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
23189
- bb.append(pubKey.toBuffer());
23190
- bb.writeUint16(weight);
23191
- }
23208
+ for (const [keyStr, weight] of keyAuths) {
23209
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
23210
+ bb.append(pubKey.toBuffer());
23211
+ bb.writeUint16(weight);
23192
23212
  }
23193
23213
  }
23194
23214
  /**
@@ -23528,18 +23548,34 @@ const verifySignature = (message, signature, publicKey) => {
23528
23548
  return false;
23529
23549
  }
23530
23550
  };
23551
+ // Serialize a transaction to its binary form for digest calculation.
23552
+ // Alias for transaction.toBuffer() so verifyTransaction mirrors signTransaction's
23553
+ // serialization path exactly (signTransaction calls transaction.toBuffer at line 161).
23554
+ const serializeTrx = (trx) => transaction.toBuffer(trx);
23531
23555
  const verifyTransaction = (transaction, publicKey) => {
23532
23556
  try {
23533
23557
  const pub = PublicKey.fromString(publicKey);
23534
23558
  if (!pub)
23535
23559
  return false;
23536
- const serialized = Buffer.from(JSON.stringify(transaction));
23537
23560
  const trx = transaction;
23538
23561
  if (!trx.signatures || !Array.isArray(trx.signatures))
23539
23562
  return false;
23563
+ // Reconstruct the exact digest signTransaction signs over:
23564
+ // Buffer.concat([chain_id, transaction.toBuffer(normalizedTrx)])
23565
+ // signatures are NOT part of the signed digest — signTransaction serializes
23566
+ // BEFORE attaching signatures — so strip them before serializing, otherwise
23567
+ // serializeTransaction() would emit the signatures array and change the digest.
23568
+ const normalizedTrx = normalizeTransactionForBroadcast(trx);
23569
+ // Build a copy without the signatures key, so serializeTransaction() does not
23570
+ // emit the signatures array into the digest (signTransaction serializes pre-signature).
23571
+ const trxWithoutSigs = { ...normalizedTrx };
23572
+ delete trxWithoutSigs.signatures;
23573
+ const chainId = getConfig().get('chain_id') || '';
23574
+ const cid = Buffer.from(chainId, 'hex');
23575
+ const buf = serializeTrx(trxWithoutSigs);
23540
23576
  return trx.signatures.some((sig) => {
23541
23577
  const signature = Signature.fromHex(sig);
23542
- return signature.verifyBuffer(serialized, pub);
23578
+ return signature.verifyBuffer(Buffer.concat([cid, buf]), pub);
23543
23579
  });
23544
23580
  }
23545
23581
  catch {
@@ -23584,6 +23620,7 @@ var auth = /*#__PURE__*/Object.freeze({
23584
23620
  normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
23585
23621
  resolveAuthorityForSerialize: resolveAuthorityForSerialize,
23586
23622
  sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
23623
+ serializeTransaction: serializeTransaction,
23587
23624
  sign: sign$1,
23588
23625
  signTransaction: signTransaction,
23589
23626
  toWif: toWif,
@@ -26392,7 +26429,7 @@ const steem = {
26392
26429
  memo,
26393
26430
  operations,
26394
26431
  utils: utils$3,
26395
- version: '1.0.18',
26432
+ version: '1.0.20',
26396
26433
  config: {
26397
26434
  set: (options) => {
26398
26435
  // If nodes is provided, extract the first node as url for API