@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.js CHANGED
@@ -21972,6 +21972,153 @@ if (typeof BigInt === "function") {
21972
21972
  };
21973
21973
  }
21974
21974
 
21975
+ /**
21976
+ * Chain-safe JSON normalization for `account_update` operations.
21977
+ *
21978
+ * Protocol types (steem/libraries/protocol):
21979
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
21980
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
21981
+ *
21982
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
21983
+ * - `authority` is a variant_object, not a bare key_auths array.
21984
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
21985
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
21986
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
21987
+ *
21988
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
21989
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
21990
+ */
21991
+ /**
21992
+ * Coerce `json_metadata` to protocol `string` (FC string field).
21993
+ * Node rejects object/array variants with bad_cast when broadcasting.
21994
+ */
21995
+ function normalizeChainJsonMetadata(value) {
21996
+ if (typeof value === 'string')
21997
+ return value;
21998
+ if (value == null)
21999
+ return '';
22000
+ if (Array.isArray(value))
22001
+ return value.length === 0 ? '' : JSON.stringify(value);
22002
+ if (typeof value === 'object')
22003
+ return JSON.stringify(value);
22004
+ return String(value);
22005
+ }
22006
+ /**
22007
+ * Parse fc::flat_map JSON (pair array) or mistaken object-map input into pair arrays.
22008
+ * Broadcast output always uses pair arrays per fc::from_variant(flat_map).
22009
+ */
22010
+ function toAuthPairs(raw) {
22011
+ if (Array.isArray(raw)) {
22012
+ return raw
22013
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
22014
+ .map(([key, weight]) => [String(key), Number(weight)])
22015
+ .filter(([, weight]) => Number.isFinite(weight));
22016
+ }
22017
+ if (raw && typeof raw === 'object') {
22018
+ return Object.entries(raw)
22019
+ .map(([key, weight]) => [String(key), Number(weight)])
22020
+ .filter(([, weight]) => Number.isFinite(weight));
22021
+ }
22022
+ return [];
22023
+ }
22024
+ function toKeyAuthPairs(raw) {
22025
+ if (Array.isArray(raw)) {
22026
+ return raw
22027
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
22028
+ .map(([key, weight]) => [String(key), Number(weight)])
22029
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
22030
+ }
22031
+ if (raw && typeof raw === 'object') {
22032
+ return Object.entries(raw)
22033
+ .map(([key, weight]) => [String(key), Number(weight)])
22034
+ .filter(([key, weight]) => key.startsWith('STM') && Number.isFinite(weight));
22035
+ }
22036
+ return [];
22037
+ }
22038
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
22039
+ function normalizeAuthoritySource(source) {
22040
+ if (Array.isArray(source) || !source || typeof source !== 'object') {
22041
+ return { weight_threshold: 1, account_auths: [], key_auths: [] };
22042
+ }
22043
+ const obj = source;
22044
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
22045
+ return {
22046
+ weight_threshold,
22047
+ account_auths: toAuthPairs(obj.account_auths),
22048
+ key_auths: toKeyAuthPairs(obj.key_auths),
22049
+ };
22050
+ }
22051
+ function sanitizeAuthority(value, field) {
22052
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
22053
+ throw new Error(`Invalid ${field} authority: expected object with weight_threshold, account_auths, key_auths`);
22054
+ }
22055
+ const obj = value;
22056
+ const weight_threshold = Math.max(1, Number(obj.weight_threshold) || 1);
22057
+ const account_auths = toAuthPairs(obj.account_auths);
22058
+ const key_auths = toKeyAuthPairs(obj.key_auths);
22059
+ if (key_auths.length === 0) {
22060
+ throw new Error(`Invalid ${field} authority: key_auths is empty or malformed`);
22061
+ }
22062
+ return { weight_threshold, account_auths, key_auths };
22063
+ }
22064
+ /**
22065
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
22066
+ * Emits fc::flat_map fields as pair arrays, not object maps.
22067
+ */
22068
+ function sanitizeAccountUpdatePayload(payload) {
22069
+ return {
22070
+ account: String(payload.account || ''),
22071
+ owner: sanitizeAuthority(payload.owner, 'owner'),
22072
+ active: sanitizeAuthority(payload.active, 'active'),
22073
+ posting: sanitizeAuthority(payload.posting, 'posting'),
22074
+ memo_key: String(payload.memo_key || ''),
22075
+ json_metadata: normalizeChainJsonMetadata(payload.json_metadata),
22076
+ };
22077
+ }
22078
+ /**
22079
+ * Normalize an operation tuple before signing or JSON broadcast.
22080
+ * Only account_update is rewritten; other operations pass through unchanged.
22081
+ */
22082
+ function normalizeOperationForBroadcast(operation) {
22083
+ if (!Array.isArray(operation) || operation.length !== 2) {
22084
+ return operation;
22085
+ }
22086
+ const [opType, opData] = operation;
22087
+ if (opType !== 'account_update') {
22088
+ return operation;
22089
+ }
22090
+ if (!opData || typeof opData !== 'object' || Array.isArray(opData)) {
22091
+ throw new Error('account_update payload must be an object');
22092
+ }
22093
+ return ['account_update', sanitizeAccountUpdatePayload(opData)];
22094
+ }
22095
+ /** Normalize transaction operations/extensions for JSON broadcast after signing. */
22096
+ function normalizeTransactionForBroadcast(trx) {
22097
+ const operations = Array.isArray(trx.operations) ? trx.operations : [];
22098
+ const extensions = Array.isArray(trx.extensions) ? trx.extensions : [];
22099
+ return {
22100
+ ...trx,
22101
+ operations: operations.map((op) => normalizeOperationForBroadcast(op)),
22102
+ extensions,
22103
+ };
22104
+ }
22105
+ /**
22106
+ * Authority value for binary serialization (fail-fast on array mistaken for object).
22107
+ * Mirrors steem wallet_api::update_account which assigns authority structs, not key_auths arrays.
22108
+ */
22109
+ function resolveAuthorityForSerialize(auth, field) {
22110
+ if (auth == null || auth === '') {
22111
+ throw new Error(`Invalid ${field} authority: value is required`);
22112
+ }
22113
+ if (Array.isArray(auth)) {
22114
+ throw new Error(`Invalid ${field} authority: expected object, got array (wrap key_auths inside an authority object)`);
22115
+ }
22116
+ if (typeof auth !== 'object') {
22117
+ throw new Error(`Invalid ${field} authority: expected object`);
22118
+ }
22119
+ return auth;
22120
+ }
22121
+
21975
22122
  /**
21976
22123
  * Serialize a transaction to binary format for Steem blockchain
21977
22124
  * This is a simplified implementation that handles the basic structure
@@ -22329,8 +22476,9 @@ function serializeAccountCreate(bb, data) {
22329
22476
  writeString(bb, String(dataObj.json_metadata || ''));
22330
22477
  }
22331
22478
  /**
22332
- * Serialize account_update operation.
22333
- * Format: account, optional owner (1 byte + authority?), optional active, optional posting, memo_key, json_metadata.
22479
+ * Serialize account_update_operation (steem_operations.hpp).
22480
+ * JSON fields: account, owner/active/posting (authority objects), memo_key, json_metadata (string).
22481
+ * Optional authorities use a presence byte before serializeAuthority.
22334
22482
  */
