@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 +25 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -15
- package/dist/index.d.ts +3 -15
- package/dist/index.js +25 -18
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
-
/**
|
|
889
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1139
|
+
this.#signer.dispose();
|
|
1133
1140
|
}
|
|
1134
1141
|
};
|
|
1135
1142
|
|