@teleportdao/bitcoin 1.6.0 → 1.7.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.
Files changed (129) hide show
  1. package/.tmp/psbt/sign-transaction.ts +9 -3
  2. package/dist/bitcoin-interface-ordinal.d.ts +104 -0
  3. package/dist/bitcoin-interface-ordinal.d.ts.map +1 -0
  4. package/dist/bitcoin-interface-ordinal.js +113 -0
  5. package/dist/bitcoin-interface-ordinal.js.map +1 -0
  6. package/dist/bitcoin-interface-teleswap.d.ts +148 -0
  7. package/dist/bitcoin-interface-teleswap.d.ts.map +1 -0
  8. package/dist/bitcoin-interface-teleswap.js +179 -0
  9. package/dist/bitcoin-interface-teleswap.js.map +1 -0
  10. package/dist/bitcoin-interface.d.ts +45 -333
  11. package/dist/bitcoin-interface.d.ts.map +1 -1
  12. package/dist/bitcoin-interface.js +68 -202
  13. package/dist/bitcoin-interface.js.map +1 -1
  14. package/dist/bitcoin-utils.d.ts +6 -53
  15. package/dist/bitcoin-utils.d.ts.map +1 -1
  16. package/dist/bitcoin-utils.js +4 -4
  17. package/dist/bitcoin-utils.js.map +1 -1
  18. package/dist/{bitcoin-base.d.ts → bitcoin-wallet-base.d.ts} +8 -8
  19. package/dist/bitcoin-wallet-base.d.ts.map +1 -0
  20. package/dist/{bitcoin-base.js → bitcoin-wallet-base.js} +12 -11
  21. package/dist/bitcoin-wallet-base.js.map +1 -0
  22. package/dist/helper/brc20-helper.d.ts +43 -0
  23. package/dist/helper/brc20-helper.d.ts.map +1 -0
  24. package/dist/helper/brc20-helper.js +129 -0
  25. package/dist/helper/brc20-helper.js.map +1 -0
  26. package/dist/helper/index.d.ts +4 -0
  27. package/dist/helper/index.d.ts.map +1 -0
  28. package/dist/helper/index.js +30 -0
  29. package/dist/helper/index.js.map +1 -0
  30. package/dist/helper/ordinal-helper.d.ts +13 -0
  31. package/dist/helper/ordinal-helper.d.ts.map +1 -0
  32. package/dist/helper/ordinal-helper.js +127 -0
  33. package/dist/helper/ordinal-helper.js.map +1 -0
  34. package/dist/helper/teleswap-helper.d.ts +84 -0
  35. package/dist/helper/teleswap-helper.d.ts.map +1 -0
  36. package/dist/helper/teleswap-helper.js +181 -0
  37. package/dist/helper/teleswap-helper.js.map +1 -0
  38. package/dist/index.d.ts +10 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +10 -3
  41. package/dist/index.js.map +1 -1
  42. package/dist/ordinal-wallet.d.ts +536 -0
  43. package/dist/ordinal-wallet.d.ts.map +1 -0
  44. package/dist/ordinal-wallet.js +446 -0
  45. package/dist/ordinal-wallet.js.map +1 -0
  46. package/dist/sign/index.d.ts +2 -0
  47. package/dist/sign/index.d.ts.map +1 -0
  48. package/dist/sign/index.js +9 -0
  49. package/dist/sign/index.js.map +1 -0
  50. package/dist/sign/sign-transaction.d.ts +2 -1
  51. package/dist/sign/sign-transaction.d.ts.map +1 -1
  52. package/dist/sign/sign-transaction.js +14 -33
  53. package/dist/sign/sign-transaction.js.map +1 -1
  54. package/dist/teleswap-wallet.d.ts +54 -0
  55. package/dist/teleswap-wallet.d.ts.map +1 -0
  56. package/dist/teleswap-wallet.js +87 -0
  57. package/dist/teleswap-wallet.js.map +1 -0
  58. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +3 -21
  59. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -1
  60. package/dist/transaction-builder/bitcoin-transaction-builder.js +7 -9
  61. package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -1
  62. package/dist/transaction-builder/index.d.ts +4 -0
  63. package/dist/transaction-builder/index.d.ts.map +1 -0
  64. package/dist/transaction-builder/index.js +20 -0
  65. package/dist/transaction-builder/index.js.map +1 -0
  66. package/dist/transaction-builder/ordinal-transaction-builder.d.ts +63 -0
  67. package/dist/transaction-builder/ordinal-transaction-builder.d.ts.map +1 -0
  68. package/dist/transaction-builder/ordinal-transaction-builder.js +131 -0
  69. package/dist/transaction-builder/ordinal-transaction-builder.js.map +1 -0
  70. package/dist/transaction-builder/transaction-builder.d.ts +32 -5
  71. package/dist/transaction-builder/transaction-builder.d.ts.map +1 -1
  72. package/dist/transaction-builder/transaction-builder.js +65 -49
  73. package/dist/transaction-builder/transaction-builder.js.map +1 -1
  74. package/dist/type.d.ts +43 -0
  75. package/dist/type.d.ts.map +1 -0
  76. package/dist/type.js +3 -0
  77. package/dist/type.js.map +1 -0
  78. package/dist/utils/tools.d.ts +4 -4
  79. package/dist/utils/tools.d.ts.map +1 -1
  80. package/dist/utils/tools.js +8 -5
  81. package/dist/utils/tools.js.map +1 -1
  82. package/package.json +6 -8
  83. package/src/bitcoin-interface-ordinal.ts +128 -0
  84. package/src/bitcoin-interface-teleswap.ts +255 -0
  85. package/src/bitcoin-interface.ts +99 -303
  86. package/src/bitcoin-utils.ts +6 -32
  87. package/src/{bitcoin-base.ts → bitcoin-wallet-base.ts} +20 -14
  88. package/src/helper/brc20-helper.ts +181 -0
  89. package/src/helper/index.ts +3 -0
  90. package/src/helper/ordinal-helper.ts +118 -0
  91. package/src/helper/teleswap-helper.ts +300 -0
  92. package/src/index.ts +13 -3
  93. package/src/ordinal-wallet.ts +738 -0
  94. package/src/sign/index.ts +1 -0
  95. package/src/sign/sign-transaction.ts +20 -9
  96. package/src/teleswap-wallet.ts +155 -0
  97. package/src/transaction-builder/bitcoin-transaction-builder.ts +8 -25
  98. package/src/transaction-builder/index.ts +3 -0
  99. package/src/transaction-builder/ordinal-transaction-builder.ts +147 -0
  100. package/src/transaction-builder/transaction-builder.ts +117 -60
  101. package/src/type.ts +43 -0
  102. package/src/utils/tools.ts +17 -11
  103. package/tsconfig.json +1 -2
  104. package/dist/bitcoin-base.d.ts.map +0 -1
  105. package/dist/bitcoin-base.js.map +0 -1
  106. package/dist/bitcoin-utils-2.d.ts +0 -2
  107. package/dist/bitcoin-utils-2.d.ts.map +0 -1
  108. package/dist/bitcoin-utils-2.js +0 -13
  109. package/dist/bitcoin-utils-2.js.map +0 -1
  110. package/dist/bundle.js +0 -17
  111. package/dist/helper/burn-request-helper.d.ts +0 -7
  112. package/dist/helper/burn-request-helper.d.ts.map +0 -1
  113. package/dist/helper/burn-request-helper.js +0 -26
  114. package/dist/helper/burn-request-helper.js.map +0 -1
  115. package/dist/helper/teleport-request-helper.d.ts +0 -47
  116. package/dist/helper/teleport-request-helper.d.ts.map +0 -1
  117. package/dist/helper/teleport-request-helper.js +0 -146
  118. package/dist/helper/teleport-request-helper.js.map +0 -1
  119. package/dist/mempool-space.d.ts +0 -69
  120. package/dist/mempool-space.d.ts.map +0 -1
  121. package/dist/mempool-space.js +0 -266
  122. package/dist/mempool-space.js.map +0 -1
  123. package/dist/teleport-dao-payments.d.ts +0 -76
  124. package/dist/teleport-dao-payments.d.ts.map +0 -1
  125. package/dist/teleport-dao-payments.js +0 -217
  126. package/dist/teleport-dao-payments.js.map +0 -1
  127. package/src/helper/burn-request-helper.js +0 -27
  128. package/src/helper/teleport-request-helper.js +0 -181
  129. package/src/teleport-dao-payments.ts +0 -347