22335
22483
  function serializeAccountUpdate(bb, data) {
22336
22484
  const dataObj = data;
@@ -22338,21 +22486,21 @@ function serializeAccountUpdate(bb, data) {
22338
22486
  // Optional authorities: 0 = not present, 1 = present then serialize authority
22339
22487
  if (dataObj.owner != null && dataObj.owner !== '') {
22340
22488
  bb.writeUint8(1);
22341
- serializeAuthority(bb, typeof dataObj.owner === 'object' ? dataObj.owner : { weight_threshold: 1, account_auths: [], key_auths: [] });
22489
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.owner, 'owner'));
22342
22490
  }
22343
22491
  else {
22344
22492
  bb.writeUint8(0);
22345
22493
  }
22346
22494
  if (dataObj.active != null && dataObj.active !== '') {
22347
22495
  bb.writeUint8(1);
22348
- serializeAuthority(bb, typeof dataObj.active === 'object' ? dataObj.active : { weight_threshold: 1, account_auths: [], key_auths: [] });
22496
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.active, 'active'));
22349
22497
  }
22350
22498
  else {
22351
22499
  bb.writeUint8(0);
22352
22500
  }
22353
22501
  if (dataObj.posting != null && dataObj.posting !== '') {
22354
22502
  bb.writeUint8(1);
22355
- serializeAuthority(bb, typeof dataObj.posting === 'object' ? dataObj.posting : { weight_threshold: 1, account_auths: [], key_auths: [] });
22503
+ serializeAuthority(bb, resolveAuthorityForSerialize(dataObj.posting, 'posting'));
22356
22504
  }
