@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.cjs CHANGED
@@ -21992,6 +21992,153 @@ if (typeof BigInt === "function") {
21992
21992
  };
21993
21993
  }
21994
21994
 
21995
+ /**
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.
22014
+ */
22015
+ function normalizeChainJsonMetadata(value) {
22016
+ if (typeof value === 'string')
22017
+ return value;
22018
+ if (value == null)
22019
+ return '';
22020
+ if (Array.isArray(value))
22021
+ return value.length === 0 ? '' : JSON.stringify(value);
22022
+ if (typeof value === 'object')
22023
+ return JSON.stringify(value);
22024
+ return String(value);
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
+ */
22030
+ function toAuthPairs(raw) {
22031
+ if (Array.isArray(raw)) {
22032
+ return raw
22033
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
22034
+ .map(([key, weight]) => [String(key), Number(weight)])
22035
+ .filter(([, weight]) => Number.isFinite(weight));
22036
+ }
22037
+ if (raw && typeof raw === 'object') {
22038
+ return Object.entries(raw)
22039
+ .map(([key, weight]) => [String(key), Number(weight)])
22040
+ .filter(([, weight]) => Number.isFinite(weight));
22041
+ }
22042
+ return [];
22043
+ }
22044
+ function toKeyAuthPairs(raw) {
22045
+ if (Array.isArray(raw)) {
22046
+ return raw
22047
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
22048
+ .map(([key, weight]) => [String(key), Number(weight)])
22049
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
22050
+ }
22051
+ if (raw && typeof raw === 'object') {
22052
+ return Object.entries(raw)
22053
+ .map(([key, weight]) => [String(key), Number(weight)])
22054
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
22055
+ }
22056
+ return [];
22057
+ }
22058
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
22059
+ function normalizeAuthoritySource(source) {
22060
+ if (Array.isArray(source) || !source || typeof source !== 'object') {
22061
+ return { weight_threshold: 1, account_auths: [], key_auths: [] };
22062
+ }
22063
+ const obj = source;
22064
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
22065
+ return {
22066
+ weight_threshold,
22067
+ account_auths: toAuthPairs(obj.account_auths),
22068
+ key_auths: toKeyAuthPairs(obj.key_auths),
22069
+ };
22070
+ }
22071
+ function sanitizeAuthority(value, field) {
22072
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
22073
+ throw new Error(`Invalid ${field} authority: expected object with weight_threshold, account_auths, key_auths`);
22074
+ }
22075
+ const obj = value;
22076
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
22077
+ const account_auths = toAuthPairs(obj.account_auths);
22078
+ const key_auths = toKeyAuthPairs(obj.key_auths);
22079
+ if (key_auths.length === 0) {
22080
+ throw new Error(`Invalid ${field} authority: key_auths is empty or malformed`);
22081
+ }
22082
+ return { weight_threshold, account_auths, key_auths };
22083
+ }
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
+ */
22088
+ function sanitizeAccountUpdatePayload(payload) {
22089
+ return {
22090
+ account: String(payload.account || ''),
22091
+ owner: sanitizeAuthority(payload.owner, 'owner'),
22092
+ active: sanitizeAuthority(payload.active, 'active'),
22093
+ posting: sanitizeAuthority(payload.posting, 'posting'),
22094
+ memo_key: String(payload.memo_key || ''),
22095
+ json_metadata: normalizeChainJsonMetadata(payload.json_metadata),
22096
+ };
22097
+ }
22098
+ /**
22099
+ * Normalize an operation tuple before signing or JSON broadcast.
22100
+ * Only account_update is rewritten; other operations pass through unchanged.
22101
+ */
22102
+ function normalizeOperationForBroadcast(operation) {
22103
+ if (!Array.isArray(operation) || operation.length !== 2) {
22104
+ return operation;
22105
+ }
22106
+ const [opType, opData] = operation;
22107
+ if (opType !== 'account_update') {
22108
+ return operation;
22109
+ }
22110
+ if (!opData || typeof opData !== 'object' || Array.isArray(opData)) {
22111
+ throw new Error('account_update payload must be an object');
22112
+ }
22113
+ return ['account_update', sanitizeAccountUpdatePayload(opData)];
22114
+ }
22115
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
22116
+ function normalizeTransactionForBroadcast(trx) {
22117
+ const operations = Array.isArray(trx.operations) ? trx.operations : [];
22118
+ const extensions = Array.isArray(trx.extensions) ? trx.extensions : [];
22119
+ return {
22120
+ ...trx,
22121
+ operations: operations.map((op) => normalizeOperationForBroadcast(op)),
22122
+ extensions,
22123
+ };
22124
+ }
22125
+ /**
22126
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
22127
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
22128
+ */
22129
+ function resolveAuthorityForSerialize(auth, field) {
22130
+ if (auth == null || auth === '') {
22131
+ throw new Error(`Invalid ${field} authority: value is required`);
22132
+ }
22133
+ if (Array.isArray(auth)) {
22134
+ throw new Error(`Invalid ${field} authority: expected object, got array (wrap key_auths inside an authority object)`);
22135
+ }
22136
+ if (typeof auth !== 'object') {
22137
+ throw new Error(`Invalid ${field} authority: expected object`);
22138
+ }
22139
+ return auth;
22140
+ }
22141
+
21995
22142
  /**
21996
22143
  * Serialize a transaction to binary format for Steem blockchain
21997
22144
  * This is a simplified implementation that handles the basic structure
@@ -22349,8 +22496,9 @@ function serializeAccountCreate(bb, data) {
22349
22496
  writeString(bb, String(dataObj.json_metadata || ''));
22350
22497
  }
22351
22498
  /**
22352
- * Serialize account_update operation.
22353
- * 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.
22354
22502
  */