@@ -0,0 +1 @@
1
+ export { default as BaseBitcoinSigner } from "./sign-transaction"
@@ -1,8 +1,9 @@
1
1
  import { Psbt, crypto, Network } from "bitcoinjs-lib"
2
2
  // import BIP32Factory from "bip32"
3
3
  import ecc from "@bitcoinerlab/secp256k1"
4
- import * as bitcoinEcPair from "bitcoinjs-ecpair"
5
- const ECPair = bitcoinEcPair.ECPair
4
+ import ECPairFactory from "ecpair"
5
+
6
+ const ECPair = ECPairFactory(ecc)
6
7
 
7
8
  function tapTweakHash(pubKey: Buffer, h?: Buffer) {
8
9
  return crypto.taggedHash("TapTweak", Buffer.concat(h ? [pubKey, h] : [pubKey]))
@@ -10,14 +11,14 @@ function tapTweakHash(pubKey: Buffer, h?: Buffer) {
10
11
 
11
12
  function tweakSigner(
12
13
  privateKey: Buffer,
14
+ network: Network,
13
15
  opts = {} as {
14
16
  [key: string]: Buffer
15
17
  },
16
- network: Network,
17
18
  ) {
18
19
  let newPrv = privateKey
19
20
  let keyPair = ECPair.fromPrivateKey(privateKey, {
20
- network: network,
21
+ network,
21
22
  compressed: true,
22
23
  })
23
24
 
@@ -36,12 +37,13 @@ function tweakSigner(
36
37
  }
37
38
 
38
39
  return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), {
39
- network: network,
40
+ network,
40
41
  })
41
42
  }
