@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.
@@ -1,13 +1,37 @@
1
1
  /**
2
- * Chain-safe JSON normalization for account_update operations.
3
- * Matches steem::protocol::account_update_operation and authority (FC_REFLECT).
4
- * Broadcast via JSON-RPC uses fc::from_variant; malformed shapes cause bad_cast_exception.
2
+ * Chain-safe JSON normalization for `account_update` operations.
3
+ *
4
+ * Protocol types (steem/libraries/protocol):
5
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
6
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
7
+ *
8
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
9
+ * - `authority` is a variant_object, not a bare key_auths array.
10
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
11
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
12
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
13
+ *
14
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
15
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
16
+ */
17
+ /** One entry in an fc::flat_map after JSON (de)serialization: [key, weight]. */
18
+ export type AuthorityWeightPair = [string, number];
19
+ /**
20
+ * `steem::protocol::authority` as returned by condenser / required for broadcast JSON.
21
+ * account_auths: flat_map<account_name_type, uint16>
22
+ * key_auths: flat_map<public_key_type, uint16> (keys are "STM..." strings in JSON)
5
23
  */
6
24
  export type ChainAuthority = {
7
25
  weight_threshold: number;
8
- account_auths: [string, number][];
9
- key_auths: [string, number][];
26
+ /** FC flat_map JSON: `[["account", weight], ...]` */
27
+ account_auths: AuthorityWeightPair[];
28
+ /** FC flat_map JSON: `[["STM...", weight], ...]` */
29
+ key_auths: AuthorityWeightPair[];
10
30
  };
