@bitgo-beta/babylonlabs-io-btc-staking-ts 0.4.0-beta.85 → 0.4.0-beta.87

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
@@ -51,6 +51,7 @@ __export(src_exports, {
51
51
  getScriptType: () => getScriptType,
52
52
  getUnbondingTxStakerSignature: () => getUnbondingTxStakerSignature,
53
53
  initBTCCurve: () => initBTCCurve,
54
+ isNativeSegwit: () => isNativeSegwit,
54
55
  isTaproot: () => isTaproot,
55
56
  isValidBabylonAddress: () => isValidBabylonAddress,
56
57
  isValidBitcoinAddress: () => isValidBitcoinAddress,
@@ -118,13 +119,13 @@ var StakingScriptData = class {
118
119
  if (allPks.length !== allPksSet.size) {
119
120
  return false;
120
121
  }
121
- if (this.covenantThreshold == 0 || this.covenantThreshold > this.covenantKeys.length) {
122
+ if (this.covenantThreshold <= 0 || this.covenantThreshold > this.covenantKeys.length) {
122
123
  return false;
123
124
  }
124
- if (this.stakingTimeLock == 0 || this.stakingTimeLock > 65535) {
125
+ if (this.stakingTimeLock <= 0 || this.stakingTimeLock > 65535) {
125
126
  return false;
126
127
  }
127
- if (this.unbondingTimeLock == 0 || this.unbondingTimeLock > 65535) {
128
+ if (this.unbondingTimeLock <= 0 || this.unbondingTimeLock > 65535) {
128
129
  return false;
129
130
  }
130
131
  return true;
@@ -345,14 +346,28 @@ var isTaproot = (taprootAddress, network) => {
345
346
  if (decoded.version !== 1) {
346
347
  return false;
347
348
  }
348
- switch (network) {
349
- case import_bitcoinjs_lib2.networks.bitcoin:
350
- return taprootAddress.startsWith("bc1p");
351
- case import_bitcoinjs_lib2.networks.testnet:
352
- return taprootAddress.startsWith("tb1p") || taprootAddress.startsWith("sb1p");
353
- default:
354
- return false;
349
+ if (network.bech32 === import_bitcoinjs_lib2.networks.bitcoin.bech32) {
350
+ return taprootAddress.startsWith("bc1p");
351
+ } else if (network.bech32 === import_bitcoinjs_lib2.networks.testnet.bech32) {
352
+ return taprootAddress.startsWith("tb1p") || taprootAddress.startsWith("sb1p");
355
353
  }
354
+ return false;
355
+ } catch (error) {
356
+ return false;
357
+ }
358
+ };
359
+ var isNativeSegwit = (segwitAddress, network) => {
360
+ try {
361
+ const decoded = import_bitcoinjs_lib2.address.fromBech32(segwitAddress);
362
+ if (decoded.version !== 0) {
363
+ return false;
364
+ }
365
+ if (network.bech32 === import_bitcoinjs_lib2.networks.bitcoin.bech32) {
366
+ return segwitAddress.startsWith("bc1q");
367
+ } else if (network.bech32 === import_bitcoinjs_lib2.networks.testnet.bech32) {
368
+ return segwitAddress.startsWith("tb1q");
369
+ }
370
+ return false;
356
371
  } catch (error) {
357
372
  return false;
358
373
  }
@@ -747,6 +762,7 @@ var REDEEM_VERSION = 192;
747
762
 
748
763
  // src/staking/transactions.ts
749
764
  var BTC_LOCKTIME_HEIGHT_TIME_CUTOFF = 5e8;
765
+ var BTC_SLASHING_FRACTION_DIGITS = 4;
750
766
  function stakingTransaction(scripts, amount, changeAddress, inputUTXOs, network, feeRate, lockHeight) {
751
767
  if (amount <= 0 || feeRate <= 0) {
752
768
  throw new Error("Amount and fee rate must be bigger than 0");
@@ -960,7 +976,7 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
960
976
  if (slashingRate <= 0 || slashingRate >= 1) {
961
977
  throw new Error("Slashing rate must be between 0 and 1");
962
978
  }
963
- slashingRate = parseFloat(slashingRate.toFixed(2));
979
+ slashingRate = parseFloat(slashingRate.toFixed(BTC_SLASHING_FRACTION_DIGITS));
964
980
  if (minimumFee <= 0 || !Number.isInteger(minimumFee)) {
965
981
  throw new Error("Minimum fee must be a positve integer");
966
982
  }
@@ -986,9 +1002,12 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
986
1002
  controlBlock: p2tr.witness[p2tr.witness.length - 1]
987
1003
  };
988
1004
  const stakingAmount = transaction.outs[outputIndex].value;
989
- const slashingAmount = Math.floor(stakingAmount * slashingRate);
990
- if (slashingAmount <= BTC_DUST_SAT) {
991
- throw new Error("Slashing amount is less than dust limit");
1005
+ const slashingAmount = Math.round(stakingAmount * slashingRate);
1006
+ const slashingOutput = Buffer.from(slashingPkScriptHex, "hex");
1007
+ if (import_bitcoinjs_lib6.opcodes.OP_RETURN != slashingOutput[0]) {
1008
+ if (slashingAmount <= BTC_DUST_SAT) {
1009
+ throw new Error("Slashing amount is less than dust limit");
1010
+ }
992
1011
  }
993
1012
  const userFunds = stakingAmount - slashingAmount - minimumFee;
994
1013
  if (userFunds <= BTC_DUST_SAT) {
@@ -1009,7 +1028,7 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
1009
1028
  sequence: NON_RBF_SEQUENCE
1010
1029
  });
1011
1030
  psbt.addOutput({
1012
- script: Buffer.from(slashingPkScriptHex, "hex"),
1031
+ script: slashingOutput,
1013
1032
  value: slashingAmount
1014
1033
  });
1015
1034
  const changeOutput = import_bitcoinjs_lib6.payments.p2tr({
@@ -1874,7 +1893,6 @@ var getBabylonParamByVersion = (version, babylonParams) => {
1874
1893
 
1875
1894
  // src/staking/manager.ts
1876
1895
  var import_bitcoinjs_lib10 = require("bitcoinjs-lib");
1877
- var import_encoding2 = require("@cosmjs/encoding");
1878
1896
  var import_babylon_proto_ts = require("@babylonlabs-io/babylon-proto-ts");
1879
1897
  var import_pop = require("@babylonlabs-io/babylon-proto-ts/dist/generated/babylon/btcstaking/v1/pop");
1880
1898
 
@@ -1895,9 +1913,6 @@ var reverseBuffer = (buffer) => {
1895
1913
  }
1896
1914
  return clonedBuffer;
1897
1915
  };
1898
- var uint8ArrayToHex = (uint8Array) => {
1899
- return Array.from(uint8Array).map((byte) => byte.toString(16).padStart(2, "0")).join("");
1900
- };
1901
1916
 
1902
1917
  // src/staking/manager.ts
1903
1918
  var SigningStep = /* @__PURE__ */ ((SigningStep2) => {
@@ -1931,7 +1946,9 @@ var BabylonBtcStakingManager = class {
1931
1946
  * @param babylonBtcTipHeight - The Babylon BTC tip height.
1932
1947
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
1933
1948
  * transaction.
1934
- * @param feeRate - The fee rate in satoshis per byte.
1949
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
1950
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
1951
+ * be included in a block.
1935
1952
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
1936
1953
  * @returns The signed babylon pre-staking registration transaction in base64
1937
1954
  * format.
@@ -1989,7 +2006,8 @@ var BabylonBtcStakingManager = class {
1989
2006
  * @param stakingTxHeight - The BTC height in which the staking transaction
1990
2007
  * is included.
1991
2008
  * @param stakingInput - The staking inputs.
1992
- * @param inclusionProof - The inclusion proof of the staking transaction.
2009
+ * @param inclusionProof - Merkle Proof of Inclusion: Verifies transaction
2010
+ * inclusion in a Bitcoin block that is k-deep.
1993
2011
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
1994
2012
  * @returns The signed babylon transaction in base64 format.
1995
2013
  */
@@ -2037,7 +2055,9 @@ var BabylonBtcStakingManager = class {
2037
2055
  * @param stakingInput - The staking inputs.
2038
2056
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
2039
2057
  * transaction.
2040
- * @param feeRate - The fee rate in satoshis per byte.
2058
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2059
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2060
+ * be included in a block.
2041
2061
  * @returns The estimated BTC fee in satoshis.
2042
2062
  */
2043
2063
  estimateBtcStakingFee(stakerBtcInfo, babylonBtcTipHeight, stakingInput, inputUTXOs, feeRate) {
@@ -2198,7 +2218,9 @@ var BabylonBtcStakingManager = class {
2198
2218
  * @param stakingParamsVersion - The params version that was used to create the
2199
2219
  * delegation in Babylon chain
2200
2220
  * @param earlyUnbondingTx - The early unbonding transaction.
2201
- * @param feeRate - The fee rate in satoshis per byte.
2221
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2222
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2223
+ * be included in a block.
2202
2224
  * @returns The signed withdrawal transaction and its fee.
2203
2225
  */
2204
2226
  async createSignedBtcWithdrawEarlyUnbondedTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, earlyUnbondingTx, feeRate) {
@@ -2235,7 +2257,9 @@ var BabylonBtcStakingManager = class {
2235
2257
  * @param stakingParamsVersion - The params version that was used to create the
2236
2258
  * delegation in Babylon chain
2237
2259
  * @param stakingTx - The staking transaction.
2238
- * @param feeRate - The fee rate in satoshis per byte.
2260
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2261
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2262
+ * be included in a block.
2239
2263
  * @returns The signed withdrawal transaction and its fee.
2240
2264
  */
2241
2265
  async createSignedBtcWithdrawStakingExpiredTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, stakingTx, feeRate) {
@@ -2272,7 +2296,9 @@ var BabylonBtcStakingManager = class {
2272
2296
  * @param stakingParamsVersion - The params version that was used to create the
2273
2297
  * delegation in Babylon chain
2274
2298
  * @param slashingTx - The slashing transaction.
2275
- * @param feeRate - The fee rate in satoshis per byte.
2299
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2300
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2301
+ * be included in a block.
2276
2302
  * @returns The signed withdrawal transaction and its fee.
2277
2303
  */
2278
2304
  async createSignedBtcWithdrawSlashingTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, slashingTx, feeRate) {
@@ -2305,20 +2331,29 @@ var BabylonBtcStakingManager = class {
2305
2331
  * @param bech32Address - The staker's bech32 address.
2306
2332
  * @returns The proof of possession.
2307
2333
  */
2308
- async createProofOfPossession(bech32Address) {
2309
- if (!this.btcProvider.signMessage) {
2310
- throw new Error("Sign message function not found");
2334
+ async createProofOfPossession(bech32Address, stakerBtcAddress) {
2335
+ let sigType = import_pop.BTCSigType.ECDSA;
2336
+ if (isTaproot(stakerBtcAddress, this.network) || isNativeSegwit(stakerBtcAddress, this.network)) {
2337
+ sigType = import_pop.BTCSigType.BIP322;
2311
2338
  }
2312
- const bech32AddressHex = uint8ArrayToHex((0, import_encoding2.fromBech32)(bech32Address).data);
2313
2339
  const signedBabylonAddress = await this.btcProvider.signMessage(
2314
2340
  "proof-of-possession" /* PROOF_OF_POSSESSION */,
2315
- bech32AddressHex,
2316
- "ecdsa"
2317
- );
2318
- const ecdsaSig = Uint8Array.from(Buffer.from(signedBabylonAddress, "base64"));
2341
+ bech32Address,
2342
+ sigType === import_pop.BTCSigType.BIP322 ? "bip322-simple" : "ecdsa"
2343
+ );
2344
+ let btcSig;
2345
+ if (sigType === import_pop.BTCSigType.BIP322) {
2346
+ const bip322Sig = import_pop.BIP322Sig.fromPartial({
2347
+ address: stakerBtcAddress,
2348
+ sig: Buffer.from(signedBabylonAddress, "base64")
2349
+ });
2350
+ btcSig = import_pop.BIP322Sig.encode(bip322Sig).finish();
2351
+ } else {
2352
+ btcSig = Buffer.from(signedBabylonAddress, "base64");
2353
+ }
2319
2354
  return {
2320
- btcSigType: import_pop.BTCSigType.ECDSA,
2321
- btcSig: ecdsaSig
2355
+ btcSigType: sigType,
2356
+ btcSig
2322
2357
  };
2323
2358
  }
2324
2359
  /**
@@ -2386,7 +2421,10 @@ var BabylonBtcStakingManager = class {
2386
2421
  if (!unbondingSignatures) {
2387
2422
  throw new Error("No signature found in the unbonding output slashing PSBT");
2388
2423
  }
2389
- const proofOfPossession = await this.createProofOfPossession(bech32Address);
2424
+ const proofOfPossession = await this.createProofOfPossession(
2425
+ bech32Address,
2426
+ stakerBtcInfo.address
2427
+ );
2390
2428
  const msg = import_babylon_proto_ts.btcstakingtx.MsgCreateBTCDelegation.fromPartial({
2391
2429
  stakerAddr: bech32Address,
2392
2430
  pop: proofOfPossession,
@@ -2504,6 +2542,7 @@ var getUnbondingTxStakerSignature = (unbondingTx) => {
2504
2542
  getScriptType,
2505
2543
  getUnbondingTxStakerSignature,
2506
2544
  initBTCCurve,
2545
+ isNativeSegwit,
2507
2546
  isTaproot,
2508
2547
  isValidBabylonAddress,
2509
2548
  isValidBitcoinAddress,
package/dist/index.d.cts CHANGED
@@ -548,6 +548,14 @@ export declare const isValidBitcoinAddress: (btcAddress: string, network: networ
548
548
  * @returns {boolean} - True if the address is a Taproot address, otherwise false.
549
549
  */
550
550
  export declare const isTaproot: (taprootAddress: string, network: networks.Network) => boolean;
551
+ /**
552
+ * Check whether the given address is a Native SegWit address.
553
+ *
554
+ * @param {string} segwitAddress - The Bitcoin bech32 encoded address to check.
555
+ * @param {object} network - The Bitcoin network (e.g., bitcoin.networks.bitcoin).
556
+ * @returns {boolean} - True if the address is a Native SegWit address, otherwise false.
557
+ */
558
+ export declare const isNativeSegwit: (segwitAddress: string, network: networks.Network) => boolean;
551
559
  /**
552
560
  * Check whether the given public key is a valid public key without a coordinate.
553
561
  *
@@ -730,9 +738,20 @@ export declare const getBabylonParamByBtcHeight: (height: number, babylonParamsV
730
738
  export declare const getBabylonParamByVersion: (version: number, babylonParams: VersionedStakingParams[]) => StakingParams;
731
739
  export interface BtcProvider {
732
740
  signPsbt(signingStep: SigningStep, psbtHex: string): Promise<string>;
733
- signMessage?: (signingStep: SigningStep, message: string, type: "ecdsa") => Promise<string>;
741
+ signMessage: (signingStep: SigningStep, message: string, type: "ecdsa" | "bip322-simple") => Promise<string>;
734
742
  }
735
743
  export interface BabylonProvider {
744
+ /**
745
+ * Signs a Babylon chain transaction using the provided signing step.
746
+ * This is primarily used for signing MsgCreateBTCDelegation transactions
747
+ * which register the BTC delegation on the Babylon Genesis chain.
748
+ *
749
+ * @param {SigningStep} signingStep - The current signing step context
750
+ * @param {object} msg - The Cosmos SDK transaction message to sign
751
+ * @param {string} msg.typeUrl - The Protobuf type URL identifying the message type
752
+ * @param {T} msg.value - The transaction message data matching the typeUrl
753
+ * @returns {Promise<Uint8Array>} The signed transaction bytes
754
+ */
736
755
  signTransaction: <T extends object>(signingStep: SigningStep, msg: {
737
756
  typeUrl: string;
738
757
  value: T;
@@ -774,7 +793,9 @@ export declare class BabylonBtcStakingManager {
774
793
  * @param babylonBtcTipHeight - The Babylon BTC tip height.
775
794
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
776
795
  * transaction.
777
- * @param feeRate - The fee rate in satoshis per byte.
796
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
797
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
798
+ * be included in a block.
778
799
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
779
800
  * @returns The signed babylon pre-staking registration transaction in base64
780
801
  * format.
@@ -794,7 +815,8 @@ export declare class BabylonBtcStakingManager {
794
815
  * @param stakingTxHeight - The BTC height in which the staking transaction
795
816
  * is included.
796
817
  * @param stakingInput - The staking inputs.
797
- * @param inclusionProof - The inclusion proof of the staking transaction.
818
+ * @param inclusionProof - Merkle Proof of Inclusion: Verifies transaction
819
+ * inclusion in a Bitcoin block that is k-deep.
798
820
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
799
821
  * @returns The signed babylon transaction in base64 format.
800
822
  */
@@ -810,7 +832,9 @@ export declare class BabylonBtcStakingManager {
810
832
  * @param stakingInput - The staking inputs.
811
833
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
812
834
  * transaction.
813
- * @param feeRate - The fee rate in satoshis per byte.
835
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
836
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
837
+ * be included in a block.
814
838
  * @returns The estimated BTC fee in satoshis.
815
839
  */
816
840
  estimateBtcStakingFee(stakerBtcInfo: StakerInfo, babylonBtcTipHeight: number, stakingInput: StakingInputs, inputUTXOs: UTXO[], feeRate: number): number;
@@ -868,7 +892,9 @@ export declare class BabylonBtcStakingManager {
868
892
  * @param stakingParamsVersion - The params version that was used to create the
869
893
  * delegation in Babylon chain
870
894
  * @param earlyUnbondingTx - The early unbonding transaction.
871
- * @param feeRate - The fee rate in satoshis per byte.
895
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
896
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
897
+ * be included in a block.
872
898
  * @returns The signed withdrawal transaction and its fee.
873
899
  */
874
900
  createSignedBtcWithdrawEarlyUnbondedTransaction(stakerBtcInfo: StakerInfo, stakingInput: StakingInputs, stakingParamsVersion: number, earlyUnbondingTx: Transaction, feeRate: number): Promise<TransactionResult>;
@@ -881,7 +907,9 @@ export declare class BabylonBtcStakingManager {
881
907
  * @param stakingParamsVersion - The params version that was used to create the
882
908
  * delegation in Babylon chain
883
909
  * @param stakingTx - The staking transaction.
884
- * @param feeRate - The fee rate in satoshis per byte.
910
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
911
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
912
+ * be included in a block.
885
913
  * @returns The signed withdrawal transaction and its fee.
886
914
  */
887
915
  createSignedBtcWithdrawStakingExpiredTransaction(stakerBtcInfo: StakerInfo, stakingInput: StakingInputs, stakingParamsVersion: number, stakingTx: Transaction, feeRate: number): Promise<TransactionResult>;
@@ -894,7 +922,9 @@ export declare class BabylonBtcStakingManager {
894
922
  * @param stakingParamsVersion - The params version that was used to create the
895
923
  * delegation in Babylon chain
896
924
  * @param slashingTx - The slashing transaction.
897
- * @param feeRate - The fee rate in satoshis per byte.
925
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
926
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
927
+ * be included in a block.
898
928
  * @returns The signed withdrawal transaction and its fee.
899
929
  */
900
930
  createSignedBtcWithdrawSlashingTransaction(stakerBtcInfo: StakerInfo, stakingInput: StakingInputs, stakingParamsVersion: number, slashingTx: Transaction, feeRate: number): Promise<TransactionResult>;
@@ -903,7 +933,7 @@ export declare class BabylonBtcStakingManager {
903
933
  * @param bech32Address - The staker's bech32 address.
904
934
  * @returns The proof of possession.
905
935
  */
906
- createProofOfPossession(bech32Address: string): Promise<ProofOfPossessionBTC>;
936
+ createProofOfPossession(bech32Address: string, stakerBtcAddress: string): Promise<ProofOfPossessionBTC>;
907
937
  /**
908
938
  * Creates the unbonding, slashing, and unbonding slashing transactions and
909
939
  * PSBTs.
package/dist/index.js CHANGED
@@ -46,13 +46,13 @@ var StakingScriptData = class {
46
46
  if (allPks.length !== allPksSet.size) {
47
47
  return false;
48
48
  }
49
- if (this.covenantThreshold == 0 || this.covenantThreshold > this.covenantKeys.length) {
49
+ if (this.covenantThreshold <= 0 || this.covenantThreshold > this.covenantKeys.length) {
50
50
  return false;
51
51
  }
52
- if (this.stakingTimeLock == 0 || this.stakingTimeLock > 65535) {
52
+ if (this.stakingTimeLock <= 0 || this.stakingTimeLock > 65535) {
53
53
  return false;
54
54
  }
55
- if (this.unbondingTimeLock == 0 || this.unbondingTimeLock > 65535) {
55
+ if (this.unbondingTimeLock <= 0 || this.unbondingTimeLock > 65535) {
56
56
  return false;
57
57
  }
58
58
  return true;
@@ -245,7 +245,7 @@ var StakingError = class _StakingError extends Error {
245
245
  };
246
246
 
247
247
  // src/staking/transactions.ts
248
- import { Psbt, Transaction as Transaction2, payments as payments3, script as script2, address as address3 } from "bitcoinjs-lib";
248
+ import { Psbt, Transaction as Transaction2, payments as payments3, script as script2, address as address3, opcodes as opcodes3 } from "bitcoinjs-lib";
249
249
 
250
250
  // src/constants/dustSat.ts
251
251
  var BTC_DUST_SAT = 546;
@@ -273,14 +273,28 @@ var isTaproot = (taprootAddress, network) => {
273
273
  if (decoded.version !== 1) {
274
274
  return false;
275
275
  }
276
- switch (network) {
277
- case networks.bitcoin:
278
- return taprootAddress.startsWith("bc1p");
279
- case networks.testnet:
280
- return taprootAddress.startsWith("tb1p") || taprootAddress.startsWith("sb1p");
281
- default:
282
- return false;
276
+ if (network.bech32 === networks.bitcoin.bech32) {
277
+ return taprootAddress.startsWith("bc1p");
278
+ } else if (network.bech32 === networks.testnet.bech32) {
279
+ return taprootAddress.startsWith("tb1p") || taprootAddress.startsWith("sb1p");
283
280
  }
281
+ return false;
282
+ } catch (error) {
283
+ return false;
284
+ }
285
+ };
286
+ var isNativeSegwit = (segwitAddress, network) => {
287
+ try {
288
+ const decoded = address.fromBech32(segwitAddress);
289
+ if (decoded.version !== 0) {
290
+ return false;
291
+ }
292
+ if (network.bech32 === networks.bitcoin.bech32) {
293
+ return segwitAddress.startsWith("bc1q");
294
+ } else if (network.bech32 === networks.testnet.bech32) {
295
+ return segwitAddress.startsWith("tb1q");
296
+ }
297
+ return false;
284
298
  } catch (error) {
285
299
  return false;
286
300
  }
@@ -675,6 +689,7 @@ var REDEEM_VERSION = 192;
675
689
 
676
690
  // src/staking/transactions.ts
677
691
  var BTC_LOCKTIME_HEIGHT_TIME_CUTOFF = 5e8;
692
+ var BTC_SLASHING_FRACTION_DIGITS = 4;
678
693
  function stakingTransaction(scripts, amount, changeAddress, inputUTXOs, network, feeRate, lockHeight) {
679
694
  if (amount <= 0 || feeRate <= 0) {
680
695
  throw new Error("Amount and fee rate must be bigger than 0");
@@ -888,7 +903,7 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
888
903
  if (slashingRate <= 0 || slashingRate >= 1) {
889
904
  throw new Error("Slashing rate must be between 0 and 1");
890
905
  }
891
- slashingRate = parseFloat(slashingRate.toFixed(2));
906
+ slashingRate = parseFloat(slashingRate.toFixed(BTC_SLASHING_FRACTION_DIGITS));
892
907
  if (minimumFee <= 0 || !Number.isInteger(minimumFee)) {
893
908
  throw new Error("Minimum fee must be a positve integer");
894
909
  }
@@ -914,9 +929,12 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
914
929
  controlBlock: p2tr.witness[p2tr.witness.length - 1]
915
930
  };
916
931
  const stakingAmount = transaction.outs[outputIndex].value;
917
- const slashingAmount = Math.floor(stakingAmount * slashingRate);
918
- if (slashingAmount <= BTC_DUST_SAT) {
919
- throw new Error("Slashing amount is less than dust limit");
932
+ const slashingAmount = Math.round(stakingAmount * slashingRate);
933
+ const slashingOutput = Buffer.from(slashingPkScriptHex, "hex");
934
+ if (opcodes3.OP_RETURN != slashingOutput[0]) {
935
+ if (slashingAmount <= BTC_DUST_SAT) {
936
+ throw new Error("Slashing amount is less than dust limit");
937
+ }
920
938
  }
921
939
  const userFunds = stakingAmount - slashingAmount - minimumFee;
922
940
  if (userFunds <= BTC_DUST_SAT) {
@@ -937,7 +955,7 @@ function slashingTransaction(scripts, scriptTree, transaction, slashingPkScriptH
937
955
  sequence: NON_RBF_SEQUENCE
938
956
  });
939
957
  psbt.addOutput({
940
- script: Buffer.from(slashingPkScriptHex, "hex"),
958
+ script: slashingOutput,
941
959
  value: slashingAmount
942
960
  });
943
961
  const changeOutput = payments3.p2tr({
@@ -1575,7 +1593,7 @@ var Staking = class {
1575
1593
  };
1576
1594
 
1577
1595
  // src/staking/observable/observableStakingScript.ts
1578
- import { opcodes as opcodes3, script as script3 } from "bitcoinjs-lib";
1596
+ import { opcodes as opcodes4, script as script3 } from "bitcoinjs-lib";
1579
1597
  var ObservableStakingScriptData = class extends StakingScriptData {
1580
1598
  constructor(stakerKey, finalityProviderKeys, covenantKeys, covenantThreshold, stakingTimelock, unbondingTimelock, magicBytes) {
1581
1599
  super(
@@ -1618,7 +1636,7 @@ var ObservableStakingScriptData = class extends StakingScriptData {
1618
1636
  this.finalityProviderKeys[0],
1619
1637
  stakingTimeLock
1620
1638
  ]);
1621
- return script3.compile([opcodes3.OP_RETURN, serializedStakingData]);
1639
+ return script3.compile([opcodes4.OP_RETURN, serializedStakingData]);
1622
1640
  }
1623
1641
  /**
1624
1642
  * Builds the staking scripts.
@@ -1802,13 +1820,13 @@ var getBabylonParamByVersion = (version, babylonParams) => {
1802
1820
 
1803
1821
  // src/staking/manager.ts
1804
1822
  import { Psbt as Psbt3 } from "bitcoinjs-lib";
1805
- import { fromBech32 as fromBech322 } from "@cosmjs/encoding";
1806
1823
  import {
1807
1824
  btccheckpoint,
1808
1825
  btcstaking,
1809
1826
  btcstakingtx
1810
1827
  } from "@babylonlabs-io/babylon-proto-ts";
1811
1828
  import {
1829
+ BIP322Sig,
1812
1830
  BTCSigType
1813
1831
  } from "@babylonlabs-io/babylon-proto-ts/dist/generated/babylon/btcstaking/v1/pop";
1814
1832
 
@@ -1829,9 +1847,6 @@ var reverseBuffer = (buffer) => {
1829
1847
  }
1830
1848
  return clonedBuffer;
1831
1849
  };
1832
- var uint8ArrayToHex = (uint8Array) => {
1833
- return Array.from(uint8Array).map((byte) => byte.toString(16).padStart(2, "0")).join("");
1834
- };
1835
1850
 
1836
1851
  // src/staking/manager.ts
1837
1852
  var SigningStep = /* @__PURE__ */ ((SigningStep2) => {
@@ -1865,7 +1880,9 @@ var BabylonBtcStakingManager = class {
1865
1880
  * @param babylonBtcTipHeight - The Babylon BTC tip height.
1866
1881
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
1867
1882
  * transaction.
1868
- * @param feeRate - The fee rate in satoshis per byte.
1883
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
1884
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
1885
+ * be included in a block.
1869
1886
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
1870
1887
  * @returns The signed babylon pre-staking registration transaction in base64
1871
1888
  * format.
@@ -1923,7 +1940,8 @@ var BabylonBtcStakingManager = class {
1923
1940
  * @param stakingTxHeight - The BTC height in which the staking transaction
1924
1941
  * is included.
1925
1942
  * @param stakingInput - The staking inputs.
1926
- * @param inclusionProof - The inclusion proof of the staking transaction.
1943
+ * @param inclusionProof - Merkle Proof of Inclusion: Verifies transaction
1944
+ * inclusion in a Bitcoin block that is k-deep.
1927
1945
  * @param babylonAddress - The Babylon bech32 encoded address of the staker.
1928
1946
  * @returns The signed babylon transaction in base64 format.
1929
1947
  */
@@ -1971,7 +1989,9 @@ var BabylonBtcStakingManager = class {
1971
1989
  * @param stakingInput - The staking inputs.
1972
1990
  * @param inputUTXOs - The UTXOs that will be used to pay for the staking
1973
1991
  * transaction.
1974
- * @param feeRate - The fee rate in satoshis per byte.
1992
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
1993
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
1994
+ * be included in a block.
1975
1995
  * @returns The estimated BTC fee in satoshis.
1976
1996
  */
1977
1997
  estimateBtcStakingFee(stakerBtcInfo, babylonBtcTipHeight, stakingInput, inputUTXOs, feeRate) {
@@ -2132,7 +2152,9 @@ var BabylonBtcStakingManager = class {
2132
2152
  * @param stakingParamsVersion - The params version that was used to create the
2133
2153
  * delegation in Babylon chain
2134
2154
  * @param earlyUnbondingTx - The early unbonding transaction.
2135
- * @param feeRate - The fee rate in satoshis per byte.
2155
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2156
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2157
+ * be included in a block.
2136
2158
  * @returns The signed withdrawal transaction and its fee.
2137
2159
  */
2138
2160
  async createSignedBtcWithdrawEarlyUnbondedTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, earlyUnbondingTx, feeRate) {
@@ -2169,7 +2191,9 @@ var BabylonBtcStakingManager = class {
2169
2191
  * @param stakingParamsVersion - The params version that was used to create the
2170
2192
  * delegation in Babylon chain
2171
2193
  * @param stakingTx - The staking transaction.
2172
- * @param feeRate - The fee rate in satoshis per byte.
2194
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2195
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2196
+ * be included in a block.
2173
2197
  * @returns The signed withdrawal transaction and its fee.
2174
2198
  */
2175
2199
  async createSignedBtcWithdrawStakingExpiredTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, stakingTx, feeRate) {
@@ -2206,7 +2230,9 @@ var BabylonBtcStakingManager = class {
2206
2230
  * @param stakingParamsVersion - The params version that was used to create the
2207
2231
  * delegation in Babylon chain
2208
2232
  * @param slashingTx - The slashing transaction.
2209
- * @param feeRate - The fee rate in satoshis per byte.
2233
+ * @param feeRate - The fee rate in satoshis per byte. Typical value for the
2234
+ * fee rate is above 1. If the fee rate is too low, the transaction will not
2235
+ * be included in a block.
2210
2236
  * @returns The signed withdrawal transaction and its fee.
2211
2237
  */
2212
2238
  async createSignedBtcWithdrawSlashingTransaction(stakerBtcInfo, stakingInput, stakingParamsVersion, slashingTx, feeRate) {
@@ -2239,20 +2265,29 @@ var BabylonBtcStakingManager = class {
2239
2265
  * @param bech32Address - The staker's bech32 address.
2240
2266
  * @returns The proof of possession.
2241
2267
  */
2242
- async createProofOfPossession(bech32Address) {
2243
- if (!this.btcProvider.signMessage) {
2244
- throw new Error("Sign message function not found");
2268
+ async createProofOfPossession(bech32Address, stakerBtcAddress) {
2269
+ let sigType = BTCSigType.ECDSA;
2270
+ if (isTaproot(stakerBtcAddress, this.network) || isNativeSegwit(stakerBtcAddress, this.network)) {
2271
+ sigType = BTCSigType.BIP322;
2245
2272
  }
2246
- const bech32AddressHex = uint8ArrayToHex(fromBech322(bech32Address).data);
2247
2273
  const signedBabylonAddress = await this.btcProvider.signMessage(
2248
2274
  "proof-of-possession" /* PROOF_OF_POSSESSION */,
2249
- bech32AddressHex,
2250
- "ecdsa"
2251
- );
2252
- const ecdsaSig = Uint8Array.from(Buffer.from(signedBabylonAddress, "base64"));
2275
+ bech32Address,
2276
+ sigType === BTCSigType.BIP322 ? "bip322-simple" : "ecdsa"
2277
+ );
2278
+ let btcSig;
2279
+ if (sigType === BTCSigType.BIP322) {
2280
+ const bip322Sig = BIP322Sig.fromPartial({
2281
+ address: stakerBtcAddress,
2282
+ sig: Buffer.from(signedBabylonAddress, "base64")
2283
+ });
2284
+ btcSig = BIP322Sig.encode(bip322Sig).finish();
2285
+ } else {
2286
+ btcSig = Buffer.from(signedBabylonAddress, "base64");
2287
+ }
2253
2288
  return {
2254
- btcSigType: BTCSigType.ECDSA,
2255
- btcSig: ecdsaSig
2289
+ btcSigType: sigType,
2290
+ btcSig
2256
2291
  };
2257
2292
  }
2258
2293
  /**
@@ -2320,7 +2355,10 @@ var BabylonBtcStakingManager = class {
2320
2355
  if (!unbondingSignatures) {
2321
2356
  throw new Error("No signature found in the unbonding output slashing PSBT");
2322
2357
  }
2323
- const proofOfPossession = await this.createProofOfPossession(bech32Address);
2358
+ const proofOfPossession = await this.createProofOfPossession(
2359
+ bech32Address,
2360
+ stakerBtcInfo.address
2361
+ );
2324
2362
  const msg = btcstakingtx.MsgCreateBTCDelegation.fromPartial({
2325
2363
  stakerAddr: bech32Address,
2326
2364
  pop: proofOfPossession,
@@ -2437,6 +2475,7 @@ export {
2437
2475
  getScriptType,
2438
2476
  getUnbondingTxStakerSignature,
2439
2477
  initBTCCurve,
2478
+ isNativeSegwit,
2440
2479
  isTaproot,
2441
2480
  isValidBabylonAddress,
2442
2481
  isValidBitcoinAddress,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitgo-beta/babylonlabs-io-btc-staking-ts",
3
- "version": "0.4.0-beta.85",
3
+ "version": "0.4.0-beta.87",
4
4
  "description": "Library exposing methods for the creation and consumption of Bitcoin transactions pertaining to Babylon's Bitcoin Staking protocol.",
5
5
  "module": "dist/index.js",
6
6
  "main": "dist/index.cjs",
@@ -45,5 +45,5 @@
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
- "gitHead": "10b15886c8a898a492fda91363dc8f5870756f5b"
48
+ "gitHead": "14020d4881c54dbddf4e27de283d0e35ea016045"
49
49
  }