dcr-ts 0.2.0 → 0.3.0

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.d.cts CHANGED
@@ -367,7 +367,16 @@ declare function pubKeyHashEd25519Address(hash: Uint8Array, network: Network): s
367
367
  declare function pubKeyHashSchnorrAddress(hash: Uint8Array, network: Network): string;
368
368
  /** Encode a compressed public key as a pay-to-pubkey (secp256k1 ECDSA) address. */
369
369
  declare function pubKeyAddress(compressedPubKey: Uint8Array, network: Network): string;
370
- /** Encode a 32-byte Ed25519 public key as a pay-to-pubkey address. */
370
+ /**
371
+ * Encode a 32-byte Ed25519 public key as a pay-to-pubkey address.
372
+ *
373
+ * This is the one address kind where a decoded string is not a stable identity.
374
+ * Identifier bytes `0x01` and `0x81` name the same address — same key, same
375
+ * pkScript — and only the `0x01` form is emitted here, so `encode(decode(x))`
376
+ * does not always give back `x`. dcrd behaves identically; see
377
+ * {@link decodeAddress}. Compare the key bytes, or re-encode first, rather than
378
+ * comparing address strings.
379
+ */
371
380
  declare function pubKeyEd25519Address(pubKey: Uint8Array, network: Network): string;
372
381
  /** Encode a compressed public key as a pay-to-pubkey (secp256k1 Schnorr) address. */
373
382
  declare function pubKeySchnorrAddress(compressedPubKey: Uint8Array, network: Network): string;
@@ -392,6 +401,17 @@ declare function addressFromScript(redeemScript: Uint8Array, network: Network):
392
401
  /**
393
402
  * Decode and validate an address. When `network` is given the address must
394
403
  * belong to it; otherwise the network is inferred from the version prefix.
404
+ *
405
+ * "Given" means a `Network`: omit the argument to infer, but anything else
406
+ * supplied for it — including a partial network missing its prefixes — is
407
+ * rejected with `invalid-argument` rather than treated as an omission. A network
408
+ * is matched by comparing its five address version prefixes rather than by object
409
+ * identity, so a second copy of this package, a `structuredClone` or a
410
+ * config-file round trip all work.
411
+ *
412
+ * One decoded string is not canonical: for the Ed25519 pay-to-pubkey kind the
413
+ * identifier bytes `0x01` and `0x81` decode to the same key and the same
414
+ * pkScript, and re-encoding always emits the `0x01` form. dcrd does the same.
395
415
  */
396
416
  declare function decodeAddress(address: string, network?: Network): DecodedAddress;
397
417
  /** True when `address` is a well-formed address (optionally for `network`). */
@@ -671,7 +691,21 @@ type Wordlist = readonly string[];
671
691
  declare const englishWordlist: Wordlist;
672
692
  /** Generate a new mnemonic. `strength` is entropy bits (128–256, default 128). */
673
693
  declare function generateMnemonic(strength?: number, wordlist?: Wordlist): string;
674
- /** Validate a mnemonic's checksum and wordlist membership. */
694
+ /**
695
+ * Validate a mnemonic's checksum and wordlist membership.
696
+ *
697
+ * Answers `false` for a non-string rather than throwing, which is what a
698
+ * predicate is for and what {@link mnemonicToMasterKey} already relies on. The
699
+ * check is written out here so that answer is a decision rather than an
700
+ * accident: `@scure` reaches the same `false` by catching its own `TypeError`
701
+ * out of `nfkd`, which would stop being true if it ever reordered those checks.
702
+ *
703
+ * The wordlist is the exception, and throws as it does at every other entry in
704
+ * this module. It is the caller's configuration rather than the subject being
705
+ * predicated, so answering `false` for a malformed one would report a bad phrase
706
+ * for what is a bad argument — the confusion `assertMnemonicString` exists to
707
+ * avoid on the other parameter.
708
+ */
675
709
  declare function validateMnemonic(mnemonic: string, wordlist?: Wordlist): boolean;
676
710
  /** Recover the raw entropy behind a mnemonic. */
677
711
  declare function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): Uint8Array;
@@ -686,14 +720,21 @@ declare function entropyToMnemonic(entropy: Uint8Array, wordlist?: Wordlist): st
686
720
  * checksum verified first. The word count *is* enforced: BIP39 is defined only
687
721
  * for 12, 15, 18, 21 or 24 words.
688
722
  *