42
43
 
43
44
  class BitcoinLikeSignTransaction {
44
45
  network: Network
46
+
45
47
  constructor(network: Network) {
46
48
  this.network = network
47
49
  }
@@ -49,8 +51,11 @@ class BitcoinLikeSignTransaction {
49
51
  async signPsbt(
50
52
  unsignedPsbt: {
51
53
  unsignedTransaction: string
54
+ inputsToSign?: number[]
52
55
  },
53
56
  privateKey: Buffer,
57
+ sighashTypes?: any[],
58
+ usingTweakSignerIfNeeded = true,
54
59
  ) {
55
60
  const { network } = this
56
61
  const keyPair = ECPair.fromPrivateKey(privateKey, {
@@ -64,12 +69,18 @@ class BitcoinLikeSignTransaction {
64
69
  let numberOfInputs = psbt.inputCount
65
70
 
66
71
  for (let i = 0; i < numberOfInputs; i += 1) {
72
+ if (unsignedPsbt.inputsToSign && !unsignedPsbt.inputsToSign.includes(i)) {
73
+ // eslint-disable-next-line no-continue
74
+ continue
75
+ }
67
76
  let type = psbt.getInputType(i)
68
- if (type === "nonstandard") {
69
- let a = tweakSigner(privateKey, undefined, this.network)
70
- await psbt.signInputAsync(i, a)
77
+
78
+ if (usingTweakSignerIfNeeded && type === "nonstandard") {
79
+ console.log("using tweak signer")
80
+ let a = tweakSigner(privateKey, this.network)
81
+ await psbt.signInputAsync(i, a, sighashTypes)
71
82
  } else {
72
- await psbt.signInputAsync(i, keyPair)
83
+ await psbt.signInputAsync(i, keyPair, sighashTypes)
73
84
  }
74
85
  }
75
86
 
@@ -0,0 +1,155 @@
1
+ import type {
2
+ ExtendedUtxo,
3
+ SignerInfo,
4
+ TargetAddress,
5
+ } from "./transaction-builder/transaction-builder"
6
+ import { BitcoinBaseWallet } from "./bitcoin-wallet-base"
7
+ import { generateWrapOpReturn } from "./helper/teleswap-helper"
8
+
9
+ export type TransferRequest = {
10
+ changeAddress?: string
11
+ lockerAddress: string
12
+ amount: number
13
+ fullAmount?: boolean
14
+ //-----------
15
+ chainId: number
16
+ appId: number
17
+ recipientAddress: string // 20 bytes
18
+ percentageFee: number // 2 bytes in satoshi
19
+ speed?: number | boolean // 1 byte
20
+ isExchange?: boolean
21
+ exchangeTokenAddress?: string // 20 bytes
22
+ outputAmount?: number // 28 bytes
23
+ deadline?: number // 4 bytes
24
+ isFixedToken?: boolean // 1 byte
25
+ feeSpeed?: "normal" | "fast" | "slow"
26
+ staticFeeRate?: number
27
+ }
28
+
29
+ export class TeleswapWallet extends BitcoinBaseWallet {
30
+ // payment
31
+ async sendToMultipleAddress(
32
+ receivers: TargetAddress[],
33
+ feeSpeed: "normal" | "fast" | "slow" = "normal",
34
+ ) {
35
+ if (!this.signerInfo || !this.privateKey) {
36
+ throw new Error("account not initialized")
37
+ }
38
+ let extendedUtxo = await this.getExtendedUtxo(this.signerInfo)
39
+ let feeRate = await this.transactionBuilder._getFeeRate(feeSpeed)
40
+ let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
41
+ extendedUtxo,
42
+ targets: receivers,
43
+ changeAddress: this.currentAccount,
44
+ feeRate,
45
+ fullAmount: false,
46
+ })
47
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
48
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
49
+ let txId = await this.transactionBuilder.sendTx(signedTx)
50
+ return txId
51
+ }
52
+
53
+ // send
54
+
55
+ async wrapUnsigned(
56
+ recipientAddress: string,
57
+ amount: number,
58
+ percentageFee: number,
59
+ signer: SignerInfo,
60
+ lockerAddress: string,
61
+ exchange?: {
62
+ outputToken: string
63
+ outputAmount: string
64
+ deadline: string
65
+ isFixedToken?: boolean
66
+ },
67
+ {
68
+ chainId = 137, // 80001
69
+ appId = exchange ? 1 : 0,
70
+ } = {},
71
+ fullAmount = false,
72
+ staticExtendedUtxo?: ExtendedUtxo[],
73
+ staticFeeRate?: number,
74
+ changeAddress?: string,
75
+ ) {
76
+ const dataHex = generateWrapOpReturn({
77
+ chainId,
78
+ appId,
79
+ recipientAddress,
80
+ percentageFee,
81
+ speed: false,
82
+ isExchange: !!exchange,
83
+ outputAmount: exchange?.outputAmount,
84
+ outputToken: exchange?.outputToken,
85
+ deadline: exchange?.deadline,
86
+ isFixedToken: exchange?.isFixedToken || false,
87
+ })
88
+ let extendedUtxo = staticExtendedUtxo || (await this.getExtendedUtxo(signer))
89
+ let feeRate = staticFeeRate || (await this.transactionBuilder._getFeeRate("normal"))
90
+
91
+ const targets = fullAmount
92
+ ? [this.transactionBuilder.getOpReturnTarget(dataHex)]
93
+ : [
94
+ {
95
+ address: lockerAddress,
96
+ value: amount,
97
+ },
98
+ this.transactionBuilder.getOpReturnTarget(dataHex),
99
+ ]
100
+
101
+ const unsignedTx = this.transactionBuilder.processUnsignedTransaction({
102
+ extendedUtxo,
103
+ feeRate,
104
+ targets,
105
+ changeAddress: changeAddress || signer.address,
106
+ fullAmount,
107
+ })
108
+
109
+ return unsignedTx
110
+ }
111
+
112
+ async wrap(
113
+ recipientAddress: string,
114
+ amount: number,
115
+ percentageFee: number,
116
+ lockerAddress: string,
117
+ exchange?: {
118
+ outputToken: string
119
+ outputAmount: string
120
+ deadline: string
121
+ isFixedToken?: boolean
122
+ },
123
+ {
124
+ chainId = 137, // 80001
125
+ appId = exchange ? 1 : 0,
126
+ } = {},
127
+ fullAmount = false,
128
+ staticExtendedUtxo?: ExtendedUtxo[],
129
+ staticFeeRate?: number,
130
+ changeAddress?: string,
131
+ ) {
132
+ if (!this.signerInfo || !this.privateKey) {
133
+ throw new Error("account not initialized")
134
+ }
135
+ let unsignedTransaction = await this.wrapUnsigned(
136
+ recipientAddress,
137
+ amount,
138
+ percentageFee,
139
+ this.signerInfo,
140
+ lockerAddress,
141
+ exchange,
142
+ {
143
+ chainId,
144
+ appId,
145
+ },
146
+ fullAmount,
147
+ staticExtendedUtxo,
148
+ staticFeeRate,
149
+ changeAddress,
150
+ )
151
+ let signedPsbt = await this.signer.signPsbt(unsignedTransaction, this.privateKey)
152
+ return this.sendSignedPsbt(signedPsbt)
153
+ }
154
+ }
155
+ export default TeleswapWallet
@@ -1,26 +1,9 @@
1
- import BaseTransactionBuilder from "./transaction-builder"
1
+ import { BaseTransactionBuilder } from "./transaction-builder"
2
2
  import { BitcoinInterface } from "../bitcoin-interface"
3
3
  import * as bitcoin from "bitcoinjs-lib"
4
+ import { BitcoinConnectionInfo } from "../type"
4
5
 
5
- export type BitcoinConnectionInfo = {
6
- rpc?: {
7
- enabled?: boolean
8
- url: string
9
- headers?: {
10
- [key: string]: string
11
- }
12
- auth?: {
13
- username: string
14
- password: string
15
- }
16
- }
17
- api: {
18
- enabled?: boolean
19
- token?: string
20
- provider: string
21
- }
22
- }
23
- class BitcoinTransactionBuilder extends BaseTransactionBuilder {
6
+ export class BitcoinTransactionBuilder extends BaseTransactionBuilder {
24
7
  btcInterface: BitcoinInterface
25
8
  constructor(
26
9
  connectionInfo: BitcoinConnectionInfo,
@@ -36,7 +19,7 @@ class BitcoinTransactionBuilder extends BaseTransactionBuilder {
36
19
  }
37
20
 
38
21
  async _getUtxo(userAddress: string) {
39
- let utxos = await this.btcInterface.getAddressesUtxo([userAddress])
22
+ let utxos = await this.btcInterface.getUtxo(userAddress)
40
23
  return utxos.map((tx) => ({
41
24
  hash: tx.txId as string,
42
25
  value: +tx.value,
@@ -49,13 +32,13 @@ class BitcoinTransactionBuilder extends BaseTransactionBuilder {
49
32
  }
50
33
 
51
34
  async _getTransactionHex(transactionId: string): Promise<string> {
52
- return this.btcInterface.provider.getRawTransaction(transactionId)
35
+ return this.btcInterface.getRawTransaction(transactionId)
53
36
  }
54
37
 
55
38
  async sendTx(txHex: string): Promise<string> {
56
- let txId = await this.btcInterface.provider.sendRawTransaction(txHex)
39
+ let txId = await (
40
+ this.btcInterface.rpcProvider || this.btcInterface.apiProvider
41
+ ).sendRawTransaction(txHex)
57
42
  return txId
58
43
  }
59
44
  }
60
-
61
- export default BitcoinTransactionBuilder
@@ -0,0 +1,3 @@
1
+ export * from "./bitcoin-transaction-builder"
2
+ export * from "./ordinal-transaction-builder"
3
+ export * from "./transaction-builder"
@@ -0,0 +1,147 @@
1
+ import { ExtendedUtxo, Target, componentBytes } from "../"
2
+ import * as bitcoin from "bitcoinjs-lib"
3
+ import { LEAF_VERSION_TAPSCRIPT } from "bitcoinjs-lib/src/payments/bip341"
4
+ import { BitcoinTransactionBuilder } from "./bitcoin-transaction-builder"
5
+ import { getOrdinalScript, toXOnly } from "../helper/ordinal-helper"
6
+
7
+ export class OrdinalTransactionBuilder extends BitcoinTransactionBuilder {
8
+ async createNftPsbt({
9
+ extendedUtxo,
10
+ nftExtendedUtxo,
11
+ receiverAddress,
12
+ feeRate,
13
+ changeAddress,
14
+ otherTargets,
15
+ }: {
16
+ nftExtendedUtxo: ExtendedUtxo
17
+ extendedUtxo: ExtendedUtxo[]
18
+ feeRate: number
19
+ changeAddress: string
20
+ receiverAddress: string
21
+ otherTargets?: Target[]
22
+ }) {
23
+ let targets: Target[] = [
24
+ {
25
+ address: receiverAddress,
26
+ value: nftExtendedUtxo.value,
27
+ },
28
+ ]
29
+
30
+ if (otherTargets) targets.push(...otherTargets)
31
+
32
+ if (!changeAddress && targets.length === 0) throw new Error("no target")
33
+ let changeObject = {
34
+ address: changeAddress,
35
+ }
36
+ let { inputs, outputs, change, fee } = await this.filterAndConvertTxDataToStandardFormat({
37
+ extendedUtxo: [nftExtendedUtxo, ...extendedUtxo],
38
+ targets,
39
+ changeObject,
40
+ feeRate,
41
+ selectType: "inOrder",
42
+ })
43
+
44
+ let unsignedTx = this.createUnsignedTransaction({
45
+ inputs,
46
+ outputs,
47
+ change,
48
+ fee,
49
+ feeRate,
50
+ })
51
+
52
+ return unsignedTx
53
+ }
54
+
55
+ createOrdinalAddress(
56
+ file: {
57
+ buffer: Buffer
58
+ type: string
59
+ },
60
+ publicKey: Buffer,
61
+ ) {
62
+ const internalPublicKey = toXOnly(publicKey).toString("hex")
63
+ let leafScript = getOrdinalScript(file, internalPublicKey)
64
+
65
+ const scriptTree = {
66
+ output: leafScript,
67
+ }
68
+ const redeem = {
69
+ output: leafScript,
70
+ redeemVersion: LEAF_VERSION_TAPSCRIPT,
71
+ }
72
+
73
+ const p2trTapOrdinalScript = bitcoin.payments.p2tr({
74
+ internalPubkey: Buffer.from(internalPublicKey, "hex"),
75
+ scriptTree,
76
+ network: this.network,
77
+ redeem,
78
+ })
79
+ let ordinalAddress = p2trTapOrdinalScript.address!
80
+ const controlBlock = p2trTapOrdinalScript.witness![p2trTapOrdinalScript.witness!.length - 1]
81
+ return {
82
+ ordinalAddress,
83
+ ordinalScript: p2trTapOrdinalScript.output!,
84
+ redeem,
85
+ controlBlock,
86
+ }
87
+ }
88
+
89
+ createInscribeUnsignedTx(
90
+ ordinalSpendDetails: {
91
+ ordinalAddress: string
92
+ ordinalScript: Buffer
93
+ redeem: {
94
+ output: Buffer
95
+ redeemVersion: number
96
+ }
97
+ controlBlock: Buffer
98
+ },
99
+ inscribeDeposit: {
100
+ hash: string
101
+ index: number
102
+ value: number
103
+ },
104
+ receiverAddress: string,
105
+ ordinalAmount = 600,
106
+ ) {
107
+ const psbt = new bitcoin.Psbt({ network: this.network })
108
+ .addInput({
109
+ ...inscribeDeposit,
110
+ witnessUtxo: { value: inscribeDeposit.value, script: ordinalSpendDetails.ordinalScript },
111
+ // tapInternalKey: toXOnly(node.publicKey),
112
+ // tapMerkleRoot: p2trTapOrdinalScript.hash!,
113
+ tapLeafScript: [
114
+ {
115
+ leafVersion: ordinalSpendDetails.redeem.redeemVersion,
116
+ script: ordinalSpendDetails.redeem.output,
117
+ controlBlock: ordinalSpendDetails.controlBlock,
118
+ },
119
+ ],
120
+ })
121
+ .addOutput({
122
+ value: ordinalAmount,
123
+ address: receiverAddress,
124
+ })
125
+
126
+ return {
127
+ unsignedTransaction: psbt.toBase64(),
128
+ inputs: [inscribeDeposit],
129
+ outputs: [
130
+ {
131
+ value: ordinalAmount,
132
+ address: receiverAddress,
133
+ },
134
+ ],
135
+ fee: inscribeDeposit.value - ordinalAmount,
136
+ }
137
+ }
138
+
139
+ // use with caution. just segwit address
140
+ getUnsignedPsbtTxId(unsignedPsbt: string): string {
141
+ // use with caution
142
+ let psbt = bitcoin.Psbt.fromBase64(unsignedPsbt, {
143
+ network: this.network,
144
+ })
145
+ return (psbt as any).__CACHE.__TX.getId()
146
+ }
147
+ }
@@ -8,28 +8,48 @@ const coinselectSplit = require("coinselect/split")
8
8
  const coinselectAccumulative = require("coinselect/accumulative")
9
9
 
10
10
  // https://bitcoin.stackexchange.com/questions/84004/how-do-virtual-size-stripped-size-and-raw-size-compare-between-legacy-address-f
11
- const componentBytes = {
11
+ // export const componentBytes = {
12
+ // bytePerInput: {
13
+ // p2pkh: 148,
14
+ // p2wpkh: 70, // 68
15
+ // "p2sh-p2wpkh": 91,
16
+ // p2tr: 60, // actual 58
17
+ // },
18
+ // baseTxBytes: 10 + 5, // +5 extra bytes to be sure
19
+ // bytePerOutput: {
20
+ // p2pkh: 35, // 34
21
+ // p2wpkh: 35, // 31
22
+ // p2sh: 35, // 32
23
+ // p2tr: 45, // 43
24
+ // default: 35,
25
+ // },
26
+ //
27
+ // }
28
+ export const componentBytes = {
12
29
  bytePerInput: {
13
30
  p2pkh: 148,
14
- p2wpkh: 70, // 68
31
+ p2wpkh: 68, // 68
15
32
  "p2sh-p2wpkh": 91,
16
- p2tr: 65, // actual 58
33
+ p2tr: 58, // actual 58
34
+ default: 100,
17
35
  },
18
- baseTxBytes: 10 + 5, // +2 extra bytes to be sure
36
+ baseTxBytes: 10 + 5, // +5 extra bytes to be sure
19
37
  bytePerOutput: {
20
- p2pkh: 35, // 34
21
- p2wpkh: 35, // 31
22
- p2sh: 35, // 32
23
- p2tr: 45, // 43
38
+ p2pkh: 34, // 34
39
+ p2wpkh: 31, // 31
40
+ p2sh: 32, // 32
41
+ p2tr: 43, // 43
24
42
  default: 35,
43
+ max: 45,
25
44
  },
26
- opReturn: {
27
- dataLessThan75: 31,
28
- dataMoreThan75: 91,
45
+
46
+ scriptExtraBytes: {
47
+ lessThan255: 12,
48
+ moreThan255: 15,
29
49
  },
30
50
  }
31
51
 
32
- const DUST = 1000
52
+ export const DUST = 1000
33
53
 
34
54
  export type Utxo = {
35
55
  hash: string
@@ -97,7 +117,23 @@ export type ExtendedUnsignedTransaction = {
97
117
  change: TargetAddress | undefined
98
118
  }
99
119
 
100
- class BaseBitcoinLikeTransaction {
120
+ function coinSelectInOrder(
121
+ utxos: { hash: string; index: number; value: number }[],
122
+ outputs: {
123
+ address?: string
124
+ script?: Buffer
125
+ value: number
126
+ }[],
127
+ feeRate: number,
128
+ ) {
129
+ let response = coinselectAccumulative(utxos, outputs, 1)
130
+ return {
131
+ ...response,
132
+ fee: +(+response.fee * feeRate).toFixed(),
133
+ }
134
+ }
135
+
136
+ export class BaseTransactionBuilder {
101
137
  testnet: boolean
102
138
  network: bitcoin.Network
103
139
  maximumNumberOfOutputsInTransaction: number
@@ -177,6 +213,52 @@ class BaseBitcoinLikeTransaction {
177
213
  return extendedUtxo
178
214
  }
179
215
 
216
+ calculateTxSize(
217
+ inputTypes: string[],
218
+ outputs: {
219
+ script?: Buffer
220
+ address?: string
221
+ value: number
222
+ }[],
223
+ changeAddressType = "default",
224
+ ) {
225
+ const inputsSizes = inputTypes.map(
226
+ (addressType) =>
227
+ componentBytes.bytePerInput[addressType as keyof typeof componentBytes.bytePerInput],
228
+ )
229
+ const outputSizes = outputs.map((outP: any) => {
230
+ if (outP.address) {
231
+ let addressType = "default"
232
+ try {
233
+ addressType = getAddressType(outP.address, this.network)
234
+ } catch {
235
+ addressType = "default"
236
+ }
237
+ return componentBytes.bytePerOutput[
238
+ addressType as keyof typeof componentBytes.bytePerOutput
239
+ ]
240
+ }
241
+
242
+ if (outP.script) {
243
+ if (outP.script.byteLength < 255) {
244
+ return outP.script.byteLength + componentBytes.scriptExtraBytes.lessThan255
245
+ }
246
+ return outP.script.byteLength + componentBytes.scriptExtraBytes.moreThan255
247
+ }
248
+
249
+ return componentBytes.bytePerOutput[
250
+ changeAddressType as keyof typeof componentBytes.bytePerOutput
251
+ ]
252
+ })
253
+
254
+ const txSize =
255
+ componentBytes.baseTxBytes +
256
+ inputsSizes.reduce((a, c) => a + c, 0) +
257
+ outputSizes.reduce((a, c) => a + c, 0)
258
+
259
+ return txSize
260
+ }
261
+
180
262
  helperHandleInputsAndOutputs({
181
263
  targets,
182
264
  extendedUtxo,
@@ -194,7 +276,7 @@ class BaseBitcoinLikeTransaction {
194
276
  derivationPath?: string
195
277
  masterFingerprint?: string
196
278
  }
197
- selectType?: "normal" | "accumulative" | "full"
279
+ selectType?: "normal" | "accumulative" | "full" | "inOrder"
198
280
  }) {
199
281
  const filteredUtxo = extendedUtxo.filter(
200
282
  (u) =>
@@ -212,6 +294,9 @@ class BaseBitcoinLikeTransaction {
212
294
  case "accumulative":
213
295
  selectResponse = coinselectAccumulative(filteredUtxo, targets, Math.round(feeRate))
214
296
  break
297
+ case "inOrder":
298
+ selectResponse = coinSelectInOrder(filteredUtxo, targets, Math.round(feeRate))
299
+ break
215
300
  case "full":
216
301
  if (!(targets[0] as TargetAddress).address) {
217
302
  throw new Error()
@@ -222,7 +307,6 @@ class BaseBitcoinLikeTransaction {
222
307
  Math.round(feeRate),
223
308
  )
224
309
  break
225
-
226
310
  default:
227
311
  break
228
312
  }
@@ -247,49 +331,24 @@ class BaseBitcoinLikeTransaction {
247
331
  fee = inputs.reduce((a, b) => a + b.value, 0) - outputs.reduce((a, b) => a + b.value, 0)
248
332
  }
249
333
 
250
- const inputsSizes = inputs.map(
251
- (i) =>
252
- componentBytes.bytePerInput[
253
- i.signerInfo.addressType as keyof typeof componentBytes.bytePerInput
254
- ],
255
- )
256
- const outputSizes = outputs.map((outP) => {
257
- if (outP.address) {
258
- let addressType = "default"
259
- try {
260
- addressType = getAddressType(outP.address, this.network)
261
- } catch {
262
- addressType = "default"
263
- }
264
- return componentBytes.bytePerOutput[
265
- addressType as keyof typeof componentBytes.bytePerOutput
266
- ]
267
- }
268
-
269
- if (outP.script) {
270
- if (outP.script.byteLength < 75) {
271
- return componentBytes.opReturn.dataLessThan75
272
- }
273
- return componentBytes.opReturn.dataMoreThan75
274
- }
275
-
276
- let addressType = "default"
277
- try {
278
- addressType = getAddressType(changeObject?.address || "", this.network)
279
- } catch {
280
- addressType = "default"
281
- }
282
-
283
- return componentBytes.bytePerOutput[addressType as keyof typeof componentBytes.bytePerOutput]
284
- })
334
+ let changeAddressType = "default"
335
+ try {
336
+ changeAddressType = getAddressType(changeObject?.address || "", this.network)
337
+ } catch {
338
+ changeAddressType = "default"
339
+ }
285
340
 
286
341
  const txSize =
287
- componentBytes.baseTxBytes +
288
- inputsSizes.reduce((a, c) => a + c, 0) +
289
- outputSizes.reduce((a, c) => a + c, 0) +
290
- componentBytes.bytePerOutput.default
291
-
292
- const txFee = txSize * Math.round(feeRate)
342
+ this.calculateTxSize(
343
+ inputs.map((i) => i.signerInfo.addressType),
344
+ outputs,
345
+ changeAddressType,
346
+ ) + componentBytes.bytePerOutput.default
347
+
348
+ let txFee = Math.round(txSize * feeRate)
349
+ if (Math.round(feeRate) === 1) {
350
+ txFee = Math.round(txFee + txFee * 0.1)
351
+ }
293
352
  if (
294
353
  inputs.reduce((a, b) => a + b.value, 0) -
295
354
  outputs.filter((o) => o.address || o.script).reduce((a, b) => a + b.value, 0) -
@@ -363,7 +422,7 @@ class BaseBitcoinLikeTransaction {
363
422
  derivationPath?: string
364
423
  masterFingerprint?: string
365
424
  }
366
- selectType?: "normal" | "accumulative" | "full"
425
+ selectType?: "normal" | "accumulative" | "full" | "inOrder"
367
426
  }) {
368
427
  let {
369
428
  inputs: filteredInputs,
@@ -617,7 +676,7 @@ class BaseBitcoinLikeTransaction {
617
676
 
618
677
  changeAddress?: string | SignerInfo
619
678
  fullAmount?: boolean
620
- selectType?: "normal" | "accumulative" | "full"
679
+ selectType?: "normal" | "accumulative" | "full" | "inOrder"
621
680
  }) {
622
681
  if (!changeAddress && targets.length === 0) throw new Error("no target")
623
682
  let changeObject =
@@ -644,5 +703,3 @@ class BaseBitcoinLikeTransaction {
644
703
  return unsignedTransaction
645
704
  }
646
705
  }
647
-
648
- export default BaseBitcoinLikeTransaction