22355
22503
  function serializeAccountUpdate(bb, data) {
22356
22504
  const dataObj = data;
@@ -22358,21 +22506,21 @@ function serializeAccountUpdate(bb, data) {
22358
22506
  // Optional authorities: 0 = not present, 1 = present then serialize authority
22359
22507
  if (dataObj.owner != null && dataObj.owner !== '') {
22360
22508
  bb.writeUint8(1);
22361
- serializeAuthority(bb, typeof dataObj.owner === 'object' ? dataObj.owner : { weight_threshold: 1, account_auths: [], key_auths: [] });
22509
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.owner, 'owner'));
22362
22510
  }
22363
22511
  else {
22364
22512
  bb.writeUint8(0);
22365
22513
  }
22366
22514
  if (dataObj.active != null && dataObj.active !== '') {
22367
22515
  bb.writeUint8(1);
22368
- serializeAuthority(bb, typeof dataObj.active === 'object' ? dataObj.active : { weight_threshold: 1, account_auths: [], key_auths: [] });
22516
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.active, 'active'));
22369
22517
  }
22370
22518
  else {
22371
22519
  bb.writeUint8(0);
22372
22520
  }
22373
22521
  if (dataObj.posting != null && dataObj.posting !== '') {
22374
22522
  bb.writeUint8(1);
22375
- serializeAuthority(bb, typeof dataObj.posting === 'object' ? dataObj.posting : { weight_threshold: 1, account_auths: [], key_auths: [] });
22523
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.posting, 'posting'));
22376
22524
  }