689
- * Both failures are checked here rather than caught from `@scure`, and the two
690
- * checks are exhaustive: `mnemonicToSeedSync` reaches the caller's input only
691
- * through `nfkd`, which rejects a non-string, and `normalize`, which rejects a
692
- * word count outside the five. Its salt is `"mnemonic" + passphrase`, a
693
- * concatenation that always yields a string, and its PBKDF2 parameters are
694
- * constants so nothing else in it can throw. Catching instead would report
695
- * `invalid-mnemonic` for a future `@scure` failure that has nothing to do with
696
- * the mnemonic, which is the one direction that wastes the most debugging time.
723
+ * Both mnemonic failures are checked here rather than caught from `@scure`, and
724
+ * the two checks are exhaustive for that argument: `mnemonicToSeedSync` reaches
725
+ * it only through `nfkd`, which rejects a non-string, and `normalize`, which
726
+ * rejects a word count outside the five. Its PBKDF2 parameters are constants, so
727
+ * nothing else in it can throw. Catching instead would report `invalid-mnemonic`
728
+ * for a future `@scure` failure that has nothing to do with the mnemonic, which
729
+ * is the one direction that wastes the most debugging time.
730
+ *
731
+ * The passphrase is checked here for the opposite reason: `@scure` never rejects
732
+ * it. Its salt is `"mnemonic" + passphrase`, a concatenation that stringifies
733
+ * whatever it is given, so `null` silently meant the passphrase `"null"` — and
734
+ * through {@link mnemonicToMasterKey}, a different wallet with nothing raised.
735
+ * `passphrase ?? null` is ordinary JavaScript, and a database column that is
736
+ * NULL for "no passphrase" is the ordinary way to arrive at it. Only literal
737
+ * `undefined` reaches the default.
697
738
  */
698
739
  declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array;
699
740
  /**
@@ -931,6 +972,11 @@ interface P2PKHInputToSign {
931
972
  * 50 inputs and 26x at 500; end to end the gain is smaller (1.7x at 250) because
932
973
  * ECDSA dominates.
933
974
  *
975
+ * Every `idx` in `toSign` must be distinct. A repeat is rejected with
976
+ * `invalid-argument` before anything is written, because letting the later entry
977
+ * win silently dropped a signing the caller asked for; see the comment on the
978
+ * check itself.
979
+ *
934
980
  * The prefix hash is taken **before** any signature script is assigned, which is
935
981
  * also why this is correct: the prefix commits to no witness data, so writing
936
982
  * signature scripts cannot invalidate it. For other hash types the cache is
package/dist/index.d.ts CHANGED
@@ -367,7 +367,16 @@ declare function pubKeyHashEd25519Address(hash: Uint8Array, network: Network): s
367
367
  declare function pubKeyHashSchnorrAddress(hash: Uint8Array, network: Network): string;
368
368
  /** Encode a compressed public key as a pay-to-pubkey (secp256k1 ECDSA) address. */
369
369
  declare function pubKeyAddress(compressedPubKey: Uint8Array, network: Network): string;
370
- /** Encode a 32-byte Ed25519 public key as a pay-to-pubkey address. */
370
+ /**
371
+ * Encode a 32-byte Ed25519 public key as a pay-to-pubkey address.
372
+ *
373
+ * This is the one address kind where a decoded string is not a stable identity.
374
+ * Identifier bytes `0x01` and `0x81` name the same address — same key, same
375
+ * pkScript — and only the `0x01` form is emitted here, so `encode(decode(x))`
376
+ * does not always give back `x`. dcrd behaves identically; see
377
+ * {@link decodeAddress}. Compare the key bytes, or re-encode first, rather than
378
+ * comparing address strings.
379
+ */
371
380
  declare function pubKeyEd25519Address(pubKey: Uint8Array, network: Network): string;
372
381
  /** Encode a compressed public key as a pay-to-pubkey (secp256k1 Schnorr) address. */
373
382
  declare function pubKeySchnorrAddress(compressedPubKey: Uint8Array, network: Network): string;
