@teleportdao/bitcoin 1.8.9 → 2.0.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 (55) hide show
  1. package/.tmp/block-parser.ts +58 -0
  2. package/dist/bitcoin-interface-ordinal.d.ts +2 -2
  3. package/dist/bitcoin-interface-ordinal.d.ts.map +1 -1
  4. package/dist/bitcoin-interface-ordinal.js +1 -1
  5. package/dist/bitcoin-interface-ordinal.js.map +1 -1
  6. package/dist/bitcoin-interface-teleswap.d.ts +2 -53
  7. package/dist/bitcoin-interface-teleswap.d.ts.map +1 -1
  8. package/dist/bitcoin-interface-teleswap.js +6 -17
  9. package/dist/bitcoin-interface-teleswap.js.map +1 -1
  10. package/dist/bitcoin-interface-wallet.d.ts +29 -0
  11. package/dist/bitcoin-interface-wallet.d.ts.map +1 -0
  12. package/dist/bitcoin-interface-wallet.js +126 -0
  13. package/dist/bitcoin-interface-wallet.js.map +1 -0
  14. package/dist/bitcoin-interface.d.ts +5 -23
  15. package/dist/bitcoin-interface.d.ts.map +1 -1
  16. package/dist/bitcoin-interface.js +13 -92
  17. package/dist/bitcoin-interface.js.map +1 -1
  18. package/dist/bitcoin-wallet-base.d.ts +55 -31
  19. package/dist/bitcoin-wallet-base.d.ts.map +1 -1
  20. package/dist/bitcoin-wallet-base.js +105 -84
  21. package/dist/bitcoin-wallet-base.js.map +1 -1
  22. package/dist/ordinal-wallet.d.ts +29 -71
  23. package/dist/ordinal-wallet.d.ts.map +1 -1
  24. package/dist/ordinal-wallet.js +48 -108
  25. package/dist/ordinal-wallet.js.map +1 -1
  26. package/dist/teleswap-wallet.d.ts +10 -16
  27. package/dist/teleswap-wallet.d.ts.map +1 -1
  28. package/dist/teleswap-wallet.js +10 -30
  29. package/dist/teleswap-wallet.js.map +1 -1
  30. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +4 -11
  31. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -1
  32. package/dist/transaction-builder/bitcoin-transaction-builder.js +2 -23
  33. package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -1
  34. package/dist/transaction-builder/ordinal-transaction-builder.d.ts +3 -2
  35. package/dist/transaction-builder/ordinal-transaction-builder.d.ts.map +1 -1
  36. package/dist/transaction-builder/ordinal-transaction-builder.js +1 -6
  37. package/dist/transaction-builder/ordinal-transaction-builder.js.map +1 -1
  38. package/dist/transaction-builder/transaction-builder.d.ts +3 -7
  39. package/dist/transaction-builder/transaction-builder.d.ts.map +1 -1
  40. package/dist/transaction-builder/transaction-builder.js +22 -49
  41. package/dist/transaction-builder/transaction-builder.js.map +1 -1
  42. package/dist/type.d.ts +33 -18
  43. package/dist/type.d.ts.map +1 -1
  44. package/package.json +4 -4
  45. package/src/bitcoin-interface-ordinal.ts +7 -3
  46. package/src/bitcoin-interface-teleswap.ts +7 -22
  47. package/src/bitcoin-interface-wallet.ts +114 -0
  48. package/src/bitcoin-interface.ts +15 -98
  49. package/src/bitcoin-wallet-base.ts +166 -132
  50. package/src/ordinal-wallet.ts +73 -162
  51. package/src/teleswap-wallet.ts +50 -72
  52. package/src/transaction-builder/bitcoin-transaction-builder.ts +6 -24
  53. package/src/transaction-builder/ordinal-transaction-builder.ts +2 -10
  54. package/src/transaction-builder/transaction-builder.ts +34 -50
  55. package/src/type.ts +45 -19
