@anywalletkit/wallet-btc 0.3.1 → 0.4.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.cjs CHANGED
@@ -885,8 +885,14 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
885
885
  /** Compressed secp256k1 public key. */
886
886
  publicKey;
887
887
  addressType;
888
- /** The signer that holds the key. */
889
- signer;
888
+ /**
889
+ * The signer that holds the key, in a private field: not a property of the account, so no code
890
+ * the account is handed to (a protocol module, a logger) can reach it and sign around the
891
+ * account's checks (audit 2026-10-29 A1).
892
+ */
893
+ #signer;
894
+ /** What holds the key: `seed`, `key` (one imported key) or `external`. */
895
+ signerKind;
890
896
  _disposed = false;
891
897
  _readOnly;
892
898
  /** `publicKey` is the signer's compressed secp256k1 key (33 bytes); it is needed synchronously for the address. */
@@ -894,9 +900,10 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
894
900
  const type = config.addressType ?? "p2wpkh";
895
901
  super(addressFromPublicKey(publicKey, networkParams(config.network ?? "bitcoin"), type), config);
896
902
  this.addressType = type;
897
- this.signer = (0, import_core9.requireCurve)(signer, "secp256k1");
903
+ this.#signer = (0, import_core9.requireCurve)(signer, "secp256k1");
904
+ this.signerKind = this.#signer.kind;
898
905
  const needed = type === "p2tr" ? "schnorr" : "ecdsa";
899
- if (!this.signer.schemes.includes(needed)) throw new import_core9.SignerError(`A ${type} account needs a signer with the '${needed}' scheme (has: ${this.signer.schemes.join(", ")}).`);
906
+ if (!this.#signer.schemes.includes(needed)) throw new import_core9.SignerError(`A ${type} account needs a signer with the '${needed}' scheme (has: ${this.#signer.schemes.join(", ")}).`);
900
907
  this.publicKey = publicKey;
901
908
  this.path = path;
902
909
  }