31
+ /**
32
+ * `steem::protocol::account_update_operation` payload for JSON broadcast and signing.
33
+ * Operation tuple form: `["account_update", AccountUpdatePayload]`.
34
+ */
11
35
  export type AccountUpdatePayload = {
12
36
  account: string;
13
37
  owner: ChainAuthority;
@@ -17,11 +41,17 @@ export type AccountUpdatePayload = {
17
41
  json_metadata: string;
18
42
  };
19
43
  export type OperationTuple = [string, Record<string, unknown>];
20
- /** Steem broadcast JSON requires json_metadata to be a string (not object/array). */
44
+ /**
45
+ * Coerce `json_metadata` to protocol `string` (FC string field).
46
+ * Node rejects object/array variants with bad_cast when broadcasting.
47
+ */
21
48
  export declare function normalizeChainJsonMetadata(value: unknown): string;
22
- /** Normalize API authority data (handles object-maps and malformed arrays). */
49
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
23
50
  export declare function normalizeAuthoritySource(source: unknown): ChainAuthority;
24
- /** Coerce account_update operation payload to chain-safe JSON (for signing and broadcast). */
51
+ /**
52
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
53
+ * Emits fc::flat_map fields as pair arrays, not object maps.
54
+ */
25
55
  export declare function sanitizeAccountUpdatePayload(payload: Record<string, unknown>): AccountUpdatePayload;
26
56
  /**
27
57
  * Normalize an operation tuple before signing or JSON broadcast.
@@ -1,5 +1,5 @@
1
1
  export { normalizeOperationForBroadcast, normalizeTransactionForBroadcast, normalizeChainJsonMetadata, sanitizeAccountUpdatePayload, normalizeAuthoritySource, resolveAuthorityForSerialize, } from './account-update-chain';
2
- export type { ChainAuthority, AccountUpdatePayload, OperationTuple } from './account-update-chain';
2
+ export type { AuthorityWeightPair, ChainAuthority, AccountUpdatePayload, OperationTuple, } from './account-update-chain';
3
3
  export interface KeyPair {
4
4
  privateKey: string;
5
5
  publicKey: string;
@@ -49,3 +49,4 @@ export declare const verifyTransaction: (transaction: unknown, publicKey: string
49
49
  export declare const getPublicKey: (privateKey: string) => string;
50
50
  export declare const getPrivateKey: (seed: string) => string;
51
51
  export declare const signTransaction: (trx: unknown, keys: string[]) => unknown;
52
+ export { serializeTransaction } from './serializer/transaction';
@@ -25301,11 +25301,25 @@ if (typeof BigInt === "function") {
25301
25301
  }
25302
25302
 
25303
25303
  /**
25304
- * Chain-safe JSON normalization for account_update operations.
25305
- * Matches steem::protocol::account_update_operation and authority (FC_REFLECT).
25306
- * Broadcast via JSON-RPC uses fc::from_variant; malformed shapes cause bad_cast_exception.
25304
+ * Chain-safe JSON normalization for `account_update` operations.
25305
+ *
25306
+ * Protocol types (steem/libraries/protocol):
25307
+ * - `authority` — weight_threshold (uint32), account_auths / key_auths (fc::flat_map)
25308
+ * - `account_update_operation` — account, optional owner/active/posting, memo_key, json_metadata (string)
25309
+ *
25310
+ * JSON-RPC (`condenser_api.broadcast_transaction` → fc::from_variant):
25311
+ * - `authority` is a variant_object, not a bare key_auths array.
25312
+ * - `fc::flat_map` serializes as an array of [key, weight] pairs (see fc/container/flat.hpp),
25313
+ * not a JSON object `{ "key": weight }` — object maps cause bad_cast_exception.
25314
+ * - `json_metadata` must be a string (protocol `string`), not a parsed JSON object.
25315
+ *
25316
+ * Binary signing (transaction digest) uses the same logical fields; the serializer packs
25317
+ * flat_maps as sorted on-wire maps (see serializeAuthority in serializer/transaction.ts).
25318
+ */
25319
+ /**
25320
+ * Coerce `json_metadata` to protocol `string` (FC string field).
25321
+ * Node rejects object/array variants with bad_cast when broadcasting.
25307
25322
  */
25308
- /** Steem broadcast JSON requires json_metadata to be a string (not object/array). */
25309
25323
  function normalizeChainJsonMetadata(value) {
25310
25324
  if (typeof value === 'string')
25311
25325
  return value;
@@ -25317,6 +25331,10 @@ function normalizeChainJsonMetadata(value) {
25317
25331
  return JSON.stringify(value);
25318
25332
  return String(value);
25319
25333
  }
25334
+ /**
25335
+ * Parse fc::flat_map JSON (pair array) or mistaken object-map input into pair arrays.
25336
+ * Broadcast output always uses pair arrays per fc::from_variant(flat_map).
25337
+ */
25320
25338
  function toAuthPairs(raw) {
25321
25339
  if (Array.isArray(raw)) {
25322
25340
  return raw
@@ -25345,7 +25363,7 @@ function toKeyAuthPairs(raw) {
25345
25363
  }
25346
25364
  return [];
25347
25365
  }
25348
- /** Normalize API authority data (handles object-maps and malformed arrays). */
25366
+ /** Normalize API authority data (get_accounts-style input; accepts pair arrays or object maps). */
25349
25367
  function normalizeAuthoritySource(source) {
25350
25368
  if (Array.isArray(source) || !source || typeof source !== 'object') {
25351
25369
  return { weight_threshold: 1, account_auths: [], key_auths: [] };
@@ -25371,7 +25389,10 @@ function sanitizeAuthority(value, field) {
25371
25389
  }
25372
25390
  return { weight_threshold, account_auths, key_auths };
25373
25391
  }
25374
- /** Coerce account_update operation payload to chain-safe JSON (for signing and broadcast). */
25392
+ /**
25393
+ * Coerce account_update operation payload to protocol JSON shape for signing and broadcast.
25394
+ * Emits fc::flat_map fields as pair arrays, not object maps.
25395
+ */
25375
25396
  function sanitizeAccountUpdatePayload(payload) {
25376
25397
  return {
25377
25398
  account: String(payload.account || ''),
@@ -25783,8 +25804,9 @@ function serializeAccountCreate(bb, data) {
25783
25804
  writeString(bb, String(dataObj.json_metadata || ''));
25784
25805
  }
25785
25806
  /**
25786
- * Serialize account_update operation.
25787
- * Format: account, optional owner (1 byte + authority?), optional active, optional posting, memo_key, json_metadata.
25807
+ * Serialize account_update_operation (steem_operations.hpp).
25808
+ * JSON fields: account, owner/active/posting (authority objects), memo_key, json_metadata (string).
25809
+ * Optional authorities use a presence byte before serializeAuthority.
25788
25810
  */
25789
25811
  function serializeAccountUpdate(bb, data) {
25790
25812
  const dataObj = data;
@@ -26451,7 +26473,26 @@ function serializeCustomJson(bb, data) {
26451
26473
  writeString(bb, String(dataObj.json || '{}'));
26452
26474
  }
26453
26475
  /**
26454
- * Serialize Authority
26476
+ * Read authority map fields for binary packing (on-wire sorted flat_map).
26477
+ * JSON-RPC uses fc::flat_map → array of [key, weight] pairs; object maps are accepted
26478
+ * here only so callers that forgot to normalize still sign the intended keys.
26479
+ */
26480
+ function authorityMapEntries(raw) {
26481
+ if (Array.isArray(raw)) {
26482
+ return raw
26483
+ .filter((entry) => Array.isArray(entry) && entry.length >= 2)
26484
+ .map(([key, weight]) => [String(key), Number(weight)])
26485
+ .filter(([, weight]) => Number.isFinite(weight));
26486
+ }
26487
+ if (raw && typeof raw === 'object') {
26488
+ return Object.entries(raw)
26489
+ .map(([key, weight]) => [String(key), Number(weight)])
26490
+ .filter(([, weight]) => Number.isFinite(weight));
26491
+ }
26492
+ return [];
26493
+ }
26494
+ /**
26495
+ * Serialize steem::protocol::authority (weight_threshold + sorted account/key flat_maps).
26455
26496
  */
26456
26497
  function serializeAuthority(bb, auth) {
26457
26498
  if (Array.isArray(auth)) {
@@ -26463,40 +26504,19 @@ function serializeAuthority(bb, auth) {
26463
26504
  const authObj = auth;
26464
26505
  bb.writeUint32(authObj.weight_threshold || 1);
26465
26506
  // Account auths (map<string, uint16>)
26466
- const accountAuths = (Array.isArray(authObj.account_auths) ? authObj.account_auths : []);
26467
- // Maps in Steem serialization are sorted by key
26468
- const accountAuthsArray = accountAuths;
26469
- accountAuthsArray.sort((a, b) => {
26470
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26471
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26472
- return aKey.localeCompare(bKey);
26473
- });
26507
+ const accountAuths = authorityMapEntries(authObj.account_auths).sort((a, b) => a[0].localeCompare(b[0]));
26474
26508
  bb.writeVarint32(accountAuths.length);
26475
- for (const authEntry of accountAuths) {
26476
- if (Array.isArray(authEntry) && authEntry.length >= 2) {
26477
- writeString(bb, String(authEntry[0]));
26478
- bb.writeUint16(authEntry[1]);
26479
- }
26509
+ for (const [account, weight] of accountAuths) {
26510
+ writeString(bb, account);
26511
+ bb.writeUint16(weight);
26480
26512
  }
26481
26513
  // Key auths (map<public_key, uint16>)
26482
- const keyAuths = (Array.isArray(authObj.key_auths) ? authObj.key_auths : []);
26483
- // Maps in Steem serialization are sorted by key (public key string)
26484
- // But serialized as bytes. Usually sorting by string representation of public key works.
26485
- const keyAuthsArray = keyAuths;
26486
- keyAuthsArray.sort((a, b) => {
26487
- const aKey = Array.isArray(a) && a[0] ? String(a[0]) : '';
26488
- const bKey = Array.isArray(b) && b[0] ? String(b[0]) : '';
26489
- return aKey.localeCompare(bKey);
26490
- });
26514
+ const keyAuths = authorityMapEntries(authObj.key_auths).sort((a, b) => a[0].localeCompare(b[0]));
26491
26515
  bb.writeVarint32(keyAuths.length);
26492
- for (const keyAuth of keyAuths) {
26493
- if (Array.isArray(keyAuth) && keyAuth.length >= 2) {
26494
- const keyStr = String(keyAuth[0]);
26495
- const weight = keyAuth[1];
26496
- const pubKey = PublicKey.fromStringOrThrow(keyStr);
26497
- bb.append(pubKey.toBuffer());
26498
- bb.writeUint16(weight);
26499
- }
26516
+ for (const [keyStr, weight] of keyAuths) {
26517
+ const pubKey = PublicKey.fromStringOrThrow(keyStr);
26518
+ bb.append(pubKey.toBuffer());
26519
+ bb.writeUint16(weight);
26500
26520
  }
26501
26521
  }
26502
26522
  /**
@@ -26836,18 +26856,34 @@ const verifySignature = (message, signature, publicKey) => {
26836
26856
  return false;
26837
26857
  }
26838
26858
  };
26859
+ // Serialize a transaction to its binary form for digest calculation.
26860
+ // Alias for transaction.toBuffer() so verifyTransaction mirrors signTransaction's
26861
+ // serialization path exactly (signTransaction calls transaction.toBuffer at line 161).
26862
+ const serializeTrx = (trx) => transaction.toBuffer(trx);
26839
26863
  const verifyTransaction = (transaction, publicKey) => {
26840
26864
  try {
26841
26865
  const pub = PublicKey.fromString(publicKey);
26842
26866
  if (!pub)
26843
26867
  return false;
26844
- const serialized = bufferExports.Buffer.from(JSON.stringify(transaction));
26845
26868
  const trx = transaction;
26846
26869
  if (!trx.signatures || !Array.isArray(trx.signatures))
26847
26870
  return false;
26871
+ // Reconstruct the exact digest signTransaction signs over:
26872
+ // Buffer.concat([chain_id, transaction.toBuffer(normalizedTrx)])
26873
+ // signatures are NOT part of the signed digest — signTransaction serializes
26874
+ // BEFORE attaching signatures — so strip them before serializing, otherwise
26875
+ // serializeTransaction() would emit the signatures array and change the digest.
26876
+ const normalizedTrx = normalizeTransactionForBroadcast(trx);
26877
+ // Build a copy without the signatures key, so serializeTransaction() does not
26878
+ // emit the signatures array into the digest (signTransaction serializes pre-signature).
26879
+ const trxWithoutSigs = { ...normalizedTrx };
26880
+ delete trxWithoutSigs.signatures;
26881
+ const chainId = getConfig().get('chain_id') || '';
26882
+ const cid = bufferExports.Buffer.from(chainId, 'hex');
26883
+ const buf = serializeTrx(trxWithoutSigs);
26848
26884
  return trx.signatures.some((sig) => {
26849
26885
  const signature = Signature.fromHex(sig);
26850
- return signature.verifyBuffer(serialized, pub);
26886
+ return signature.verifyBuffer(bufferExports.Buffer.concat([cid, buf]), pub);
26851
26887
  });
26852
26888
  }
26853
26889
  catch {
@@ -26892,6 +26928,7 @@ const auth = /*#__PURE__*/Object.freeze({
26892
26928
  normalizeTransactionForBroadcast: normalizeTransactionForBroadcast,
26893
26929
  resolveAuthorityForSerialize: resolveAuthorityForSerialize,
26894
26930
  sanitizeAccountUpdatePayload: sanitizeAccountUpdatePayload,
26931
+ serializeTransaction: serializeTransaction,
26895
26932
  sign: sign$1,
26896
26933
  signTransaction: signTransaction,
26897
26934
  toWif: toWif,
@@ -29700,7 +29737,7 @@ const steem = {
29700
29737
  memo,
29701
29738
  operations,
29702
29739
  utils: utils$3,
29703
- version: '1.0.18',
29740
+ version: '1.0.20',
29704
29741
  config: {
29705
29742
  set: (options) => {
29706
29743
  // If nodes is provided, extract the first node as url for API