@@ -5,17 +5,24 @@ import BIP32Factory from "bip32"
5
5
  import ecc from "@bitcoinerlab/secp256k1"
6
6
  import BigNumber from "bignumber.js"
7
7
  import { BitcoinTransactionBuilder } from "./transaction-builder"
8
- import type { BitcoinConnectionInfo } from "./type"
8
+ import type { APIConnectionInfo, RPCConnectionInfo, UtxoConnectionInfo } from "./type"
9
9
 
10
- import type { ExtendedUtxo, SignerInfo, Target } from "./transaction-builder/transaction-builder"
10
+ import type {
11
+ ExtendedUtxo,
12
+ SignerInfo,
13
+ Target,
14
+ TargetAddress,
15
+ } from "./transaction-builder/transaction-builder"
11
16
  import BitcoinSign from "./sign/sign-transaction"
12
17
 
13
18
  import { getPubKeyFromPrivateKeyHex } from "./bitcoin-utils"
14
19
  import networks from "./utils/networks"
15
- import { BitcoinInterface } from "./bitcoin-interface"
20
+ import { BitcoinInterfaceWallet } from "./bitcoin-interface-wallet"
21
+ import { runWithRetries } from "./utils/tools"
16
22
 
17
23
  const bip32 = BIP32Factory(ecc)
18
24
 