@@ -916,7 +923,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
916
923
  async sign(message) {
917
924
  this.live();
918
925
  if (this.addressType === "p2tr") throw new UnsupportedAddressTypeError("BIP-137 message signatures do not exist for taproot accounts (BIP-322 is not implemented).");
919
- return signMessageWith(message, this.signer, "p2wpkh");
926
+ return signMessageWith(message, this.#signer, "p2wpkh");
920
927
  }
921
928
  /** The x-only internal key and its BIP-86 tweak (no script tree) for taproot key-path spends. */
922
929
  taprootKeys() {
@@ -928,13 +935,13 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
928
935
  * p2wpkh → `partialSig` (DER + SIGHASH_ALL), p2tr → `tapKeySig` (64 bytes, DEFAULT).
929
936
  * btc-signer only computes the sighash and assembles the witness; it never sees a key.
930
937
  */
931
- async signInputs(tx, values) {
938
+ async #signInputs(tx, values) {
932
939
  if (this.addressType === "p2tr") {
933
940
  const { tweak } = this.taprootKeys();
934
941
  const scripts = Array.from({ length: tx.inputsLength }, () => this.scriptPubKey);
935
942
  for (let i = 0; i < tx.inputsLength; i++) {
936
943
  const sighash = tx.preimageWitnessV1(i, scripts, import_btc_signer4.SigHash.DEFAULT, values);
937
- const sig = await this.signer.sign(sighash, { scheme: "schnorr", context: "btc-taproot-keypath", tweak });
944
+ const sig = await this.#signer.sign(sighash, { scheme: "schnorr", context: "btc-taproot-keypath", tweak });
938
945
  tx.updateInput(i, { tapKeySig: sig }, true);
939
946
  }
940
947
  return;
@@ -942,7 +949,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
942
949
  const scriptCode = (0, import_btc_signer4.p2pkh)(this.publicKey).script;
943
950
  for (let i = 0; i < tx.inputsLength; i++) {
944
951
  const sighash = tx.preimageWitnessV0(i, scriptCode, import_btc_signer4.SigHash.ALL, values[i]);
945
- const rs = await this.signer.sign(sighash, { scheme: "ecdsa", context: "btc-sighash" });
952
+ const rs = await this.#signer.sign(sighash, { scheme: "ecdsa", context: "btc-sighash" });
946
953
  const der = import_secp256k12.secp256k1.Signature.fromBytes(rs, "compact").toBytes("der");
947
954
  const sig = new Uint8Array(der.length + 1);
948
955
  sig.set(der, 0);
@@ -953,7 +960,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
953
960
  ctx(intended) {
954
961
  return { network: this._network, ownAddress: this.address, intended, maxFeeRate: this.maxFeeRate };
955
962
  }
956
- async signPlan(plan, to, recipientValue, change, lockTime, rbf) {
963
+ async #signPlan(plan, to, recipientValue, change, lockTime, rbf) {
957
964
  const spend = spendInfo(this.publicKey, this._network, this.addressType);
958
965
  const tx = new import_btc_signer4.Transaction({ lockTime });
959
966
  const sequence = rbf ? RBF_SEQUENCE : FINAL_SEQUENCE;
@@ -962,7 +969,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
962
969
  }
963
970
  tx.addOutputAddress(to, recipientValue, this._network);
964
971
  if (change > 0n) tx.addOutputAddress(this.address, change, this._network);
965
- await this.signInputs(tx, plan.utxos.map((u) => u.value));
972
+ await this.#signInputs(tx, plan.utxos.map((u) => u.value));
966
973
  tx.finalize();
967
974
  return tx;
968
975
  }
@@ -971,7 +978,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
971
978
  * signed vsize somehow needs more, the difference comes out of change (never the
972
979
  * recipient) and the tx is re-signed once.
973
980
  */
974
- async signSpend(spend) {
981
+ async #signSpend(spend) {
975
982
  this.live();
976
983
  const plan = await this.planSpend(spend);
977
984
  await this.verifyPrevouts(plan.utxos);
@@ -979,7 +986,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
979
986
  const rbf = spend.rbf ?? this._config.rbf ?? true;
980
987
  let change = plan.change;
981
988
  let fee = plan.fee;
982
- let tx = await this.signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
989
+ let tx = await this.#signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
983
990
  const required = BigInt(tx.vsize) * plan.feeRate;
984
991
  if (required > fee) {
985
992
  const delta = required - fee;
@@ -992,7 +999,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
992
999
  fee = plan.fee + plan.change;
993
1000
  change = 0n;
994
1001
  } else fee = required;
995
- tx = await this.signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
1002
+ tx = await this.#signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
996
1003
  if (BigInt(tx.vsize) * plan.feeRate > fee) throw new Error("Fee shortfall after output rebalance.");
997
1004
  }
998
1005
  const ctx = this.ctx({ to: spend.to, value: plan.amount });
@@ -1063,7 +1070,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
1063
1070
  /** "What you sign": the fully resolved spend (signed, NOT broadcast) or a caller's raw hex. */
1064
1071
  async describeTransaction(tx) {
1065
1072
  if (typeof tx === "string") return (await this.describeRaw(tx)).desc;
1066
- return (await this.signSpend(tx)).desc;
1073
+ return (await this.#signSpend(tx)).desc;
1067
1074
  }
1068
1075
  /** Sign without broadcasting. Returns the raw transaction hex. Raw hex input gets the checks `sendTransaction` runs (prevouts through the client) and is returned unchanged, never re-signed. */
1069
1076
  async signTransaction(tx, options = {}) {
@@ -1073,7 +1080,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
1073
1080
  this.checkMax(desc.fee, (0, import_core9.feeLimit)(this._config.transactionMaxFee, options), "transaction");
1074
1081
  return tx;
1075
1082
  }
1076
- const { tx: built, fee } = await this.signSpend(tx);
1083
+ const { tx: built, fee } = await this.#signSpend(tx);
1077
1084
  this.checkMax(fee, (0, import_core9.feeLimit)(this._config.transactionMaxFee, options), "transaction");
1078
1085
  return built.hex;
1079
1086
  }
@@ -1091,7 +1098,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
1091
1098
  hash = parsed.id;
1092
1099
  fee = desc.fee;
1093
1100
  } else {
1094
- const built = await this.signSpend(tx);
1101
+ const built = await this.#signSpend(tx);
1095
1102
  rawHex = built.tx.hex;
1096
1103
  hash = built.tx.id;
1097
1104
  fee = built.fee;
@@ -1104,7 +1111,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
1104
1111
  if (o.token) throw new Error("Tokens are not supported on the bitcoin blockchain.");
1105
1112
  this.live();
1106
1113
  const client = this.requireClient("transfer");
1107
- const built = await this.signSpend({ to: o.recipient, value: o.amount });
1114
+ const built = await this.#signSpend({ to: o.recipient, value: o.amount });
1108
1115
  this.checkMax(built.fee, (0, import_core9.feeLimit)(this._config.transferMaxFee, writeOptions), "transfer");
1109
1116
  return { hash: await this.broadcast(client, built.tx.hex, built.tx.id), fee: built.fee };
1110
1117
  }
@@ -1129,7 +1136,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
1129
1136
  }
1130
1137
  dispose() {
1131
1138
  this._disposed = true;
1132
- this.signer.dispose();
1139
+ this.#signer.dispose();
1133
1140
  }
1134
1141
  };
1135
1142