@@ -392,6 +401,17 @@ declare function addressFromScript(redeemScript: Uint8Array, network: Network):
392
401
  /**
393
402
  * Decode and validate an address. When `network` is given the address must
394
403
  * belong to it; otherwise the network is inferred from the version prefix.
404
+ *
405
+ * "Given" means a `Network`: omit the argument to infer, but anything else
406
+ * supplied for it — including a partial network missing its prefixes — is
407
+ * rejected with `invalid-argument` rather than treated as an omission. A network
408
+ * is matched by comparing its five address version prefixes rather than by object
409
+ * identity, so a second copy of this package, a `structuredClone` or a
410
+ * config-file round trip all work.
411
+ *
412
+ * One decoded string is not canonical: for the Ed25519 pay-to-pubkey kind the
413
+ * identifier bytes `0x01` and `0x81` decode to the same key and the same
414
+ * pkScript, and re-encoding always emits the `0x01` form. dcrd does the same.
395
415
  */
396
416
  declare function decodeAddress(address: string, network?: Network): DecodedAddress;
397
417
  /** True when `address` is a well-formed address (optionally for `network`). */
@@ -671,7 +691,21 @@ type Wordlist = readonly string[];
671
691
  declare const englishWordlist: Wordlist;
672
692
  /** Generate a new mnemonic. `strength` is entropy bits (128–256, default 128). */
673
693
  declare function generateMnemonic(strength?: number, wordlist?: Wordlist): string;
674
- /** Validate a mnemonic's checksum and wordlist membership. */
694
+ /**
695
+ * Validate a mnemonic's checksum and wordlist membership.
696
+ *
697
+ * Answers `false` for a non-string rather than throwing, which is what a
698
+ * predicate is for and what {@link mnemonicToMasterKey} already relies on. The
699
+ * check is written out here so that answer is a decision rather than an
700
+ * accident: `@scure` reaches the same `false` by catching its own `TypeError`
701
+ * out of `nfkd`, which would stop being true if it ever reordered those checks.
702
+ *
703
+ * The wordlist is the exception, and throws as it does at every other entry in
704
+ * this module. It is the caller's configuration rather than the subject being
705
+ * predicated, so answering `false` for a malformed one would report a bad phrase
706
+ * for what is a bad argument — the confusion `assertMnemonicString` exists to
707
+ * avoid on the other parameter.
708
+ */
675
709
  declare function validateMnemonic(mnemonic: string, wordlist?: Wordlist): boolean;
676
710
  /** Recover the raw entropy behind a mnemonic. */
677
711
  declare function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): Uint8Array;
@@ -686,14 +720,21 @@ declare function entropyToMnemonic(entropy: Uint8Array, wordlist?: Wordlist): st
686
720
  * checksum verified first. The word count *is* enforced: BIP39 is defined only
687
721
  * for 12, 15, 18, 21 or 24 words.
688
722
  *
689
- * Both failures are checked here rather than caught from `@scure`, and the two
690
- * checks are exhaustive: `mnemonicToSeedSync` reaches the caller's input only
691
- * through `nfkd`, which rejects a non-string, and `normalize`, which rejects a
692
- * word count outside the five. Its salt is `"mnemonic" + passphrase`, a
693
- * concatenation that always yields a string, and its PBKDF2 parameters are
694
- * constants so nothing else in it can throw. Catching instead would report
695
- * `invalid-mnemonic` for a future `@scure` failure that has nothing to do with
696
- * the mnemonic, which is the one direction that wastes the most debugging time.
723
+ * Both mnemonic failures are checked here rather than caught from `@scure`, and
724
+ * the two checks are exhaustive for that argument: `mnemonicToSeedSync` reaches
725
+ * it only through `nfkd`, which rejects a non-string, and `normalize`, which
726
+ * rejects a word count outside the five. Its PBKDF2 parameters are constants, so
727
+ * nothing else in it can throw. Catching instead would report `invalid-mnemonic`
728
+ * for a future `@scure` failure that has nothing to do with the mnemonic, which
729
+ * is the one direction that wastes the most debugging time.
730
+ *
731
+ * The passphrase is checked here for the opposite reason: `@scure` never rejects
732
+ * it. Its salt is `"mnemonic" + passphrase`, a concatenation that stringifies
733
+ * whatever it is given, so `null` silently meant the passphrase `"null"` — and
734
+ * through {@link mnemonicToMasterKey}, a different wallet with nothing raised.
735
+ * `passphrase ?? null` is ordinary JavaScript, and a database column that is
736
+ * NULL for "no passphrase" is the ordinary way to arrive at it. Only literal
737
+ * `undefined` reaches the default.
697
738
  */
698
739
  declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array;