25
+ export type FeeRateType = "normal" | "slow" | "fast" | number
19
26
  export class BitcoinBaseWallet {
20
27
  network: Network
21
28
  hdWalletPath: {
@@ -28,7 +35,7 @@ export class BitcoinBaseWallet {
28
35
  p2tr: string
29
36
  }
30
37
  transactionBuilder: BitcoinTransactionBuilder
31
- btcInterface: BitcoinInterface
38
+ btcInterface: BitcoinInterfaceWallet
32
39
  signer: BitcoinSign
33
40
  currentAccount?: string
34
41
  currentAccountType?: string
@@ -39,33 +46,41 @@ export class BitcoinBaseWallet {
39
46
  bitcoinAddress: string | undefined
40
47
  constructor(
41
48
  networkName: string,
42
- connectionInfo: BitcoinConnectionInfo = {
43
- api: {
44
- provider: "BlockStream",
45
- },
49
+ connectionInfo?: {
50
+ utxo?: UtxoConnectionInfo
51
+ // rpc used for getRawTransaction in transaction builder if set (optional)
52
+ rpc?: RPCConnectionInfo
46
53
  },
47
54
  ) {
48
55
  this.network = networks[networkName]
49
56
  this.hdWalletPath = hdWalletPath.bitcoin
50
57
 
51
58
  this.transactionBuilder = new BitcoinTransactionBuilder(
52
- connectionInfo,
53
59
  networkName,
54
60
  this.network,
61
+ connectionInfo,
55
62
  )
56
63
  this.btcInterface = this.transactionBuilder.btcInterface
57
64
 
58
65
  this.signer = new BitcoinSign(this.network)
59
66
 
67
+ // initialize account
60
68
  this.currentAccount = undefined
61
69
  this.currentAccountType = undefined
62
-
63
70
  this.privateKey = undefined
64
71
  this.publicKey = undefined
65
- // todo multisig
66
- this.publicKeys = []
67
72
  }
68
73
 
74
+ static satoshiToBTC(satoshi: number | string) {
75
+ return new BigNumber(satoshi).dividedBy(1e8).toString()
76
+ }
77
+
78
+ static btcToSatoshi(btc: number | string) {
79
+ return new BigNumber(btc).multipliedBy(1e8).toFixed(0)
80
+ }
81
+
82
+ // initialize account
83
+
69
84
  get signerInfo() {
70
85
  return this.privateKey
71
86
  ? {
@@ -76,82 +91,26 @@ export class BitcoinBaseWallet {
76
91
  : undefined
77
92
  }
78
93
 
79
- createTransactionInputsAndOutputs({
80
- targets,
81
- extendedUtxo,
82
- changeAddress,
83
- feeRate,
84
- }: {
85
- targets: Target[]
86
- extendedUtxo: ExtendedUtxo[]
87
- changeAddress: string
88
- feeRate: number
89
- fullAmount?: boolean
90
- }) {
91
- return this.transactionBuilder.helperHandleInputsAndOutputs({
92
- targets,
93
- extendedUtxo,
94
- changeObject: {
95
- address: changeAddress,
96
- },
97
- feeRate,
98
- })
99
- }
100
-
101
- checkBalanceIsSufficient({
102
- targets,
103
- extendedUtxo,
104
- changeAddress,
105
- feeRate,
106
- fullAmount = false,
107
- }: {
108
- targets: Target[]
109
- extendedUtxo: ExtendedUtxo[]
110
- changeAddress: string
111
- feeRate: number
112
- fullAmount?: boolean
113
- }) {
114
- try {
115
- this.transactionBuilder.helperHandleInputsAndOutputs({
116
- targets,
117
- extendedUtxo,
118
- changeObject: {
119
- address: changeAddress,
120
- },
121
- feeRate,
122
- })
123
- return true
124
- } catch (err) {
125
- return false
126
- }
127
- }
128
-
129
- // todo : not completed
130
- setMultiSigAccount(accountType = "p2sh") {
131
- throw new Error("not supported yet")
132
-
133
- /* eslint-disable no-unreachable */
134
- // todo : not completed
135
- switch (accountType) {
136
- // case 'p2sh':
137
- // this.currentAccount = ''
138
- // break
139
- // case 'p2wsh':
140
- // this.currentAccount = ''
141
- // break
142
- // case 'p2sh-p2wsh':
143
- // this.currentAccount = ''
144
- // break
145
- default:
146
- throw new Error("accountType is incorrect")
147
- }
148
- this.currentAccountType = accountType
149
- }
150
-
151
94
  setAccountPrivateKey(privateKeyHex: string) {
152
95
  this.privateKey = Buffer.from(privateKeyHex, "hex")
153
96
  let publicKey = getPubKeyFromPrivateKeyHex(privateKeyHex, this.network)
154
97
  this.publicKey = publicKey
98
+ this.setAccountType("p2wpkh")
99
+ }
100
+
101
+ setAccountType(accountType = "p2pkh") {
102
+ if (!this.publicKey) {
103
+ throw new Error("account not initialized")
104
+ }
105
+ let addressObj = this.transactionBuilder.createAddressObject({
106
+ addressType: accountType,
107
+ publicKey: this.publicKey,
108
+ })
109
+ this.currentAccount = addressObj.address
110
+ this.currentAccountType = accountType
111
+ this.addressObj = addressObj
112
+ this.bitcoinAddress = addressObj.address
113
+ return addressObj.address!
155
114
  }
156
115
 
157
116
  setAccountPrivateKeyByMnemonic({
@@ -179,87 +138,162 @@ export class BitcoinBaseWallet {
179
138
  const account = node.derivePath(path)
180
139
  const userKeyPair = account.derive(index)
181
140
  this.setAccountPrivateKey(userKeyPair.privateKey!.toString("hex"))
182
- return this.setAccount(addressType)
141
+ return this.setAccountType(addressType)
183
142
  }
184
143
 
185
- setAccountPublicKey(publicKeyHex: string) {
186
- this.publicKey = Buffer.from(publicKeyHex, "hex")
187
- }
144
+ // helper function
188
145
 
189
- setAccount(accountType = "p2pkh") {
190
- if (!this.publicKey) {
191
- throw new Error("account not initialized")
146
+ checkBalanceIsSufficient({
147
+ targets,
148
+ extendedUtxo,
149
+ changeAddress,
150
+ feeRate,
151
+ }: {
152
+ targets: Target[]
153
+ extendedUtxo: ExtendedUtxo[]
154
+ changeAddress: string
155
+ feeRate: number
156
+ }) {
157
+ try {
158
+ this.transactionBuilder.helperHandleInputsAndOutputs({
159
+ targets,
160
+ extendedUtxo,
161
+ changeObject: {
162
+ address: changeAddress,
163
+ },
164
+ feeRate,
165
+ })
166
+ return true
167
+ } catch (err) {
168
+ return false
192
169
  }
193
- let addressObj = this.transactionBuilder.createAddressObject({
194
- addressType: accountType,
195
- publicKey: this.publicKey,
196
- })
197
- this.currentAccount = addressObj.address
198
- this.currentAccountType = accountType
199
- this.addressObj = addressObj
200
- this.bitcoinAddress = addressObj.address
201
- return addressObj.address
202
170
  }
203
171
 
204
- //
205
-
206
172
  async getExtendedUtxo(input: SignerInfo) {
207
- return this.transactionBuilder.getExtendedUtxo(input)
173
+ return this.btcInterface.getExtendedUtxo(input)
174
+ }
175
+
176
+ async getFeeRate(feeRate: FeeRateType = "normal") {
177
+ if (feeRate === 0) throw new Error("feeRate should be greater than 0")
178
+ if (+feeRate > 0) return +feeRate
179
+ if (typeof feeRate === "string") return this.btcInterface.getFeeRate(feeRate)
180
+ throw new Error("incorrect feeRate")
208
181
  }
209
182
 
210
- async send(
183
+ // signed methods
184
+
185
+ async sendBTC(
211
186
  receiverAddress: string,
212
- amount: string | number,
213
- fullAmount = false,
214
- speed: "normal" | "slow" | "fast" = "normal",
187
+ amountInSatoshi: number | "all",
188
+ fee: FeeRateType = "normal",
189
+ staticExtendedUtxo?: ExtendedUtxo[],
215
190
  ) {
216
- if (!this.currentAccount || !this.currentAccountType || !this.publicKey || !this.privateKey) {
191
+ if (!this.signerInfo! || !this.privateKey) {
217
192
  throw new Error("account not initialized")
218
193
  }
194
+ const unsignedTx = await this.sendBTCUnsignedTx(
195
+ receiverAddress,
196
+ amountInSatoshi,
197
+ this.signerInfo!,
198
+ fee,
199
+ staticExtendedUtxo,
200
+ )
201
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
202
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
203
+ let txId = await this.sendSignedTx(signedTx)
204
+ return txId
205
+ }
206
+
207
+ async sendBTCMultipleAddress(
208
+ receivers: TargetAddress[],
209
+ fee: FeeRateType = "normal",
210
+ staticExtendedUtxo?: ExtendedUtxo[],
211
+ ) {
212
+ if (!this.signerInfo! || !this.privateKey) {
213
+ throw new Error("account not initialized")
214
+ }
215
+ const unsignedTx = await this.sendBTCToMultipleAddressUnsignedTx(
216
+ receivers,
217
+ this.signerInfo!,
218
+ fee,
219
+ staticExtendedUtxo,
220
+ )
221
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
222
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
223
+ let txId = await this.sendSignedTx(signedTx)
224
+ return txId
225
+ }
226
+
227
+ // unsigned methods
228
+ async sendBTCToMultipleAddressUnsignedTx(
229
+ receivers: TargetAddress[],
230
+ signerInfo: SignerInfo,
231
+ fee: FeeRateType = "normal",
232
+ staticExtendedUtxo?: ExtendedUtxo[],
233
+ fullAmount = false,
234
+ ) {
235
+ if (!fullAmount) {
236
+ receivers.forEach((r) => {
237
+ if (BigNumber(r.value).isEqualTo(0)) throw new Error("incorrect amount")
238
+ })
239
+ } else if (receivers.length > 1) throw new Error("fullAmount only support one receiver")
240
+
241
+ let feeRate = await this.getFeeRate(fee)
242
+ let extendedUtxo = staticExtendedUtxo || (await this.getExtendedUtxo(signerInfo))
219
243
 
220
- let extendedUtxo = await this.getExtendedUtxo({
221
- address: this.currentAccount,
222
- addressType: this.currentAccountType,
223
- publicKey: this.publicKey.toString("hex"),
224
- })
225
- if (!fullAmount && BigNumber(amount).isEqualTo(0))
226
- throw new Error("incorrect amount. amount should be in satoshi")
227
- let feeRate = await this.transactionBuilder._getFeeRate(speed)
228
244
  let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
229
245
  extendedUtxo,
230
- targets: [
231
- {
232
- address: receiverAddress,
233
- value: +amount,
234
- },
235
- ],
236
- changeAddress: this.currentAccount,
246
+ targets: receivers,
247
+ changeAddress: signerInfo.address,
237
248
  feeRate,
238
249
  fullAmount,
239
250
  })
240
- let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
241
- let signedTx = this.signer.finalizePsbts([signedPsbt])
242
- let txId = await this.transactionBuilder.sendTx(signedTx)
251
+ return unsignedTx
252
+ }
253
+
254
+ async sendBTCUnsignedTx(
255
+ receiver: string,
256
+ amountInSatoshi: number | "all",
257
+ signerInfo: SignerInfo,
258
+ fee: FeeRateType = "normal",
259
+ staticExtendedUtxo?: ExtendedUtxo[],
260
+ ) {
261
+ return this.sendBTCToMultipleAddressUnsignedTx(
262
+ [{ address: receiver, value: amountInSatoshi === "all" ? 0 : amountInSatoshi }],
263
+ signerInfo,
264
+ fee,
265
+ staticExtendedUtxo,
266
+ amountInSatoshi === "all",
267
+ )
268
+ }
269
+
270
+ // send tx
271
+
272
+ async sendSignedTx(signedTx: string) {
273
+ let txId = await this.btcInterface.sendRawTransaction(signedTx)
243
274
  return txId
244
275
  }
245
276
 
246
277
  async sendSignedPsbt(signedPsbt: string) {
247
278
  let signedTx = this.signer.finalizePsbts([signedPsbt])
248
- let txId = await this.transactionBuilder.sendTx(signedTx)
279
+ let txId = await this.sendSignedTx(signedTx)
249
280
  return txId
250
281
  }
251
282
 
252
- async sendSignedTx(signedTx: string) {
253
- let txId = await this.transactionBuilder.sendTx(signedTx)
254
- return txId
283
+ async sendSignedPsbtWithRetry(signedPsbt: string, { maxTries = 5, retrySleep = 5000 } = {}) {
284
+ return runWithRetries(() => this.sendSignedPsbt(signedPsbt), {
285
+ retrySleep,
286
+ maxTries,
287
+ })
255
288
  }
256
289
 
257
290
  async sendMultiSignedPsbt(signedPsbts: string[] = []) {
258
291
  let signedTx = this.signer.finalizePsbts(signedPsbts)
259
- let txId = await this.transactionBuilder.sendTx(signedTx)
292
+ let txId = await this.btcInterface.sendRawTransaction(signedTx)
260
293
  return txId
261
294
  }
262
295
 
296
+ // increase transaction fee (beta vesrion)
263
297
  async increaseTransactionFeeUnsignedPsbt(
264
298
  txId: string,
265
299
  signerInfos: SignerInfo[],
@@ -284,7 +318,7 @@ export class BitcoinBaseWallet {
284
318
  transaction.vin.find((vi) => vo.address === vi.address || vo.address === changeAddress),
285
319
  )
286
320
 
287
- const feeRate = staticFeeRate || (await this.transactionBuilder._getFeeRate("fast"))
321
+ const feeRate = staticFeeRate || (await this.btcInterface.getFeeRate("fast"))
288
322
 
289
323
  let targets = transaction.vout
290
324
  .filter((_, index) => index !== changeIndex)