@teleportdao/bitcoin 1.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 (72) hide show
  1. package/dist/bitcoin-base.d.ts +56 -0
  2. package/dist/bitcoin-base.d.ts.map +1 -0
  3. package/dist/bitcoin-base.js +138 -0
  4. package/dist/bitcoin-base.js.map +1 -0
  5. package/dist/bitcoin-interface-utils.d.ts +18 -0
  6. package/dist/bitcoin-interface-utils.d.ts.map +1 -0
  7. package/dist/bitcoin-interface-utils.js +31 -0
  8. package/dist/bitcoin-interface-utils.js.map +1 -0
  9. package/dist/bitcoin-interface.d.ts +154 -0
  10. package/dist/bitcoin-interface.d.ts.map +1 -0
  11. package/dist/bitcoin-interface.js +248 -0
  12. package/dist/bitcoin-interface.js.map +1 -0
  13. package/dist/bitcoin-utils.d.ts +70 -0
  14. package/dist/bitcoin-utils.d.ts.map +1 -0
  15. package/dist/bitcoin-utils.js +388 -0
  16. package/dist/bitcoin-utils.js.map +1 -0
  17. package/dist/helper/burn-request-helper.d.ts +7 -0
  18. package/dist/helper/burn-request-helper.d.ts.map +1 -0
  19. package/dist/helper/burn-request-helper.js +26 -0
  20. package/dist/helper/burn-request-helper.js.map +1 -0
  21. package/dist/helper/teleport-request-helper.d.ts +45 -0
  22. package/dist/helper/teleport-request-helper.d.ts.map +1 -0
  23. package/dist/helper/teleport-request-helper.js +141 -0
  24. package/dist/helper/teleport-request-helper.js.map +1 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +15 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/sign/sign-transaction.d.ts +8 -0
  30. package/dist/sign/sign-transaction.d.ts.map +1 -0
  31. package/dist/sign/sign-transaction.js +41 -0
  32. package/dist/sign/sign-transaction.js.map +1 -0
  33. package/dist/teleport-dao-payments.d.ts +92 -0
  34. package/dist/teleport-dao-payments.d.ts.map +1 -0
  35. package/dist/teleport-dao-payments.js +203 -0
  36. package/dist/teleport-dao-payments.js.map +1 -0
  37. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts +12 -0
  38. package/dist/transaction-builder/bitcoin-transaction-builder.d.ts.map +1 -0
  39. package/dist/transaction-builder/bitcoin-transaction-builder.js +50 -0
  40. package/dist/transaction-builder/bitcoin-transaction-builder.js.map +1 -0
  41. package/dist/transaction-builder/transaction-builder-common.d.ts +80 -0
  42. package/dist/transaction-builder/transaction-builder-common.d.ts.map +1 -0
  43. package/dist/transaction-builder/transaction-builder-common.js +170 -0
  44. package/dist/transaction-builder/transaction-builder-common.js.map +1 -0
  45. package/dist/transaction-builder/transaction-builder.d.ts +19 -0
  46. package/dist/transaction-builder/transaction-builder.d.ts.map +1 -0
  47. package/dist/transaction-builder/transaction-builder.js +130 -0
  48. package/dist/transaction-builder/transaction-builder.js.map +1 -0
  49. package/dist/utils/networks.d.ts +36 -0
  50. package/dist/utils/networks.d.ts.map +1 -0
  51. package/dist/utils/networks.js +30 -0
  52. package/dist/utils/networks.js.map +1 -0
  53. package/dist/utils/tools.d.ts +13 -0
  54. package/dist/utils/tools.d.ts.map +1 -0
  55. package/dist/utils/tools.js +65 -0
  56. package/dist/utils/tools.js.map +1 -0
  57. package/package.json +34 -0
  58. package/src/bitcoin-base.js +174 -0
  59. package/src/bitcoin-interface-utils.js +42 -0
  60. package/src/bitcoin-interface.js +267 -0
  61. package/src/bitcoin-utils.js +443 -0
  62. package/src/helper/burn-request-helper.js +27 -0
  63. package/src/helper/teleport-request-helper.js +162 -0
  64. package/src/index.js +15 -0
  65. package/src/sign/sign-transaction.js +36 -0
  66. package/src/teleport-dao-payments.js +276 -0
  67. package/src/transaction-builder/bitcoin-transaction-builder.js +37 -0
  68. package/src/transaction-builder/transaction-builder-common.js +228 -0
  69. package/src/transaction-builder/transaction-builder.js +135 -0
  70. package/src/utils/networks.js +31 -0
  71. package/src/utils/tools.js +72 -0
  72. package/tsconfig.json +9 -0