699
740
  /**
@@ -931,6 +972,11 @@ interface P2PKHInputToSign {
931
972
  * 50 inputs and 26x at 500; end to end the gain is smaller (1.7x at 250) because
932
973
  * ECDSA dominates.
933
974
  *
975
+ * Every `idx` in `toSign` must be distinct. A repeat is rejected with
976
+ * `invalid-argument` before anything is written, because letting the later entry
977
+ * win silently dropped a signing the caller asked for; see the comment on the
978
+ * check itself.
979
+ *
934
980
  * The prefix hash is taken **before** any signature script is assigned, which is
935
981
  * also why this is correct: the prefix commits to no witness data, so writing
936
982
  * signature scripts cannot invalidate it. For other hash types the cache is
package/dist/index.js CHANGED
@@ -29,6 +29,9 @@ function err(code, who, message) {
29
29
 
30
30
  // src/bytes.ts
31
31
  function copyOf(src, off, n) {
32
+ if (!isBytes(src)) {
33
+ throw err("invalid-argument", "copyOf", `source must be a Uint8Array, got ${typeName(src)}`);
34
+ }
32
35
  if (!Number.isInteger(off) || !Number.isInteger(n) || off < 0 || n < 0) {
33
36
  throw err(
34
37
  "not-an-integer",
@@ -116,6 +119,7 @@ var Writer = class {
116
119
  // steps. Every input amount and output value in a transaction crosses one of
117
120
  // these, and the loop version measured ~33x slower on the primitive.
118
121
  u64(v) {
122
+ if (typeof v !== "bigint") throw err("invalid-argument", "Writer.u64", `value must be a bigint, got ${typeName(v)}`);
119
123
  if (v < 0n || v > 0xffffffffffffffffn) throw err("out-of-range", "Writer.u64", "value must fit in an unsigned 64-bit integer");
120
124
  this.ensure(8);
121
125
  this.view.setBigUint64(this.len, v, true);
@@ -124,6 +128,7 @@ var Writer = class {
124
128
  }
125
129
  /** Signed 64-bit little-endian (two's complement). Used for atom amounts. */
