@leather.io/bitcoin 0.19.29 → 0.19.31
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +41 -0
- package/dist/index.d.ts +206 -157
- package/dist/index.js +341 -205
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/bip322/bip322-utils.ts +1 -1
- package/src/bip322/sign-message-bip322-bitcoinjs.ts +8 -3
- package/src/bip322/sign-message-bip322.spec.ts +13 -13
- package/src/coin-selection/calculate-max-spend.spec.ts +19 -17
- package/src/coin-selection/calculate-max-spend.ts +26 -16
- package/src/coin-selection/coin-selection.spec.ts +29 -26
- package/src/coin-selection/coin-selection.ts +1 -1
- package/src/coin-selection/coin-selection.utils.spec.ts +2 -1
- package/src/coin-selection/coin-selection.utils.ts +5 -8
- package/src/fees/bitcoin-fees.spec.ts +7 -10
- package/src/index.ts +21 -9
- package/src/mocks/mocks.ts +39 -0
- package/src/{p2tr-address-gen.spec.ts → payments/p2tr-address-gen.spec.ts} +1 -1
- package/src/{p2tr-address-gen.ts → payments/p2tr-address-gen.ts} +2 -2
- package/src/{p2wpkh-address-gen.ts → payments/p2wpkh-address-gen.ts} +2 -2
- package/src/psbt/psbt-details.ts +3 -3
- package/src/psbt/psbt-inputs.ts +9 -6
- package/src/psbt/psbt-outputs.ts +10 -7
- package/src/psbt/psbt-totals.ts +3 -3
- package/src/{bitcoin-signer.ts → signer/bitcoin-signer.ts} +6 -5
- package/src/transactions/generate-unsigned-transaction.spec.ts +1 -1
- package/src/transactions/generate-unsigned-transaction.ts +3 -3
- package/src/{bitcoin.network.ts → utils/bitcoin.network.ts} +2 -0
- package/src/{bitcoin.utils.spec.ts → utils/bitcoin.utils.spec.ts} +19 -14
- package/src/{bitcoin.utils.ts → utils/bitcoin.utils.ts} +19 -13
- package/src/{lookup-derivation-by-address.spec.ts → utils/lookup-derivation-by-address.spec.ts} +11 -6
- package/src/{lookup-derivation-by-address.ts → utils/lookup-derivation-by-address.ts} +4 -3
- package/src/validation/address-validation.spec.ts +396 -0
- package/src/validation/address-validation.ts +28 -0
- package/src/validation/amount-validation.spec.ts +39 -0
- package/src/validation/amount-validation.ts +31 -0
- package/src/validation/bitcoin-address.ts +23 -0
- package/src/{bitcoin-error.ts → validation/bitcoin-error.ts} +4 -2
- package/src/validation/transaction-validation.spec.ts +60 -0
- package/src/validation/transaction-validation.ts +46 -0
- /package/src/{btc-size-fee-estimator.spec.ts → fees/btc-size-fee-estimator.spec.ts} +0 -0
- /package/src/{btc-size-fee-estimator.ts → fees/btc-size-fee-estimator.ts} +0 -0
- /package/src/{p2wpkh-address-gen.spec.ts → payments/p2wpkh-address-gen.spec.ts} +0 -0
- /package/src/{p2wsh-p2sh-address-gen.spec.ts → payments/p2wsh-p2sh-address-gen.spec.ts} +0 -0
- /package/src/{p2wsh-p2sh-address-gen.ts → payments/p2wsh-p2sh-address-gen.ts} +0 -0
- /package/src/{bitcoin-signer.spec.ts → signer/bitcoin-signer.spec.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { ECPairFactory } from "ecpair";
|
|
|
7
7
|
import { encode } from "varuint-bitcoin";
|
|
8
8
|
import { isString } from "@leather.io/utils";
|
|
9
9
|
|
|
10
|
-
// src/bitcoin.utils.ts
|
|
10
|
+
// src/utils/bitcoin.utils.ts
|
|
11
11
|
import { hexToBytes } from "@noble/hashes/utils";
|
|
12
12
|
import { HDKey } from "@scure/bip32";
|
|
13
13
|
import { mnemonicToSeedSync } from "@scure/bip39";
|
|
@@ -19,7 +19,11 @@ import {
|
|
|
19
19
|
} from "@leather.io/crypto";
|
|
20
20
|
import { defaultWalletKeyId, isDefined, whenNetwork } from "@leather.io/utils";
|
|
21
21
|
|
|
22
|
-
// src/
|
|
22
|
+
// src/payments/p2tr-address-gen.ts
|
|
23
|
+
import * as btc from "@scure/btc-signer";
|
|
24
|
+
import { DerivationPathDepth } from "@leather.io/crypto";
|
|
25
|
+
|
|
26
|
+
// src/utils/bitcoin.network.ts
|
|
23
27
|
import * as bitcoinJs from "bitcoinjs-lib";
|
|
24
28
|
var bitcoinMainnet = {
|
|
25
29
|
bech32: "bc",
|
|
@@ -60,9 +64,7 @@ function getBitcoinJsLibNetworkConfigByMode(network) {
|
|
|
60
64
|
return bitcoinJsLibNetworks[network];
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
// src/p2tr-address-gen.ts
|
|
64
|
-
import * as btc from "@scure/btc-signer";
|
|
65
|
-
import { DerivationPathDepth } from "@leather.io/crypto";
|
|
67
|
+
// src/payments/p2tr-address-gen.ts
|
|
66
68
|
function makeTaprootAccountDerivationPath(network, accountIndex) {
|
|
67
69
|
return `m/86'/${getBitcoinCoinTypeIndexByNetwork(network)}'/${accountIndex}'`;
|
|
68
70
|
}
|
|
@@ -108,7 +110,7 @@ function deriveTaprootReceiveAddressIndexZero({
|
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
// src/p2wpkh-address-gen.ts
|
|
113
|
+
// src/payments/p2wpkh-address-gen.ts
|
|
112
114
|
import * as btc2 from "@scure/btc-signer";
|
|
113
115
|
import { DerivationPathDepth as DerivationPathDepth2 } from "@leather.io/crypto";
|
|
114
116
|
function makeNativeSegwitAccountDerivationPath(network, accountIndex) {
|
|
@@ -146,7 +148,7 @@ function deriveNativeSegwitReceiveAddressIndexZero({
|
|
|
146
148
|
};
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
// src/bitcoin.utils.ts
|
|
151
|
+
// src/utils/bitcoin.utils.ts
|
|
150
152
|
function initBitcoinAccount(derivationPath, policy) {
|
|
151
153
|
const xpub = extractExtendedPublicKeyFromPolicy(policy);
|
|
152
154
|
const network = inferNetworkFromPath(derivationPath);
|
|
@@ -485,7 +487,7 @@ import validate, { AddressType, getAddressInfo } from "bitcoin-address-validatio
|
|
|
485
487
|
import { BTC_P2WPKH_DUST_AMOUNT } from "@leather.io/constants";
|
|
486
488
|
import { sumNumbers } from "@leather.io/utils";
|
|
487
489
|
|
|
488
|
-
// src/btc-size-fee-estimator.ts
|
|
490
|
+
// src/fees/btc-size-fee-estimator.ts
|
|
489
491
|
import BigNumber from "bignumber.js";
|
|
490
492
|
import { assertUnreachable } from "@leather.io/utils";
|
|
491
493
|
var BtcSizeFeeEstimator = class {
|
|
@@ -757,11 +759,7 @@ function getSizeInfo(payload) {
|
|
|
757
759
|
});
|
|
758
760
|
return sizeInfo;
|
|
759
761
|
}
|
|
760
|
-
function getSpendableAmount({
|
|
761
|
-
utxos,
|
|
762
|
-
feeRate,
|
|
763
|
-
recipients
|
|
764
|
-
}) {
|
|
762
|
+
function getSpendableAmount({ utxos, feeRate, recipients }) {
|
|
765
763
|
const balance = utxos.map((utxo) => Number(utxo.value)).reduce((prevVal, curVal) => prevVal + curVal, 0);
|
|
766
764
|
const size = getSizeInfo({
|
|
767
765
|
inputLength: utxos.length,
|
|
@@ -796,33 +794,34 @@ function filterUneconomicalUtxos({
|
|
|
796
794
|
}
|
|
797
795
|
|
|
798
796
|
// src/coin-selection/calculate-max-spend.ts
|
|
799
|
-
function
|
|
800
|
-
|
|
797
|
+
function calculateMaxSpend({
|
|
798
|
+
recipient,
|
|
801
799
|
utxos,
|
|
802
800
|
feeRate,
|
|
803
|
-
|
|
801
|
+
feeRates
|
|
804
802
|
}) {
|
|
805
|
-
if (!utxos.length || !
|
|
803
|
+
if (!utxos.length || !feeRates)
|
|
806
804
|
return {
|
|
807
805
|
spendAllFee: 0,
|
|
808
806
|
amount: createMoney(0, "BTC"),
|
|
809
|
-
|
|
807
|
+
spendableBtc: new BigNumber3(0)
|
|
810
808
|
};
|
|
811
|
-
const currentFeeRate = feeRate ??
|
|
809
|
+
const currentFeeRate = feeRate ?? feeRates.halfHourFee.toNumber();
|
|
812
810
|
const filteredUtxos = filterUneconomicalUtxos({
|
|
813
811
|
utxos,
|
|
814
812
|
feeRate: currentFeeRate,
|
|
815
|
-
recipients: [{ address:
|
|
813
|
+
recipients: [{ address: recipient, amount: createMoney(0, "BTC") }]
|
|
816
814
|
});
|
|
817
815
|
const { spendableAmount, fee } = getSpendableAmount({
|
|
818
816
|
utxos: filteredUtxos,
|
|
819
817
|
feeRate: currentFeeRate,
|
|
820
|
-
recipients: [{ address:
|
|
818
|
+
recipients: [{ address: recipient, amount: createMoney(0, "BTC") }],
|
|
819
|
+
isSendMax: true
|
|
821
820
|
});
|
|
822
821
|
return {
|
|
823
822
|
spendAllFee: fee,
|
|
824
823
|
amount: createMoney(spendableAmount, "BTC"),
|
|
825
|
-
|
|
824
|
+
spendableBtc: satToBtc(spendableAmount)
|
|
826
825
|
};
|
|
827
826
|
}
|
|
828
827
|
|
|
@@ -832,7 +831,7 @@ import { validate as validate2 } from "bitcoin-address-validation";
|
|
|
832
831
|
import { BTC_P2WPKH_DUST_AMOUNT as BTC_P2WPKH_DUST_AMOUNT2 } from "@leather.io/constants";
|
|
833
832
|
import { createMoney as createMoney2, sumMoney } from "@leather.io/utils";
|
|
834
833
|
|
|
835
|
-
// src/bitcoin-error.ts
|
|
834
|
+
// src/validation/bitcoin-error.ts
|
|
836
835
|
var BitcoinError = class extends Error {
|
|
837
836
|
message;
|
|
838
837
|
constructor(message) {
|
|
@@ -964,133 +963,74 @@ function getBitcoinFees({ feeRates, isSendingMax, recipients, utxos }) {
|
|
|
964
963
|
};
|
|
965
964
|
}
|
|
966
965
|
|
|
967
|
-
// src/
|
|
968
|
-
import {
|
|
969
|
-
import
|
|
970
|
-
function
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
recipients,
|
|
978
|
-
utxos
|
|
979
|
-
}) {
|
|
980
|
-
const determineUtxosArgs = { feeRate, recipients, utxos };
|
|
981
|
-
const { inputs, outputs, fee } = isSendingMax ? determineUtxosForSpendAll(determineUtxosArgs) : determineUtxosForSpend(determineUtxosArgs);
|
|
982
|
-
if (!inputs.length) throw new BitcoinError("NoInputsToSign");
|
|
983
|
-
if (!outputs.length) throw new BitcoinError("NoOutputsToSign");
|
|
984
|
-
const tx = new btc4.Transaction();
|
|
985
|
-
const p2wpkh3 = btc4.p2wpkh(hexToBytes3(payerPublicKey), network);
|
|
986
|
-
for (const input of inputs) {
|
|
987
|
-
tx.addInput({
|
|
988
|
-
txid: input.txid,
|
|
989
|
-
index: input.vout,
|
|
990
|
-
sequence: 0,
|
|
991
|
-
bip32Derivation,
|
|
992
|
-
witnessUtxo: {
|
|
993
|
-
// script = 0014 + pubKeyHash
|
|
994
|
-
script: p2wpkh3.script,
|
|
995
|
-
amount: BigInt(input.value)
|
|
996
|
-
}
|
|
997
|
-
});
|
|
966
|
+
// src/validation/address-validation.ts
|
|
967
|
+
import { Network, validate as validate3 } from "bitcoin-address-validation";
|
|
968
|
+
import { isEmptyString, isUndefined } from "@leather.io/utils";
|
|
969
|
+
function getBitcoinAddressNetworkType(network) {
|
|
970
|
+
if (network === "signet") return Network.testnet;
|
|
971
|
+
return network;
|
|
972
|
+
}
|
|
973
|
+
function isValidBitcoinAddress(address2) {
|
|
974
|
+
if (isUndefined(address2) || isEmptyString(address2)) {
|
|
975
|
+
return false;
|
|
998
976
|
}
|
|
999
|
-
|
|
1000
|
-
if (!output.address) {
|
|
1001
|
-
tx.addOutputAddress(payerAddress, BigInt(output.value), network);
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
tx.addOutputAddress(output.address, BigInt(output.value), network);
|
|
1005
|
-
});
|
|
1006
|
-
return { tx, hex: tx.hex, psbt: tx.toPSBT(), inputs, fee };
|
|
977
|
+
return validate3(address2);
|
|
1007
978
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
DerivationPathDepth as DerivationPathDepth4,
|
|
1014
|
-
appendAddressIndexToPath,
|
|
1015
|
-
decomposeDescriptor,
|
|
1016
|
-
deriveKeychainFromXpub,
|
|
1017
|
-
keyOriginToDerivationPath
|
|
1018
|
-
} from "@leather.io/crypto";
|
|
1019
|
-
import { hexToNumber, toHexString } from "@leather.io/utils";
|
|
1020
|
-
function initializeBitcoinAccountKeychainFromDescriptor(descriptor) {
|
|
1021
|
-
const { fingerprint, keyOrigin } = decomposeDescriptor(descriptor);
|
|
1022
|
-
return {
|
|
1023
|
-
descriptor,
|
|
1024
|
-
xpub: extractExtendedPublicKeyFromPolicy(descriptor),
|
|
1025
|
-
keyOrigin,
|
|
1026
|
-
masterKeyFingerprint: fingerprint,
|
|
1027
|
-
keychain: deriveKeychainFromXpub(extractExtendedPublicKeyFromPolicy(descriptor))
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
function deriveBitcoinPayerFromAccount(descriptor, network) {
|
|
1031
|
-
const { fingerprint, keyOrigin } = decomposeDescriptor(descriptor);
|
|
1032
|
-
const accountKeychain = deriveKeychainFromXpub(extractExtendedPublicKeyFromPolicy(descriptor));
|
|
1033
|
-
const paymentType = inferPaymentTypeFromPath(keyOrigin);
|
|
1034
|
-
if (accountKeychain.depth !== DerivationPathDepth4.Account)
|
|
1035
|
-
throw new Error("Keychain passed is not an account");
|
|
1036
|
-
return ({ receive = 0, addressIndex }) => {
|
|
1037
|
-
const childKeychain = accountKeychain.deriveChild(receive).deriveChild(addressIndex);
|
|
1038
|
-
const derivePayerFromAccount = whenSupportedPaymentType(paymentType)({
|
|
1039
|
-
p2tr: getTaprootPaymentFromAddressIndex,
|
|
1040
|
-
p2wpkh: getNativeSegwitPaymentFromAddressIndex
|
|
1041
|
-
});
|
|
1042
|
-
const payment = derivePayerFromAccount(childKeychain, network);
|
|
1043
|
-
return {
|
|
1044
|
-
keyOrigin: appendAddressIndexToPath(keyOrigin, 0),
|
|
1045
|
-
masterKeyFingerprint: fingerprint,
|
|
1046
|
-
paymentType,
|
|
1047
|
-
network,
|
|
1048
|
-
payment,
|
|
1049
|
-
get address() {
|
|
1050
|
-
if (!payment.address) throw new Error("Payment address could not be derived");
|
|
1051
|
-
return payment.address;
|
|
1052
|
-
},
|
|
1053
|
-
get publicKey() {
|
|
1054
|
-
if (!childKeychain.publicKey) throw new Error("Public key could not be derived");
|
|
1055
|
-
return childKeychain.publicKey;
|
|
1056
|
-
}
|
|
1057
|
-
};
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
function payerToBip32Derivation(args) {
|
|
1061
|
-
return [
|
|
1062
|
-
args.publicKey,
|
|
1063
|
-
{
|
|
1064
|
-
fingerprint: hexToNumber(args.masterKeyFingerprint),
|
|
1065
|
-
path: btc5.bip32Path(keyOriginToDerivationPath(args.keyOrigin))
|
|
1066
|
-
}
|
|
1067
|
-
];
|
|
1068
|
-
}
|
|
1069
|
-
function payerToTapBip32Derivation(args) {
|
|
1070
|
-
return [
|
|
1071
|
-
// TODO: @kyranjamie to default to schnoor when TR so conversion isn't
|
|
1072
|
-
// necessary here?
|
|
1073
|
-
ecdsaPublicKeyToSchnorr(args.publicKey),
|
|
1074
|
-
{
|
|
1075
|
-
hashes: [],
|
|
1076
|
-
der: {
|
|
1077
|
-
fingerprint: hexToNumber(args.masterKeyFingerprint),
|
|
1078
|
-
path: btc5.bip32Path(keyOriginToDerivationPath(args.keyOrigin))
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
];
|
|
979
|
+
function isValidBitcoinNetworkAddress(address2, network) {
|
|
980
|
+
if (!isValidBitcoinAddress(address2) || !network) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
return validate3(address2, getBitcoinAddressNetworkType(network));
|
|
1082
984
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
985
|
+
|
|
986
|
+
// src/validation/bitcoin-address.ts
|
|
987
|
+
function isBitcoinAddress(value) {
|
|
988
|
+
try {
|
|
989
|
+
isValidBitcoinAddress(value);
|
|
990
|
+
return true;
|
|
991
|
+
} catch {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
1086
994
|
}
|
|
1087
|
-
function
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
995
|
+
function createBitcoinAddress(value) {
|
|
996
|
+
if (!isBitcoinAddress(value)) {
|
|
997
|
+
throw new BitcoinError("InvalidAddress");
|
|
998
|
+
}
|
|
999
|
+
return value;
|
|
1091
1000
|
}
|
|
1092
1001
|
|
|
1093
|
-
// src/
|
|
1002
|
+
// src/mocks/mocks.ts
|
|
1003
|
+
var TEST_ACCOUNT_1_NATIVE_SEGWIT_ADDRESS = createBitcoinAddress(
|
|
1004
|
+
"bc1q530dz4h80kwlzywlhx2qn0k6vdtftd93c499yq"
|
|
1005
|
+
);
|
|
1006
|
+
var TEST_ACCOUNT_1_TAPROOT_ADDRESS = createBitcoinAddress(
|
|
1007
|
+
"bc1putuzj9lyfcm8fef9jpy85nmh33cxuq9u6wyuk536t9kemdk37yjqmkc0pg"
|
|
1008
|
+
);
|
|
1009
|
+
var TEST_ACCOUNT_2_TAPROOT_ADDRESS = createBitcoinAddress(
|
|
1010
|
+
"bc1pmk2sacpfyy4v5phl8tq6eggu4e8laztep7fsgkkx0nc6m9vydjesaw0g2r"
|
|
1011
|
+
);
|
|
1012
|
+
var TEST_TESNET_ACCOUNT_1_NATIVE_SEGWIT_ADDRESS = createBitcoinAddress(
|
|
1013
|
+
"tb1q4qgnjewwun2llgken94zqjrx5kpqqycaz5522d"
|
|
1014
|
+
);
|
|
1015
|
+
var TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS = createBitcoinAddress(
|
|
1016
|
+
"tb1qr8me8t9gu9g6fu926ry5v44yp0wyljrespjtnz"
|
|
1017
|
+
);
|
|
1018
|
+
var TEST_TESTNET_ACCOUNT_2_TAPROOT_ADDRESS = createBitcoinAddress(
|
|
1019
|
+
"tb1pve00jmp43whpqj2wpcxtc7m8wqhz0azq689y4r7h8tmj8ltaj87qj2nj6w"
|
|
1020
|
+
);
|
|
1021
|
+
var recipientAddress = createBitcoinAddress("tb1qt28eagxcl9gvhq2rpj5slg7dwgxae2dn2hk93m");
|
|
1022
|
+
var legacyAddress = createBitcoinAddress("15PyZveQd28E2SHZu2ugkWZBp6iER41vXj");
|
|
1023
|
+
var segwitAddress = createBitcoinAddress("33SVjoCHJovrXxjDKLFSXo1h3t5KgkPzfH");
|
|
1024
|
+
var taprootAddress = createBitcoinAddress(
|
|
1025
|
+
"tb1parwmj7533de3k2fw2kntyqacspvhm67qnjcmpqnnpfvzu05l69nsczdywd"
|
|
1026
|
+
);
|
|
1027
|
+
var invalidAddress = "whoop-de-da-boop-da-de-not-a-bitcoin-address";
|
|
1028
|
+
var inValidCharactersAddress = createBitcoinAddress(
|
|
1029
|
+
"tb1&*%wmj7533de3k2fw2kntyqacspvhm67qnjcmpqnnpfvzu05l69nsczdywd"
|
|
1030
|
+
);
|
|
1031
|
+
var inValidLengthAddress = createBitcoinAddress("tb1parwmj7533de3k2fw2kntyqacspvhm67wd");
|
|
1032
|
+
|
|
1033
|
+
// src/payments/p2wsh-p2sh-address-gen.ts
|
|
1094
1034
|
import { ripemd160 } from "@noble/hashes/ripemd160";
|
|
1095
1035
|
import { sha256 as sha2562 } from "@noble/hashes/sha256";
|
|
1096
1036
|
import { base58check } from "@scure/base";
|
|
@@ -1132,54 +1072,6 @@ function publicKeyToPayToScriptHashAddress(publicKey, network) {
|
|
|
1132
1072
|
return makePayToScriptHashAddress(addrBytes, network);
|
|
1133
1073
|
}
|
|
1134
1074
|
|
|
1135
|
-
// src/lookup-derivation-by-address.ts
|
|
1136
|
-
import { HARDENED_OFFSET as HARDENED_OFFSET2, HDKey as HDKey3 } from "@scure/bip32";
|
|
1137
|
-
import { createCounter } from "@leather.io/utils";
|
|
1138
|
-
function lookupDerivationByAddress(args) {
|
|
1139
|
-
const { taprootXpub, nativeSegwitXpub, iterationLimit } = args;
|
|
1140
|
-
const taprootKeychain = HDKey3.fromExtendedKey(taprootXpub);
|
|
1141
|
-
const nativeSegwitKeychain = HDKey3.fromExtendedKey(nativeSegwitXpub);
|
|
1142
|
-
return (address2) => {
|
|
1143
|
-
const network = inferNetworkFromAddress(address2);
|
|
1144
|
-
const paymentType = inferPaymentTypeFromAddress(address2);
|
|
1145
|
-
const accountIndex = whenSupportedPaymentType(paymentType)({
|
|
1146
|
-
p2tr: taprootKeychain.index - HARDENED_OFFSET2,
|
|
1147
|
-
p2wpkh: nativeSegwitKeychain.index - HARDENED_OFFSET2
|
|
1148
|
-
});
|
|
1149
|
-
function getTaprootAddressAtIndex(index) {
|
|
1150
|
-
return getTaprootAddress({ index, keychain: taprootKeychain, network });
|
|
1151
|
-
}
|
|
1152
|
-
function getNativeSegwitAddressAtIndex(index) {
|
|
1153
|
-
return getNativeSegwitAddress({ index, keychain: nativeSegwitKeychain, network });
|
|
1154
|
-
}
|
|
1155
|
-
const paymentFn = whenSupportedPaymentType(paymentType)({
|
|
1156
|
-
p2tr: getTaprootAddressAtIndex,
|
|
1157
|
-
p2wpkh: getNativeSegwitAddressAtIndex
|
|
1158
|
-
});
|
|
1159
|
-
const derivationPathFn = whenSupportedPaymentType(paymentType)({
|
|
1160
|
-
p2tr: makeTaprootAddressIndexDerivationPath,
|
|
1161
|
-
p2wpkh: makeNativeSegwitAddressIndexDerivationPath
|
|
1162
|
-
});
|
|
1163
|
-
const count = createCounter();
|
|
1164
|
-
const t0 = performance.now();
|
|
1165
|
-
while (count.getValue() <= iterationLimit) {
|
|
1166
|
-
const currentIndex = count.getValue();
|
|
1167
|
-
const addressToCheck = paymentFn(currentIndex);
|
|
1168
|
-
if (addressToCheck !== address2) {
|
|
1169
|
-
count.increment();
|
|
1170
|
-
continue;
|
|
1171
|
-
}
|
|
1172
|
-
const t1 = performance.now();
|
|
1173
|
-
return {
|
|
1174
|
-
status: "success",
|
|
1175
|
-
duration: t1 - t0,
|
|
1176
|
-
path: derivationPathFn(network, accountIndex, currentIndex)
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
return { status: "failure" };
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
1075
|
// src/psbt/psbt-totals.ts
|
|
1184
1076
|
import { createMoney as createMoney3, sumNumbers as sumNumbers2 } from "@leather.io/utils";
|
|
1185
1077
|
function calculateAddressInputsTotal(addresses, inputs) {
|
|
@@ -1219,7 +1111,7 @@ function getPsbtTotals({ psbtAddresses, parsedInputs, parsedOutputs }) {
|
|
|
1219
1111
|
|
|
1220
1112
|
// src/psbt/psbt-inputs.ts
|
|
1221
1113
|
import { bytesToHex } from "@noble/hashes/utils";
|
|
1222
|
-
import { isDefined as isDefined2, isUndefined } from "@leather.io/utils";
|
|
1114
|
+
import { isDefined as isDefined2, isUndefined as isUndefined2 } from "@leather.io/utils";
|
|
1223
1115
|
function getParsedInputs({
|
|
1224
1116
|
inputs,
|
|
1225
1117
|
indexesToSign,
|
|
@@ -1227,15 +1119,16 @@ function getParsedInputs({
|
|
|
1227
1119
|
psbtAddresses
|
|
1228
1120
|
}) {
|
|
1229
1121
|
const bitcoinNetwork = getBtcSignerLibNetworkConfigByMode(networkMode);
|
|
1230
|
-
const signAll =
|
|
1122
|
+
const signAll = isUndefined2(indexesToSign);
|
|
1231
1123
|
const psbtInputs = inputs.map((input, i) => {
|
|
1232
1124
|
const inputAddress = isDefined2(input.index) ? getBitcoinInputAddress(input, bitcoinNetwork) : "";
|
|
1233
|
-
const
|
|
1125
|
+
const bitcoinAddress = createBitcoinAddress(inputAddress);
|
|
1126
|
+
const isCurrentAddress = psbtAddresses.includes(bitcoinAddress);
|
|
1234
1127
|
const canChange = isCurrentAddress && !(!input.sighashType || input.sighashType === 0 || input.sighashType === 1);
|
|
1235
1128
|
const toSignAll = isCurrentAddress && signAll;
|
|
1236
1129
|
const toSignIndex = isCurrentAddress && !signAll && indexesToSign.includes(i);
|
|
1237
1130
|
return {
|
|
1238
|
-
address:
|
|
1131
|
+
address: bitcoinAddress,
|
|
1239
1132
|
index: input.index,
|
|
1240
1133
|
bip32Derivation: input.bip32Derivation,
|
|
1241
1134
|
tapBip32Derivation: input.tapBip32Derivation,
|
|
@@ -1251,7 +1144,7 @@ function getParsedInputs({
|
|
|
1251
1144
|
}
|
|
1252
1145
|
|
|
1253
1146
|
// src/psbt/psbt-outputs.ts
|
|
1254
|
-
import { isDefined as isDefined3, isUndefined as
|
|
1147
|
+
import { isDefined as isDefined3, isUndefined as isUndefined3 } from "@leather.io/utils";
|
|
1255
1148
|
function getParsedOutputs({
|
|
1256
1149
|
isPsbtMutable,
|
|
1257
1150
|
outputs,
|
|
@@ -1260,10 +1153,12 @@ function getParsedOutputs({
|
|
|
1260
1153
|
}) {
|
|
1261
1154
|
const bitcoinNetwork = getBtcSignerLibNetworkConfigByMode(networkMode);
|
|
1262
1155
|
return outputs.map((output) => {
|
|
1263
|
-
if (
|
|
1156
|
+
if (isUndefined3(output.script)) {
|
|
1264
1157
|
return;
|
|
1265
1158
|
}
|
|
1266
|
-
const outputAddress =
|
|
1159
|
+
const outputAddress = createBitcoinAddress(
|
|
1160
|
+
getAddressFromOutScript(output.script, bitcoinNetwork)
|
|
1161
|
+
);
|
|
1267
1162
|
const isCurrentAddress = psbtAddresses.includes(outputAddress);
|
|
1268
1163
|
return {
|
|
1269
1164
|
address: outputAddress,
|
|
@@ -1278,16 +1173,16 @@ function getParsedOutputs({
|
|
|
1278
1173
|
import { createMoney as createMoney4, subtractMoney } from "@leather.io/utils";
|
|
1279
1174
|
|
|
1280
1175
|
// src/psbt/utils.ts
|
|
1281
|
-
import { hexToBytes as
|
|
1282
|
-
import * as
|
|
1176
|
+
import { hexToBytes as hexToBytes3 } from "@noble/hashes/utils";
|
|
1177
|
+
import * as btc4 from "@scure/btc-signer";
|
|
1283
1178
|
import { RawPSBTV0, RawPSBTV2 } from "@scure/btc-signer/psbt";
|
|
1284
1179
|
import { isString as isString2 } from "@leather.io/utils";
|
|
1285
1180
|
function getPsbtAsTransaction(psbt) {
|
|
1286
|
-
const bytes = isString2(psbt) ?
|
|
1287
|
-
return
|
|
1181
|
+
const bytes = isString2(psbt) ? hexToBytes3(psbt) : psbt;
|
|
1182
|
+
return btc4.Transaction.fromPSBT(bytes);
|
|
1288
1183
|
}
|
|
1289
1184
|
function getRawPsbt(psbt) {
|
|
1290
|
-
const bytes = isString2(psbt) ?
|
|
1185
|
+
const bytes = isString2(psbt) ? hexToBytes3(psbt) : psbt;
|
|
1291
1186
|
try {
|
|
1292
1187
|
return RawPSBTV0.decode(bytes);
|
|
1293
1188
|
} catch (e1) {
|
|
@@ -1342,14 +1237,240 @@ function getPsbtDetails({
|
|
|
1342
1237
|
psbtOutputs: parsedOutputs
|
|
1343
1238
|
};
|
|
1344
1239
|
}
|
|
1240
|
+
|
|
1241
|
+
// src/signer/bitcoin-signer.ts
|
|
1242
|
+
import { HARDENED_OFFSET } from "@scure/bip32";
|
|
1243
|
+
import * as btc5 from "@scure/btc-signer";
|
|
1244
|
+
import {
|
|
1245
|
+
DerivationPathDepth as DerivationPathDepth4,
|
|
1246
|
+
appendAddressIndexToPath,
|
|
1247
|
+
decomposeDescriptor,
|
|
1248
|
+
deriveKeychainFromXpub,
|
|
1249
|
+
keyOriginToDerivationPath
|
|
1250
|
+
} from "@leather.io/crypto";
|
|
1251
|
+
import { hexToNumber, toHexString } from "@leather.io/utils";
|
|
1252
|
+
function initializeBitcoinAccountKeychainFromDescriptor(descriptor) {
|
|
1253
|
+
const { fingerprint, keyOrigin } = decomposeDescriptor(descriptor);
|
|
1254
|
+
return {
|
|
1255
|
+
descriptor,
|
|
1256
|
+
xpub: extractExtendedPublicKeyFromPolicy(descriptor),
|
|
1257
|
+
keyOrigin,
|
|
1258
|
+
masterKeyFingerprint: fingerprint,
|
|
1259
|
+
keychain: deriveKeychainFromXpub(extractExtendedPublicKeyFromPolicy(descriptor))
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
function deriveBitcoinPayerFromAccount(descriptor, network) {
|
|
1263
|
+
const { fingerprint, keyOrigin } = decomposeDescriptor(descriptor);
|
|
1264
|
+
const accountKeychain = deriveKeychainFromXpub(extractExtendedPublicKeyFromPolicy(descriptor));
|
|
1265
|
+
const paymentType = inferPaymentTypeFromPath(keyOrigin);
|
|
1266
|
+
if (accountKeychain.depth !== DerivationPathDepth4.Account)
|
|
1267
|
+
throw new Error("Keychain passed is not an account");
|
|
1268
|
+
return ({ receive = 0, addressIndex }) => {
|
|
1269
|
+
const childKeychain = accountKeychain.deriveChild(receive).deriveChild(addressIndex);
|
|
1270
|
+
const derivePayerFromAccount = whenSupportedPaymentType(paymentType)({
|
|
1271
|
+
p2tr: getTaprootPaymentFromAddressIndex,
|
|
1272
|
+
p2wpkh: getNativeSegwitPaymentFromAddressIndex
|
|
1273
|
+
});
|
|
1274
|
+
const payment = derivePayerFromAccount(childKeychain, network);
|
|
1275
|
+
return {
|
|
1276
|
+
keyOrigin: appendAddressIndexToPath(keyOrigin, 0),
|
|
1277
|
+
masterKeyFingerprint: fingerprint,
|
|
1278
|
+
paymentType,
|
|
1279
|
+
network,
|
|
1280
|
+
payment,
|
|
1281
|
+
get address() {
|
|
1282
|
+
if (!payment.address) throw new Error("Payment address could not be derived");
|
|
1283
|
+
return payment.address;
|
|
1284
|
+
},
|
|
1285
|
+
get publicKey() {
|
|
1286
|
+
if (!childKeychain.publicKey) throw new Error("Public key could not be derived");
|
|
1287
|
+
return childKeychain.publicKey;
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
function payerToBip32Derivation(args) {
|
|
1293
|
+
return [
|
|
1294
|
+
args.publicKey,
|
|
1295
|
+
{
|
|
1296
|
+
fingerprint: hexToNumber(args.masterKeyFingerprint),
|
|
1297
|
+
path: btc5.bip32Path(keyOriginToDerivationPath(args.keyOrigin))
|
|
1298
|
+
}
|
|
1299
|
+
];
|
|
1300
|
+
}
|
|
1301
|
+
function payerToTapBip32Derivation(args) {
|
|
1302
|
+
return [
|
|
1303
|
+
// TODO: @kyranjamie to default to schnoor when TR so conversion isn't
|
|
1304
|
+
// necessary here?
|
|
1305
|
+
ecdsaPublicKeyToSchnorr(args.publicKey),
|
|
1306
|
+
{
|
|
1307
|
+
hashes: [],
|
|
1308
|
+
der: {
|
|
1309
|
+
fingerprint: hexToNumber(args.masterKeyFingerprint),
|
|
1310
|
+
path: btc5.bip32Path(keyOriginToDerivationPath(args.keyOrigin))
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
];
|
|
1314
|
+
}
|
|
1315
|
+
function serializeKeyOrigin({ fingerprint, path }) {
|
|
1316
|
+
const values = path.map((num) => num >= HARDENED_OFFSET ? num - HARDENED_OFFSET + "'" : num);
|
|
1317
|
+
return `${toHexString(fingerprint)}/${values.join("/")}`;
|
|
1318
|
+
}
|
|
1319
|
+
function extractRequiredKeyOrigins(derivation) {
|
|
1320
|
+
return derivation.map(
|
|
1321
|
+
([_pubkey, path]) => serializeKeyOrigin("hashes" in path ? path.der : path)
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/transactions/generate-unsigned-transaction.ts
|
|
1326
|
+
import { hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
|
|
1327
|
+
import * as btc6 from "@scure/btc-signer";
|
|
1328
|
+
function generateBitcoinUnsignedTransactionNativeSegwit({
|
|
1329
|
+
feeRate,
|
|
1330
|
+
isSendingMax,
|
|
1331
|
+
payerAddress,
|
|
1332
|
+
payerPublicKey,
|
|
1333
|
+
bip32Derivation,
|
|
1334
|
+
network,
|
|
1335
|
+
recipients,
|
|
1336
|
+
utxos
|
|
1337
|
+
}) {
|
|
1338
|
+
const determineUtxosArgs = { feeRate, recipients, utxos };
|
|
1339
|
+
const { inputs, outputs, fee } = isSendingMax ? determineUtxosForSpendAll(determineUtxosArgs) : determineUtxosForSpend(determineUtxosArgs);
|
|
1340
|
+
if (!inputs.length) throw new BitcoinError("NoInputsToSign");
|
|
1341
|
+
if (!outputs.length) throw new BitcoinError("NoOutputsToSign");
|
|
1342
|
+
const tx = new btc6.Transaction();
|
|
1343
|
+
const p2wpkh3 = btc6.p2wpkh(hexToBytes4(payerPublicKey), network);
|
|
1344
|
+
for (const input of inputs) {
|
|
1345
|
+
tx.addInput({
|
|
1346
|
+
txid: input.txid,
|
|
1347
|
+
index: input.vout,
|
|
1348
|
+
sequence: 0,
|
|
1349
|
+
bip32Derivation,
|
|
1350
|
+
witnessUtxo: {
|
|
1351
|
+
// script = 0014 + pubKeyHash
|
|
1352
|
+
script: p2wpkh3.script,
|
|
1353
|
+
amount: BigInt(input.value)
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
outputs.forEach((output) => {
|
|
1358
|
+
if (!output.address) {
|
|
1359
|
+
tx.addOutputAddress(payerAddress, BigInt(output.value), network);
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
tx.addOutputAddress(output.address, BigInt(output.value), network);
|
|
1363
|
+
});
|
|
1364
|
+
return { tx, hex: tx.hex, psbt: tx.toPSBT(), inputs, fee };
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/validation/amount-validation.ts
|
|
1368
|
+
import BigNumber5 from "bignumber.js";
|
|
1369
|
+
import { btcToSat } from "@leather.io/utils";
|
|
1370
|
+
var minSpendAmountInSats = 546;
|
|
1371
|
+
function isBtcBalanceSufficient({
|
|
1372
|
+
amount: { amount },
|
|
1373
|
+
spendableBtc
|
|
1374
|
+
}) {
|
|
1375
|
+
if (!spendableBtc) return false;
|
|
1376
|
+
const desiredSpend = new BigNumber5(amount);
|
|
1377
|
+
if (desiredSpend.isGreaterThan(spendableBtc)) return false;
|
|
1378
|
+
return true;
|
|
1379
|
+
}
|
|
1380
|
+
function isBtcMinimumSpend({ amount: { amount } }) {
|
|
1381
|
+
if (!amount) return false;
|
|
1382
|
+
const desiredSpend = btcToSat(amount);
|
|
1383
|
+
if (desiredSpend.isLessThan(minSpendAmountInSats)) return false;
|
|
1384
|
+
return true;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
// src/validation/transaction-validation.ts
|
|
1388
|
+
function isValidBitcoinTransaction({
|
|
1389
|
+
amount,
|
|
1390
|
+
payer,
|
|
1391
|
+
recipient,
|
|
1392
|
+
network,
|
|
1393
|
+
utxos,
|
|
1394
|
+
feeRate,
|
|
1395
|
+
feeRates
|
|
1396
|
+
}) {
|
|
1397
|
+
if (!isValidBitcoinAddress(payer) || !isValidBitcoinAddress(recipient)) {
|
|
1398
|
+
throw new BitcoinError("InvalidAddress");
|
|
1399
|
+
}
|
|
1400
|
+
if (!isValidBitcoinNetworkAddress(payer, network) || !isValidBitcoinNetworkAddress(recipient, network)) {
|
|
1401
|
+
throw new BitcoinError("InvalidNetworkAddress");
|
|
1402
|
+
}
|
|
1403
|
+
if (!isBtcMinimumSpend({ amount })) {
|
|
1404
|
+
throw new BitcoinError("InsufficientAmount");
|
|
1405
|
+
}
|
|
1406
|
+
const { spendableBtc } = calculateMaxSpend({ recipient, utxos, feeRate, feeRates });
|
|
1407
|
+
if (!isBtcBalanceSufficient({ amount, spendableBtc })) {
|
|
1408
|
+
throw new BitcoinError("InsufficientFunds");
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/utils/lookup-derivation-by-address.ts
|
|
1413
|
+
import { HARDENED_OFFSET as HARDENED_OFFSET2, HDKey as HDKey3 } from "@scure/bip32";
|
|
1414
|
+
import { createCounter } from "@leather.io/utils";
|
|
1415
|
+
function lookupDerivationByAddress(args) {
|
|
1416
|
+
const { taprootXpub, nativeSegwitXpub, iterationLimit } = args;
|
|
1417
|
+
const taprootKeychain = HDKey3.fromExtendedKey(taprootXpub);
|
|
1418
|
+
const nativeSegwitKeychain = HDKey3.fromExtendedKey(nativeSegwitXpub);
|
|
1419
|
+
return (address2) => {
|
|
1420
|
+
const network = inferNetworkFromAddress(address2);
|
|
1421
|
+
const paymentType = inferPaymentTypeFromAddress(address2);
|
|
1422
|
+
const accountIndex = whenSupportedPaymentType(paymentType)({
|
|
1423
|
+
p2tr: taprootKeychain.index - HARDENED_OFFSET2,
|
|
1424
|
+
p2wpkh: nativeSegwitKeychain.index - HARDENED_OFFSET2
|
|
1425
|
+
});
|
|
1426
|
+
function getTaprootAddressAtIndex(index) {
|
|
1427
|
+
return getTaprootAddress({ index, keychain: taprootKeychain, network });
|
|
1428
|
+
}
|
|
1429
|
+
function getNativeSegwitAddressAtIndex(index) {
|
|
1430
|
+
return getNativeSegwitAddress({ index, keychain: nativeSegwitKeychain, network });
|
|
1431
|
+
}
|
|
1432
|
+
const paymentFn = whenSupportedPaymentType(paymentType)({
|
|
1433
|
+
p2tr: getTaprootAddressAtIndex,
|
|
1434
|
+
p2wpkh: getNativeSegwitAddressAtIndex
|
|
1435
|
+
});
|
|
1436
|
+
const derivationPathFn = whenSupportedPaymentType(paymentType)({
|
|
1437
|
+
p2tr: makeTaprootAddressIndexDerivationPath,
|
|
1438
|
+
p2wpkh: makeNativeSegwitAddressIndexDerivationPath
|
|
1439
|
+
});
|
|
1440
|
+
const count = createCounter();
|
|
1441
|
+
const t0 = performance.now();
|
|
1442
|
+
while (count.getValue() <= iterationLimit) {
|
|
1443
|
+
const currentIndex = count.getValue();
|
|
1444
|
+
const addressToCheck = paymentFn(currentIndex);
|
|
1445
|
+
if (addressToCheck !== address2) {
|
|
1446
|
+
count.increment();
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
const t1 = performance.now();
|
|
1450
|
+
return {
|
|
1451
|
+
status: "success",
|
|
1452
|
+
duration: t1 - t0,
|
|
1453
|
+
path: derivationPathFn(network, accountIndex, currentIndex)
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
return { status: "failure" };
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1345
1459
|
export {
|
|
1346
1460
|
BitcoinError,
|
|
1461
|
+
TEST_ACCOUNT_1_NATIVE_SEGWIT_ADDRESS,
|
|
1462
|
+
TEST_ACCOUNT_1_TAPROOT_ADDRESS,
|
|
1463
|
+
TEST_ACCOUNT_2_TAPROOT_ADDRESS,
|
|
1464
|
+
TEST_TESNET_ACCOUNT_1_NATIVE_SEGWIT_ADDRESS,
|
|
1465
|
+
TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS,
|
|
1466
|
+
TEST_TESTNET_ACCOUNT_2_TAPROOT_ADDRESS,
|
|
1347
1467
|
bip322TransactionToSignValues,
|
|
1348
1468
|
bitcoinNetworkModeToCoreNetworkMode,
|
|
1349
1469
|
bitcoinNetworkToCoreNetworkMap,
|
|
1350
1470
|
btcSignerLibPaymentTypeToPaymentTypeMap,
|
|
1351
|
-
|
|
1471
|
+
calculateMaxSpend,
|
|
1352
1472
|
coinTypeMap,
|
|
1473
|
+
createBitcoinAddress,
|
|
1353
1474
|
createNativeSegwitBitcoinJsSigner,
|
|
1354
1475
|
createTaprootBitcoinJsSigner,
|
|
1355
1476
|
createToSpendTx,
|
|
@@ -1376,6 +1497,7 @@ export {
|
|
|
1376
1497
|
filterUneconomicalUtxos,
|
|
1377
1498
|
generateBitcoinUnsignedTransactionNativeSegwit,
|
|
1378
1499
|
getAddressFromOutScript,
|
|
1500
|
+
getBitcoinAddressNetworkType,
|
|
1379
1501
|
getBitcoinCoinTypeIndexByNetwork,
|
|
1380
1502
|
getBitcoinFees,
|
|
1381
1503
|
getBitcoinInputAddress,
|
|
@@ -1406,14 +1528,24 @@ export {
|
|
|
1406
1528
|
getTaprootPaymentFromAddressIndex,
|
|
1407
1529
|
getUtxoTotal,
|
|
1408
1530
|
hashBip322Message,
|
|
1531
|
+
inValidCharactersAddress,
|
|
1532
|
+
inValidLengthAddress,
|
|
1409
1533
|
inferNetworkFromAddress,
|
|
1410
1534
|
inferNetworkFromPath,
|
|
1411
1535
|
inferPaymentTypeFromAddress,
|
|
1412
1536
|
inferPaymentTypeFromPath,
|
|
1413
1537
|
initBitcoinAccount,
|
|
1414
1538
|
initializeBitcoinAccountKeychainFromDescriptor,
|
|
1539
|
+
invalidAddress,
|
|
1540
|
+
isBitcoinAddress,
|
|
1541
|
+
isBtcBalanceSufficient,
|
|
1542
|
+
isBtcMinimumSpend,
|
|
1415
1543
|
isBtcSignerLibPaymentType,
|
|
1416
1544
|
isSupportedMessageSigningPaymentType,
|
|
1545
|
+
isValidBitcoinAddress,
|
|
1546
|
+
isValidBitcoinNetworkAddress,
|
|
1547
|
+
isValidBitcoinTransaction,
|
|
1548
|
+
legacyAddress,
|
|
1417
1549
|
lookUpLedgerKeysByPath,
|
|
1418
1550
|
lookupDerivationByAddress,
|
|
1419
1551
|
makeNativeSegwitAccountDerivationPath,
|
|
@@ -1423,6 +1555,7 @@ export {
|
|
|
1423
1555
|
makePayToScriptHashKeyHash,
|
|
1424
1556
|
makeTaprootAccountDerivationPath,
|
|
1425
1557
|
makeTaprootAddressIndexDerivationPath,
|
|
1558
|
+
minSpendAmountInSats,
|
|
1426
1559
|
mnemonicToRootNode,
|
|
1427
1560
|
parseKnownPaymentType,
|
|
1428
1561
|
payToScriptHashTestnetPrefix,
|
|
@@ -1430,8 +1563,11 @@ export {
|
|
|
1430
1563
|
payerToTapBip32Derivation,
|
|
1431
1564
|
paymentTypeMap,
|
|
1432
1565
|
publicKeyToPayToScriptHashAddress,
|
|
1566
|
+
recipientAddress,
|
|
1567
|
+
segwitAddress,
|
|
1433
1568
|
serializeKeyOrigin,
|
|
1434
1569
|
signBip322MessageSimple,
|
|
1570
|
+
taprootAddress,
|
|
1435
1571
|
toXOnly,
|
|
1436
1572
|
tweakSigner,
|
|
1437
1573
|
whenBitcoinNetwork,
|