@@ -0,0 +1,276 @@
1
+ const BitcoinBase = require("./bitcoin-base")
2
+
3
+ class TeleportDaoPayment extends BitcoinBase {
4
+ // payment
5
+ async payBurnRequest(receivers, feeSpeed = "normal") {
6
+ let extendedUtxo = await this.transactionBuilder.getExtendedUtxo({
7
+ address: this.currentAccount,
8
+ addressType: this.currentAccountType,
9
+ publicKey: this.publicKey.toString("hex"),
10
+ })
11
+
12
+ let feeRate = await this.transactionBuilder._getFeeRate(feeSpeed)
13
+ let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
14
+ extendedUtxo,
15
+ targets: receivers,
16
+ changeAddress: this.currentAccount,
17
+ feeRate,
18
+ fullAmount: false,
19
+ })
20
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
21
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
22
+
23
+ console.log(signedTx, signedPsbt)
24
+ let txId = await this.transactionBuilder.sendTx(signedTx)
25
+ console.log(txId)
26
+ return txId
27
+ }
28
+
29
+ // send
30
+ async transferBitcoinToEth({
31
+ lockerAddress,
32
+ amount,
33
+ //-----------
34
+ chainId,
35
+ appId,
36
+ recipientAddress, // 20 bytes
37
+ percentageFee, // 2 bytes in satoshi
38
+ speed = 0, // 1 byte
39
+ isExchange = false,
40
+ exchangeTokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
41
+ outputAmount = 0, // 28 bytes
42
+ deadline, // 4 bytes
43
+ isFixedToken = false, // 1 byte
44
+ feeSpeed = "normal",
45
+ }) {
46
+ let extendedUtxo = await this.getExtendedUtxo({
47
+ address: this.currentAccount,
48
+ addressType: this.currentAccountType,
49
+ publicKey: this.publicKey.toString("hex"),
50
+ })
51
+ let unsignedTx = await this.getBitcoinToEthUnsignedPsbt({
52
+ changeAddress: this.currentAccount,
53
+ extendedUtxo,
54
+ lockerAddress,
55
+ amount,
56
+ //-----------
57
+ chainId,
58
+ appId,
59
+ recipientAddress,
60
+ percentageFee,
61
+ speed,
62
+ isExchange,
63
+ exchangeTokenAddress,
64
+ outputAmount,
65
+ deadline,
66
+ isFixedToken,
67
+ feeSpeed,
68
+ })
69
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
70
+ let txId = await this.sendSignedPsbt(signedPsbt)
71
+ return txId
72
+ }
73
+
74
+ // get
75
+ async getBitcoinToEthTargetOutputs({
76
+ lockerAddress,
77
+ amount,
78
+ //-----------
79
+ chainId,
80
+ appId,
81
+ recipientAddress, // 20 bytes
82
+ percentageFee, // 2 bytes in satoshi
83
+ speed = 0, // 1 byte
84
+ isExchange = false,
85
+ exchangeTokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
86
+ outputAmount = 0, // 28 bytes
87
+ deadline, // 4 bytes
88
+ isFixedToken = false, // 1 byte
89
+ }) {
90
+ let dataHex = TeleportDaoPayment.getTransferOpReturnData({
91
+ chainId,
92
+ appId,
93
+ recipientAddress,
94
+ percentageFee,
95
+ speed,
96
+ isExchange,
97
+ exchangeTokenAddress,
98
+ outputAmount,
99
+ deadline,
100
+ isFixedToken,
101
+ })
102
+ let opTarget = this.transactionBuilder.getOpReturnTarget(dataHex)
103
+ return [
104
+ {
105
+ address: lockerAddress,
106
+ value: amount,
107
+ },
108
+ opTarget,
109
+ ]
110
+ }
111
+
112
+ async getBitcoinToEthUnsignedPsbt({
113
+ changeAddress,
114
+ extendedUtxo,
115
+ lockerAddress,
116
+ amount,
117
+ //-----------
118
+ chainId,
119
+ appId,
120
+ recipientAddress, // 20 bytes
121
+ percentageFee, // 2 bytes in satoshi
122
+ speed = 0, // 1 byte
123
+ isExchange = false,
124
+ exchangeTokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
125
+ outputAmount = 0, // 28 bytes
126
+ deadline, // 4 bytes
127
+ isFixedToken = false, // 1 byte
128
+ feeSpeed = "normal",
129
+ }) {
130
+ let feeRate = await this.transactionBuilder._getFeeRate(feeSpeed)
131
+ let targets = await this.getBitcoinToEthTargetOutputs({
132
+ lockerAddress,
133
+ amount,
134
+ chainId,
135
+ appId,
136
+ recipientAddress,
137
+ percentageFee,
138
+ speed,
139
+ isExchange,
140
+ exchangeTokenAddress,
141
+ outputAmount,
142
+ deadline,
143
+ isFixedToken,
144
+ })
145
+ let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
146
+ extendedUtxo,
147
+ targets,
148
+ changeAddress,
149
+ feeRate,
150
+ fullAmount: false,
151
+ })
152
+ return unsignedTx
153
+ }
154
+
155
+ // send
156
+ async bitcoinToEthLend({
157
+ lockerAddress,
158
+ amount,
159
+ //-----------
160
+ chainId,
161
+ appId,
162
+ recipientAddress, // 20 bytes
163
+ percentageFee, // 2 bytes in satoshi
164
+ mode = 0, // 1 byte
165
+ isBorrow = false,
166
+ tokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
167
+ borrowAmount = 0, // 28 bytes
168
+ }) {
169
+ let dataHex = TeleportDaoPayment.getLendingOpReturnData({
170
+ chainId,
171
+ appId,
172
+ recipientAddress,
173
+ percentageFee,
174
+ mode,
175
+ isBorrow,
176
+ tokenAddress,
177
+ borrowAmount,
178
+ })
179
+ let opTarget = this.transactionBuilder.getOpReturnTarget(dataHex)
180
+
181
+ let extendedUtxo = await this.transactionBuilder.getExtendedUtxo({
182
+ address: this.currentAccount,
183
+ addressType: this.currentAccountType,
184
+ publicKey: this.publicKey.toString("hex"),
185
+ })
186
+ let unsignedTx = await this.transactionBuilder.processUnsignedTransaction({
187
+ extendedUtxo,
188
+ targets: [
189
+ {
190
+ address: lockerAddress,
191
+ value: amount,
192
+ },
193
+ opTarget,
194
+ ],
195
+ changeAddress: this.currentAccount,
196
+ feeRate: 1,
197
+ fullAmount: false,
198
+ })
199
+ let signedPsbt = await this.signer.signPsbt(unsignedTx, this.privateKey)
200
+ let signedTx = this.signer.finalizePsbts([signedPsbt])
201
+ console.log(signedTx)
202
+ let txId = await this.transactionBuilder.sendTx(signedTx)
203
+ return txId
204
+ }
205
+
206
+ static getTransferOpReturnData({
207
+ chainId,
208
+ appId,
209
+ recipientAddress, // 20 bytes
210
+ percentageFee, // 2 bytes in satoshi
211
+ speed = 0, // 1 byte
212
+ isExchange = false,
213
+ exchangeTokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
214
+ outputAmount = 0, // 28 bytes
215
+ deadline, // 4 bytes
216
+ isFixedToken = false, // 1 byte
217
+ }) {
218
+ let chainIdHex = Number(chainId).toString(16).padStart(2, "0")
219
+ let appIdHex = Number(appId).toString(16).padStart(4, "0")
220
+ let recipientAddressHex = recipientAddress.replace("0x", "").toLowerCase().padStart(40, "0")
221
+ let percentageFeeHex = Number((percentageFee * 100).toFixed(0))
222
+ .toString(16)
223
+ .padStart(4, "0")
224
+ let speedHex = speed ? "01" : "00"
225
+ let dataHex = chainIdHex + appIdHex + recipientAddressHex + percentageFeeHex + speedHex
226
+ if (!isExchange) {
227
+ if (dataHex.length !== 26 * 2) throw new Error("invalid data length")
228
+ return dataHex
229
+ }
230
+
231
+ let exchangeTokenAddressHex = exchangeTokenAddress
232
+ .replace("0x", "")
233
+ .toLowerCase()
234
+ .padStart(40, "0")
235
+ let outputAmountHex = Number(outputAmount).toString(16).padStart(56, "0")
236
+ let deadlineHex = Number(deadline).toString(16).padStart(8, "0")
237
+ let isFixedTokenHex = isFixedToken ? "01" : "00"
238
+
239
+ dataHex = dataHex + exchangeTokenAddressHex + outputAmountHex + deadlineHex + isFixedTokenHex
240
+ if (dataHex.length !== 79 * 2) throw new Error("invalid data length")
241
+ return dataHex
242
+ }
243
+
244
+ static getLendingOpReturnData({
245
+ chainId, // 1 byte
246
+ appId, // 1 byte
247
+ recipientAddress, // 20 byte
248
+ percentageFee, // 2 byte
249
+ mode, // 1 byte
250
+ // ------
251
+ isBorrow = false,
252
+ tokenAddress = "0x0000000000000000000000000000000000000000", // 20 bytes
253
+ borrowAmount = 0, // 28 bytes
254
+ }) {
255
+ let chainIdHex = Number(chainId).toString(16).padStart(2, "0")
256
+ let appIdHex = Number(appId).toString(16).padStart(4, "0")
257
+ let recipientAddressHex = recipientAddress.replace("0x", "").toLowerCase().padStart(40, "0")
258
+ let percentageFeeHex = Number((percentageFee * 100).toFixed(0))
259
+ .toString(16)
260
+ .padStart(4, "0")
261
+ let modeHex = Number(mode).toString(16).padStart(2, "0")
262
+ let dataHex = chainIdHex + appIdHex + recipientAddressHex + percentageFeeHex + modeHex
263
+ if (!isBorrow) {
264
+ if (dataHex.length !== 26 * 2) throw new Error("invalid data length")
265
+ return dataHex
266
+ }
267
+
268
+ let tokenAddressHex = tokenAddress.replace("0x", "").toLowerCase().padStart(40, "0")
269
+ let borrowAmountHex = Number(borrowAmount).toString(16).padStart(56, "0")
270
+ dataHex = dataHex + tokenAddressHex + borrowAmountHex
271
+ if (dataHex.length !== 74 * 2) throw new Error("invalid data length")
272
+ return dataHex
273
+ }
274
+ }
275
+
276
+ module.exports = TeleportDaoPayment
@@ -0,0 +1,37 @@
1
+ const BaseTransactionBuilder = require("./transaction-builder")
2
+ const BitcoinInterface = require("../bitcoin-interface")
3
+
4
+ class BitcoinTransactionBuilder extends BaseTransactionBuilder {
5
+ constructor(connectionInfo, networkName, network) {
6
+ super({
7
+ network,
8
+ testnet: networkName?.includes("_testnet"),
9
+ dustLimit: 1000,
10
+ })
11
+ this.btcInterface = new BitcoinInterface(connectionInfo, networkName)
12
+ }
13
+
14
+ async _getUtxo(userAddress) {
15
+ let utxos = await this.btcInterface.getAddressesUtxo([userAddress])
16
+ return utxos.map((tx) => ({
17
+ hash: tx.txId,
18
+ value: tx.value,
19
+ index: tx.index,
20
+ }))
21
+ }
22
+
23
+ async _getFeeRate(speed) {
24
+ return this.btcInterface.getFeeRate(speed)
25
+ }
26
+
27
+ async _getTransactionHex(transactionId) {
28
+ return this.btcInterface.provider.getRawTransaction(transactionId)
29
+ }
30
+
31
+ async sendTx(txHex) {
32
+ let txId = await this.btcInterface.provider.sendRawTransaction(txHex)
33
+ return txId
34
+ }
35
+ }
36
+
37
+ module.exports = BitcoinTransactionBuilder
@@ -0,0 +1,228 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
3
+ const bitcoin = require("bitcoinjs-lib")
4
+ const coinselect = require("coinselect")
5
+ const coinselectSplit = require("coinselect/split")
6
+ const { createAddressObjectByPublicKey } = require("../bitcoin-utils")
7
+
8
+ const TX_EMPTY_SIZE = 4 + 1 + 1 + 4
9
+ const TX_INPUT_BASE = 32 + 4 + 1 + 4
10
+ const TX_INPUT_P2PKH = 107
11
+ const TX_INPUT_P2SH_P2PKH = 50
12
+ const TX_INPUT_P2WPKH = 47
13
+ const TX_OUTPUT_BASE = 8 + 1
14
+ const TX_OUTPUT_P2PKH = 25
15
+
16
+ const componentBytes = {
17
+ bytePerInput: {
18
+ p2pkh: TX_INPUT_BASE + TX_INPUT_P2PKH,
19
+ p2wpkh: TX_INPUT_BASE + TX_INPUT_P2WPKH,
20
+ p2shp2wpkh: TX_INPUT_BASE + TX_INPUT_P2SH_P2PKH,
21
+ },
22
+ baseTxBytes: TX_EMPTY_SIZE,
23
+ bytePerOutput: TX_OUTPUT_BASE + TX_OUTPUT_P2PKH,
24
+ }
25
+
26
+ class BaseBitcoinLikeTransaction {
27
+ // abstract
28
+ constructor({
29
+ network,
30
+ testnet,
31
+ feeMin = 0,
32
+ dustLimit,
33
+ maximumNumberOfOutputsInTransaction = 50,
34
+ }) {
35
+ this.testnet = testnet
36
+ this.network = network
37
+ this.maximumNumberOfOutputsInTransaction = maximumNumberOfOutputsInTransaction
38
+ this.feeMin = feeMin
39
+ this.dustLimit = dustLimit || 1 * 2 * componentBytes.bytePerInput.p2pkh
40
+ }
41
+
42
+ // eslint-disable-next-line no-unused-vars, class-methods-use-this
43
+ createAddressObject({ addressType, publicKey }) {
44
+ return createAddressObjectByPublicKey({ addressType, publicKey }, this.network)
45
+ }
46
+
47
+ validateAddress(address) {
48
+ try {
49
+ let isValid = false
50
+ let network = this.network
51
+ let isAddressSegwit = address.startsWith(network.bech32)
52
+ if (isAddressSegwit) {
53
+ bitcoin.address.fromBech32(address)
54
+ isValid = true
55
+ } else {
56
+ let base58Data = bitcoin.address.fromBase58Check(address)
57
+ isValid =
58
+ base58Data.version === Number(network.scriptHash) ||
59
+ base58Data.version === Number(network.pubKeyHash)
60
+ }
61
+ return isValid
62
+ } catch (error) {
63
+ return false
64
+ }
65
+ }
66
+
67
+ // eslint-disable-next-line no-unused-vars, class-methods-use-this
68
+ async _getUtxo(userAddress) {
69
+ // The child has implemented this method.
70
+ throw new Error("Do not call abstract method directly")
71
+ // return utxo
72
+ }
73
+
74
+ // eslint-disable-next-line no-unused-vars, class-methods-use-this
75
+ async _getTransactionHex(transactionId) {
76
+ // The child has implemented this method.
77
+ throw new Error("Do not call abstract method directly")
78
+ // return utxo
79
+ }
80
+
81
+ // eslint-disable-next-line no-unused-vars, class-methods-use-this
82
+ async convertBaseInputsToInputs(baseInputs) {
83
+ // The child has implemented this method.
84
+ throw new Error("Do not call abstract method directly")
85
+ // return utxo
86
+ }
87
+
88
+ // eslint-disable-next-line no-unused-vars, class-methods-use-this
89
+ async createUnsignedTransaction(baseInputs) {
90
+ // The child has implemented this method.
91
+ throw new Error("Do not call abstract method directly")
92
+ // return utxo
93
+ }
94
+
95
+ static helperHandleInputsAndOutputs({
96
+ targets,
97
+ extendedUtxo,
98
+ feeRate,
99
+ changeAddress,
100
+ fullAmount,
101
+ }) {
102
+ let { inputs, outputs, fee } = fullAmount
103
+ ? coinselectSplit(extendedUtxo, [{ address: targets[0].address }], Math.round(feeRate))
104
+ : coinselect(extendedUtxo, targets, Math.round(feeRate))
105
+
106
+ if (!inputs || !outputs) {
107
+ throw new Error("not enough balance")
108
+ }
109
+ let changeIndex = outputs.findIndex((x) => !x.address && !x.script && x.value > 0)
110
+ let change
111
+ if (changeIndex >= 0) {
112
+ change = {
113
+ address: changeAddress,
114
+ value: outputs[changeIndex].value,
115
+ }
116
+ outputs.splice(changeIndex, 1)
117
+ }
118
+
119
+ return {
120
+ inputs,
121
+ fee,
122
+ outputs,
123
+ change,
124
+ }
125
+ }
126
+
127
+ /**
128
+ *
129
+ * @param {Object} signerInfo
130
+ * @param {String} signerInfo.address
131
+ * @param {String} signerInfo.addressType
132
+ * @param {String?} signerInfo.publicKey
133
+ * *@param {Array?} signerInfo.publicKeys // todo not used yet- used in multi sig
134
+ * @param {String?} signerInfo.privateKeyId
135
+ * @param {String?} signerInfo.derivationPath
136
+ * @returns
137
+ */
138
+ async getExtendedUtxo(signerInfo) {
139
+ let utxo = await this._getUtxo(signerInfo.address)
140
+ const extendedUtxo = utxo.map((input) => ({
141
+ ...input,
142
+ signerInfo,
143
+ }))
144
+ if (!extendedUtxo || extendedUtxo.length === 0) {
145
+ throw new Error("no utxo found")
146
+ }
147
+ return extendedUtxo
148
+ }
149
+
150
+ /**
151
+ *
152
+ * @param {Object[]} extendedUtxo
153
+ * @param {String} extendedUtxo[].hash
154
+ * @param {Number} extendedUtxo[].index
155
+ * @param {Number} extendedUtxo[].value
156
+ * @param {Object} extendedUtxo[].signerInfo
157
+ * @param {Object} extendedUtxo[].signerInfo.address
158
+ * @param {Object} extendedUtxo[].signerInfo.publicKey
159
+ * @param {String} extendedUtxo[].signerInfo.addressType
160
+ * @param {Number} extendedUtxo[].signerInfo.privateKeyId
161
+ */
162
+ async convertUtxoToInput({ extendedUtxo, targets, changeAddress, fullAmount = false, feeRate }) {
163
+ let {
164
+ inputs: filteredInputs,
165
+ outputs,
166
+ change,
167
+ fee,
168
+ } = BaseBitcoinLikeTransaction.helperHandleInputsAndOutputs({
169
+ targets,
170
+ extendedUtxo,
171
+ fullAmount,
172
+ feeRate,
173
+ changeAddress,
174
+ })
175
+
176
+ let inputs = await this.convertBaseInputsToInputs(filteredInputs)
177
+
178
+ return {
179
+ inputs,
180
+ outputs,
181
+ change,
182
+ fee,
183
+ feeRate,
184
+ }
185
+ }
186
+
187
+ async processUnsignedTransaction({
188
+ extendedUtxo,
189
+ targets = [],
190
+ changeAddress = undefined,
191
+ fullAmount = false,
192
+ feeRate,
193
+ selfTransaction = false,
194
+ }) {
195
+ if (!selfTransaction && targets.length === 0) throw new Error("no target")
196
+
197
+ const { inputs, outputs, change, fee } = await this.convertUtxoToInput({
198
+ extendedUtxo,
199
+ targets,
200
+ changeAddress,
201
+ fullAmount,
202
+ feeRate,
203
+ })
204
+ let unsignedTransaction = await this.createUnsignedTransaction({
205
+ inputs,
206
+ outputs,
207
+ change,
208
+ fee,
209
+ feeRate,
210
+ })
211
+
212
+ return unsignedTransaction
213
+ }
214
+
215
+ getOpReturnTarget(dataHex) {
216
+ if (!dataHex.length > 0) throw new Error("invalid data in hex")
217
+ const embed = bitcoin.payments.embed({
218
+ data: [Buffer.from(dataHex, "hex")],
219
+ network: this.network,
220
+ })
221
+ return {
222
+ script: embed.output,
223
+ value: 0,
224
+ }
225
+ }
226
+ }
227
+
228
+ module.exports = BaseBitcoinLikeTransaction
@@ -0,0 +1,135 @@
1
+ const bitcoin = require("bitcoinjs-lib")
2
+ const BaseBitcoinLikeTransactionBuilderCommon = require("./transaction-builder-common")
3
+
4
+ class BaseBitcoinLikeTransaction extends BaseBitcoinLikeTransactionBuilderCommon {
5
+ async convertBaseInputsToInputs(baseInputs = []) {
6
+ let inputs = baseInputs
7
+ let transactionId = null
8
+ let transactionHex = null
9
+ for (let i in inputs) {
10
+ let { address, publicKey, addressType } = inputs[i].signerInfo
11
+ let addressObject = this.createAddressObject({
12
+ address,
13
+ publicKey: publicKey ? Buffer.from(publicKey, "hex") : null,
14
+ addressType,
15
+ })
16
+ if (addressType === "p2pkh") {
17
+ // add p2pkh data
18
+ if (transactionId === inputs[i].hash) {
19
+ inputs[i].nonWitnessUtxo = Buffer.from(transactionHex, "hex")
20
+ } else {
21
+ transactionHex = await this._getTransactionHex(inputs[i].hash)
22
+ inputs[i].nonWitnessUtxo = Buffer.from(transactionHex, "hex")
23
+ transactionId = inputs[i].hash
24
+ }
25
+ } else if (addressType === "p2wpkh") {
26
+ // add p2wpkh data
27
+ inputs[i].witnessUtxo = {
28
+ script: addressObject.output,
29
+ value: inputs[i].value,
30
+ }
31
+ } else if (addressType === "p2sh-p2wpkh") {
32
+ // add p2sh-p2wpkh data
33
+ inputs[i].witnessUtxo = {
34
+ script: addressObject.output,
35
+ value: inputs[i].value,
36
+ }
37
+ inputs[i].redeemScript = addressObject.redeem.output
38
+ }
39
+ }
40
+
41
+ return inputs
42
+ }
43
+
44
+ createUnsignedTransaction({
45
+ inputs,
46
+ outputs,
47
+ change,
48
+ fee, // not used in this section - just returned
49
+ feeRate,
50
+ }) {
51
+ const { network } = this
52
+ const newPsbt = new bitcoin.Psbt({ network })
53
+ newPsbt.setMaximumFeeRate = feeRate + feeRate / 100
54
+ // add input
55
+ for (const input of inputs) {
56
+ let { addressType } = input.signerInfo
57
+ switch (addressType) {
58
+ case "p2pkh":
59
+ newPsbt.addInput({
60
+ hash: input.hash,
61
+ index: Number(input.index),
62
+ nonWitnessUtxo: input.nonWitnessUtxo,
63
+ sequence: 0xffffffff - 1,
64
+ })
65
+ break
66
+ case "p2wpkh":
67
+ newPsbt.addInput({
68
+ hash: input.hash,
69
+ index: Number(input.index),
70
+ witnessUtxo: input.witnessUtxo,
71
+ sequence: 0xffffffff - 1,
72
+ })
73
+ break
74
+ case "p2sh-p2wpkh":
75
+ newPsbt.addInput({
76
+ hash: input.hash,
77
+ index: Number(input.index),
78
+ witnessUtxo: input.witnessUtxo,
79
+ redeemScript: input.redeemScript,
80
+ sequence: 0xffffffff - 1,
81
+ })
82
+ break
83
+ default:
84
+ throw new Error("address type is incorrect")
85
+ }
86
+ }
87
+
88
+ // add outputs
89
+ for (const target of outputs) {
90
+ newPsbt.addOutput(target)
91
+ }
92
+
93
+ // add changeAddress
94
+ if (change && Object.keys(change).length !== 0) {
95
+ newPsbt.addOutput({
96
+ address: change.address,
97
+ value: Number(change.value),
98
+ })
99
+ }
100
+
101
+ // check created outputs with targets
102
+ for (let i in outputs) {
103
+ if (newPsbt.txOutputs[i].address !== outputs[i].address) {
104
+ throw new Error("error address")
105
+ }
106
+ if (newPsbt.txOutputs[i].value !== outputs[i].value) {
107
+ throw new Error("error value")
108
+ }
109
+ }
110
+ if (change && Object.keys(change).length !== 0) {
111
+ if (newPsbt.txOutputs[outputs.length].address !== change.address) {
112
+ throw new Error("error change address")
113
+ }
114
+ if (newPsbt.txOutputs[outputs.length].value !== change.value) {
115
+ throw new Error("error change value")
116
+ }
117
+ }
118
+
119
+ const unsignedPsbtBaseText = newPsbt.toBase64()
120
+ return {
121
+ unsignedTransaction: unsignedPsbtBaseText,
122
+ outputs,
123
+ inputs: inputs.map((tx) => ({
124
+ hash: tx.hash,
125
+ value: Number(tx.value),
126
+ index: tx.index,
127
+ signerInfo: tx.signerInfo,
128
+ })),
129
+ fee,
130
+ change,
131
+ }
132
+ }
133
+ }
134
+
135
+ module.exports = BaseBitcoinLikeTransaction
@@ -0,0 +1,31 @@
1
+ const bitcoinLib = require("bitcoinjs-lib")
2
+
3
+ const networks = {
4
+ bitcoin: bitcoinLib.networks.bitcoin,
5
+ bitcoin_testnet: bitcoinLib.networks.testnet,
6
+
7
+ litecoin: {
8
+ messagePrefix: "\x19Litecoin Signed Message:\n",
9
+ bech32: "ltc",
10
+ bip32: {
11
+ public: 0x0488b21e,
12
+ private: 0x0488ade4,
13
+ },
14
+ pubKeyHash: 0x30,
15
+ scriptHash: 0x32,
16
+ wif: 0xb0,
17
+ },
18
+ litecoin_testnet: {
19
+ messagePrefix: "\x18Litecoin Signed Message:\n",
20
+ bech32: "tltc",
21
+ bip32: {
22
+ public: 0x043587cf,
23
+ private: 0x04358394,
24
+ },
25
+ pubKeyHash: 0x6f,
26
+ scriptHash: 0x3a, // old 0xc4 -> 2 deprecated
27
+ wif: 0xef,
28
+ },
29
+ }
30
+
31
+ module.exports = networks