22357
22505
  else {
22358
22506
  bb.writeUint8(0);
@@ -22997,46 +23145,50 @@ function serializeCustomJson(bb, data) {
22997
23145
  writeString(bb, String(dataObj.json || '{}'));
22998
23146
  }
22999
23147
  /**
23000
- * Serialize Authority
23148
+ * Read authority map fields for binary packing (on-wire sorted flat_map).
23149
+ * JSON-RPC uses fc::flat_map → array of [key, weight] pairs; object maps are accepted
23150
+ * here only so callers that forgot to normalize still sign the intended keys.
23151
+ */
23152
+ function authorityMapEntries(raw) {
23153
+ if (Array.isArray(raw)) {
23154
+ return raw
23155
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
23156
+ .map(([key, weight]) => [String(key), Number(weight)])
23157
+ .filter(([, weight]) => Number.isFinite(weight));
23158
+ }
23159
+ if (raw && typeof raw === 'object') {
23160
+ return Object.entries(raw)
23161
+ .map(([key, weight]) => [String(key), Number(weight)])
23162
+ .filter(([, weight]) => Number.isFinite(weight));
23163
+ }
23164
+ return [];
23165
+ }
23166
+ /**
23167
+ * Serialize steem::protocol::authority (weight_threshold + sorted account/key flat_maps).
23001
23168
  */
23002
23169
  function serializeAuthority(bb, auth) {
23170
+ if (Array.isArray(auth)) {
23171
+ throw new Error('Invalid authority: expected object, got array');
23172
+ }
23173
+ if (auth == null || typeof auth !== 'object') {
23174
+ throw new Error('Invalid authority: expected object');
23175
+ }
23003
23176
  const authObj = auth;
23004
23177
  bb.writeUint32(authObj.weight_threshold || 1);
23005
23178
  // Account auths (map<string, uint16>)
23006
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
23007
- // Maps in Steem serialization are sorted by key
23008
- const accountAuthsArray = accountAuths;
23009
- accountAuthsArray.sort((a, b) => {
23010
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23011
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23012
- return aKey.localeCompare(bKey);
23013
- });
23179
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
23014
23180
  bb.writeVarint32(accountAuths.length);
23015
- for (const authEntry of accountAuths) {
23016
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
23017
- writeString(bb, String(authEntry[0]));
23018
- bb.writeUint16(authEntry[1]);
23019
- }
23181
+ for (const [account, weight] of accountAuths) {
23182
+ writeString(bb, account);
23183
+ bb.writeUint16(weight);
23020
23184
  }
23021
23185
  // Key auths (map<public_key, uint16>)
23022
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
23023
- // Maps in Steem serialization are sorted by key (public key string)
23024
- // But serialized as bytes. Usually sorting by string representation of public key works.
23025
- const keyAuthsArray = keyAuths;
23026
- keyAuthsArray.sort((a, b) => {
23027
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
23028
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
23029
- return aKey.localeCompare(bKey);
23030
- });
23186
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
23031
23187
  bb.writeVarint32(keyAuths.length);
23032
- for (const keyAuth of keyAuths) {
23033
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
23034
- const keyStr = String(keyAuth[0]);
23035
- const weight = keyAuth[1];
23036
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
23037
- bb.append(pubKey.toBuffer());
23038
- bb.writeUint16(weight);
23039
- }
23188
+ for (const [keyStr, weight] of keyAuths) {
23189
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
23190
+ bb.append(pubKey.toBuffer());
23191
+ bb.writeUint16(weight);
23040
23192
  }
23041
23193
  }
23042
23194
  /**
@@ -23335,17 +23487,18 @@ const Auth = {
23335
23487
  if (Array.isArray(trxObjWithSigs.signatures)) {
23336
23488
  signatures.push(...trxObjWithSigs.signatures.map((sig) => Buffer.isBuffer(sig) ? sig.toString('hex') : String(sig)));
23337
23489
  }
23490
+ const trxObj = trx;
23491
+ const normalizedTrx = normalizeTransactionForBroadcast(trxObj);
23338
23492
  const chainId = getConfig().get('chain_id') || '';
23339
23493
  const cid = Buffer.from(chainId, 'hex');
23340
- const buf = transaction.toBuffer(trx);
23494
+ const buf = transaction.toBuffer(normalizedTrx);
23341
23495
  for (const key of keys) {
23342
23496
  const sig = Signature.signBuffer(Buffer.concat([cid, buf]), key);
23343
23497
  // Use toBuffer() to match old-steem-js behavior
23344
23498
  // The serializer will convert Buffer to hex string when needed
23345
23499
  signatures.push(sig.toBuffer().toString('hex'));
23346
23500
  }
23347
- const trxObj = trx;
23348
- return signed_transaction.toObject(Object.assign(trxObj, { signatures }));
23501
+ return signed_transaction.toObject(Object.assign({}, normalizedTrx, { signatures }));
23349
23502
  }
23350
23503
  };
23351
23504
  // Export individual functions
@@ -23425,6 +23578,12 @@ const auth = /*#__PURE__*/Object.freeze({
23425
23578
  getPublicKey: getPublicKey,
23426
23579
  isPubkey: isPubkey,
23427
23580
  isWif: isWif,
23581
+ normalizeAuthoritySource: normalizeAuthoritySource,
23582
+ normalizeChainJsonMetadata: normalizeChainJsonMetadata,
23583
+ normalizeOperationForBroadcast: normalizeOperationForBroadcast,
23584
+ normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
23585
+ resolveAuthorityForSerialize: resolveAuthorityForSerialize,
23586
+ sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
23428
23587
  sign: sign$1,
23429
23588
  signTransaction: signTransaction,
23430
23589
  toWif: toWif,
@@ -26233,7 +26392,7 @@ const steem = {
26233
26392
  memo,
26234
26393
  operations,
26235
26394
  utils: utils$3,
26236
- version: '1.0.17',
26395
+ version: '1.0.19',
26237
26396
  config: {
26238
26397
  set: (options) => {
26239
26398
  // If nodes is provided, extract the first node as url for API