126
130
  i64(v) {
131
+ if (typeof v !== "bigint") throw err("invalid-argument", "Writer.i64", `value must be a bigint, got ${typeName(v)}`);
127
132
  if (v < -(1n << 63n) || v >= 1n << 63n) throw err("out-of-range", "Writer.i64", "value must fit in a signed 64-bit integer");
128
133
  this.ensure(8);
129
134
  this.view.setBigInt64(this.len, v, true);
@@ -131,6 +136,7 @@ var Writer = class {
131
136
  return this;
132
137
  }
133
138
  bytes(b) {
139
+ if (!isBytes(b)) throw err("invalid-argument", "Writer.bytes", `value must be a Uint8Array, got ${typeName(b)}`);
134
140
  this.ensure(b.length);
135
141
  this.buf.set(b, this.len);
136
142
  this.len += b.length;
@@ -773,6 +779,9 @@ for (let i = 0; i < ALPHABET.length; i++) {
773
779
  INDEX[ALPHABET.charCodeAt(i)] = i;
774
780
  }
775
781
  function base58Encode(bytes) {
782
+ if (!isBytes(bytes)) {
783
+ throw err("invalid-argument", "base58Encode", `input must be a Uint8Array, got ${typeName(bytes)}`);
784
+ }
776
785
  let zeros = 0;
777
786
  while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
778
787
  let num = 0n;
@@ -908,6 +917,7 @@ function publicKeyFromPrivate(privateKey, compressed = true) {
908
917
  return secp256k1.getPublicKey(privateKey, compressed);
909
918
  }
910
919
  function isValidPublicKey(key) {
920
+ if (!isBytes(key)) return false;
911
921
  try {
912
922
  secp256k1.ProjectivePoint.fromHex(key);
913
923
  return true;
@@ -1202,6 +1212,32 @@ for (const network of Object.values(networks)) {
1202
1212
  { network, kind: "pubkey-ecdsa", prefix: network.pubKeyAddrId }
1203
1213
  );
1204
1214
  }
1215
+ function eq2(a, b) {
1216
+ return a[0] === b[0] && a[1] === b[1];
1217
+ }
1218
+ var ADDRESS_PREFIX_FIELDS = [
1219
+ "pubKeyAddrId",
1220
+ "pubKeyHashAddrId",
1221
+ "pubKeyHashEdwardsAddrId",
1222
+ "pubKeyHashSchnorrAddrId",
1223
+ "scriptHashAddrId"
1224
+ ];
1225
+ function assertNetwork(network, who) {
1226
+ const shaped = typeof network === "object" && network !== null && typeof network.name === "string" && ADDRESS_PREFIX_FIELDS.every((f) => {
1227
+ const p = network[f];
1228
+ return Array.isArray(p) && p.length === 2 && typeof p[0] === "number" && typeof p[1] === "number";
1229
+ });
1230
+ if (!shaped) {
1231
+ throw err(
1232
+ "invalid-argument",
1233
+ who,
1234
+ `network must be a Network with its address prefixes, got ${typeName(network)} (pass mainnet, testnet3, simnet or regnet)`
1235
+ );
1236
+ }
1237
+ }
1238
+ function sameNetwork(a, b) {
1239
+ return a === b || eq2(a.pubKeyAddrId, b.pubKeyAddrId) && eq2(a.pubKeyHashAddrId, b.pubKeyHashAddrId) && eq2(a.pubKeyHashEdwardsAddrId, b.pubKeyHashEdwardsAddrId) && eq2(a.pubKeyHashSchnorrAddrId, b.pubKeyHashSchnorrAddrId) && eq2(a.scriptHashAddrId, b.scriptHashAddrId);
1240
+ }
1205
1241
  function encode(prefix, payload) {
1206
1242
  const data = new Uint8Array(2 + payload.length);
1207
1243
  data[0] = prefix[0];
@@ -1209,19 +1245,28 @@ function encode(prefix, payload) {
1209
1245
  data.set(payload, 2);
1210
1246
  return checkEncode(data);
1211
1247
  }
1248
+ function assertHash20(hash, who) {
1249
+ if (!isBytes(hash)) {
1250
+ throw err("invalid-argument", who, `hash must be a Uint8Array, got ${typeName(hash)}`);
1251
+ }
1252
+ }
1212
1253
  function pubKeyHashAddress(hash, network) {
1254
+ assertHash20(hash, "pubKeyHashAddress");
1213
1255
  if (hash.length !== 20) throw err("bad-length", "pubKeyHashAddress", `hash must be 20 bytes, got ${hash.length}`);
1214
1256
  return encode(network.pubKeyHashAddrId, hash);
1215
1257
  }
1216
1258
  function scriptHashAddress(hash, network) {
1259
+ assertHash20(hash, "scriptHashAddress");
1217
1260
  if (hash.length !== 20) throw err("bad-length", "scriptHashAddress", `hash must be 20 bytes, got ${hash.length}`);
1218
1261
  return encode(network.scriptHashAddrId, hash);
1219
1262
  }
1220
1263
  function pubKeyHashEd25519Address(hash, network) {
1264
+ assertHash20(hash, "pubKeyHashEd25519Address");
1221
1265
  if (hash.length !== 20) throw err("bad-length", "pubKeyHashEd25519Address", `hash must be 20 bytes, got ${hash.length}`);
1222
1266
  return encode(network.pubKeyHashEdwardsAddrId, hash);
1223
1267
  }
1224
1268
  function pubKeyHashSchnorrAddress(hash, network) {
1269
+ assertHash20(hash, "pubKeyHashSchnorrAddress");
1225
1270
  if (hash.length !== 20) throw err("bad-length", "pubKeyHashSchnorrAddress", `hash must be 20 bytes, got ${hash.length}`);
1226
1271
  return encode(network.pubKeyHashSchnorrAddrId, hash);
1227
1272
  }
@@ -1291,6 +1336,7 @@ function decodeAddress(address, network) {
1291
1336
  if (typeof address !== "string") {
1292
1337
  throw err("invalid-argument", "decodeAddress", `address must be a string, got ${typeName(address)}`);
1293
1338
  }
1339
+ if (network !== void 0) assertNetwork(network, "decodeAddress");
1294
1340
  if (address.length > MAX_ADDRESS_LENGTH) {
1295
1341
  throw err(
1296
1342
  "input-too-long",
@@ -1302,21 +1348,18 @@ function decodeAddress(address, network) {
1302
1348
  if (data.length < 3) throw err("bad-length", "decodeAddress", `payload is ${data.length} bytes, too short to hold a prefix and data`);
1303
1349
  const prefix = [data[0], data[1]];
1304
1350
  const payload = data.subarray(2);
1305
- const match = PREFIXES.find(
1306
- (e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1] && (!network || e.network === network)
1307
- );
1351
+ const match = PREFIXES.find((e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1]);
1308
1352
  if (!match) {
1309
1353
  const hex = `0x${prefix[0].toString(16).padStart(2, "0")}${prefix[1].toString(16).padStart(2, "0")}`;
1310
- const onAnotherNetwork = network && PREFIXES.find((e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1]);
1311
- if (onAnotherNetwork) {
1312
- throw err(
1313
- "wrong-network",
1314
- "decodeAddress",
1315
- `address is a ${onAnotherNetwork.kind} address for ${onAnotherNetwork.network.name}, not ${network.name}`
1316
- );
1317
- }
1318
1354
  throw err("unknown-prefix", "decodeAddress", `unknown address prefix ${hex}`);
1319
1355
  }
1356
+ if (network && !sameNetwork(match.network, network)) {
1357
+ throw err(
1358
+ "wrong-network",
1359
+ "decodeAddress",
1360
+ `address is a ${match.kind} address for ${match.network.name}, not ${network.name}`
1361
+ );
1362
+ }
1320
1363
  if (match.kind === "pubkey-ecdsa") {
1321
1364
  if (payload.length !== 33) throw err("bad-length", "decodeAddress", `pay-to-pubkey payload must be 33 bytes, got ${payload.length}`);
1322
1365
  const { kind, pubKey } = decodePubKeyData(payload);
@@ -1339,13 +1382,7 @@ function isValidAddress(address, network) {
1339
1382
  }
1340
1383
  }
1341
1384
  function addressToScript(address, network) {
1342
- if (!network || typeof network.name !== "string") {
1343
- throw err(
1344
- "invalid-argument",
1345
- "addressToScript",
1346
- "a network is required (pass mainnet, testnet3, simnet or regnet)"
1347
- );
1348
- }
1385
+ assertNetwork(network, "addressToScript");
1349
1386
  const d = decodeAddress(address, network);
1350
1387
  switch (d.kind) {
1351
1388
  case "pubkeyhash-ecdsa":
@@ -1388,7 +1425,7 @@ function encodeWif(privateKey, network, signatureType = 0 /* Ecdsa */) {
1388
1425
  if (!isBytes(privateKey)) throw err("invalid-argument", "encodeWif", "private key must be a Uint8Array");
1389
1426
  if (privateKey.length !== 32) throw err("bad-length", "encodeWif", `private key must be 32 bytes, got ${privateKey.length}`);
1390
1427
  if (!Number.isInteger(signatureType) || SignatureType[signatureType] === void 0) {
1391
- throw err("unsupported-signature-type", "encodeWif", `unknown signature type ${signatureType}`);
1428
+ throw err("unsupported-signature-type", "encodeWif", `unknown signature type ${shown(signatureType)}`);
1392
1429
  }
1393
1430
  assertWifScalar(privateKey, signatureType, "encodeWif");
1394
1431
  const payload = new Uint8Array(3 + 32);
@@ -1488,6 +1525,9 @@ var ExtendedKey = class _ExtendedKey {
1488
1525
  cachedPoint = void 0;
1489
1526
  /** Derive a master key from a BIP32 seed (16–64 bytes). */
1490
1527
  static fromSeed(seed, network) {
1528
+ if (!isBytes(seed)) {
1529
+ throw err("invalid-argument", "ExtendedKey.fromSeed", `seed must be a Uint8Array, got ${typeName(seed)}`);
1530
+ }
1491
1531
  if (seed.length < 16 || seed.length > 64) {
1492
1532
  throw err("out-of-range", "ExtendedKey.fromSeed", `seed must be 16..64 bytes, got ${seed.length}`);
1493
1533
  }
@@ -1648,6 +1688,9 @@ var ExtendedKey = class _ExtendedKey {
1648
1688
  return this.derivePathInner(path, true);
1649
1689
  }
1650
1690
  derivePathInner(path, strictBip32) {
1691
+ if (typeof path !== "string") {
1692
+ throw err("invalid-path", "ExtendedKey.derivePath", `path must be a string, got ${typeName(path)}`);
1693
+ }
1651
1694
  const parts = path.trim().split("/");
1652
1695
  if (parts[0] === "m" || parts[0] === "M") parts.shift();
1653
1696
  let key = this;
@@ -1736,6 +1779,9 @@ var ExtendedKey = class _ExtendedKey {
1736
1779
  * key it just parsed. See {@link copyOf}.
1737
1780
  */
1738
1781
  static fromSerialized(data) {
1782
+ if (!isBytes(data)) {
1783
+ throw err("invalid-argument", "ExtendedKey.fromSerialized", `serialization must be a Uint8Array, got ${typeName(data)}`);
1784
+ }
1739
1785
  if (data.length !== SERIALIZED_LENGTH) throw err("bad-length", "ExtendedKey.fromSerialized", `expected ${SERIALIZED_LENGTH} bytes, got ${data.length}`);
1740
1786
  const version = [data[0], data[1], data[2], data[3]];
1741
1787
  const depth = data[4];
@@ -1796,7 +1842,15 @@ function assertMnemonicString(mnemonic, who) {
1796
1842
  throw err("invalid-argument", who, `mnemonic must be a string, got ${typeof mnemonic}`);
1797
1843
  }
1798
1844
  }
1845
+ function assertPassphrase(passphrase, who) {
1846
+ if (typeof passphrase !== "string") {
1847
+ throw err("invalid-argument", who, `passphrase must be a string, got ${typeName(passphrase)}`);
1848
+ }
1849
+ }
1799
1850
  function assertWordlist(wordlist, who) {
1851
+ if (!Array.isArray(wordlist)) {
1852
+ throw err("invalid-argument", who, `wordlist must be an array of 2048 words, got ${typeName(wordlist)}`);
1853
+ }
1800
1854
  if (wordlist.length !== 2048) {
1801
1855
  throw err("invalid-argument", who, `wordlist must hold 2048 words, got ${wordlist.length}`);
1802
1856
  }
@@ -1813,6 +1867,8 @@ function generateMnemonic(strength = 128, wordlist$1 = wordlist) {
1813
1867
  return generateMnemonic$1(wordlist$1, strength);
1814
1868
  }
1815
1869
  function validateMnemonic(mnemonic, wordlist$1 = wordlist) {
1870
+ assertWordlist(wordlist$1, "validateMnemonic");
1871
+ if (typeof mnemonic !== "string") return false;
1816
1872
  return validateMnemonic$1(mnemonic, wordlist$1);
1817
1873
  }
1818
1874
  function mnemonicToEntropy(mnemonic, wordlist$1 = wordlist) {
@@ -1841,6 +1897,7 @@ function entropyToMnemonic(entropy, wordlist$1 = wordlist) {
1841
1897
  }
1842
1898
  function mnemonicToSeed(mnemonic, passphrase = "") {
1843
1899
  assertMnemonicString(mnemonic, "mnemonicToSeed");
1900
+ assertPassphrase(passphrase, "mnemonicToSeed");
1844
1901
  const words = mnemonic.normalize("NFKD").split(" ").length;
1845
1902
  if (!MNEMONIC_WORD_COUNTS.includes(words)) {
1846
1903
  throw err(
@@ -1853,6 +1910,7 @@ function mnemonicToSeed(mnemonic, passphrase = "") {
1853
1910
  }
1854
1911
  function mnemonicToMasterKey(mnemonic, network, passphrase = "", wordlist$1 = wordlist) {
1855
1912
  assertMnemonicString(mnemonic, "mnemonicToMasterKey");
1913
+ assertPassphrase(passphrase, "mnemonicToMasterKey");
1856
1914
  assertWordlist(wordlist$1, "mnemonicToMasterKey");
1857
1915
  if (!validateMnemonic(mnemonic, wordlist$1)) {
1858
1916
  throw err(
@@ -1952,6 +2010,20 @@ var Transaction = class _Transaction {
1952
2010
  `outpoint tree must be ${0 /* Regular */} (regular) or ${1 /* Stake */} (stake), got ${shown(previousOutPoint.tree)}`
1953
2011
  );
1954
2012
  }
2013
+ if (opts.valueIn != null && typeof opts.valueIn !== "bigint") {
2014
+ throw err(
2015
+ "invalid-argument",
2016
+ "tx.addInput",
2017
+ `valueIn must be a bigint, got ${typeName(opts.valueIn)}`
2018
+ );
2019
+ }
2020
+ if (opts.signatureScript && !isBytes(opts.signatureScript)) {
2021
+ throw err(
2022
+ "invalid-argument",
2023
+ "tx.addInput",
2024
+ `signatureScript must be a Uint8Array, got ${typeName(opts.signatureScript)}`
2025
+ );
2026
+ }
1955
2027
  this.inputs.push({
1956
2028
  previousOutPoint: {
1957
2029
  hash: copyOf(previousOutPoint.hash, 0, 32),
@@ -1968,6 +2040,13 @@ var Transaction = class _Transaction {
1968
2040
  }
1969
2041
  /** Add an output. The script is copied; see {@link addInput}. */
1970
2042
  addOutput(value, pkScript, version = 0) {
2043
+ if (typeof value !== "bigint") {
2044
+ throw err(
2045
+ "invalid-argument",
2046
+ "tx.addOutput",
2047
+ `value must be a bigint, got ${typeName(value)}`
2048
+ );
2049
+ }
1971
2050
  if (!isBytes(pkScript)) {
1972
2051
  throw err(
1973
2052
  "invalid-argument",
@@ -2178,6 +2257,9 @@ function calcSignatureHash(subScript, hashType, tx, idx, cachedPrefix) {
2178
2257
  const prefixIsInputIndependent = masked === 1 /* All */ && !anyoneCanPay;
2179
2258
  let prefixHash;
2180
2259
  if (cachedPrefix !== void 0 && prefixIsInputIndependent) {
2260
+ if (!isBytes(cachedPrefix)) {
2261
+ throw err("invalid-argument", "sighash", `cachedPrefix must be a Uint8Array, got ${typeName(cachedPrefix)}`);
2262
+ }
2181
2263
  if (cachedPrefix.length !== 32) {
2182
2264
  throw err("bad-length", "sighash", `cachedPrefix must be 32 bytes, got ${cachedPrefix.length}`);
2183
2265
  }
@@ -2239,6 +2321,9 @@ function signHash(hash, privateKey) {
2239
2321
  }
2240
2322
  function verifyHash(hash, derSignature, publicKey) {
2241
2323
  assertHash32(hash, "verifyHash");
2324
+ if (!isBytes(publicKey)) {
2325
+ throw err("invalid-argument", "verifyHash", `public key must be a Uint8Array, got ${typeName(publicKey)}`);
2326
+ }
2242
2327
  try {
2243
2328
  const sig = secp256k1.Signature.fromDER(derSignature);
2244
2329
  const canonical = sig.toDERRawBytes();
@@ -2284,6 +2369,17 @@ function signP2PKHInput(tx, idx, subScript, privateKey, hashType = 1 /* All */,
2284
2369
  }
2285
2370
  function signP2PKHInputs(tx, toSign, hashType = 1 /* All */) {
2286
2371
  assertSignableSigHashType(hashType);
2372
+ const seen = /* @__PURE__ */ new Set();
2373
+ for (const { idx } of toSign) {
2374
+ if (seen.has(idx)) {
2375
+ throw err(
2376
+ "invalid-argument",
2377
+ "signP2PKHInputs",
2378
+ `input index ${idx} is listed more than once`
2379
+ );
2380
+ }
2381
+ seen.add(idx);
2382
+ }
2287
2383
  const cachedPrefix = sigHashPrefixAll(tx);
2288
2384
  for (const { idx, subScript, privateKey, compressed = true } of toSign) {
2289
2385
  tx.inputs[idx].signatureScript = signatureScript(
@@ -2303,6 +2399,9 @@ function signP2PKHInputs(tx, toSign, hashType = 1 /* All */) {
2303
2399
  var ATOMS_PER_COIN = 100000000n;
2304
2400
  var COIN_DECIMALS = 8;
2305
2401
  function dcrToAtoms(dcr) {
2402
+ if (typeof dcr !== "string") {
2403
+ throw err("invalid-amount", "dcrToAtoms", `amount must be a string, got ${typeName(dcr)}`);
2404
+ }
2306
2405
  const s = dcr.trim();
2307
2406
  if (!/^-?\d+(\.\d+)?$/.test(s)) throw err("invalid-amount", "dcrToAtoms", `cannot parse ${shown(dcr)}`);
2308
2407
  const negative = s.startsWith("-");
@@ -2316,6 +2415,9 @@ function dcrToAtoms(dcr) {
2316
2415
  return negative ? -atoms : atoms;
2317
2416
  }
2318
2417
  function atomsToDcr(atoms) {
2418
+ if (typeof atoms !== "bigint") {
2419
+ throw err("invalid-amount", "atomsToDcr", `atoms must be a bigint, got ${typeName(atoms)}`);
2420
+ }
2319
2421
  const negative = atoms < 0n;
2320
2422
  const a = negative ? -atoms : atoms;
2321
2423
  const intPart = a / ATOMS_PER_COIN;