@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.d.cts
CHANGED
|
@@ -324,6 +324,7 @@ interface Built {
|
|
|
324
324
|
* ownership) before signing AND before broadcast — also for caller-supplied raw hex.
|
|
325
325
|
*/
|
|
326
326
|
declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWalletAccount {
|
|
327
|
+
#private;
|
|
327
328
|
/** UTXOs, fee rate and tip height come from the network before signing. */
|
|
328
329
|
readonly needsNetwork = true;
|
|
329
330
|
/** Derivation path, or `external` for an external signer. */
|
|
@@ -331,8 +332,8 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
331
332
|
/** Compressed secp256k1 public key. */
|
|
332
333
|
readonly publicKey: Uint8Array;
|
|
333
334
|
readonly addressType: BtcAddressType;
|
|
334
|
-
/**
|
|
335
|
-
readonly
|
|
335
|
+
/** What holds the key: `seed`, `key` (one imported key) or `external`. */
|
|
336
|
+
readonly signerKind: Signer['kind'];
|
|
336
337
|
private _disposed;
|
|
337
338
|
private _readOnly;
|
|
338
339
|
/** `publicKey` is the signer's compressed secp256k1 key (33 bytes); it is needed synchronously for the address. */
|
|
@@ -346,20 +347,7 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
346
347
|
sign(message: string | Uint8Array): Promise<string>;
|
|
347
348
|
/** The x-only internal key and its BIP-86 tweak (no script tree) for taproot key-path spends. */
|
|
348
349
|
private taprootKeys;
|
|
349
|
-
/**
|
|
350
|
-
* Sign every input through the signer and attach the witness-ready signature:
|
|
351
|
-
* p2wpkh → `partialSig` (DER + SIGHASH_ALL), p2tr → `tapKeySig` (64 bytes, DEFAULT).
|
|
352
|
-
* btc-signer only computes the sighash and assembles the witness; it never sees a key.
|
|
353
|
-
*/
|
|
354
|
-
private signInputs;
|
|
355
350
|
private ctx;
|
|
356
|
-
private signPlan;
|
|
357
|
-
/**
|
|
358
|
-
* Plan → sign → measure. The plan's fee assumes 72-byte DER signatures; if the
|
|
359
|
-
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
360
|
-
* recipient) and the tx is re-signed once.
|
|
361
|
-
*/
|
|
362
|
-
protected signSpend(spend: BtcSpend): Promise<Built>;
|
|
363
351
|
private checkMax;
|
|
364
352
|
/**
|
|
365
353
|
* BIP-143 (p2wpkh) commits each input's signature to that input's CLAIMED amount
|
package/dist/index.d.ts
CHANGED
|
@@ -324,6 +324,7 @@ interface Built {
|
|
|
324
324
|
* ownership) before signing AND before broadcast — also for caller-supplied raw hex.
|
|
325
325
|
*/
|
|
326
326
|
declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWalletAccount {
|
|
327
|
+
#private;
|
|
327
328
|
/** UTXOs, fee rate and tip height come from the network before signing. */
|
|
328
329
|
readonly needsNetwork = true;
|
|
329
330
|
/** Derivation path, or `external` for an external signer. */
|
|
@@ -331,8 +332,8 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
331
332
|
/** Compressed secp256k1 public key. */
|
|
332
333
|
readonly publicKey: Uint8Array;
|
|
333
334
|
readonly addressType: BtcAddressType;
|
|
334
|
-
/**
|
|
335
|
-
readonly
|
|
335
|
+
/** What holds the key: `seed`, `key` (one imported key) or `external`. */
|
|
336
|
+
readonly signerKind: Signer['kind'];
|
|
336
337
|
private _disposed;
|
|
337
338
|
private _readOnly;
|
|
338
339
|
/** `publicKey` is the signer's compressed secp256k1 key (33 bytes); it is needed synchronously for the address. */
|
|
@@ -346,20 +347,7 @@ declare class WalletAccountBtc extends WalletAccountReadOnlyBtc implements IWall
|
|
|
346
347
|
sign(message: string | Uint8Array): Promise<string>;
|
|
347
348
|
/** The x-only internal key and its BIP-86 tweak (no script tree) for taproot key-path spends. */
|
|
348
349
|
private taprootKeys;
|
|
349
|
-
/**
|
|
350
|
-
* Sign every input through the signer and attach the witness-ready signature:
|
|
351
|
-
* p2wpkh → `partialSig` (DER + SIGHASH_ALL), p2tr → `tapKeySig` (64 bytes, DEFAULT).
|
|
352
|
-
* btc-signer only computes the sighash and assembles the witness; it never sees a key.
|
|
353
|
-
*/
|
|
354
|
-
private signInputs;
|
|
355
350
|
private ctx;
|
|
356
|
-
private signPlan;
|
|
357
|
-
/**
|
|
358
|
-
* Plan → sign → measure. The plan's fee assumes 72-byte DER signatures; if the
|
|
359
|
-
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
360
|
-
* recipient) and the tx is re-signed once.
|
|
361
|
-
*/
|
|
362
|
-
protected signSpend(spend: BtcSpend): Promise<Built>;
|
|
363
351
|
private checkMax;
|
|
364
352
|
/**
|
|
365
353
|
* BIP-143 (p2wpkh) commits each input's signature to that input's CLAIMED amount
|
package/dist/index.js
CHANGED
|
@@ -658,8 +658,14 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
658
658
|
/** Compressed secp256k1 public key. */
|
|
659
659
|
publicKey;
|
|
660
660
|
addressType;
|
|
661
|
-
/**
|
|
662
|
-
|
|
661
|
+
/**
|
|
662
|
+
* The signer that holds the key, in a private field: not a property of the account, so no code
|
|
663
|
+
* the account is handed to (a protocol module, a logger) can reach it and sign around the
|
|
664
|
+
* account's checks (audit 2026-10-29 A1).
|
|
665
|
+
*/
|
|
666
|
+
#signer;
|
|
667
|
+
/** What holds the key: `seed`, `key` (one imported key) or `external`. */
|
|
668
|
+
signerKind;
|
|
663
669
|
_disposed = false;
|
|
664
670
|
_readOnly;
|
|
665
671
|
/** `publicKey` is the signer's compressed secp256k1 key (33 bytes); it is needed synchronously for the address. */
|
|
@@ -667,9 +673,10 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
667
673
|
const type = config.addressType ?? "p2wpkh";
|
|
668
674
|
super(addressFromPublicKey(publicKey, networkParams(config.network ?? "bitcoin"), type), config);
|
|
669
675
|
this.addressType = type;
|
|
670
|
-
this
|
|
676
|
+
this.#signer = requireCurve(signer, "secp256k1");
|
|
677
|
+
this.signerKind = this.#signer.kind;
|
|
671
678
|
const needed = type === "p2tr" ? "schnorr" : "ecdsa";
|
|
672
|
-
if (!this
|
|
679
|
+
if (!this.#signer.schemes.includes(needed)) throw new SignerError2(`A ${type} account needs a signer with the '${needed}' scheme (has: ${this.#signer.schemes.join(", ")}).`);
|
|
673
680
|
this.publicKey = publicKey;
|
|
674
681
|
this.path = path;
|
|
675
682
|
}
|
|
@@ -689,7 +696,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
689
696
|
async sign(message) {
|
|
690
697
|
this.live();
|
|
691
698
|
if (this.addressType === "p2tr") throw new UnsupportedAddressTypeError("BIP-137 message signatures do not exist for taproot accounts (BIP-322 is not implemented).");
|
|
692
|
-
return signMessageWith(message, this
|
|
699
|
+
return signMessageWith(message, this.#signer, "p2wpkh");
|
|
693
700
|
}
|
|
694
701
|
/** The x-only internal key and its BIP-86 tweak (no script tree) for taproot key-path spends. */
|
|
695
702
|
taprootKeys() {
|
|
@@ -701,13 +708,13 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
701
708
|
* p2wpkh → `partialSig` (DER + SIGHASH_ALL), p2tr → `tapKeySig` (64 bytes, DEFAULT).
|
|
702
709
|
* btc-signer only computes the sighash and assembles the witness; it never sees a key.
|
|
703
710
|
*/
|
|
704
|
-
async signInputs(tx, values) {
|
|
711
|
+
async #signInputs(tx, values) {
|
|
705
712
|
if (this.addressType === "p2tr") {
|
|
706
713
|
const { tweak } = this.taprootKeys();
|
|
707
714
|
const scripts = Array.from({ length: tx.inputsLength }, () => this.scriptPubKey);
|
|
708
715
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
709
716
|
const sighash = tx.preimageWitnessV1(i, scripts, SigHash.DEFAULT, values);
|
|
710
|
-
const sig = await this
|
|
717
|
+
const sig = await this.#signer.sign(sighash, { scheme: "schnorr", context: "btc-taproot-keypath", tweak });
|
|
711
718
|
tx.updateInput(i, { tapKeySig: sig }, true);
|
|
712
719
|
}
|
|
713
720
|
return;
|
|
@@ -715,7 +722,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
715
722
|
const scriptCode = p2pkh(this.publicKey).script;
|
|
716
723
|
for (let i = 0; i < tx.inputsLength; i++) {
|
|
717
724
|
const sighash = tx.preimageWitnessV0(i, scriptCode, SigHash.ALL, values[i]);
|
|
718
|
-
const rs = await this
|
|
725
|
+
const rs = await this.#signer.sign(sighash, { scheme: "ecdsa", context: "btc-sighash" });
|
|
719
726
|
const der = secp256k12.Signature.fromBytes(rs, "compact").toBytes("der");
|
|
720
727
|
const sig = new Uint8Array(der.length + 1);
|
|
721
728
|
sig.set(der, 0);
|
|
@@ -726,7 +733,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
726
733
|
ctx(intended) {
|
|
727
734
|
return { network: this._network, ownAddress: this.address, intended, maxFeeRate: this.maxFeeRate };
|
|
728
735
|
}
|
|
729
|
-
async signPlan(plan, to, recipientValue, change, lockTime, rbf) {
|
|
736
|
+
async #signPlan(plan, to, recipientValue, change, lockTime, rbf) {
|
|
730
737
|
const spend = spendInfo(this.publicKey, this._network, this.addressType);
|
|
731
738
|
const tx = new Transaction2({ lockTime });
|
|
732
739
|
const sequence = rbf ? RBF_SEQUENCE : FINAL_SEQUENCE;
|
|
@@ -735,7 +742,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
735
742
|
}
|
|
736
743
|
tx.addOutputAddress(to, recipientValue, this._network);
|
|
737
744
|
if (change > 0n) tx.addOutputAddress(this.address, change, this._network);
|
|
738
|
-
await this
|
|
745
|
+
await this.#signInputs(tx, plan.utxos.map((u) => u.value));
|
|
739
746
|
tx.finalize();
|
|
740
747
|
return tx;
|
|
741
748
|
}
|
|
@@ -744,7 +751,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
744
751
|
* signed vsize somehow needs more, the difference comes out of change (never the
|
|
745
752
|
* recipient) and the tx is re-signed once.
|
|
746
753
|
*/
|
|
747
|
-
async signSpend(spend) {
|
|
754
|
+
async #signSpend(spend) {
|
|
748
755
|
this.live();
|
|
749
756
|
const plan = await this.planSpend(spend);
|
|
750
757
|
await this.verifyPrevouts(plan.utxos);
|
|
@@ -752,7 +759,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
752
759
|
const rbf = spend.rbf ?? this._config.rbf ?? true;
|
|
753
760
|
let change = plan.change;
|
|
754
761
|
let fee = plan.fee;
|
|
755
|
-
let tx = await this
|
|
762
|
+
let tx = await this.#signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
|
|
756
763
|
const required = BigInt(tx.vsize) * plan.feeRate;
|
|
757
764
|
if (required > fee) {
|
|
758
765
|
const delta = required - fee;
|
|
@@ -765,7 +772,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
765
772
|
fee = plan.fee + plan.change;
|
|
766
773
|
change = 0n;
|
|
767
774
|
} else fee = required;
|
|
768
|
-
tx = await this
|
|
775
|
+
tx = await this.#signPlan(plan, spend.to, plan.amount, change, lockTime, rbf);
|
|
769
776
|
if (BigInt(tx.vsize) * plan.feeRate > fee) throw new Error("Fee shortfall after output rebalance.");
|
|
770
777
|
}
|
|
771
778
|
const ctx = this.ctx({ to: spend.to, value: plan.amount });
|
|
@@ -836,7 +843,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
836
843
|
/** "What you sign": the fully resolved spend (signed, NOT broadcast) or a caller's raw hex. */
|
|
837
844
|
async describeTransaction(tx) {
|
|
838
845
|
if (typeof tx === "string") return (await this.describeRaw(tx)).desc;
|
|
839
|
-
return (await this
|
|
846
|
+
return (await this.#signSpend(tx)).desc;
|
|
840
847
|
}
|
|
841
848
|
/** 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. */
|
|
842
849
|
async signTransaction(tx, options = {}) {
|
|
@@ -846,7 +853,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
846
853
|
this.checkMax(desc.fee, feeLimit(this._config.transactionMaxFee, options), "transaction");
|
|
847
854
|
return tx;
|
|
848
855
|
}
|
|
849
|
-
const { tx: built, fee } = await this
|
|
856
|
+
const { tx: built, fee } = await this.#signSpend(tx);
|
|
850
857
|
this.checkMax(fee, feeLimit(this._config.transactionMaxFee, options), "transaction");
|
|
851
858
|
return built.hex;
|
|
852
859
|
}
|
|
@@ -864,7 +871,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
864
871
|
hash = parsed.id;
|
|
865
872
|
fee = desc.fee;
|
|
866
873
|
} else {
|
|
867
|
-
const built = await this
|
|
874
|
+
const built = await this.#signSpend(tx);
|
|
868
875
|
rawHex = built.tx.hex;
|
|
869
876
|
hash = built.tx.id;
|
|
870
877
|
fee = built.fee;
|
|
@@ -877,7 +884,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
877
884
|
if (o.token) throw new Error("Tokens are not supported on the bitcoin blockchain.");
|
|
878
885
|
this.live();
|
|
879
886
|
const client = this.requireClient("transfer");
|
|
880
|
-
const built = await this
|
|
887
|
+
const built = await this.#signSpend({ to: o.recipient, value: o.amount });
|
|
881
888
|
this.checkMax(built.fee, feeLimit(this._config.transferMaxFee, writeOptions), "transfer");
|
|
882
889
|
return { hash: await this.broadcast(client, built.tx.hex, built.tx.id), fee: built.fee };
|
|
883
890
|
}
|
|
@@ -902,7 +909,7 @@ var WalletAccountBtc = class _WalletAccountBtc extends WalletAccountReadOnlyBtc
|
|
|
902
909
|
}
|
|
903
910
|
dispose() {
|
|
904
911
|
this._disposed = true;
|
|
905
|
-
this
|
|
912
|
+
this.#signer.dispose();
|
|
906
913
|
}
|
|
907
914
|
};
|
|
908
915
|
|