22377
22525
  else {
22378
22526
  bb.writeUint8(0);
@@ -23017,46 +23165,50 @@ function serializeCustomJson(bb, data) {
23017
23165
  writeString(bb, String(dataObj.json || '{}'));
23018
23166
  }
23019
23167
  /**
23020
- * 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).
23021
23188
  */
23022
23189
  function serializeAuthority(bb, auth) {
23190
+ if (Array.isArray(auth)) {
23191
+ throw new Error('Invalid authority: expected object, got array');
23192
+ }
23193
+ if (auth == null || typeof auth !== 'object') {
23194
+ throw new Error('Invalid authority: expected object');
23195
+ }
23023
23196
  const authObj = auth;
23024
23197
  bb.writeUint32(authObj.weight_threshold || 1);
23025
23198
  // Account auths (map<string, uint16>)
23026
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
23027
- // Maps in Steem serialization are sorted by key
23028
- const accountAuthsArray = accountAuths;
23029
- accountAuthsArray.sort((a, b) => {
23030
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23031
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23032
- return aKey.localeCompare(bKey);
23033
- });
23199
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
23034
23200
  bb.writeVarint32(accountAuths.length);
23035
- for (const authEntry of accountAuths) {
23036
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
23037
- writeString(bb, String(authEntry[0]));
23038
- bb.writeUint16(authEntry[1]);
23039
- }
23201
+ for (const [account, weight] of accountAuths) {
23202
+ writeString(bb, account);
23203
+ bb.writeUint16(weight);
23040
23204
  }
23041
23205
  // Key auths (map<public_key, uint16>)
23042
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
23043
- // Maps in Steem serialization are sorted by key (public key string)
23044
- // But serialized as bytes. Usually sorting by string representation of public key works.
23045
- const keyAuthsArray = keyAuths;
23046
- keyAuthsArray.sort((a, b) => {
23047
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23048
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23049
- return aKey.localeCompare(bKey);
23050
- });
23206
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
23051
23207
  bb.writeVarint32(keyAuths.length);
23052
- for (const keyAuth of keyAuths) {
23053
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
23054
- const keyStr = String(keyAuth[0]);
23055
- const weight = keyAuth[1];
23056
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
23057
- bb.append(pubKey.toBuffer());
23058
- bb.writeUint16(weight);
23059
- }
23208
+ for (const [keyStr, weight] of keyAuths) {
23209
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
23210
+ bb.append(pubKey.toBuffer());
23211
+ bb.writeUint16(weight);
23060
23212
  }
23061
23213
  }
23062
23214
  /**
@@ -23355,17 +23507,18 @@ const Auth = {
23355
23507
  if (Array.isArray(trxObjWithSigs.signatures)) {
23356
23508
  signatures.push(...trxObjWithSigs.signatures.map((sig) => Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig)));
23357
23509
  }
23510
+ const trxObj = trx;
23511
+ const normalizedTrx = normalizeTransactionForBroadcast(trxObj);
23358
23512
  const chainId = getConfig().get('chain_id') || '';
23359
23513
  const cid = Buffer.from(chainId, 'hex');
23360
- const buf = transaction.toBuffer(trx);
23514
+ const buf = transaction.toBuffer(normalizedTrx);
23361
23515
  for (const key of keys) {
23362
23516
  const sig = Signature.signBuffer(Buffer.concat([cid, buf]), key);
23363
23517
  // Use toBuffer() to match old-steem-js behavior
23364
23518
  // The serializer will convert Buffer to hex string when needed
23365
23519
  signatures.push(sig.toBuffer().toString('hex'));
23366
23520
  }
23367
- const trxObj = trx;
23368
- return signed_transaction.toObject(Object.assign(trxObj, { signatures }));
23521
+ return signed_transaction.toObject(Object.assign({}, normalizedTrx, { signatures }));
23369
23522
  }
23370
23523
  };
23371
23524
  // Export individual functions
@@ -23445,6 +23598,12 @@ var auth = /*#__PURE__*/Object.freeze({
23445
23598
  getPublicKey: getPublicKey,
23446
23599
  isPubkey: isPubkey,
23447
23600
  isWif: isWif,
23601
+ normalizeAuthoritySource: normalizeAuthoritySource,
23602
+ normalizeChainJsonMetadata: normalizeChainJsonMetadata,
23603
+ normalizeOperationForBroadcast: normalizeOperationForBroadcast,
23604
+ normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
23605
+ resolveAuthorityForSerialize: resolveAuthorityForSerialize,
23606
+ sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
23448
23607
  sign: sign$1,
23449
23608
  signTransaction: signTransaction,
23450
23609
  toWif: toWif,
@@ -26253,7 +26412,7 @@ const steem = {
26253
26412
  memo,
26254
26413
  operations,
26255
26414
  utils: utils$3,
26256
- version: '1.0.17',
26415
+ version: '1.0.19',
26257
26416
  config: {
26258
26417
  set: (options) => {
26259
26418
  // If nodes is provided, extract the first node as url for API